Skip to content

Commit abf43a4

Browse files
committed
Fix guided CIF preparation and native task imports; stream scalar metadata
1 parent 4f9a4ca commit abf43a4

4 files changed

Lines changed: 55 additions & 10 deletions

File tree

‎platform/api/tests/test_md_native_engine.py‎

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,23 @@ def config(tmp_path):
3030

3131

3232
@pytest.fixture
33-
def native_double(monkeypatch):
33+
def native_double(monkeypatch, tmp_path):
3434
calls = []
35+
# Metadata now streams through Popen rather than the captured-log helper.
36+
# Keep this explicitly inert command fixture at the existing boundary.
37+
import os
38+
import sys
39+
executable = tmp_path / "fixture-gmx"
40+
executable.write_text(
41+
f"#!{sys.executable}\nimport sys\n"
42+
"if sys.argv[1:3] == ['dump', '-s']:\n"
43+
" print('dt = 0.001\\ntinit = 7\\nnsteps = 7\\nld-seed = 73')\n"
44+
"elif sys.argv[1:3] == ['dump', '-cp']:\n"
45+
" print('step = 7\\nt = 7.007')\n"
46+
"else: sys.exit(2)\n"
47+
)
48+
executable.chmod(0o755)
49+
monkeypatch.setenv("PATH", str(tmp_path) + os.pathsep + os.environ["PATH"])
3550

3651
def run(command, *, cwd, log_path, stdin_text=None):
3752
calls.append(command)
@@ -51,10 +66,6 @@ def run(command, *, cwd, log_path, stdin_text=None):
5166
(prefix.parent / "pullx.xvg").write_text("7.007 0.6\n")
5267
elif command[1] == "editconf":
5368
Path(command[command.index("-o") + 1]).write_text("inert PDB\n")
54-
elif command[1:3] == ["dump", "-s"]:
55-
text = " dt = 0.001\n tinit = 7\n nsteps = 7\n ld-seed = 73\n"
56-
elif command[1:3] == ["dump", "-cp"]:
57-
text = "step = 7\nt = 7.007\n"
5869
else:
5970
pytest.fail(f"unexpected native command: {command}")
6071
log_path.write_text(text)
@@ -80,6 +91,7 @@ def test_ordered_native_roles_and_no_invented_trajectory(tmp_path, native_double
8091
assert "-maxwarn" not in command
8192
assert (tmp_path / "sample/sample.mdp").read_text() == render_mdp("sample", cfg, 0)
8293
assert manifest["final_stage"] == "sample"
94+
assert not list(tmp_path.glob("*/*.dump.txt"))
8395
assert manifest["window_id"] == "w0" and manifest["replicate_index"] == 0
8496
assert manifest["native_endpoints"]["sample"]["step"] == 7
8597
assert manifest["native_endpoints"]["sample"]["time_ps"] == 7.007

‎scripts/bms_md/chemistry/amber_prepare_worker.py‎

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,23 @@ def _preprocess_pdb(source: Path, destination: Path) -> list[str]:
1717
records: list[str] = []
1818
residue_order: list[tuple[str, str, str]] = []
1919
sulfurs: dict[tuple[str, str, str], tuple[float, float, float]] = {}
20-
for raw in source.read_text(encoding="utf-8", errors="strict").splitlines():
20+
source_text = source.read_text(encoding="utf-8", errors="strict")
21+
if any(line.lstrip().startswith("_atom_site.") for line in source_text.splitlines()):
22+
# Managed source bytes remain unchanged in source.pdb and its manifest.
23+
# Normalize CIF only for Amber's PDB reader, using the installed parser.
24+
# Retain every model; choosing an ensemble member is not preparation's job.
25+
import io
26+
from openmm.app import PDBFile, PDBxFile
27+
28+
parsed = PDBxFile(io.StringIO(source_text))
29+
normalized = io.StringIO()
30+
PDBFile.writeHeader(parsed.topology, normalized)
31+
for frame in range(parsed.getNumFrames()):
32+
PDBFile.writeModel(parsed.topology, parsed.getPositions(frame=frame), normalized,
33+
modelIndex=frame + 1, keepIds=True)
34+
PDBFile.writeFooter(parsed.topology, normalized)
35+
source_text = normalized.getvalue()
36+
for raw in source_text.splitlines():
2137
if raw.startswith("TER"):
2238
if records and not records[-1].startswith("TER"):
2339
records.append(raw.ljust(80))

‎scripts/bms_md/contract.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ def _expected_snapshot_metadata(input_config: Mapping[str, Any], field: str) ->
199199

200200

201201
def _verify_open_snapshot(source: Path, expected_digest: str, expected_bytes: int) -> None:
202-
from lib.portable_inputs import resolve_input_path
202+
from scripts.lib.portable_inputs import resolve_input_path
203203
source = resolve_input_path(source)
204204
digest = hashlib.sha256()
205205
consumed = 0
@@ -296,7 +296,7 @@ def _copy_verified_snapshot(
296296
expected_digest: str,
297297
expected_bytes: int,
298298
) -> None:
299-
from lib.portable_inputs import resolve_input_path
299+
from scripts.lib.portable_inputs import resolve_input_path
300300
source = resolve_input_path(source)
301301
destination.parent.mkdir(parents=True, exist_ok=True)
302302
descriptor, temporary_name = tempfile.mkstemp(prefix=".verified.", suffix=".tmp", dir=destination.parent)

‎scripts/bms_md/native_pipeline.py‎

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,26 @@ def run_native_stages(config: Mapping[str, Any], *, config_path: Path, output_di
8282
publication_errors = {}
8383

8484
def observe_dump(flag: str, path: Path, log_path: Path) -> str:
85+
# TPR/CPT dumps contain entire coordinate/state arrays. Consume them
86+
# incrementally and retain only the native scalar metadata used below.
87+
from collections import deque
88+
89+
scalars = []
90+
tail = deque(maxlen=20)
91+
scalar = re.compile(r"^\s*(?:integrator|nsteps|init-step|dt|tinit|ld-seed|pull-ncoords|step|t)\s*=")
8592
try:
86-
return _run_command([gmx_binary, "dump", flag, str(path)], cwd=path.parent, log_path=log_path)
87-
except RuntimeError as exc:
93+
with subprocess.Popen([gmx_binary, "dump", flag, str(path)], cwd=path.parent,
94+
stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True) as process:
95+
assert process.stdout is not None
96+
for line in process.stdout:
97+
tail.append(line)
98+
if scalar.match(line):
99+
scalars.append(line)
100+
returncode = process.wait()
101+
if returncode:
102+
raise RuntimeError(f"GROMACS metadata dump failed with exit code {returncode}: " + "".join(tail))
103+
return "".join(scalars)
104+
except (RuntimeError, OSError) as exc:
88105
publication_errors[str(log_path.relative_to(output_dir))] = str(exc)
89106
return ""
90107
for stage in stages:

0 commit comments

Comments
 (0)