-
Notifications
You must be signed in to change notification settings - Fork 5
/
md.py
126 lines (102 loc) · 4.27 KB
/
md.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
"""Class to run md calculations."""
from aiida.common import datastructures
import aiida.common.folders
from aiida.engine import CalcJobProcessSpec
import aiida.engine.processes
from aiida.orm import Dict, SinglefileData, Str, StructureData, TrajectoryData
from aiida_mlip.calculations.base import BaseJanus
class MD(BaseJanus): # numpydoc ignore=PR01
"""
Calcjob implementation to run geometry MD calculations using mlips.
Methods
-------
define(spec: CalcJobProcessSpec) -> None:
Define the process specification, its inputs, outputs and exit codes.
prepare_for_submission(folder: Folder) -> CalcInfo:
Create the input files for the `CalcJob`.
"""
DEFAULT_TRAJ_FILE = "aiida-traj.xyz"
DEFAULT_STATS_FILE = "aiida-stats.dat"
DEFAULT_SUMMARY_FILE = "md_summary.yml"
@classmethod
def define(cls, spec: CalcJobProcessSpec) -> None:
"""
Define the process specification, its inputs, outputs and exit codes.
Parameters
----------
spec : `aiida.engine.CalcJobProcessSpec`
The calculation job process spec to define.
"""
super().define(spec)
# Additional inputs for molecular dynamics
spec.input(
"ensemble",
valid_type=Str,
required=False,
help="Name for thermodynamic ensemble",
)
spec.input(
"md_kwargs",
valid_type=Dict,
required=False,
default=lambda: Dict(
{
"traj-file": cls.DEFAULT_TRAJ_FILE,
"stats-file": cls.DEFAULT_STATS_FILE,
"summary": cls.DEFAULT_SUMMARY_FILE,
}
),
help="Keywords for molecular dynamics",
)
spec.inputs["metadata"]["options"]["parser_name"].default = "mlip.md_parser"
spec.output(
"results_dict",
valid_type=Dict,
help="The `results_dict` output node of the successful calculation.",
)
spec.output("summary", valid_type=SinglefileData)
spec.output("stats_file", valid_type=SinglefileData)
spec.output("traj_file", valid_type=SinglefileData)
spec.output("traj_output", valid_type=TrajectoryData)
spec.output("final_structure", valid_type=StructureData)
spec.default_output_node = "results_dict"
def prepare_for_submission(
self, folder: aiida.common.folders.Folder
) -> datastructures.CalcInfo:
"""
Create the input files for the `Calcjob`.
Parameters
----------
folder : aiida.common.folders.Folder
Folder where the calculation is run.
Returns
-------
aiida.common.datastructures.CalcInfo
An instance of `aiida.common.datastructures.CalcInfo`.
"""
# Call the parent class method to prepare common inputs
calcinfo = super().prepare_for_submission(folder)
codeinfo = calcinfo.codes_info[0]
md_dictionary = self.inputs.md_kwargs.get_dict()
md_dictionary.setdefault("traj-file", str(self.DEFAULT_TRAJ_FILE))
md_dictionary.setdefault("stats-file", str(self.DEFAULT_STATS_FILE))
md_dictionary.setdefault("summary", str(self.DEFAULT_SUMMARY_FILE))
if "ensemble" in self.inputs:
ensemble = self.inputs.ensemble.value.lower()
elif "config" in self.inputs and "ensemble" in self.inputs.config.as_dictionary:
ensemble = self.inputs.config.as_dictionary["ensemble"]
else:
raise ValueError("'ensemble' not provided.")
# md is overwriting the placeholder "calculation" from the base.py file
codeinfo.cmdline_params[0] = "md"
codeinfo.cmdline_params += ["--ensemble", ensemble]
for flag, value in md_dictionary.items():
# Add boolean flags without value if True
if isinstance(value, bool) and value:
codeinfo.cmdline_params.append(f"--{flag}")
else:
codeinfo.cmdline_params += [f"--{flag}", value]
calcinfo.retrieve_list.append(md_dictionary["traj-file"])
calcinfo.retrieve_list.append(md_dictionary["stats-file"])
calcinfo.retrieve_list.append(md_dictionary["summary"])
return calcinfo