asyncmd 0.3.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,351 @@
1
+ # This file is part of asyncmd.
2
+ #
3
+ # asyncmd is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # asyncmd is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with asyncmd. If not, see <https://www.gnu.org/licenses/>.
15
+ import shlex
16
+ import logging
17
+ from ..mdconfig import LineBasedMDConfig
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+
22
+ class MDP(LineBasedMDConfig):
23
+ """
24
+ Parse, modify and write gromacs .mdp files.
25
+
26
+ Make all options set in a given mdp file available via a dictionary of
27
+ option, list of values pairs. Includes automatic types for known options
28
+ and keeps track if any options have been changed compared to the original
29
+ file.
30
+
31
+ Parameters
32
+ ----------
33
+ original_file : str
34
+ absolute or relative path to original config file to parse
35
+
36
+ Methods
37
+ -------
38
+ write(outfile)
39
+ write the current (modified) configuration state to a given file
40
+ parse()
41
+ read the current original_file and update own state with it
42
+ """
43
+
44
+ _KEY_VALUE_SEPARATOR = " = "
45
+ _INTER_VALUE_CHAR = " "
46
+ # MDP param types, sorted into groups/by headings as in the gromacs manual
47
+ # https://manual.gromacs.org/documentation/current/user-guide/mdp-options.html
48
+ _FLOAT_PARAMS = []
49
+ _FLOAT_SINGLETON_PARAMS = []
50
+ _INT_PARAMS = []
51
+ _INT_SINGLETON_PARAMS = []
52
+ _STR_SINGLETON_PARAMS = []
53
+ # Run control
54
+ _FLOAT_SINGLETON_PARAMS += ["tinit", "dt"]
55
+ _INT_SINGLETON_PARAMS += ["nsteps", "init-step", "simulation-part",
56
+ "nstcomm"]
57
+ _STR_SINGLETON_PARAMS += ["integrator", "comm-mode"]
58
+ # Langevin dynamics
59
+ _FLOAT_SINGLETON_PARAMS += ["bd-fric"]
60
+ _INT_SINGLETON_PARAMS += ["ld-seed"]
61
+ # Energy minimization
62
+ _FLOAT_SINGLETON_PARAMS += ["emtol", "emstep"]
63
+ _INT_SINGLETON_PARAMS += ["nstcgsteep", "nbfgscorr"]
64
+ # Shell Molecular Dynamics
65
+ _FLOAT_SINGLETON_PARAMS += ["fcstep"]
66
+ _INT_SINGLETON_PARAMS += ["niter"]
67
+ # Test particle insertion
68
+ _FLOAT_SINGLETON_PARAMS += ["rtpi"]
69
+ # Output control
70
+ # NOTE: 'nstxtcout' and 'xtc-precision' are deprecated since GMX v5.0
71
+ _FLOAT_SINGLETON_PARAMS += ["compressed-x-precision", "xtc-precision"]
72
+ _INT_SINGLETON_PARAMS += ["nstxout", "nstvout", "nstfout", "nstlog",
73
+ "nstcalcenergy", "nstenergy",
74
+ "nstxout-compressed", "nstxtcout"]
75
+ # Neighbor searching
76
+ # NOTE: 'rlistlong' and 'nstcalclr' are used with group cutoff scheme,
77
+ # i.e. deprecated since GMX v5.0
78
+ _FLOAT_SINGLETON_PARAMS += ["verlet-buffer-tolerance", "rlist",
79
+ "rlistlong"]
80
+ _INT_SINGLETON_PARAMS += ["nstlist", "nstcalclr"]
81
+ _STR_SINGLETON_PARAMS += ["cutoff-scheme", "ns-type", "pbc",
82
+ "periodic-molecules"]
83
+ # Electrostatics
84
+ _FLOAT_SINGLETON_PARAMS += ["rcoulomb-switch", "rcoulomb", "epsilon-r",
85
+ "epsilon-rf"]
86
+ _STR_SINGLETON_PARAMS += ["coulombtype", "coulomb-modifier"]
87
+ # Van der Waals
88
+ _FLOAT_SINGLETON_PARAMS += ["rvdw-switch", "rvdw"]
89
+ _STR_SINGLETON_PARAMS += ["vdwtype", "vdw-modifier", "DispCorr"]
90
+ # Ewald
91
+ _FLOAT_SINGLETON_PARAMS += ["fourierspacing", "ewald-rtol",
92
+ "ewald-rtol-lj"]
93
+ _INT_SINGLETON_PARAMS += ["fourier-nx", "fourier-ny", "fourier-nz",
94
+ "pme-order"]
95
+ _STR_SINGLETON_PARAMS += ["lj-pme-comb-rule", "ewald-geometry"]
96
+ # Temperature coupling
97
+ _FLOAT_PARAMS += ["tau-t", "ref-t"]
98
+ _INT_SINGLETON_PARAMS += ["nsttcouple", "nh-chain-length"]
99
+ _STR_SINGLETON_PARAMS += ["tcoupl", "Tcoupl"] # GMX accepts both versions
100
+ # Pressure coupling
101
+ _FLOAT_SINGLETON_PARAMS += ["tau-p"]
102
+ _FLOAT_PARAMS += ["compressibility", "ref-p"]
103
+ _INT_SINGLETON_PARAMS += ["nstpcouple"]
104
+ _STR_SINGLETON_PARAMS += ["pcoupl", "Pcoupl", # GMX accepts both versions
105
+ "pcoupltype", "refcoord-scaling"]
106
+ # Simulated annealing
107
+ _FLOAT_PARAMS += ["annealing-time", "annealing-temp"]
108
+ _INT_PARAMS += ["annealing-npoints"]
109
+ # Velocity generation
110
+ _FLOAT_SINGLETON_PARAMS += ["gen-temp"]
111
+ _INT_SINGLETON_PARAMS += ["gen-seed"]
112
+ _STR_SINGLETON_PARAMS += ["gen-vel"]
113
+ # Bonds
114
+ _FLOAT_SINGLETON_PARAMS += ["shake-tol", "lincs-warnangle"]
115
+ _INT_SINGLETON_PARAMS += ["lincs-order", "lincs-iter"]
116
+ _STR_SINGLETON_PARAMS += ["constraints", "constraint-algorithm",
117
+ # the next two are referencing the same option
118
+ "continuation", "unconstrained-start",
119
+ "morse"]
120
+ # Walls
121
+ _INT_SINGLETON_PARAMS += ["nwall"]
122
+ _STR_SINGLETON_PARAMS += ["wall-atomtype", "wall-type"]
123
+ _FLOAT_SINGLETON_PARAMS += ["wall-r-linpot", "wall-density",
124
+ "wall-ewald-zfac"]
125
+ # COM Pulling
126
+ _STR_SINGLETON_PARAMS += ["pull", "pull-print-com", "pull-print-ref-value",
127
+ "pull-print-components",
128
+ "pull-pbc-ref-prev-step-com",
129
+ "pull-xout-average", "pull-fout-average"]
130
+ _FLOAT_SINGLETON_PARAMS += ["pull-cylinder-r", "pull-constr-tol"]
131
+ _INT_SINGLETON_PARAMS += ["pull-nstxout", "pull-nstfout", "pull-ngroups",
132
+ "pull-ncoords"]
133
+ # Note: gromacs has a maximum of 256 groups, see e.g.
134
+ # https://manual.gromacs.org/current/reference-manual/algorithms/group-concept.html
135
+ # I (hejung) did not find a maximum for the number of pull coordinates,
136
+ # but we go with 512 here for now (assuming at most two coords per group)
137
+ _STR_SINGLETON_PARAMS += (
138
+ [f"pull-group{n}-name" for n in range(1, 257)]
139
+ + [f"pull-coord{n}-type" for n in range(1, 513)]
140
+ + [f"pull-coord{n}-potential-provider" for n in range(1, 513)]
141
+ + [f"pull-coord{n}-geometry" for n in range(1, 513)]
142
+ + [f" pull-coord{n}-start" for n in range(1, 513)]
143
+ )
144
+ _FLOAT_SINGLETON_PARAMS += (
145
+ [f"pull-coord{n}-init" for n in range(1, 513)]
146
+ + [f"pull-coord{n}-rate" for n in range(1, 513)]
147
+ + [f"pull-coord{n}-k" for n in range(1, 513)]
148
+ + [f"pull-coord{n}-kB" for n in range(1, 513)]
149
+ )
150
+ _FLOAT_PARAMS += (
151
+ [f"pull-group{n}-weights" for n in range(1, 257)]
152
+ + [f"pull-coord{n}-origin" for n in range(1, 513)]
153
+ + [f"pull-coord{n}-vec" for n in range(1, 513)]
154
+ )
155
+ _INT_SINGLETON_PARAMS += [f"pull-group{n}-pbcatom" for n in range(1, 257)]
156
+ _INT_PARAMS += [f"pull-coord{n}-groups" for n in range(1, 513)]
157
+ # AWH adaptive biasing
158
+ # Note we assume a maximum number of 20 awh coordinates, each consisting of
159
+ # a maximum of 4 (pull coordinate) dimensions
160
+ _STR_SINGLETON_PARAMS += (
161
+ ["awh", "awh-potential", "awh-share-multisim"]
162
+ + [f"awh{n}-growth" for n in range(1, 21)]
163
+ + [f"awh{n}-equilibrate-histogram" for n in range(1, 21)]
164
+ + [f"awh{n}-target" for n in range(1, 21)]
165
+ + [f"awh{n}-user-data" for n in range(1, 21)]
166
+ + [f"awh{n}-dim{d}-coord-provider"
167
+ for n in range(1, 21) for d in range(1, 5)]
168
+ )
169
+ _INT_SINGLETON_PARAMS += (
170
+ ["awh-seed", "awh-nstout", "awh-nstsample", "awh-nsamples-update",
171
+ "awh-nbias"]
172
+ + [f"awh{n}-share-group" for n in range(1, 21)]
173
+ + [f"awh{n}-ndim" for n in range(1, 21)]
174
+ + [f"awh{n}-dim{d}-coord-index"
175
+ for n in range(1, 21) for d in range(1, 5)]
176
+ )
177
+ _FLOAT_SINGLETON_PARAMS += (
178
+ [f"awh{n}-error-init" for n in range(1, 21)]
179
+ + [f"awh{n}-target-beta-scaling" for n in range(1, 21)]
180
+ + [f"awh{n}-target-cutoff" for n in range(1, 21)]
181
+ + [f"awh{n}-dim{d}-force-constant"
182
+ for n in range(1, 21) for d in range(1, 5)]
183
+ + [f"awh{n}-dim{d}-start"
184
+ for n in range(1, 21) for d in range(1, 5)]
185
+ + [f"awh{n}-dim{d}-end"
186
+ for n in range(1, 21) for d in range(1, 5)]
187
+ + [f"awh{n}-dim{d}-period"
188
+ for n in range(1, 21) for d in range(1, 5)]
189
+ + [f"awh{n}-dim{d}-diffusion"
190
+ for n in range(1, 21) for d in range(1, 5)]
191
+ + [f"awh{n}-dim{d}-cover-diameter"
192
+ for n in range(1, 21) for d in range(1, 5)]
193
+ )
194
+ # Enforced rotation
195
+ # Note: rotation groups are zero indexed, we assume a maximum of 30
196
+ _STR_SINGLETON_PARAMS += (["rotation"]
197
+ + [f"rot-group{n}" for n in range(30)]
198
+ + [f"rot-type{n}" for n in range(30)]
199
+ + [f"rot-massw{n}" for n in range(30)]
200
+ + [f"rot-fit-method{n}" for n in range(30)]
201
+ )
202
+ _INT_SINGLETON_PARAMS += ["rot-ngroups", "rot-nstrout", "rot-nstsout"]
203
+ _FLOAT_SINGLETON_PARAMS += ([f"rot-rate{n}" for n in range(30)]
204
+ + [f"rot-k{n}" for n in range(30)]
205
+ + [f"rot-slab-dist{n}" for n in range(30)]
206
+ + [f"rot-min-gauss{n}" for n in range(30)]
207
+ + [f"rot-eps{n}" for n in range(30)]
208
+ + [f"rot-potfit-step{n}" for n in range(30)]
209
+ )
210
+ _FLOAT_PARAMS += ([f"rot-vec{n}" for n in range(30)]
211
+ + [f"rot-pivot{n}" for n in range(30)]
212
+ )
213
+ # NMR refinement
214
+ _STR_SINGLETON_PARAMS += ["disre", "disre-weighting", "disre-mixed",
215
+ "orire", "orire-fitgrp"]
216
+ _FLOAT_SINGLETON_PARAMS += ["disre-fc", "disre-tau", "orire-fc",
217
+ "orire-tau"]
218
+ _INT_SINGLETON_PARAMS += ["nstdisreout", "nstorireout"]
219
+ # Free energy calculations
220
+ _STR_SINGLETON_PARAMS += ["free-energy", "expanded", "sc-coul",
221
+ "couple-moltype", "couple-lambda0",
222
+ "couple-lambda1", "couple-intramol",
223
+ "dhdl-derivatives", "dhdl-print-energy",
224
+ "separate-dhdl-file"]
225
+ _FLOAT_SINGLETON_PARAMS += ["init-lambda", "delta-lambda", "sc-alpha",
226
+ "sc-sigma", "dh-hist-spacing"]
227
+ _INT_SINGLETON_PARAMS += ["init-lambda-state", "calc-lambda-neighbors",
228
+ "sc-r-power", "sc-power", "nstdhdl",
229
+ "dh-hist-size"]
230
+ _FLOAT_PARAMS += ["fep-lambdas", "coul-lambdas", "vdw-lambdas",
231
+ "bonded-lambdas", "restraint-lambdas", "mass-lambdas",
232
+ "temperature-lambdas"]
233
+ # Expanded Ensemble calculations
234
+ _INT_SINGLETON_PARAMS += ["nstexpanded", "lmc-seed", "lmc-repeats",
235
+ "lmc-gibbsdelta", "lmc-forced-nstart",
236
+ "nst-transition-matrix", "mininum-var-min"]
237
+ _STR_SINGLETON_PARAMS += ["lmc-stats", "lmc-mc-move", "wl-oneovert",
238
+ "symmetrized-transition-matrix",
239
+ "lmc-weights-equil", "simulated-tempering",
240
+ "simulated-tempering-scaling"]
241
+ _FLOAT_SINGLETON_PARAMS += ["mc-temperature", "wl-ratio", "wl-scale",
242
+ "init-wl-delta", "sim-temp-low",
243
+ "sim-temp-high"]
244
+ _FLOAT_PARAMS += ["init-lambda-weights"]
245
+ # Non-equilibrium MD
246
+ _FLOAT_SINGLETON_PARAMS += ["accelerate", "cos-acceleration"]
247
+ _FLOAT_PARAMS += ["deform"]
248
+ # Electric fields
249
+ _FLOAT_PARAMS += ["electric-field-x", "electric-field-y",
250
+ "electric-field-z"]
251
+ # Mixed quantum/classical molecular dynamics
252
+ _STR_SINGLETON_PARAMS += ["QMMM", "QMMMscheme", "QMmethod", "QMbasis",
253
+ "SH"]
254
+ _INT_SINGLETON_PARAMS += ["QMcharge", "QMmult", "CASorbitals",
255
+ "CASelectrons"]
256
+ # Implicit solvent
257
+ _STR_SINGLETON_PARAMS += ["implicit-solvent", "gb-algorithm",
258
+ "sa-algorithm"]
259
+ _INT_SINGLETON_PARAMS += ["nstgbradii"]
260
+ _FLOAT_SINGLETON_PARAMS += ["rgbradii", "gb-epsilon-solvent",
261
+ "gb-saltconc", "gb-obc-alpha", "gb-obc-beta",
262
+ "gb-obc-gamma", "gb-dielectric-offset",
263
+ "sa-surface-tension"]
264
+ # Computational Electrophysiology
265
+ # Note: we assume a maximum of 10 controlled ion types
266
+ _STR_SINGLETON_PARAMS += (["swapcoords", "split-group0", "split-group1",
267
+ "massw-split0", "massw-split1", "solvent-group"]
268
+ + [f"iontype{n}-name" for n in range(10)]
269
+ )
270
+ _INT_SINGLETON_PARAMS += (["swap-frequency", "coupl-steps", "iontypes",
271
+ "threshold"]
272
+ + [f"iontype{n}-in-A" for n in range(10)]
273
+ + [f"iontype{n}-in-B" for n in range(10)]
274
+ )
275
+ _FLOAT_SINGLETON_PARAMS += ["bulk-offsetA", "bulk-offsetB", "cyl0-r",
276
+ "cyl0-up", "cyl0-down", "cyl1-r", "cyl1-up",
277
+ "cyl1-down"]
278
+ # User defined thingies
279
+ _INT_SINGLETON_PARAMS += [f"userint{n}" for n in range(1, 5)]
280
+ _FLOAT_SINGLETON_PARAMS += [f"userreal{n}" for n in range(1, 5)]
281
+
282
+ def _parse_line(self, line):
283
+ # NOTE: we need to do this so complicated, because gmx accepts
284
+ # "key=value" i.e. without spaces, so we can not use shlex.shlex
285
+ # for separating the key and value reliably although this will be
286
+ # a corner case as most mdp files have "key = value" with spaces
287
+ # split only at first equal sign
288
+ splits_at_equal = line.split("=", maxsplit=1)
289
+ # split at first comment sign
290
+ splits_at_comment = line.split(";", maxsplit=1)
291
+ # now we have multiple options:
292
+ # 1. split only at '=' and not at ';' -> key=value line without comment
293
+ # 2. split only at ';' and not at '=' -> (probably) a comment line,
294
+ # at least if the comment is the
295
+ # first char, otherwise we need
296
+ # an equal sign to be valid (?)
297
+ # 3. split at ';' and at '=' -> need to find out if the comment is
298
+ # before or after the equal sign
299
+ # 4. no splits at '=' and no splits at ';' -> weired line, probably
300
+ # not a valid line(?)
301
+ if splits_at_comment[0] == "":
302
+ # option 2 (and 3 if the comment is before the equal sign)
303
+ # comment sign is the first letter, so the whole line is
304
+ # (most probably) a comment line
305
+ logger.debug(f"mdp line parsed as comment: {line}")
306
+ return {}
307
+ if ((len(splits_at_equal) == 2 and len(splits_at_comment) == 1) # option 1
308
+ # or option 3 with equal sign before comment sign
309
+ or ((len(splits_at_equal) == 2 and len(splits_at_comment) == 2)
310
+ and (len(splits_at_comment[0]) > len(splits_at_equal[0])))):
311
+ key = splits_at_equal[0].strip() # strip of the white space
312
+ # make sure the key is a single word, i.e. contains no spaces
313
+ # if it is not we will raise the error below
314
+ if key.split()[0] == key:
315
+ value_unparsed = splits_at_equal[1]
316
+ parser = shlex.shlex(value_unparsed,
317
+ posix=True, punctuation_chars=True)
318
+ parser.commenters = ";"
319
+ # puncutation_chars=True adds "~-./*?=" to wordchars
320
+ # such that we do not split floats and file paths and similar
321
+ tokens = list(parser)
322
+ # gromacs mdp can have 0-N tokens/values to the RHS of the '='
323
+ if len(tokens) == 0:
324
+ # line with empty options, e.g. 'define = '
325
+ return {self._key_char_replace(key): []}
326
+ # lines with content, we always return a list (and let our
327
+ # type_dispatch sort out the singleton options and the typing)
328
+ return {self._key_char_replace(key): tokens}
329
+ # if we end up here we did not know how to parse properly, e.g.
330
+ # option 4 and option 3 with comment before equal but not at the
331
+ # first position of the line (i.e. not a full comment line)
332
+ # so no idea what happend here: best to let the user have a look :)
333
+ raise ValueError(f"Could not parse the following mdp line: {line}")
334
+
335
+ def _key_char_replace(self, key):
336
+ # make it possible to use CHARMM-GUI generated mdp-files, because
337
+ # CHARMM-GUI uses "_" instead of "-" in the option names,
338
+ # which seems to be an undocumented gromacs feature,
339
+ # i.e. gromacs reads these mdp-files without complaints :)
340
+ # we will however stick with "-" all the time to make sure every option
341
+ # exists only once, i.e. we convert all keys to use "-" instead of "_"
342
+ return key.replace("_", "-")
343
+
344
+ def __getitem__(self, key):
345
+ return super().__getitem__(self._key_char_replace(key))
346
+
347
+ def __setitem__(self, key, value):
348
+ return super().__setitem__(self._key_char_replace(key), value)
349
+
350
+ def __delitem__(self, key):
351
+ return super().__delitem__(self._key_char_replace(key))