-
Notifications
You must be signed in to change notification settings - Fork 15
Support for PLUMED output #94
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -322,7 +322,7 @@ def setup_output( | |
| trajectory = ET.Element( | ||
| "trajectory", | ||
| filename="trajectory", | ||
| stride=str(step), | ||
| stride=str(step), # TODO: separate stride for trajectory and properties? | ||
| format="ase", | ||
| bead="0", | ||
| ) | ||
|
|
@@ -335,6 +335,22 @@ def setup_output( | |
| ) | ||
| properties.text = create_xml_list(observables) | ||
| output.append(properties) | ||
| extras_list = [] | ||
| for comp in components: | ||
| if comp.name.startswith("Plumed"): # technically other hamiltonians could also have extras | ||
| extras_list += comp.hamiltonian.plumed_extras # maybe extras should be a general property of the Hamiltonian class? | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree. That would also enable us to extract more information from MACEHamiltonian in the future. |
||
| observables += [extra + "{au}" for extra in extras_list] # for SimulationOutput TODO: what to do with units? | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If there is no real way to query PLUMED for units, then this is probably the best we can do. |
||
| if extras_list: | ||
| extras = ",".join(list(set(extras_list))) | ||
| extras_element = ET.Element( | ||
| "trajectory", | ||
| filename=f"extras", | ||
| stride=str(step), | ||
| extra_type=extras, | ||
| bead="0", | ||
| ) | ||
| extras_element.text = f" extras " | ||
| output.append(extras_element) | ||
| return output, observables | ||
|
|
||
|
|
||
|
|
@@ -512,7 +528,7 @@ def _sample( | |
| if step is not None: | ||
| start = math.floor(start / step) # start is applied on subsampled quantities | ||
| if step is None: | ||
| keep_trajectory = False | ||
| keep_trajectory = False # TODO: warning here? | ||
| # TODO: check whether observables are valid? | ||
| output, observables = setup_output( | ||
| hamiltonian_components, # for potential components | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,6 +13,7 @@ | |
| from ase.units import Bohr, Ha, _e, _hbar | ||
| from ipi.engine.simulation import Simulation | ||
| from ipi.utils.softexit import softexit | ||
| from ipi.utils.parsing import read_output | ||
|
|
||
| from psiflow.geometry import Geometry | ||
| from psiflow.sampling.utils import create_xml_list | ||
|
|
@@ -93,11 +94,33 @@ def wait_for_clients(input_xml, timeout: int = 60) -> None: | |
| raise ConnectionError(msg) | ||
|
|
||
|
|
||
| def add_extras(noutputs: int) -> None: | ||
| # property headers | ||
| file_props = next(Path.cwd().glob("*0*.properties")) # properties should be the same for all coupled walkers? | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For the moment, I think so. Cannot immediately find a use case where we would not want this. |
||
| _, info_dict = read_output(file_props) | ||
| props_headers = ["# column {} --> {}{} : {}".format(i + 1, key, "{" + info_dict[key][0] + "}", info_dict[key][1]) for i, key in enumerate(info_dict)] | ||
| # extras headers | ||
| file_extras = next(Path.cwd().glob("*0*.extras*")) # extras should be the same for all coupled walkers? | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This might be a bit complex. I imagine a single PLUMED bias for one walker, and no bias for the other in REX. Throughout the simulation, the bias will sometimes be attached to walker 1, other times to walker 2. How would that work with the extras output? Empty entries? |
||
| with open(file_extras, "r") as f: | ||
| line = f.readline() | ||
| start = line.find("(") + 1 | ||
| end = line.find(")") | ||
| extra_names = line[start:end].split(",") | ||
| extras_headers = ["# column {} --> {}{} : {}".format(i + 1 + len(props_headers), name, "{au}", "PLUMED variable") for i, name in enumerate(extra_names)] | ||
| # add extras values to properties files | ||
| for idx in range(noutputs): | ||
| file_props = next(Path.cwd().glob(f"*{idx}*.properties")) | ||
| file_extras = next(Path.cwd().glob(f"*{idx}*.extras*")) | ||
| np.savetxt(file_props, np.hstack((np.loadtxt(file_props), np.loadtxt(file_extras))), fmt='%10.8e', delimiter=' ', header="\n".join(props_headers) + "\n" + "\n".join(extras_headers), comments="") | ||
| # granted i-Pi properties file has some weird indentations so the files do not perfectly match | ||
| # but output parser still works so who cares | ||
|
|
||
|
|
||
| def run(start_xyz: str, input_xml: str): | ||
| # prepare starting geometries from context_dir | ||
| data_start: list[ase.Atoms] = read(start_xyz, index=":") | ||
| for i, at in enumerate(data_start): | ||
| print(at.pbc) | ||
| print(at.pbc) # TODO: why print? | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Think that was for debugging. Feel free to remove. |
||
| if not any(at.pbc): # set fake large cell for i-PI | ||
| at.pbc = True | ||
| at.cell = Cell(NONPERIODIC_CELL) | ||
|
|
@@ -134,6 +157,14 @@ def cleanup(output_xyz: str, output_props: str, output_trajs: str) -> None: | |
| _write_frames(*states, outputs=[output_xyz]) | ||
| print("Moved checkpoint geometries") | ||
|
|
||
| output_props = _.split(",") if (_ := output_props) else [] | ||
| output_trajs = _.split(",") if (_ := output_trajs) else [] | ||
|
|
||
| # Add extras to simulation properties, if they exist | ||
| if len(output_props) and any(Path.cwd().glob("*.extras*")): | ||
| add_extras(len(output_props)) | ||
| print("Added i-Pi extras output to properties files") | ||
|
|
||
| prefix = "" | ||
| if "remd" in content: | ||
| # unshuffle simulation output according to ensemble | ||
|
|
@@ -144,9 +175,6 @@ def cleanup(output_xyz: str, output_props: str, output_trajs: str) -> None: | |
| assert out.returncode == 0 # TODO: what if it isn't? | ||
| print("REMDSORT") | ||
|
|
||
| output_props = _.split(",") if (_ := output_props) else [] | ||
| output_trajs = _.split(",") if (_ := output_trajs) else [] | ||
|
|
||
| # move recorded simulation observables | ||
| if len(output_props): | ||
| assert len(states) == len(output_props) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes.