Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 23 additions & 4 deletions openff/packmol/_packmol.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import shutil
import subprocess
import tempfile
import uuid
from collections.abc import Callable
from copy import deepcopy
from typing import Literal
Expand Down Expand Up @@ -67,6 +68,10 @@
"""


def get_uuid():
return uuid.uuid4().hex


def _find_packmol() -> str | None:
"""
Attempt to find the path to the `packmol` binary.
Expand Down Expand Up @@ -407,7 +412,7 @@ def _create_solute_pdb(
),
)
# Write to pdb
solute_pdb_filename = "_PACKING_SOLUTE.pdb"
solute_pdb_filename = f"_PACKING_SOLUTE_{get_uuid()}.pdb"
topology.to_file(
solute_pdb_filename,
file_format="PDB",
Expand Down Expand Up @@ -510,8 +515,9 @@ def _build_input_file(
box_size = box_size.m_as("angstrom") if PACKMOL_USE_PBC else (box_size - tolerance).m_as("angstrom")
tolerance = tolerance.m_as("angstrom")

this_id = get_uuid()
# Add the global header options.
output_file_path = "packmol_output.pdb"
output_file_path = f"OUT_{this_id}.pdb"
input_lines = [
f"tolerance {tolerance:f}",
"filetype pdb",
Expand Down Expand Up @@ -549,10 +555,11 @@ def _build_input_file(
],
)

print(f"{input_lines=}")
packmol_input = "\n".join(input_lines)

# Write packmol input
packmol_file_name = "packmol_input.txt"
packmol_file_name = f"packmol_input_{this_id}.txt"

with open(packmol_file_name, "w") as file_handle:
file_handle.write(packmol_input)
Expand Down Expand Up @@ -748,6 +755,12 @@ def pack_box(
os.makedirs(working_directory, exist_ok=True)

with temporary_cd(working_directory):
path = pathlib.Path(".")

# List only items that are files
files = [f for f in path.iterdir() if f.is_file()]
print(f"Working directory: {path.resolve()}")
print(f"{files=}")
solute_pdb_filename = _create_solute_pdb(
solute,
box_vectors,
Expand Down Expand Up @@ -781,7 +794,9 @@ def pack_box(
raise PACKMOLRuntimeError(
f"PACKMOL failed with error code {error.returncode}. Wrote file packmol_error.log in working "
"directory, which might be a temporary directory. Set the argument `working_directory` to "
"point this to a persistent path.",
"point this to a persistent path. "
+ f"\nPackmol input was:\n{open(input_file_path).read()}\n"
+ f"\nPackmol output was:\n{error.stdout.decode('utf-8')}",
) from error

packmol_succeeded = result.decode("utf-8").find("Success!") > 0
Expand All @@ -792,6 +807,10 @@ def pack_box(
"Please raise an issue showing how you arrived at this error.",
)

assert pathlib.Path(output_file_path).exists(), (
f"PACKMOL reported success but output file ({output_file_path}) not found. "
f"Files in this directory are: {list(pathlib.Path('.').iterdir())}. "
)
positions = _load_positions(output_file_path)

# TODO: This currently does not run if we encountered an error in the
Expand Down
34 changes: 34 additions & 0 deletions openff/packmol/_tests/test_issues.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import threading

import pytest
from openff.toolkit import Molecule, Quantity

from openff.packmol import pack_box


@pytest.fixture
def water():
return Molecule.from_smiles("O")


@pytest.mark.filterwarnings("error::pytest.PytestUnhandledThreadExceptionWarning")
def test_thread_safety(water):
"""See https://github.com/openforcefield/openff-packmol/issues/10."""
threads = []
for i in range(3):
t = threading.Thread(
target=pack_box,
args=(
[water],
[100 + i],
None,
Quantity(0.2, "nanometer"),
None,
Quantity(0.5, "g/mL"),
),
)
threads.append(t)
t.start()

for t in threads:
t.join()
Loading
Loading