Skip to content
Open
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
22 changes: 14 additions & 8 deletions src/scm/plams/interfaces/adfsuite/ams.py
Original file line number Diff line number Diff line change
Expand Up @@ -3281,7 +3281,13 @@ def unspec(value: T) -> Union[T, str]:
return special[spec_type](value)
return value

def serialize(key: str, value: Any, indent: int, end: str = "End") -> str:
def serialize(
key: str,
value: Any,
indent: int,
end: str = "End",
parent_key: Optional[str] = None,
) -> str:
"""Given a *key* and its corresponding *value* from the |Settings| instance produce a snippet of the input file representing this pair.

If the value is a nested |Settings| instance, use recursive calls to build the snippet for the entire block. Indent the result with *indent* spaces.
Expand All @@ -3300,9 +3306,9 @@ def serialize(key: str, value: Any, indent: int, end: str = "End") -> str:
while f"_{i}" in value:
if isinstance(value[f"_{i}"], Settings):
for ckey in value[f"_{i}"]:
ret += serialize(ckey, value[f"_{i}"][ckey], indent + 2)
ret += serialize(ckey, value[f"_{i}"][ckey], indent + 2, parent_key=key)
else:
ret += serialize("", value["_" + str(i)], indent + 2)
ret += serialize("", value["_" + str(i)], indent + 2, parent_key=key)
i += 1

# Figure out the order in which we should serialize the entries in the block
Expand All @@ -3329,22 +3335,22 @@ def serialize(key: str, value: Any, indent: int, end: str = "End") -> str:
split_key = key.lower().split()
split_ckey = ckey.lower().split()
if len(split_key) > 0 and split_key[0] == "engine" and ckey.lower() == "input":
ret += serialize(ckey, cvalue, indent + 2, "EndInput")
ret += serialize(ckey, cvalue, indent + 2, "EndInput", parent_key=key)
# REB: For the hybrid engine. How to deal with the space in ckey (Engine DFTB)? Replace by underscore?
elif len(split_ckey) > 0 and split_ckey[0] == "engine":
engine = " ".join(ckey.split("_"))
ret += serialize(engine, cvalue, indent + 2, end="EndEngine") + "\n"
ret += serialize(engine, cvalue, indent + 2, end="EndEngine", parent_key=key) + "\n"
else:
ret += serialize(ckey, cvalue, indent + 2)
ret += serialize(ckey, cvalue, indent + 2, parent_key=key)

# Close block
if key.lower() == "input":
if key.lower() == "input" and (parent_key or "").lower() != "plumed":
end = "endinput"
ret += " " * indent + end + "\n"

elif isinstance(value, list):
for el in value:
ret += serialize(key, el, indent, end)
ret += serialize(key, el, indent, end, parent_key=parent_key)
elif value == "" or value is True:
ret += " " * indent + key + "\n"
elif value is False or value is None:
Expand Down
19 changes: 19 additions & 0 deletions unit_tests/test_amsjob.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,25 @@
from test_helpers import skip_if_no_scm_pisa, skip_if_no_scm_base


class TestAMSJobInputSerialization:
def test_plumed_input_uses_normal_end(self):
settings = Settings()
settings.input.ams.MolecularDynamics.Plumed.Input._1 = "PRINT ARG=x FILE=COLVAR"

generated = AMSJob(settings=settings).get_input()

assert re.search(r"Plumed\n\s+Input\n\s+PRINT ARG=x FILE=COLVAR\n\s+End\n\s+End", generated)
assert "endinput" not in generated.lower()

def test_engine_input_still_uses_endinput(self):
settings = Settings()
settings.input.External.Input._1 = "some external-engine input"

generated = AMSJob(settings=settings).get_input()

assert re.search(r"Engine External\n\s+Input\n\s+some external-engine input\n\s+endinput", generated)


class TestAMSJob:
"""
Test suite for AMSJob without using PISA / CS for input.
Expand Down
Loading