nomad-parser-plugins-atomistic 1.0__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.
- atomisticparsers/__init__.py +400 -0
- atomisticparsers/amber/__init__.py +19 -0
- atomisticparsers/amber/__main__.py +31 -0
- atomisticparsers/amber/metainfo/__init__.py +19 -0
- atomisticparsers/amber/metainfo/amber.py +495 -0
- atomisticparsers/amber/parser.py +42 -0
- atomisticparsers/asap/__init__.py +19 -0
- atomisticparsers/asap/__main__.py +31 -0
- atomisticparsers/asap/metainfo/__init__.py +19 -0
- atomisticparsers/asap/metainfo/asap.py +75 -0
- atomisticparsers/asap/parser.py +197 -0
- atomisticparsers/bopfox/__init__.py +19 -0
- atomisticparsers/bopfox/__main__.py +31 -0
- atomisticparsers/bopfox/metainfo/__init__.py +19 -0
- atomisticparsers/bopfox/metainfo/bopfox.py +225 -0
- atomisticparsers/bopfox/parser.py +808 -0
- atomisticparsers/dftbplus/__init__.py +19 -0
- atomisticparsers/dftbplus/__main__.py +31 -0
- atomisticparsers/dftbplus/metainfo/__init__.py +19 -0
- atomisticparsers/dftbplus/metainfo/dftbplus.py +217 -0
- atomisticparsers/dftbplus/parser.py +500 -0
- atomisticparsers/dlpoly/__init__.py +19 -0
- atomisticparsers/dlpoly/__main__.py +31 -0
- atomisticparsers/dlpoly/metainfo/__init__.py +19 -0
- atomisticparsers/dlpoly/metainfo/dl_poly.py +312 -0
- atomisticparsers/dlpoly/parser.py +798 -0
- atomisticparsers/gromacs/__init__.py +19 -0
- atomisticparsers/gromacs/__main__.py +31 -0
- atomisticparsers/gromacs/metainfo/__init__.py +19 -0
- atomisticparsers/gromacs/metainfo/gromacs.py +2388 -0
- atomisticparsers/gromacs/parser.py +1581 -0
- atomisticparsers/gromos/__init__.py +19 -0
- atomisticparsers/gromos/__main__.py +31 -0
- atomisticparsers/gromos/metainfo/__init__.py +19 -0
- atomisticparsers/gromos/metainfo/gromos.py +1995 -0
- atomisticparsers/gromos/parser.py +58 -0
- atomisticparsers/gulp/__init__.py +19 -0
- atomisticparsers/gulp/__main__.py +31 -0
- atomisticparsers/gulp/metainfo/__init__.py +19 -0
- atomisticparsers/gulp/metainfo/gulp.py +1117 -0
- atomisticparsers/gulp/parser.py +1316 -0
- atomisticparsers/h5md/__init__.py +19 -0
- atomisticparsers/h5md/__main__.py +31 -0
- atomisticparsers/h5md/metainfo/__init__.py +19 -0
- atomisticparsers/h5md/metainfo/h5md.py +239 -0
- atomisticparsers/h5md/parser.py +901 -0
- atomisticparsers/lammps/__init__.py +19 -0
- atomisticparsers/lammps/__main__.py +31 -0
- atomisticparsers/lammps/metainfo/__init__.py +19 -0
- atomisticparsers/lammps/metainfo/lammps.py +1417 -0
- atomisticparsers/lammps/parser.py +1753 -0
- atomisticparsers/libatoms/__init__.py +19 -0
- atomisticparsers/libatoms/__main__.py +31 -0
- atomisticparsers/libatoms/metainfo/__init__.py +19 -0
- atomisticparsers/libatoms/metainfo/lib_atoms.py +251 -0
- atomisticparsers/libatoms/parser.py +38 -0
- atomisticparsers/namd/__init__.py +19 -0
- atomisticparsers/namd/__main__.py +31 -0
- atomisticparsers/namd/metainfo/__init__.py +19 -0
- atomisticparsers/namd/metainfo/namd.py +1605 -0
- atomisticparsers/namd/parser.py +312 -0
- atomisticparsers/tinker/__init__.py +19 -0
- atomisticparsers/tinker/__main__.py +31 -0
- atomisticparsers/tinker/metainfo/__init__.py +18 -0
- atomisticparsers/tinker/metainfo/tinker.py +1363 -0
- atomisticparsers/tinker/parser.py +685 -0
- atomisticparsers/utils/__init__.py +22 -0
- atomisticparsers/utils/mdanalysis.py +662 -0
- atomisticparsers/utils/parsers.py +226 -0
- atomisticparsers/xtb/__init__.py +19 -0
- atomisticparsers/xtb/__main__.py +32 -0
- atomisticparsers/xtb/metainfo/__init__.py +19 -0
- atomisticparsers/xtb/metainfo/xtb.py +256 -0
- atomisticparsers/xtb/parser.py +979 -0
- nomad_parser_plugins_atomistic-1.0.dist-info/LICENSE +202 -0
- nomad_parser_plugins_atomistic-1.0.dist-info/METADATA +327 -0
- nomad_parser_plugins_atomistic-1.0.dist-info/RECORD +80 -0
- nomad_parser_plugins_atomistic-1.0.dist-info/WHEEL +5 -0
- nomad_parser_plugins_atomistic-1.0.dist-info/entry_points.txt +15 -0
- nomad_parser_plugins_atomistic-1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1753 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright The NOMAD Authors.
|
|
3
|
+
#
|
|
4
|
+
# This file is part of NOMAD.
|
|
5
|
+
# See https://nomad-lab.eu for further info.
|
|
6
|
+
#
|
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
# you may not use this file except in compliance with the License.
|
|
9
|
+
# You may obtain a copy of the License at
|
|
10
|
+
#
|
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
#
|
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
# See the License for the specific language governing permissions and
|
|
17
|
+
# limitations under the License.
|
|
18
|
+
#
|
|
19
|
+
import numpy as np
|
|
20
|
+
import os
|
|
21
|
+
from ase import data as asedata
|
|
22
|
+
import re
|
|
23
|
+
|
|
24
|
+
from nomad.units import ureg
|
|
25
|
+
|
|
26
|
+
from nomad.parsing.file_parser import Quantity, TextParser
|
|
27
|
+
from runschema.run import Run, Program
|
|
28
|
+
from runschema.method import (
|
|
29
|
+
NeighborSearching,
|
|
30
|
+
ForceCalculations,
|
|
31
|
+
ForceField,
|
|
32
|
+
Method,
|
|
33
|
+
Model,
|
|
34
|
+
AtomParameters,
|
|
35
|
+
)
|
|
36
|
+
from runschema.system import AtomsGroup
|
|
37
|
+
from simulationworkflowschema import (
|
|
38
|
+
GeometryOptimization,
|
|
39
|
+
GeometryOptimizationMethod,
|
|
40
|
+
GeometryOptimizationResults,
|
|
41
|
+
)
|
|
42
|
+
from .metainfo.lammps import (
|
|
43
|
+
x_lammps_section_input_output_files,
|
|
44
|
+
x_lammps_section_control_parameters,
|
|
45
|
+
)
|
|
46
|
+
from atomisticparsers.utils import MDAnalysisParser, MDParser
|
|
47
|
+
from simulationworkflowschema.molecular_dynamics import get_bond_list_from_model_contributions
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
re_float = r'[-+]?\d+\.*\d*(?:[Ee][-+]\d+)?'
|
|
51
|
+
re_n = r'[\n\r]'
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def get_unit(units_type, property_type=None, dimension=3):
|
|
55
|
+
mole = 6.022140857e23
|
|
56
|
+
|
|
57
|
+
units_type = units_type.lower()
|
|
58
|
+
if units_type == 'real':
|
|
59
|
+
units = dict(
|
|
60
|
+
mass=ureg.g / mole,
|
|
61
|
+
distance=ureg.angstrom,
|
|
62
|
+
time=ureg.fs,
|
|
63
|
+
energy=ureg.J * 4184.0 / mole,
|
|
64
|
+
velocity=ureg.angstrom / ureg.fs,
|
|
65
|
+
force=ureg.J * 4184.0 / ureg.angstrom / mole,
|
|
66
|
+
torque=ureg.J * 4184.0 / mole,
|
|
67
|
+
temperature=ureg.K,
|
|
68
|
+
pressure=ureg.atm,
|
|
69
|
+
dynamic_viscosity=ureg.poise,
|
|
70
|
+
charge=ureg.elementary_charge,
|
|
71
|
+
dipole=ureg.elementary_charge * ureg.angstrom,
|
|
72
|
+
electric_field=ureg.V / ureg.angstrom,
|
|
73
|
+
density=ureg.g / ureg.cm**dimension,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
elif units_type == 'metal':
|
|
77
|
+
units = dict(
|
|
78
|
+
mass=ureg.g / mole,
|
|
79
|
+
distance=ureg.angstrom,
|
|
80
|
+
time=ureg.ps,
|
|
81
|
+
energy=ureg.eV,
|
|
82
|
+
velocity=ureg.angstrom / ureg.ps,
|
|
83
|
+
force=ureg.eV / ureg.angstrom,
|
|
84
|
+
torque=ureg.eV,
|
|
85
|
+
temperature=ureg.K,
|
|
86
|
+
pressure=ureg.bar,
|
|
87
|
+
dynamic_viscosity=ureg.poise,
|
|
88
|
+
charge=ureg.elementary_charge,
|
|
89
|
+
dipole=ureg.elementary_charge * ureg.angstrom,
|
|
90
|
+
electric_field=ureg.V / ureg.angstrom,
|
|
91
|
+
density=ureg.g / ureg.cm**dimension,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
elif units_type == 'si':
|
|
95
|
+
units = dict(
|
|
96
|
+
mass=ureg.kg,
|
|
97
|
+
distance=ureg.m,
|
|
98
|
+
time=ureg.s,
|
|
99
|
+
energy=ureg.J,
|
|
100
|
+
velocity=ureg.m / ureg.s,
|
|
101
|
+
force=ureg.N,
|
|
102
|
+
torque=ureg.N * ureg.m,
|
|
103
|
+
temperature=ureg.K,
|
|
104
|
+
pressure=ureg.Pa,
|
|
105
|
+
dynamic_viscosity=ureg.Pa * ureg.s,
|
|
106
|
+
charge=ureg.C,
|
|
107
|
+
dipole=ureg.C * ureg.m,
|
|
108
|
+
electric_field=ureg.V / ureg.m,
|
|
109
|
+
density=ureg.kg / ureg.m**dimension,
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
elif units_type == 'cgs':
|
|
113
|
+
units = dict(
|
|
114
|
+
mass=ureg.g,
|
|
115
|
+
distance=ureg.cm,
|
|
116
|
+
time=ureg.s,
|
|
117
|
+
energy=ureg.erg,
|
|
118
|
+
velocity=ureg.cm / ureg.s,
|
|
119
|
+
force=ureg.dyne,
|
|
120
|
+
torque=ureg.dyne * ureg.cm,
|
|
121
|
+
temperature=ureg.K,
|
|
122
|
+
pressure=ureg.dyne / ureg.cm**2,
|
|
123
|
+
dynamic_viscosity=ureg.poise,
|
|
124
|
+
charge=ureg.esu,
|
|
125
|
+
dipole=ureg.esu * ureg.cm,
|
|
126
|
+
electric_field=ureg.dyne / ureg.esu,
|
|
127
|
+
density=ureg.g / ureg.cm**dimension,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
elif units_type == 'electron':
|
|
131
|
+
units = dict(
|
|
132
|
+
mass=ureg.amu,
|
|
133
|
+
distance=ureg.bohr,
|
|
134
|
+
time=ureg.fs,
|
|
135
|
+
energy=ureg.hartree,
|
|
136
|
+
velocity=ureg.bohr / ureg.atomic_unit_of_time,
|
|
137
|
+
force=ureg.hartree / ureg.bohr,
|
|
138
|
+
temperature=ureg.K,
|
|
139
|
+
pressure=ureg.Pa,
|
|
140
|
+
charge=ureg.elementary_charge,
|
|
141
|
+
dipole=ureg.debye,
|
|
142
|
+
electric_field=ureg.V / ureg.cm,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
elif units_type == 'micro':
|
|
146
|
+
units = dict(
|
|
147
|
+
mass=ureg.pg,
|
|
148
|
+
distance=ureg.microm,
|
|
149
|
+
time=ureg.micros,
|
|
150
|
+
energy=ureg.pg * ureg.microm**2 / ureg.micros**2,
|
|
151
|
+
velocity=ureg.microm / ureg.micros,
|
|
152
|
+
force=ureg.pg * ureg.microm / ureg.micros**2,
|
|
153
|
+
torque=ureg.pg * ureg.microm**2 / ureg.micros**2,
|
|
154
|
+
temperature=ureg.K,
|
|
155
|
+
pressure=ureg.pg / (ureg.microm * ureg.micros**2),
|
|
156
|
+
dynamic_viscosity=ureg.pg / (ureg.microm * ureg.micros),
|
|
157
|
+
charge=ureg.pC,
|
|
158
|
+
dipole=ureg.pC * ureg.microm,
|
|
159
|
+
electric_field=ureg.V / ureg.microm,
|
|
160
|
+
density=ureg.pg / ureg.microm**dimension,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
elif units_type == 'nano':
|
|
164
|
+
units = dict(
|
|
165
|
+
mass=ureg.ag,
|
|
166
|
+
distance=ureg.nm,
|
|
167
|
+
time=ureg.ns,
|
|
168
|
+
energy=ureg.ag * ureg.nm**2 / ureg.ns**2,
|
|
169
|
+
velocity=ureg.nm / ureg.ns,
|
|
170
|
+
force=ureg.ag * ureg.nm / ureg.ns**2,
|
|
171
|
+
torque=ureg.ag * ureg.nm**2 / ureg.ns**2,
|
|
172
|
+
temperature=ureg.K,
|
|
173
|
+
pressure=ureg.ag / (ureg.nm * ureg.ns**2),
|
|
174
|
+
dynamic_viscosity=ureg.ag / (ureg.nm * ureg.ns),
|
|
175
|
+
charge=ureg.elementary_charge,
|
|
176
|
+
dipole=ureg.elementary_charge * ureg.nm,
|
|
177
|
+
electric_field=ureg.V / ureg.nm,
|
|
178
|
+
density=ureg.ag / ureg.nm**dimension,
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
else:
|
|
182
|
+
# units = dict(
|
|
183
|
+
# mass=1, distance=1, time=1, energy=1, velocity=1, force=1,
|
|
184
|
+
# torque=1, temperature=1, pressure=1, dynamic_viscosity=1, charge=1,
|
|
185
|
+
# dipole=1, electric_field=1, density=1)
|
|
186
|
+
units = dict()
|
|
187
|
+
|
|
188
|
+
if property_type:
|
|
189
|
+
return units.get(property_type, None)
|
|
190
|
+
else:
|
|
191
|
+
return units
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
class DataParser(TextParser):
|
|
195
|
+
def __init__(self):
|
|
196
|
+
self._headers = [
|
|
197
|
+
'atoms',
|
|
198
|
+
'bonds',
|
|
199
|
+
'angles',
|
|
200
|
+
'dihedrals',
|
|
201
|
+
'impropers',
|
|
202
|
+
'atom types',
|
|
203
|
+
'bond types',
|
|
204
|
+
'angle types',
|
|
205
|
+
'dihedral types',
|
|
206
|
+
'improper types',
|
|
207
|
+
'extra bond per atom',
|
|
208
|
+
'extra/bond/per/atom',
|
|
209
|
+
'extra angle per atom',
|
|
210
|
+
'extra/angle/per/atom',
|
|
211
|
+
'extra dihedral per atom',
|
|
212
|
+
'extra/dihedral/per/atom',
|
|
213
|
+
'extra improper per atom',
|
|
214
|
+
'extra/improper/per/atom',
|
|
215
|
+
'extra special per atom',
|
|
216
|
+
'extra/special/per/atom',
|
|
217
|
+
'ellipsoids',
|
|
218
|
+
'lines',
|
|
219
|
+
'triangles',
|
|
220
|
+
'bodies',
|
|
221
|
+
]
|
|
222
|
+
self._sections = [
|
|
223
|
+
'Atoms',
|
|
224
|
+
'Velocities',
|
|
225
|
+
'Masses',
|
|
226
|
+
'Ellipsoids',
|
|
227
|
+
'Lines',
|
|
228
|
+
'Triangles',
|
|
229
|
+
'Bodies',
|
|
230
|
+
'Bonds',
|
|
231
|
+
'Angles',
|
|
232
|
+
'Dihedrals',
|
|
233
|
+
'Impropers',
|
|
234
|
+
'Pair Coeffs',
|
|
235
|
+
'PairIJ Coeffs',
|
|
236
|
+
'Bond Coeffs',
|
|
237
|
+
'Angle Coeffs',
|
|
238
|
+
'Dihedral Coeffs',
|
|
239
|
+
'Improper Coeffs',
|
|
240
|
+
'BondBond Coeffs',
|
|
241
|
+
'BondAngle Coeffs',
|
|
242
|
+
'MiddleBondTorsion Coeffs',
|
|
243
|
+
'EndBondTorsion Coeffs',
|
|
244
|
+
'AngleTorsion Coeffs',
|
|
245
|
+
'AngleAngleTorsion Coeffs',
|
|
246
|
+
'BondBond13 Coeffs',
|
|
247
|
+
'AngleAngle Coeffs',
|
|
248
|
+
]
|
|
249
|
+
self._interactions = [
|
|
250
|
+
section for section in self._sections if section.endswith('Coeffs')
|
|
251
|
+
]
|
|
252
|
+
super().__init__(None)
|
|
253
|
+
|
|
254
|
+
def init_quantities(self):
|
|
255
|
+
self._quantities = [
|
|
256
|
+
Quantity(header, rf'{re_n} *(\d+) +{header}', repeats=True, dtype=np.int32)
|
|
257
|
+
for header in self._headers
|
|
258
|
+
]
|
|
259
|
+
|
|
260
|
+
def get_section_value(val):
|
|
261
|
+
val = val.strip().splitlines()
|
|
262
|
+
name = None
|
|
263
|
+
|
|
264
|
+
if val[0][0] == '#':
|
|
265
|
+
name = val[0][1:].strip()
|
|
266
|
+
val = val[1:]
|
|
267
|
+
|
|
268
|
+
value = []
|
|
269
|
+
for i in range(len(val)):
|
|
270
|
+
v = val[i].split('#')[0].split()
|
|
271
|
+
if not v:
|
|
272
|
+
continue
|
|
273
|
+
|
|
274
|
+
try:
|
|
275
|
+
value.append(np.array(v, dtype=float))
|
|
276
|
+
except Exception:
|
|
277
|
+
break
|
|
278
|
+
|
|
279
|
+
return name, np.array(value)
|
|
280
|
+
|
|
281
|
+
self._quantities.extend(
|
|
282
|
+
[
|
|
283
|
+
Quantity(
|
|
284
|
+
section,
|
|
285
|
+
rf'{section} *(#*.*{re_n}\s+(?:[\d ]+{re_float}.+\s+)+)',
|
|
286
|
+
str_operation=get_section_value,
|
|
287
|
+
repeats=True,
|
|
288
|
+
)
|
|
289
|
+
for section in self._sections
|
|
290
|
+
]
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
def get_interactions(self):
|
|
294
|
+
styles_coeffs = []
|
|
295
|
+
for interaction in self._interactions:
|
|
296
|
+
coeffs = self.get(interaction, None)
|
|
297
|
+
if coeffs is None:
|
|
298
|
+
continue
|
|
299
|
+
if isinstance(coeffs, tuple):
|
|
300
|
+
coeffs = list(coeffs)
|
|
301
|
+
|
|
302
|
+
styles_coeffs += coeffs
|
|
303
|
+
|
|
304
|
+
return styles_coeffs
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
class TrajParser(TextParser):
|
|
308
|
+
def __init__(self):
|
|
309
|
+
self._masses = None
|
|
310
|
+
self._reference_masses = dict(
|
|
311
|
+
masses=np.array(asedata.atomic_masses), symbols=asedata.chemical_symbols
|
|
312
|
+
)
|
|
313
|
+
self._chemical_symbols = None
|
|
314
|
+
super().__init__(None)
|
|
315
|
+
|
|
316
|
+
def init_quantities(self):
|
|
317
|
+
def get_pbc_cell(val):
|
|
318
|
+
val = val.split()
|
|
319
|
+
|
|
320
|
+
pbc = [v == 'pp' for v in val[:3]]
|
|
321
|
+
|
|
322
|
+
cell = np.zeros((3, 3))
|
|
323
|
+
for i in range(3):
|
|
324
|
+
cell[i][i] = float(val[i * 2 + 4]) - float(val[i * 2 + 3])
|
|
325
|
+
|
|
326
|
+
return pbc, cell
|
|
327
|
+
|
|
328
|
+
def get_atoms_info(val):
|
|
329
|
+
val = val.split('\n')
|
|
330
|
+
keys = val[0].split()
|
|
331
|
+
values = np.array([v.split() for v in val[1:] if v], dtype=float)
|
|
332
|
+
values = values[values[:, 0].argsort()].T
|
|
333
|
+
return {keys[i]: values[i] for i in range(len(keys))}
|
|
334
|
+
|
|
335
|
+
self._quantities = [
|
|
336
|
+
Quantity(
|
|
337
|
+
'time_step',
|
|
338
|
+
r'\s*ITEM:\s*TIMESTEP\s*\n\s*(\d+)\s*\n',
|
|
339
|
+
comment='#',
|
|
340
|
+
repeats=True,
|
|
341
|
+
),
|
|
342
|
+
Quantity(
|
|
343
|
+
'n_atoms',
|
|
344
|
+
r'\s*ITEM:\s*NUMBER OF ATOMS\s*\n\s*(\d+)\s*\n',
|
|
345
|
+
comment='#',
|
|
346
|
+
repeats=True,
|
|
347
|
+
),
|
|
348
|
+
Quantity(
|
|
349
|
+
'pbc_cell',
|
|
350
|
+
r'\s*ITEM: BOX BOUNDS\s*([\s\w]+)\n([\+\-\d\.eE\s]+)\n',
|
|
351
|
+
str_operation=get_pbc_cell,
|
|
352
|
+
comment='#',
|
|
353
|
+
repeats=True,
|
|
354
|
+
),
|
|
355
|
+
Quantity(
|
|
356
|
+
'atoms_info',
|
|
357
|
+
r'\s*ITEM:\s*ATOMS\s*([ \w]+\n)*?([\+\-eE\d\.\n ]+)',
|
|
358
|
+
str_operation=get_atoms_info,
|
|
359
|
+
comment='#',
|
|
360
|
+
repeats=True,
|
|
361
|
+
),
|
|
362
|
+
]
|
|
363
|
+
|
|
364
|
+
@property
|
|
365
|
+
def with_trajectory(self):
|
|
366
|
+
return self.get('atoms_info') is not None
|
|
367
|
+
|
|
368
|
+
@property
|
|
369
|
+
def n_frames(self):
|
|
370
|
+
return len(self.get('atoms_info', []))
|
|
371
|
+
|
|
372
|
+
@property
|
|
373
|
+
def masses(self):
|
|
374
|
+
return self._masses
|
|
375
|
+
|
|
376
|
+
@masses.setter
|
|
377
|
+
def masses(self, val):
|
|
378
|
+
self._masses = val
|
|
379
|
+
if self._masses is None:
|
|
380
|
+
return
|
|
381
|
+
|
|
382
|
+
self._masses = val
|
|
383
|
+
if self._chemical_symbols is None:
|
|
384
|
+
masses = self._masses[0][1]
|
|
385
|
+
self._chemical_symbols = {}
|
|
386
|
+
for i in range(len(masses)):
|
|
387
|
+
symbol_idx = np.argmin(
|
|
388
|
+
abs(self._reference_masses['masses'] - masses[i][1])
|
|
389
|
+
)
|
|
390
|
+
self._chemical_symbols[masses[i][0]] = self._reference_masses[
|
|
391
|
+
'symbols'
|
|
392
|
+
][symbol_idx]
|
|
393
|
+
|
|
394
|
+
def get_atom_labels(self, idx):
|
|
395
|
+
atoms_info = self.get('atoms_info')
|
|
396
|
+
if atoms_info is None:
|
|
397
|
+
return
|
|
398
|
+
|
|
399
|
+
atoms_id = atoms_info[idx].get('id')
|
|
400
|
+
default = ['X' for _ in atoms_id] if atoms_id is not None else None
|
|
401
|
+
atoms_type = atoms_info[idx].get('type')
|
|
402
|
+
if atoms_type is None:
|
|
403
|
+
return default
|
|
404
|
+
if self._chemical_symbols is None:
|
|
405
|
+
return default
|
|
406
|
+
|
|
407
|
+
try:
|
|
408
|
+
atom_labels = [self._chemical_symbols[atype] for atype in atoms_type]
|
|
409
|
+
except Exception:
|
|
410
|
+
self.logger.error('Error resolving atom labels.')
|
|
411
|
+
return
|
|
412
|
+
|
|
413
|
+
return atom_labels
|
|
414
|
+
|
|
415
|
+
def get_positions(self, idx):
|
|
416
|
+
atoms_info = self.get('atoms_info')
|
|
417
|
+
if atoms_info is None:
|
|
418
|
+
return
|
|
419
|
+
|
|
420
|
+
atoms_info = atoms_info[idx]
|
|
421
|
+
|
|
422
|
+
cell = self.get('pbc_cell')
|
|
423
|
+
cell = None if cell is None else cell[idx][1]
|
|
424
|
+
if 'xs' in atoms_info and 'ys' in atoms_info and 'zs' in atoms_info:
|
|
425
|
+
if cell is None:
|
|
426
|
+
return
|
|
427
|
+
positions = np.array(
|
|
428
|
+
[atoms_info['xs'], atoms_info['ys'], atoms_info['zs']]
|
|
429
|
+
).T
|
|
430
|
+
positions = positions * np.linalg.norm(cell, axis=1) + np.amin(cell, axis=1)
|
|
431
|
+
|
|
432
|
+
elif 'xu' in atoms_info and 'yu' in atoms_info and 'zu' in atoms_info:
|
|
433
|
+
positions = np.array(
|
|
434
|
+
[atoms_info['xu'], atoms_info['yu'], atoms_info['zu']]
|
|
435
|
+
).T
|
|
436
|
+
|
|
437
|
+
elif 'xsu' in atoms_info and 'ysu' in atoms_info and 'zsu' in atoms_info:
|
|
438
|
+
if cell is None:
|
|
439
|
+
return
|
|
440
|
+
positions = np.array(
|
|
441
|
+
[atoms_info['xsu'], atoms_info['ysu'], atoms_info['zsu']]
|
|
442
|
+
).T
|
|
443
|
+
positions = positions * np.linalg.norm(cell, axis=1) + np.amin(cell, axis=1)
|
|
444
|
+
|
|
445
|
+
elif 'x' in atoms_info and 'y' in atoms_info and 'z' in atoms_info:
|
|
446
|
+
positions = np.array([atoms_info['x'], atoms_info['y'], atoms_info['z']]).T
|
|
447
|
+
if 'ix' in atoms_info and 'iy' in atoms_info and 'iz' in atoms_info:
|
|
448
|
+
if cell is None:
|
|
449
|
+
return
|
|
450
|
+
positions_img = np.array(
|
|
451
|
+
[atoms_info['ix'], atoms_info['iy'], atoms_info['iz']]
|
|
452
|
+
).T
|
|
453
|
+
|
|
454
|
+
positions += positions_img * np.linalg.norm(cell, axis=1)
|
|
455
|
+
else:
|
|
456
|
+
positions = None
|
|
457
|
+
|
|
458
|
+
return positions
|
|
459
|
+
|
|
460
|
+
def get_velocities(self, idx):
|
|
461
|
+
atoms_info = self.get('atoms_info')
|
|
462
|
+
if atoms_info is None:
|
|
463
|
+
return
|
|
464
|
+
atoms_info = atoms_info[idx]
|
|
465
|
+
if 'vx' not in atoms_info or 'vy' not in atoms_info or 'vz' not in atoms_info:
|
|
466
|
+
return
|
|
467
|
+
|
|
468
|
+
return np.array([atoms_info['vx'], atoms_info['vy'], atoms_info['vz']]).T
|
|
469
|
+
|
|
470
|
+
def get_forces(self, idx):
|
|
471
|
+
atoms_info = self.get('atoms_info')
|
|
472
|
+
if atoms_info is None:
|
|
473
|
+
return
|
|
474
|
+
atoms_info = atoms_info[idx]
|
|
475
|
+
if 'fx' not in atoms_info or 'fy' not in atoms_info or 'fz' not in atoms_info:
|
|
476
|
+
return
|
|
477
|
+
return np.array([atoms_info['fx'], atoms_info['fy'], atoms_info['fz']]).T
|
|
478
|
+
|
|
479
|
+
def get_lattice_vectors(self, idx):
|
|
480
|
+
pbc_cell = self.get('pbc_cell')
|
|
481
|
+
if pbc_cell is None:
|
|
482
|
+
return
|
|
483
|
+
return pbc_cell[idx][1]
|
|
484
|
+
|
|
485
|
+
def get_pbc(self, idx):
|
|
486
|
+
pbc_cell = self.get('pbc_cell')
|
|
487
|
+
if pbc_cell is None:
|
|
488
|
+
return
|
|
489
|
+
return pbc_cell[idx][0]
|
|
490
|
+
|
|
491
|
+
def get_n_atoms(self, idx):
|
|
492
|
+
n_atoms = self.get('n_atoms')
|
|
493
|
+
if n_atoms is None:
|
|
494
|
+
return len(self.get_positions(idx))
|
|
495
|
+
return n_atoms[idx]
|
|
496
|
+
|
|
497
|
+
def get_step(self, idx):
|
|
498
|
+
step = self.get('time_step')
|
|
499
|
+
if step is None:
|
|
500
|
+
return
|
|
501
|
+
return step[idx]
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
class XYZTrajParser(TrajParser):
|
|
505
|
+
def __init__(self):
|
|
506
|
+
super().__init__()
|
|
507
|
+
|
|
508
|
+
def init_quantities(self):
|
|
509
|
+
def get_atoms_info(val_in):
|
|
510
|
+
val = [v.split('#')[0].split() for v in val_in.strip().split('\n')]
|
|
511
|
+
symbols = []
|
|
512
|
+
for v in val:
|
|
513
|
+
if v[0].isalpha():
|
|
514
|
+
if v[0] not in symbols:
|
|
515
|
+
symbols.append(v[0])
|
|
516
|
+
v[0] = symbols.index(v[0]) + 1
|
|
517
|
+
val = np.transpose(np.array([v for v in val if len(v) == 4], dtype=float))
|
|
518
|
+
# val[0] is the atomic number
|
|
519
|
+
val[0] = [list(set(val[0])).index(v) + 1 for v in val[0]]
|
|
520
|
+
return dict(type=val[0], x=val[1], y=val[2], z=val[3])
|
|
521
|
+
|
|
522
|
+
self.quantities = [
|
|
523
|
+
Quantity(
|
|
524
|
+
'atoms_info',
|
|
525
|
+
r'((?:\d+|[A-Z][a-z]?) [\s\S]+?)(?:\s\d+\n|\Z)',
|
|
526
|
+
str_operation=get_atoms_info,
|
|
527
|
+
comment='#',
|
|
528
|
+
repeats=True,
|
|
529
|
+
)
|
|
530
|
+
]
|
|
531
|
+
|
|
532
|
+
|
|
533
|
+
class LogParser(TextParser):
|
|
534
|
+
def __init__(self):
|
|
535
|
+
self._commands = [
|
|
536
|
+
'angle_coeff',
|
|
537
|
+
'angle_style',
|
|
538
|
+
'atom_modify',
|
|
539
|
+
'atom_style',
|
|
540
|
+
'balance',
|
|
541
|
+
'bond_coeff',
|
|
542
|
+
'bond_style',
|
|
543
|
+
'bond_write',
|
|
544
|
+
'boundary',
|
|
545
|
+
'change_box',
|
|
546
|
+
'clear',
|
|
547
|
+
'comm_modify',
|
|
548
|
+
'comm_style',
|
|
549
|
+
'compute',
|
|
550
|
+
'compute_modify',
|
|
551
|
+
'create_atoms',
|
|
552
|
+
'create_bonds',
|
|
553
|
+
'create_box',
|
|
554
|
+
'delete_bonds',
|
|
555
|
+
'dielectric',
|
|
556
|
+
'dihedral_coeff',
|
|
557
|
+
'dihedral_style',
|
|
558
|
+
'dimension',
|
|
559
|
+
'displace_atoms',
|
|
560
|
+
'dump',
|
|
561
|
+
'dump_modify',
|
|
562
|
+
'dynamical_matrix',
|
|
563
|
+
'echo',
|
|
564
|
+
'fix',
|
|
565
|
+
'fix_modify',
|
|
566
|
+
'group',
|
|
567
|
+
'group2ndx',
|
|
568
|
+
'ndx2group',
|
|
569
|
+
'hyper',
|
|
570
|
+
'if',
|
|
571
|
+
'improper_coeff',
|
|
572
|
+
'improper_style',
|
|
573
|
+
'include',
|
|
574
|
+
'info',
|
|
575
|
+
'jump',
|
|
576
|
+
'kim_init',
|
|
577
|
+
'kim_interactions',
|
|
578
|
+
'kim_query',
|
|
579
|
+
'kim_param',
|
|
580
|
+
'kim_property',
|
|
581
|
+
'kspace_modify',
|
|
582
|
+
'kspace_style',
|
|
583
|
+
'label',
|
|
584
|
+
'lattice',
|
|
585
|
+
'log',
|
|
586
|
+
'mass',
|
|
587
|
+
'message',
|
|
588
|
+
'min_modify',
|
|
589
|
+
'min_style',
|
|
590
|
+
'minimize',
|
|
591
|
+
'minimize/kk',
|
|
592
|
+
'molecule',
|
|
593
|
+
'neb',
|
|
594
|
+
'neb/spin',
|
|
595
|
+
'neigh_modify',
|
|
596
|
+
'neighbor',
|
|
597
|
+
'newton',
|
|
598
|
+
'next',
|
|
599
|
+
'package',
|
|
600
|
+
'pair_coeff',
|
|
601
|
+
'pair_modify',
|
|
602
|
+
'pair_style',
|
|
603
|
+
'pair_write',
|
|
604
|
+
'partition',
|
|
605
|
+
'prd',
|
|
606
|
+
'print',
|
|
607
|
+
'processors',
|
|
608
|
+
'quit',
|
|
609
|
+
'read_data',
|
|
610
|
+
'read_dump',
|
|
611
|
+
'read_restart',
|
|
612
|
+
'region',
|
|
613
|
+
'replicate',
|
|
614
|
+
'rerun',
|
|
615
|
+
'reset_atom_ids',
|
|
616
|
+
'reset_mol_ids',
|
|
617
|
+
'reset_timestep',
|
|
618
|
+
'restart',
|
|
619
|
+
'run',
|
|
620
|
+
'run_style',
|
|
621
|
+
'server',
|
|
622
|
+
'set',
|
|
623
|
+
'shell',
|
|
624
|
+
'special_bonds',
|
|
625
|
+
'suffix',
|
|
626
|
+
'tad',
|
|
627
|
+
'temper/grem',
|
|
628
|
+
'temper/npt',
|
|
629
|
+
'thermo',
|
|
630
|
+
'thermo_modify',
|
|
631
|
+
'thermo_style',
|
|
632
|
+
'third_order',
|
|
633
|
+
'timer',
|
|
634
|
+
'timestep',
|
|
635
|
+
'uncompute',
|
|
636
|
+
'undump',
|
|
637
|
+
'unfix',
|
|
638
|
+
'units',
|
|
639
|
+
'variable',
|
|
640
|
+
'velocity',
|
|
641
|
+
'write_coeff',
|
|
642
|
+
'write_data',
|
|
643
|
+
'write_dump',
|
|
644
|
+
'write_restart',
|
|
645
|
+
]
|
|
646
|
+
self._interactions = [
|
|
647
|
+
'atom',
|
|
648
|
+
'pair',
|
|
649
|
+
'bond',
|
|
650
|
+
'angle',
|
|
651
|
+
'dihedral',
|
|
652
|
+
'improper',
|
|
653
|
+
'kspace',
|
|
654
|
+
]
|
|
655
|
+
self._units = None
|
|
656
|
+
super().__init__(None)
|
|
657
|
+
|
|
658
|
+
def init_quantities(self):
|
|
659
|
+
def str_op(val):
|
|
660
|
+
val = val.split('#')[0]
|
|
661
|
+
val = val.replace('&\n', ' ').split()
|
|
662
|
+
val = val if len(val) > 1 else val[0]
|
|
663
|
+
return val
|
|
664
|
+
|
|
665
|
+
self._quantities = [
|
|
666
|
+
Quantity(
|
|
667
|
+
name,
|
|
668
|
+
r'\n\s*%s\s+(?!.*\$\{)([${}\w\. \/\#\-]+)(\&\n[\w\. \/\#\-]*)*' % name,
|
|
669
|
+
str_operation=str_op,
|
|
670
|
+
comment='#',
|
|
671
|
+
repeats=True,
|
|
672
|
+
)
|
|
673
|
+
for name in self._commands
|
|
674
|
+
]
|
|
675
|
+
|
|
676
|
+
self._quantities.append(
|
|
677
|
+
Quantity(
|
|
678
|
+
'program_version',
|
|
679
|
+
r'\s*LAMMPS\s*\(([\w ]+)\)\n',
|
|
680
|
+
dtype=str,
|
|
681
|
+
repeats=False,
|
|
682
|
+
flatten=False,
|
|
683
|
+
)
|
|
684
|
+
)
|
|
685
|
+
|
|
686
|
+
self._quantities.append(
|
|
687
|
+
Quantity('finished', r'\s*Dangerous builds\s*=\s*(\d+)', repeats=False)
|
|
688
|
+
)
|
|
689
|
+
|
|
690
|
+
self._quantities.append(
|
|
691
|
+
Quantity(
|
|
692
|
+
'minimization_stats',
|
|
693
|
+
r'\s*Minimization stats:\s*([\s\S]+?)\n\n',
|
|
694
|
+
flatten=False,
|
|
695
|
+
)
|
|
696
|
+
)
|
|
697
|
+
|
|
698
|
+
def str_to_thermo(val):
|
|
699
|
+
res = {}
|
|
700
|
+
if val.count('Step') > 1:
|
|
701
|
+
val = (
|
|
702
|
+
val.replace('--', '').replace('=', '').replace('(sec)', '').split()
|
|
703
|
+
)
|
|
704
|
+
val = [v.strip() for v in val]
|
|
705
|
+
|
|
706
|
+
for i in range(len(val)):
|
|
707
|
+
if val[i][0].isalpha():
|
|
708
|
+
res.setdefault(val[i], [])
|
|
709
|
+
res[val[i]].append(float(val[i + 1]))
|
|
710
|
+
|
|
711
|
+
else:
|
|
712
|
+
val = val.split('\n')
|
|
713
|
+
keys = [v.strip() for v in val[0].split()]
|
|
714
|
+
val = np.array([v.split() for v in val[1:] if v], dtype=float).T
|
|
715
|
+
|
|
716
|
+
res = {key: [] for key in keys}
|
|
717
|
+
for i in range(len(keys)):
|
|
718
|
+
res[keys[i]] = val[i]
|
|
719
|
+
|
|
720
|
+
return res
|
|
721
|
+
|
|
722
|
+
self._quantities.append(
|
|
723
|
+
Quantity(
|
|
724
|
+
'thermo_data',
|
|
725
|
+
r'\s*\-*(\s*Step\s*[\-\s\w\.\=\(\)]*[ \-\.\d\n]+)Loop',
|
|
726
|
+
str_operation=str_to_thermo,
|
|
727
|
+
repeats=False,
|
|
728
|
+
convert=False,
|
|
729
|
+
)
|
|
730
|
+
)
|
|
731
|
+
|
|
732
|
+
@property
|
|
733
|
+
def units(self):
|
|
734
|
+
if self._units is None:
|
|
735
|
+
units_type = self.get('units', ['lj'])[0]
|
|
736
|
+
self._units = get_unit(units_type)
|
|
737
|
+
return self._units
|
|
738
|
+
|
|
739
|
+
def get_thermodynamic_data(self):
|
|
740
|
+
thermo_data = self.get('thermo_data')
|
|
741
|
+
|
|
742
|
+
if thermo_data is None:
|
|
743
|
+
return
|
|
744
|
+
|
|
745
|
+
data = {}
|
|
746
|
+
for key, val in thermo_data.items():
|
|
747
|
+
low_key = key.lower()
|
|
748
|
+
if low_key.startswith('e_') or low_key.endswith('eng'):
|
|
749
|
+
data[key] = val * self.units.get('energy', 1)
|
|
750
|
+
elif low_key == 'press':
|
|
751
|
+
data[key] = val * self.units.get('pressure', 1)
|
|
752
|
+
elif low_key == 'temp':
|
|
753
|
+
data[key] = val * self.units.get('temperature', 1)
|
|
754
|
+
else:
|
|
755
|
+
data[key] = val
|
|
756
|
+
return data
|
|
757
|
+
|
|
758
|
+
def get_traj_files(self):
|
|
759
|
+
dump = self.get('dump')
|
|
760
|
+
if dump is None:
|
|
761
|
+
self.logger.warning('Trajectory not specified in directory, will scan.')
|
|
762
|
+
# TODO improve matching of traj file
|
|
763
|
+
traj_files = os.listdir(self.maindir)
|
|
764
|
+
traj_files = [
|
|
765
|
+
f for f in traj_files if f.endswith('trj') or f.endswith('xyz')
|
|
766
|
+
]
|
|
767
|
+
# further eliminate
|
|
768
|
+
if len(traj_files) > 1:
|
|
769
|
+
prefix = os.path.basename(self.mainfile).rsplit('.', 1)[0]
|
|
770
|
+
traj_files = [f for f in traj_files if prefix in f]
|
|
771
|
+
else:
|
|
772
|
+
traj_files = []
|
|
773
|
+
if type(dump[0]) in [str, int]:
|
|
774
|
+
dump = [dump]
|
|
775
|
+
traj_files = [d[4] for d in dump]
|
|
776
|
+
traj_files = [
|
|
777
|
+
i for n, i in enumerate(traj_files) if i not in traj_files[:n]
|
|
778
|
+
] # remove duplicates
|
|
779
|
+
|
|
780
|
+
return [os.path.join(self.maindir, f) for f in traj_files]
|
|
781
|
+
|
|
782
|
+
def get_data_files(self):
|
|
783
|
+
def check_file_header(file_path, regex_pattern):
|
|
784
|
+
header_size = 1024
|
|
785
|
+
file_path = f'{self.maindir}/{file_path}'
|
|
786
|
+
try:
|
|
787
|
+
with open(file_path, 'rb') as file:
|
|
788
|
+
file_header = file.read(header_size)
|
|
789
|
+
file_header_str = file_header.decode(errors='ignore')
|
|
790
|
+
except Exception:
|
|
791
|
+
file_header_str = ''
|
|
792
|
+
|
|
793
|
+
return re.search(regex_pattern, file_header_str)
|
|
794
|
+
|
|
795
|
+
read_data = self.get('read_data')
|
|
796
|
+
if read_data is None or 'CPU' in read_data:
|
|
797
|
+
self.logger.warning('Data file not specified in directory, will scan.')
|
|
798
|
+
data_files = os.listdir(self.maindir)
|
|
799
|
+
data_files = [
|
|
800
|
+
f for f in data_files if f.endswith('data') or f.startswith('data')
|
|
801
|
+
]
|
|
802
|
+
if not data_files:
|
|
803
|
+
data_files = os.listdir(self.maindir)
|
|
804
|
+
data_files = [
|
|
805
|
+
f for f in data_files if check_file_header(f, 'LAMMPS data file')
|
|
806
|
+
] # TODO: Should this be the default?
|
|
807
|
+
if len(data_files) > 1:
|
|
808
|
+
prefix = os.path.basename(self.mainfile).rsplit('.', 1)
|
|
809
|
+
prefix = (
|
|
810
|
+
prefix[1] if len(prefix) > 1 and prefix[1] != 'log' else prefix[0]
|
|
811
|
+
)
|
|
812
|
+
data_files = [f for f in data_files if prefix in f]
|
|
813
|
+
else:
|
|
814
|
+
data_files = read_data
|
|
815
|
+
|
|
816
|
+
return [os.path.join(self.maindir, f) for f in data_files]
|
|
817
|
+
|
|
818
|
+
def get_pbc(self):
|
|
819
|
+
pbc = self.get('boundary', ['p', 'p', 'p'])
|
|
820
|
+
return [v == 'p' for v in pbc]
|
|
821
|
+
|
|
822
|
+
def get_sampling_method(self):
|
|
823
|
+
fix_style = self.get('fix', [[''] * 3])[0][2]
|
|
824
|
+
|
|
825
|
+
sampling_method = (
|
|
826
|
+
'langevin_dynamics' if 'langevin' in fix_style else 'molecular_dynamics'
|
|
827
|
+
)
|
|
828
|
+
return sampling_method, fix_style
|
|
829
|
+
|
|
830
|
+
def get_thermostat_settings(self):
|
|
831
|
+
fix = self.get('fix', [None])[0]
|
|
832
|
+
if fix is None:
|
|
833
|
+
return {}
|
|
834
|
+
|
|
835
|
+
try:
|
|
836
|
+
fix_style = fix[2]
|
|
837
|
+
except IndexError:
|
|
838
|
+
return {}
|
|
839
|
+
|
|
840
|
+
temp_unit = self.units.get('temperature', 1)
|
|
841
|
+
press_unit = self.units.get('pressure', 1)
|
|
842
|
+
time_unit = self.units.get('time', 1)
|
|
843
|
+
|
|
844
|
+
res = dict()
|
|
845
|
+
if fix_style.lower() == 'nvt':
|
|
846
|
+
try:
|
|
847
|
+
res['target_T'] = float(fix[5]) * temp_unit
|
|
848
|
+
res['thermostat_tau'] = float(fix[6]) * time_unit
|
|
849
|
+
except Exception:
|
|
850
|
+
pass
|
|
851
|
+
|
|
852
|
+
elif fix_style.lower() == 'npt':
|
|
853
|
+
try:
|
|
854
|
+
res['target_T'] = float(fix[5]) * temp_unit
|
|
855
|
+
res['thermostat_tau'] = float(fix[6]) * time_unit
|
|
856
|
+
res['target_P'] = float(fix[9]) * press_unit
|
|
857
|
+
res['barostat_tau'] = float(fix[10]) * time_unit
|
|
858
|
+
except Exception:
|
|
859
|
+
pass
|
|
860
|
+
|
|
861
|
+
elif fix_style.lower() == 'nph':
|
|
862
|
+
try:
|
|
863
|
+
res['target_P'] = float(fix[5]) * press_unit
|
|
864
|
+
res['barostat_tau'] = float(fix[6]) * time_unit
|
|
865
|
+
except Exception:
|
|
866
|
+
pass
|
|
867
|
+
|
|
868
|
+
elif fix_style.lower() == 'langevin':
|
|
869
|
+
try:
|
|
870
|
+
res['target_T'] = float(fix[4]) * temp_unit
|
|
871
|
+
res['langevin_gamma'] = float(fix[5]) * time_unit
|
|
872
|
+
except Exception:
|
|
873
|
+
pass
|
|
874
|
+
|
|
875
|
+
else:
|
|
876
|
+
self.logger.warning('Fix style not supported', data=dict(style=fix_style))
|
|
877
|
+
|
|
878
|
+
return res
|
|
879
|
+
|
|
880
|
+
def get_interactions(self):
|
|
881
|
+
styles_coeffs = []
|
|
882
|
+
for interaction in self._interactions:
|
|
883
|
+
styles = self.get('%s_style' % interaction, None)
|
|
884
|
+
if styles is None:
|
|
885
|
+
continue
|
|
886
|
+
|
|
887
|
+
if isinstance(styles[0], str):
|
|
888
|
+
styles = [styles]
|
|
889
|
+
|
|
890
|
+
for i in range(len(styles)):
|
|
891
|
+
if interaction == 'kspace':
|
|
892
|
+
coeff = [[float(c) for c in styles[i][1:]]]
|
|
893
|
+
style = styles[i][0]
|
|
894
|
+
|
|
895
|
+
else:
|
|
896
|
+
coeff = self.get('%s_coeff' % interaction)
|
|
897
|
+
style = ' '.join([str(si) for si in styles[i]])
|
|
898
|
+
|
|
899
|
+
styles_coeffs.append((style.strip(), coeff))
|
|
900
|
+
|
|
901
|
+
return styles_coeffs
|
|
902
|
+
|
|
903
|
+
|
|
904
|
+
class TrajParsers:
|
|
905
|
+
def __init__(self, parsers):
|
|
906
|
+
self._parsers = parsers
|
|
907
|
+
for parser in parsers:
|
|
908
|
+
parser.parse()
|
|
909
|
+
|
|
910
|
+
def __getitem__(self, index):
|
|
911
|
+
if self._parsers:
|
|
912
|
+
return self._parsers[index]
|
|
913
|
+
|
|
914
|
+
def eval(self, key, *args, **kwargs):
|
|
915
|
+
for parser in self._parsers:
|
|
916
|
+
parser_method = getattr(parser, key)
|
|
917
|
+
if parser_method is not None:
|
|
918
|
+
val = (
|
|
919
|
+
parser_method(*args, **kwargs) if args or kwargs else parser_method
|
|
920
|
+
)
|
|
921
|
+
if val is not None:
|
|
922
|
+
return val
|
|
923
|
+
|
|
924
|
+
|
|
925
|
+
class LammpsParser(MDParser):
|
|
926
|
+
def __init__(self):
|
|
927
|
+
super().__init__()
|
|
928
|
+
self.log_parser = LogParser()
|
|
929
|
+
self._traj_parser = TrajParser()
|
|
930
|
+
self._xyztraj_parser = XYZTrajParser()
|
|
931
|
+
self._mdanalysistraj_parser = MDAnalysisParser(
|
|
932
|
+
topology_format='DATA', format='LAMMPSDUMP'
|
|
933
|
+
)
|
|
934
|
+
self.data_parser = DataParser()
|
|
935
|
+
self.aux_log_parser = LogParser()
|
|
936
|
+
self._energy_mapping = {
|
|
937
|
+
'e_pair': 'pair',
|
|
938
|
+
'e_bond': 'bond',
|
|
939
|
+
'e_angle': 'angle',
|
|
940
|
+
'e_dihed': 'dihedral',
|
|
941
|
+
'e_impro': 'improper',
|
|
942
|
+
'e_coul': 'coulomb',
|
|
943
|
+
'e_vdwl': 'van der Waals',
|
|
944
|
+
'e_mol': 'molecular',
|
|
945
|
+
'e_long': 'kspace long range',
|
|
946
|
+
'e_tail': 'van der Waals long range',
|
|
947
|
+
'kineng': 'kinetic',
|
|
948
|
+
'poteng': 'potential',
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
def get_time_step(self):
|
|
952
|
+
time_unit = self.log_parser.units.get('time', None)
|
|
953
|
+
time_step = self.log_parser.get('timestep', [0], unit=time_unit)[0]
|
|
954
|
+
return time_step
|
|
955
|
+
|
|
956
|
+
def parse_thermodynamic_data(self):
|
|
957
|
+
sec_run = self.archive.run[-1]
|
|
958
|
+
# sec_system = sec_run.system
|
|
959
|
+
|
|
960
|
+
time_step = self.get_time_step()
|
|
961
|
+
thermo_data = self.log_parser.get_thermodynamic_data()
|
|
962
|
+
if thermo_data is None:
|
|
963
|
+
thermo_data = self.aux_log_parser.get_thermodynamic_data()
|
|
964
|
+
if not thermo_data:
|
|
965
|
+
thermo_data = {}
|
|
966
|
+
self.thermodynamics_steps = [int(n) for n in thermo_data.get('Step', [])]
|
|
967
|
+
|
|
968
|
+
if not thermo_data:
|
|
969
|
+
return
|
|
970
|
+
|
|
971
|
+
for step in self.thermodynamics_steps:
|
|
972
|
+
step_data = {
|
|
973
|
+
'step': step,
|
|
974
|
+
'time': step * time_step,
|
|
975
|
+
'method_ref': sec_run.method[-1] if sec_run.method else None,
|
|
976
|
+
'energy': {'contributions': []},
|
|
977
|
+
}
|
|
978
|
+
if step in self._trajectory_steps:
|
|
979
|
+
step_data['forces'] = (
|
|
980
|
+
dict(
|
|
981
|
+
total=dict(
|
|
982
|
+
value=self.traj_parsers.eval(
|
|
983
|
+
'get_forces', self._trajectory_steps.index(step)
|
|
984
|
+
)
|
|
985
|
+
)
|
|
986
|
+
),
|
|
987
|
+
)
|
|
988
|
+
|
|
989
|
+
data_n = self._thermodynamics_steps.index(step)
|
|
990
|
+
for key, val in thermo_data.items():
|
|
991
|
+
key = key.lower()
|
|
992
|
+
if (kind := self._energy_mapping.get(key)) is not None:
|
|
993
|
+
step_data['energy']['contributions'].append(
|
|
994
|
+
dict(kind=kind, value=val[data_n])
|
|
995
|
+
)
|
|
996
|
+
elif key == 'toteng':
|
|
997
|
+
step_data['energy']['current'] = dict(value=val[data_n])
|
|
998
|
+
step_data['energy']['total'] = dict(value=val[data_n])
|
|
999
|
+
elif key == 'press':
|
|
1000
|
+
step_data['pressure'] = val[data_n]
|
|
1001
|
+
elif key == 'temp':
|
|
1002
|
+
step_data['temperature'] = val[data_n]
|
|
1003
|
+
elif key == 'cpu':
|
|
1004
|
+
# approx time calc is dt / dstep
|
|
1005
|
+
max_step = len(self._thermodynamics_steps) - 1
|
|
1006
|
+
# calc time cannot be calculated for last iter, will be zero
|
|
1007
|
+
delta_time = float(val[min(data_n + 1, max_step)]) - float(
|
|
1008
|
+
val[data_n]
|
|
1009
|
+
)
|
|
1010
|
+
delta_step = (
|
|
1011
|
+
1
|
|
1012
|
+
if data_n == max_step
|
|
1013
|
+
else self._thermodynamics_steps[data_n + 1] - step
|
|
1014
|
+
)
|
|
1015
|
+
step_data['time_calculation'] = delta_time * ureg.s / delta_step
|
|
1016
|
+
step_data['time_physical'] = (
|
|
1017
|
+
float(val[data_n]) * ureg.s + step_data['time_calculation']
|
|
1018
|
+
)
|
|
1019
|
+
|
|
1020
|
+
self.parse_thermodynamics_step(step_data)
|
|
1021
|
+
|
|
1022
|
+
def parse_workflow(self):
|
|
1023
|
+
sec_run = self.archive.run[-1]
|
|
1024
|
+
sec_calc = sec_run.get('calculation')
|
|
1025
|
+
sec_lammps = sec_run.x_lammps_section_control_parameters[-1]
|
|
1026
|
+
|
|
1027
|
+
units = self.log_parser.units
|
|
1028
|
+
if not units:
|
|
1029
|
+
self.logger.warning(
|
|
1030
|
+
'Unit information not available. Assuming "real" units in workflow metainfo!'
|
|
1031
|
+
)
|
|
1032
|
+
units = get_unit('real')
|
|
1033
|
+
energy_conversion = ureg.convert(1.0, units.get('energy'), ureg.joule)
|
|
1034
|
+
force_conversion = ureg.convert(1.0, units.get('force'), ureg.newton)
|
|
1035
|
+
temperature_conversion = ureg.convert(
|
|
1036
|
+
1.0, units.get('temperature'), ureg.kelvin
|
|
1037
|
+
)
|
|
1038
|
+
pressure_conversion = ureg.convert(1.0, units.get('pressure'), ureg.pascal)
|
|
1039
|
+
|
|
1040
|
+
minimization_stats = self.log_parser.get('minimization_stats', None)
|
|
1041
|
+
workflow = None
|
|
1042
|
+
if minimization_stats is not None:
|
|
1043
|
+
workflow = GeometryOptimization(
|
|
1044
|
+
method=GeometryOptimizationMethod(),
|
|
1045
|
+
results=GeometryOptimizationResults(),
|
|
1046
|
+
)
|
|
1047
|
+
workflow.method.type = 'atomic'
|
|
1048
|
+
|
|
1049
|
+
min_style = self.log_parser.get('min_style')
|
|
1050
|
+
min_style = min_style[0].lower() if min_style else 'none'
|
|
1051
|
+
min_style_map = {
|
|
1052
|
+
'cg': 'polak_ribiere_conjugant_gradient',
|
|
1053
|
+
'hftn': 'hessian_free_truncated_newton',
|
|
1054
|
+
'sd': 'steepest_descent',
|
|
1055
|
+
'quickmin': 'damped_dynamics',
|
|
1056
|
+
'fire': 'damped_dynamics',
|
|
1057
|
+
'spin': 'damped_dynamics',
|
|
1058
|
+
}
|
|
1059
|
+
value = min_style_map.get(
|
|
1060
|
+
min_style,
|
|
1061
|
+
[val for key, val in min_style_map.items() if key in min_style],
|
|
1062
|
+
)
|
|
1063
|
+
value = (
|
|
1064
|
+
value
|
|
1065
|
+
if not isinstance(value, list)
|
|
1066
|
+
else value[0]
|
|
1067
|
+
if len(value) != 0
|
|
1068
|
+
else None
|
|
1069
|
+
)
|
|
1070
|
+
workflow.method.method = value
|
|
1071
|
+
|
|
1072
|
+
minimization_stats = minimization_stats.split('\n')
|
|
1073
|
+
energy_index = [
|
|
1074
|
+
i
|
|
1075
|
+
for i, s in enumerate(minimization_stats)
|
|
1076
|
+
if 'Energy initial, next-to-last, final' in s
|
|
1077
|
+
]
|
|
1078
|
+
if len(energy_index) != 0:
|
|
1079
|
+
energy_stats = minimization_stats[energy_index[0] + 1].split()
|
|
1080
|
+
workflow.results.final_energy_difference = (
|
|
1081
|
+
float(energy_stats[-1]) - float(energy_stats[-2])
|
|
1082
|
+
) * energy_conversion
|
|
1083
|
+
|
|
1084
|
+
force_index = [
|
|
1085
|
+
i
|
|
1086
|
+
for i, s in enumerate(minimization_stats)
|
|
1087
|
+
if 'Force two-norm initial, final = 3167.24 0.509175' in s
|
|
1088
|
+
]
|
|
1089
|
+
if len(force_index) != 0:
|
|
1090
|
+
force_stats = minimization_stats[force_index[0]].split('=')[1]
|
|
1091
|
+
force_stats = force_stats.split()
|
|
1092
|
+
workflow.results.final_force_maximum = (
|
|
1093
|
+
float(force_stats[-1]) * force_conversion
|
|
1094
|
+
)
|
|
1095
|
+
|
|
1096
|
+
minimize_parameters = self.log_parser.get('minimize')
|
|
1097
|
+
if not minimize_parameters:
|
|
1098
|
+
minimize_parameters = self.log_parser.get('minimize/kk')
|
|
1099
|
+
if minimize_parameters:
|
|
1100
|
+
workflow.method.optimization_steps_maximum = int(
|
|
1101
|
+
minimize_parameters[0][2]
|
|
1102
|
+
)
|
|
1103
|
+
workflow.method.convergence_tolerance_force_maximum = (
|
|
1104
|
+
minimize_parameters[0][1] * force_conversion
|
|
1105
|
+
)
|
|
1106
|
+
workflow.method.convergence_tolerance_energy_difference = (
|
|
1107
|
+
minimize_parameters[0][0] * energy_conversion
|
|
1108
|
+
)
|
|
1109
|
+
|
|
1110
|
+
energies = []
|
|
1111
|
+
steps = []
|
|
1112
|
+
for calc in sec_calc:
|
|
1113
|
+
val = calc.get('energy')
|
|
1114
|
+
energy = val.get('total') if val else None
|
|
1115
|
+
if energy:
|
|
1116
|
+
energies.append(energy.value.magnitude)
|
|
1117
|
+
step = calc.get('step')
|
|
1118
|
+
steps.append(step)
|
|
1119
|
+
workflow.results.energies = energies
|
|
1120
|
+
workflow.results.steps = steps
|
|
1121
|
+
workflow.results.optimization_steps = len(energies) + 1
|
|
1122
|
+
self.archive.workflow2 = workflow
|
|
1123
|
+
|
|
1124
|
+
else:
|
|
1125
|
+
method, results = {}, {}
|
|
1126
|
+
results['finished_normally'] = self.log_parser.get('finished') is not None
|
|
1127
|
+
dump_params = sec_lammps.x_lammps_inout_control_dump.split()
|
|
1128
|
+
if ',' in dump_params[3]:
|
|
1129
|
+
coordinate_save_frequency = dump_params[3].replace(',', '')
|
|
1130
|
+
else:
|
|
1131
|
+
coordinate_save_frequency = dump_params[3]
|
|
1132
|
+
method['coordinate_save_frequency'] = int(coordinate_save_frequency)
|
|
1133
|
+
method['n_steps'] = (len(sec_run.system) - 1) * method[
|
|
1134
|
+
'coordinate_save_frequency'
|
|
1135
|
+
]
|
|
1136
|
+
if (
|
|
1137
|
+
'vx' in dump_params[7:]
|
|
1138
|
+
or 'vy' in dump_params[7:]
|
|
1139
|
+
or 'vz' in dump_params[7:]
|
|
1140
|
+
):
|
|
1141
|
+
method['velocity_save_frequency'] = int(dump_params[3])
|
|
1142
|
+
if (
|
|
1143
|
+
'fx' in dump_params[7:]
|
|
1144
|
+
or 'fy' in dump_params[7:]
|
|
1145
|
+
or 'fz' in dump_params[7:]
|
|
1146
|
+
):
|
|
1147
|
+
method['force_save_frequency'] = int(dump_params[3])
|
|
1148
|
+
if sec_lammps.x_lammps_inout_control_thermo is not None:
|
|
1149
|
+
method['thermodynamics_save_frequency'] = int(
|
|
1150
|
+
sec_lammps.x_lammps_inout_control_thermo.split()[0]
|
|
1151
|
+
)
|
|
1152
|
+
# runstyle has 2 options: Velocity-Verlet (default) or rRESPA Multi-Timescale
|
|
1153
|
+
runstyle = sec_lammps.x_lammps_inout_control_runstyle
|
|
1154
|
+
if runstyle is not None:
|
|
1155
|
+
if 'respa' in runstyle.lower:
|
|
1156
|
+
method['integrator_type'] = 'rRESPA_multitimescale'
|
|
1157
|
+
else:
|
|
1158
|
+
method['integrator_type'] = 'velocity_verlet'
|
|
1159
|
+
else:
|
|
1160
|
+
method['integrator_type'] = 'velocity_verlet'
|
|
1161
|
+
integration_timestep = self.get_time_step()
|
|
1162
|
+
method['integration_timestep'] = integration_timestep
|
|
1163
|
+
|
|
1164
|
+
thermostat_parameters, barostat_parameters = {}, {}
|
|
1165
|
+
val = self.log_parser.get('fix', None)
|
|
1166
|
+
if val is not None:
|
|
1167
|
+
val_remove_duplicates = val if len(val) == 1 else []
|
|
1168
|
+
val_tmp = val[0]
|
|
1169
|
+
for i in range(1, len(val)):
|
|
1170
|
+
if val[i][:3] == val[i - 1][:3]:
|
|
1171
|
+
val_tmp = val[i]
|
|
1172
|
+
else:
|
|
1173
|
+
val_remove_duplicates.append(val_tmp)
|
|
1174
|
+
val_tmp = val[i]
|
|
1175
|
+
val_remove_duplicates.append(val_tmp)
|
|
1176
|
+
val = val_remove_duplicates
|
|
1177
|
+
for fix in val:
|
|
1178
|
+
fix = [str(i).lower() for i in fix]
|
|
1179
|
+
fix = np.array(fix)
|
|
1180
|
+
fix_group = fix[1]
|
|
1181
|
+
fix_style = fix[2]
|
|
1182
|
+
|
|
1183
|
+
if fix_group != 'all': # ignore any complex settings
|
|
1184
|
+
continue
|
|
1185
|
+
|
|
1186
|
+
reference_temperature = None
|
|
1187
|
+
coupling_constant = None
|
|
1188
|
+
if 'nvt' in fix_style or 'npt' in fix_style:
|
|
1189
|
+
thermostat_parameters['thermostat_type'] = 'nose_hoover'
|
|
1190
|
+
if 'temp' in fix:
|
|
1191
|
+
i_temp = np.where(fix == 'temp')[0]
|
|
1192
|
+
reference_temperature = float(fix[i_temp + 2]) # stop temp
|
|
1193
|
+
coupling_constant = (
|
|
1194
|
+
float(fix[i_temp + 3]) * integration_timestep
|
|
1195
|
+
)
|
|
1196
|
+
elif fix_style == 'temp/berendsen':
|
|
1197
|
+
thermostat_parameters['thermostat_type'] = 'berendsen'
|
|
1198
|
+
i_temp = 3
|
|
1199
|
+
reference_temperature = float(fix[i_temp + 2]) # stop temp
|
|
1200
|
+
coupling_constant = (
|
|
1201
|
+
float(fix[i_temp + 3]) * integration_timestep
|
|
1202
|
+
)
|
|
1203
|
+
elif fix_style == 'temp/csvr':
|
|
1204
|
+
thermostat_parameters['thermostat_type'] = 'velocity_rescaling'
|
|
1205
|
+
i_temp = 3
|
|
1206
|
+
reference_temperature = float(fix[i_temp + 2]) # stop temp
|
|
1207
|
+
coupling_constant = (
|
|
1208
|
+
float(fix[i_temp + 3]) * integration_timestep
|
|
1209
|
+
)
|
|
1210
|
+
elif fix_style == 'temp/csld':
|
|
1211
|
+
thermostat_parameters['thermostat_type'] = (
|
|
1212
|
+
'velocity_rescaling_langevin'
|
|
1213
|
+
)
|
|
1214
|
+
i_temp = 3
|
|
1215
|
+
reference_temperature = float(fix[i_temp + 2]) # stop temp
|
|
1216
|
+
coupling_constant = (
|
|
1217
|
+
float(fix[i_temp + 3]) * integration_timestep
|
|
1218
|
+
)
|
|
1219
|
+
elif fix_style == 'langevin':
|
|
1220
|
+
thermostat_parameters['thermostat_type'] = 'langevin_schneider'
|
|
1221
|
+
i_temp = 3
|
|
1222
|
+
reference_temperature = float(fix[i_temp + 2]) # stop temp
|
|
1223
|
+
coupling_constant = (
|
|
1224
|
+
float(fix[i_temp + 3]) * integration_timestep
|
|
1225
|
+
)
|
|
1226
|
+
elif 'brownian' in fix_style:
|
|
1227
|
+
thermostat_parameters['thermostat_type'] = 'brownian'
|
|
1228
|
+
i_temp = 3
|
|
1229
|
+
reference_temperature = float(fix[i_temp + 2]) # stop temp
|
|
1230
|
+
# coupling_constant = # ignore multiple coupling parameters
|
|
1231
|
+
thermostat_parameters['reference_temperature'] = (
|
|
1232
|
+
reference_temperature * temperature_conversion
|
|
1233
|
+
)
|
|
1234
|
+
thermostat_parameters['coupling_constant'] = coupling_constant
|
|
1235
|
+
|
|
1236
|
+
barostat_type = None
|
|
1237
|
+
if 'npt' in fix_style or 'nph' in fix_style:
|
|
1238
|
+
coupling_constant = np.zeros(shape=(3, 3))
|
|
1239
|
+
reference_pressure = np.zeros(shape=(3, 3))
|
|
1240
|
+
compressibility = None
|
|
1241
|
+
barostat_type = 'nose_hoover'
|
|
1242
|
+
if 'iso' in fix:
|
|
1243
|
+
i_baro = np.where(fix == 'iso')[0]
|
|
1244
|
+
barostat_parameters['coupling_type'] = 'isotropic'
|
|
1245
|
+
np.fill_diagonal(coupling_constant, float(fix[i_baro + 3]))
|
|
1246
|
+
np.fill_diagonal(reference_pressure, float(fix[i_baro + 2]))
|
|
1247
|
+
else:
|
|
1248
|
+
barostat_parameters['coupling_type'] = 'anisotropic'
|
|
1249
|
+
if 'x' in fix:
|
|
1250
|
+
i_baro = np.where(fix == 'x')[0]
|
|
1251
|
+
coupling_constant[0, 0] = float(fix[i_baro + 3])
|
|
1252
|
+
reference_pressure[0, 0] = float(fix[i_baro + 2])
|
|
1253
|
+
if 'y' in fix:
|
|
1254
|
+
i_baro = np.where(fix == 'y')[0]
|
|
1255
|
+
coupling_constant[1, 1] = float(fix[i_baro + 3])
|
|
1256
|
+
reference_pressure[1, 1] = float(fix[i_baro + 2])
|
|
1257
|
+
if 'z' in fix:
|
|
1258
|
+
i_baro = np.where(fix == 'z')[0]
|
|
1259
|
+
coupling_constant[2, 2] = float(fix[i_baro + 3])
|
|
1260
|
+
reference_pressure[2, 2] = float(fix[i_baro + 2])
|
|
1261
|
+
if 'xy' in fix:
|
|
1262
|
+
i_baro = np.where(fix == 'xy')[0]
|
|
1263
|
+
coupling_constant[0, 1] = float(fix[i_baro + 3])
|
|
1264
|
+
coupling_constant[1, 0] = float(fix[i_baro + 3])
|
|
1265
|
+
reference_pressure[0, 1] = float(fix[i_baro + 2])
|
|
1266
|
+
reference_pressure[1, 0] = float(fix[i_baro + 2])
|
|
1267
|
+
if 'yz' in fix:
|
|
1268
|
+
i_baro = np.where(fix == 'yz')[0]
|
|
1269
|
+
coupling_constant[1, 2] = float(fix[i_baro + 3])
|
|
1270
|
+
coupling_constant[2, 1] = float(fix[i_baro + 3])
|
|
1271
|
+
reference_pressure[1, 2] = float(fix[i_baro + 2])
|
|
1272
|
+
reference_pressure[2, 1] = float(fix[i_baro + 2])
|
|
1273
|
+
if 'xz' in fix:
|
|
1274
|
+
i_baro = np.where(fix == 'xz')[0]
|
|
1275
|
+
coupling_constant[0, 3] = float(fix[i_baro + 3])
|
|
1276
|
+
coupling_constant[3, 0] = float(fix[i_baro + 3])
|
|
1277
|
+
reference_pressure[0, 3] = float(fix[i_baro + 2])
|
|
1278
|
+
reference_pressure[3, 0] = float(fix[i_baro + 2])
|
|
1279
|
+
barostat_parameters['reference_pressure'] = (
|
|
1280
|
+
reference_pressure * pressure_conversion
|
|
1281
|
+
) # stop pressure
|
|
1282
|
+
barostat_parameters['coupling_constant'] = (
|
|
1283
|
+
coupling_constant * integration_timestep
|
|
1284
|
+
)
|
|
1285
|
+
barostat_parameters['compressibility'] = compressibility
|
|
1286
|
+
|
|
1287
|
+
if fix_style == 'press/berendsen':
|
|
1288
|
+
barostat_type = 'berendsen'
|
|
1289
|
+
if 'iso' in fix:
|
|
1290
|
+
i_baro = np.where(fix == 'iso')[0]
|
|
1291
|
+
barostat_parameters['coupling_type'] = 'isotropic'
|
|
1292
|
+
np.fill_diagonal(coupling_constant, float(fix[i_baro + 3]))
|
|
1293
|
+
elif 'aniso' in fix:
|
|
1294
|
+
i_baro = np.where(fix == 'aniso')[0]
|
|
1295
|
+
barostat_parameters['coupling_type'] = 'anisotropic'
|
|
1296
|
+
coupling_constant[:3] += 1.0
|
|
1297
|
+
coupling_constant[:3] *= float(fix[i_baro + 3])
|
|
1298
|
+
else:
|
|
1299
|
+
barostat_parameters['coupling_type'] = 'anisotropic'
|
|
1300
|
+
if 'x' in fix:
|
|
1301
|
+
i_baro = np.where(fix == 'x')[0]
|
|
1302
|
+
coupling_constant[0] = float(fix[i_baro + 3])
|
|
1303
|
+
if 'y' in fix:
|
|
1304
|
+
i_baro = np.where(fix == 'y')[0]
|
|
1305
|
+
coupling_constant[1] = float(fix[i_baro + 3])
|
|
1306
|
+
if 'z' in fix:
|
|
1307
|
+
i_baro = np.where(fix == 'z')[0]
|
|
1308
|
+
coupling_constant[2] = float(fix[i_baro + 3])
|
|
1309
|
+
if 'couple' in fix:
|
|
1310
|
+
i_baro = np.where(fix == 'couple')[0]
|
|
1311
|
+
couple = fix[i_baro]
|
|
1312
|
+
if couple == 'xyz':
|
|
1313
|
+
barostat_parameters['coupling_type'] = 'isotropic'
|
|
1314
|
+
elif couple == 'xy' or couple == 'yz' or couple == 'xz':
|
|
1315
|
+
barostat_parameters['coupling_type'] = 'anisotropic'
|
|
1316
|
+
barostat_parameters['reference_pressure'] = (
|
|
1317
|
+
float(fix[i_baro + 2]) * pressure_conversion
|
|
1318
|
+
) # stop pressure
|
|
1319
|
+
barostat_parameters['coupling_constant'] = (
|
|
1320
|
+
np.ones(shape=(3, 3))
|
|
1321
|
+
* float(fix[i_baro + 3])
|
|
1322
|
+
* integration_timestep
|
|
1323
|
+
)
|
|
1324
|
+
barostat_parameters['barostat_type'] = barostat_type
|
|
1325
|
+
|
|
1326
|
+
if thermostat_parameters:
|
|
1327
|
+
method['thermodynamic_ensemble'] = (
|
|
1328
|
+
'NPT' if barostat_type == 'nose_hoover' else 'NVT'
|
|
1329
|
+
)
|
|
1330
|
+
elif barostat_type == 'nose_hoover':
|
|
1331
|
+
method['thermodynamic_ensemble'] = 'NPH'
|
|
1332
|
+
else:
|
|
1333
|
+
method['thermodynamic_ensemble'] = 'NVE'
|
|
1334
|
+
|
|
1335
|
+
method['thermostat_parameters'] = thermostat_parameters
|
|
1336
|
+
method['barostat_parameters'] = barostat_parameters
|
|
1337
|
+
|
|
1338
|
+
self.parse_md_workflow(dict(method=method, results=results))
|
|
1339
|
+
|
|
1340
|
+
def parse_system(self):
|
|
1341
|
+
sec_run = self.archive.run[-1]
|
|
1342
|
+
|
|
1343
|
+
n_traj = self.traj_parsers.eval('n_frames')
|
|
1344
|
+
if n_traj is None:
|
|
1345
|
+
return
|
|
1346
|
+
|
|
1347
|
+
self.n_atoms = [self.traj_parsers.eval('get_n_atoms', n) for n in range(n_traj)]
|
|
1348
|
+
self.trajectory_steps = [
|
|
1349
|
+
step
|
|
1350
|
+
for n in range(n_traj)
|
|
1351
|
+
if (step := self.traj_parsers.eval('get_step', n)) is not None
|
|
1352
|
+
]
|
|
1353
|
+
|
|
1354
|
+
units = self.log_parser.units
|
|
1355
|
+
|
|
1356
|
+
def apply_unit(value, unit):
|
|
1357
|
+
if not hasattr(value, 'units'):
|
|
1358
|
+
value = value * units.get(unit, 1)
|
|
1359
|
+
return value
|
|
1360
|
+
|
|
1361
|
+
def get_composition(children_names):
|
|
1362
|
+
children_count_tup = np.unique(children_names, return_counts=True)
|
|
1363
|
+
formula = ''.join(
|
|
1364
|
+
[f'{name}({count})' for name, count in zip(*children_count_tup)]
|
|
1365
|
+
)
|
|
1366
|
+
return formula
|
|
1367
|
+
|
|
1368
|
+
for step in self.trajectory_steps:
|
|
1369
|
+
traj_n = self._trajectory_steps.index(step)
|
|
1370
|
+
lattice_vectors = self.traj_parsers.eval('get_lattice_vectors', traj_n)
|
|
1371
|
+
if lattice_vectors is not None:
|
|
1372
|
+
lattice_vectors = apply_unit(lattice_vectors, 'distance')
|
|
1373
|
+
velocities = self.traj_parsers.eval('get_velocities', traj_n)
|
|
1374
|
+
if velocities is not None:
|
|
1375
|
+
velocities = apply_unit(velocities, 'velocity')
|
|
1376
|
+
bond_list = []
|
|
1377
|
+
if traj_n == 0: # TODO add references to the bond list for other steps
|
|
1378
|
+
bond_list = get_bond_list_from_model_contributions(
|
|
1379
|
+
sec_run, method_index=-1, model_index=-1
|
|
1380
|
+
)
|
|
1381
|
+
self.parse_trajectory_step(
|
|
1382
|
+
{
|
|
1383
|
+
'atoms': {
|
|
1384
|
+
'n_atoms': self.traj_parsers.eval('get_n_atoms', traj_n),
|
|
1385
|
+
'lattice_vectors': lattice_vectors,
|
|
1386
|
+
'periodic': self.traj_parsers.eval('get_pbc', traj_n),
|
|
1387
|
+
'positions': apply_unit(
|
|
1388
|
+
self.traj_parsers.eval('get_positions', traj_n), 'distance'
|
|
1389
|
+
),
|
|
1390
|
+
'labels': self.traj_parsers.eval('get_atom_labels', traj_n),
|
|
1391
|
+
'velocities': velocities,
|
|
1392
|
+
'bond_list': bond_list if bond_list else None,
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
)
|
|
1396
|
+
|
|
1397
|
+
if not sec_run.system:
|
|
1398
|
+
return
|
|
1399
|
+
|
|
1400
|
+
sec_system = sec_run.system[-1]
|
|
1401
|
+
# parse atomsgroup (moltypes --> molecules --> residues)
|
|
1402
|
+
atoms_info = self._mdanalysistraj_parser.get('atoms_info', None)
|
|
1403
|
+
if atoms_info is None:
|
|
1404
|
+
atoms_info = self.traj_parsers.eval('atoms_info')
|
|
1405
|
+
if isinstance(atoms_info, list):
|
|
1406
|
+
atoms_info = (
|
|
1407
|
+
atoms_info[0] if atoms_info else None
|
|
1408
|
+
) # using info from the initial frame
|
|
1409
|
+
if atoms_info is not None:
|
|
1410
|
+
atoms_moltypes = np.array(atoms_info.get('moltypes', []))
|
|
1411
|
+
atoms_molnums = np.array(atoms_info.get('molnums', []))
|
|
1412
|
+
atoms_resids = np.array(atoms_info.get('resids', []))
|
|
1413
|
+
atoms_elements = np.array(atoms_info.get('elements', ['X'] * self.n_atoms))
|
|
1414
|
+
atoms_types = np.array(atoms_info.get('types', []))
|
|
1415
|
+
atom_labels = sec_system.atoms.get('labels')
|
|
1416
|
+
if 'X' in atoms_elements:
|
|
1417
|
+
atoms_elements = (
|
|
1418
|
+
np.array(atom_labels)
|
|
1419
|
+
if atom_labels and 'X' not in atom_labels
|
|
1420
|
+
else atoms_types
|
|
1421
|
+
)
|
|
1422
|
+
atoms_resnames = np.array(atoms_info.get('resnames', []))
|
|
1423
|
+
moltypes = np.unique(atoms_moltypes)
|
|
1424
|
+
for i_moltype, moltype in enumerate(moltypes):
|
|
1425
|
+
# Only add atomsgroup for initial system for now
|
|
1426
|
+
sec_molecule_group = AtomsGroup()
|
|
1427
|
+
sec_run.system[0].atoms_group.append(sec_molecule_group)
|
|
1428
|
+
sec_molecule_group.label = f'group_{moltype}'
|
|
1429
|
+
sec_molecule_group.type = 'molecule_group'
|
|
1430
|
+
sec_molecule_group.index = i_moltype
|
|
1431
|
+
sec_molecule_group.atom_indices = np.where(atoms_moltypes == moltype)[0]
|
|
1432
|
+
sec_molecule_group.n_atoms = len(sec_molecule_group.atom_indices)
|
|
1433
|
+
sec_molecule_group.is_molecule = False
|
|
1434
|
+
# mol_nums is the molecule identifier for each atom
|
|
1435
|
+
mol_nums = atoms_molnums[sec_molecule_group.atom_indices]
|
|
1436
|
+
moltype_count = np.unique(mol_nums).shape[0]
|
|
1437
|
+
sec_molecule_group.composition_formula = f'{moltype}({moltype_count})'
|
|
1438
|
+
|
|
1439
|
+
molecules = atoms_molnums
|
|
1440
|
+
for i_molecule, molecule in enumerate(
|
|
1441
|
+
np.unique(molecules[sec_molecule_group.atom_indices])
|
|
1442
|
+
):
|
|
1443
|
+
sec_molecule = AtomsGroup()
|
|
1444
|
+
sec_molecule_group.atoms_group.append(sec_molecule)
|
|
1445
|
+
sec_molecule.index = i_molecule
|
|
1446
|
+
sec_molecule.atom_indices = np.where(molecules == molecule)[0]
|
|
1447
|
+
sec_molecule.n_atoms = len(sec_molecule.atom_indices)
|
|
1448
|
+
# use first particle to get the moltype
|
|
1449
|
+
# not sure why but this value is being cast to int, cast back to str
|
|
1450
|
+
sec_molecule.label = str(
|
|
1451
|
+
atoms_moltypes[sec_molecule.atom_indices[0]]
|
|
1452
|
+
)
|
|
1453
|
+
sec_molecule.type = 'molecule'
|
|
1454
|
+
sec_molecule.is_molecule = True
|
|
1455
|
+
|
|
1456
|
+
mol_resids = np.unique(atoms_resids[sec_molecule.atom_indices])
|
|
1457
|
+
n_res = mol_resids.shape[0]
|
|
1458
|
+
if n_res == 1:
|
|
1459
|
+
elements = atoms_elements[sec_molecule.atom_indices]
|
|
1460
|
+
sec_molecule.composition_formula = get_composition(elements)
|
|
1461
|
+
else:
|
|
1462
|
+
mol_resnames = atoms_resnames[sec_molecule.atom_indices]
|
|
1463
|
+
restypes = np.unique(mol_resnames)
|
|
1464
|
+
for i_restype, restype in enumerate(restypes):
|
|
1465
|
+
sec_monomer_group = AtomsGroup()
|
|
1466
|
+
sec_molecule.atoms_group.append(sec_monomer_group)
|
|
1467
|
+
restype_indices = np.where(atoms_resnames == restype)[0]
|
|
1468
|
+
sec_monomer_group.label = f'group_{restype}'
|
|
1469
|
+
sec_monomer_group.type = 'monomer_group'
|
|
1470
|
+
sec_monomer_group.index = i_restype
|
|
1471
|
+
sec_monomer_group.atom_indices = np.intersect1d(
|
|
1472
|
+
restype_indices, sec_molecule.atom_indices
|
|
1473
|
+
)
|
|
1474
|
+
sec_monomer_group.n_atoms = len(
|
|
1475
|
+
sec_monomer_group.atom_indices
|
|
1476
|
+
)
|
|
1477
|
+
sec_monomer_group.is_molecule = False
|
|
1478
|
+
|
|
1479
|
+
restype_resids = np.unique(
|
|
1480
|
+
atoms_resids[sec_monomer_group.atom_indices]
|
|
1481
|
+
)
|
|
1482
|
+
restype_count = restype_resids.shape[0]
|
|
1483
|
+
sec_monomer_group.composition_formula = (
|
|
1484
|
+
f'{restype}({restype_count})'
|
|
1485
|
+
)
|
|
1486
|
+
for i_res, res_id in enumerate(restype_resids):
|
|
1487
|
+
sec_residue = AtomsGroup()
|
|
1488
|
+
sec_monomer_group.atoms_group.append(sec_residue)
|
|
1489
|
+
sec_residue.index = i_res
|
|
1490
|
+
atom_indices = np.where(atoms_resids == res_id)[0]
|
|
1491
|
+
sec_residue.atom_indices = np.intersect1d(
|
|
1492
|
+
atom_indices, sec_monomer_group.atom_indices
|
|
1493
|
+
)
|
|
1494
|
+
sec_residue.n_atoms = len(sec_residue.atom_indices)
|
|
1495
|
+
sec_residue.label = str(restype)
|
|
1496
|
+
sec_residue.type = 'monomer'
|
|
1497
|
+
sec_residue.is_molecule = False
|
|
1498
|
+
elements = atoms_elements[sec_residue.atom_indices]
|
|
1499
|
+
sec_residue.composition_formula = get_composition(
|
|
1500
|
+
elements
|
|
1501
|
+
)
|
|
1502
|
+
|
|
1503
|
+
names = atoms_resnames[sec_molecule.atom_indices]
|
|
1504
|
+
ids = atoms_resids[sec_molecule.atom_indices]
|
|
1505
|
+
# filter for the first instance of each residue, as to not overcount
|
|
1506
|
+
__, ids_count = np.unique(ids, return_counts=True)
|
|
1507
|
+
# get the index of the first atom of each residue
|
|
1508
|
+
ids_firstatom = np.cumsum(ids_count)[:-1]
|
|
1509
|
+
# add the 0th index manually
|
|
1510
|
+
ids_firstatom = np.insert(ids_firstatom, 0, 0)
|
|
1511
|
+
names_firstatom = names[ids_firstatom]
|
|
1512
|
+
sec_molecule.composition_formula = get_composition(
|
|
1513
|
+
names_firstatom
|
|
1514
|
+
)
|
|
1515
|
+
|
|
1516
|
+
def parse_method(self):
|
|
1517
|
+
sec_run = self.archive.run[-1]
|
|
1518
|
+
|
|
1519
|
+
if self.traj_parsers[0].mainfile is None or self.data_parser.mainfile is None:
|
|
1520
|
+
return
|
|
1521
|
+
|
|
1522
|
+
if self.traj_parsers.eval('n_frames') is None:
|
|
1523
|
+
return
|
|
1524
|
+
|
|
1525
|
+
sec_method = Method()
|
|
1526
|
+
sec_run.method.append(sec_method)
|
|
1527
|
+
sec_force_field = ForceField()
|
|
1528
|
+
sec_method.force_field = sec_force_field
|
|
1529
|
+
sec_model = Model()
|
|
1530
|
+
sec_force_field.model.append(sec_model)
|
|
1531
|
+
|
|
1532
|
+
# Old parsing of method with text parser
|
|
1533
|
+
masses = self.data_parser.get('Masses', None)
|
|
1534
|
+
self.traj_parsers[0].masses = masses
|
|
1535
|
+
# @Landinesa: we should be able to set the atom masses with the TrajParser, but I don't quite understand how to use this.
|
|
1536
|
+
# Can you add the implementation here, and then we can make the MDA implementation below as a backup?
|
|
1537
|
+
# Can you also get the charges somehow?
|
|
1538
|
+
|
|
1539
|
+
# parse method with MDAnalysis (should be a backup for the charges and masses...but the interactions are most easily read from the MDA universe right now)
|
|
1540
|
+
n_atoms = self.traj_parsers.eval('get_n_atoms', 0)
|
|
1541
|
+
if n_atoms is not None:
|
|
1542
|
+
atoms_info = self._mdanalysistraj_parser.get('atoms_info', None)
|
|
1543
|
+
for n in range(n_atoms):
|
|
1544
|
+
sec_atom = AtomParameters()
|
|
1545
|
+
sec_method.atom_parameters.append(sec_atom)
|
|
1546
|
+
sec_atom.charge = atoms_info.get('charges', [None] * (n + 1))[n]
|
|
1547
|
+
sec_atom.mass = atoms_info.get('masses', [None] * (n + 1))[n]
|
|
1548
|
+
|
|
1549
|
+
# TODO address case types are numbered instead of giving atom labels (fix tests accordingly)
|
|
1550
|
+
interactions = self._mdanalysistraj_parser.get_interactions()
|
|
1551
|
+
# for interaction in interactions:
|
|
1552
|
+
# for key, val in interaction.items():
|
|
1553
|
+
# quantity_def = Interaction.m_def.all_quantities.get(key)
|
|
1554
|
+
# if quantity_def and quantity_def.shape:
|
|
1555
|
+
# # TODO reshape properly
|
|
1556
|
+
# interaction[key] = [val]
|
|
1557
|
+
self.parse_interactions(interactions, sec_model)
|
|
1558
|
+
|
|
1559
|
+
# Force Calculation Parameters
|
|
1560
|
+
sec_force_calculations = ForceCalculations()
|
|
1561
|
+
sec_force_field.force_calculations = sec_force_calculations
|
|
1562
|
+
for pairstyle in self.log_parser.get('pair_style', []):
|
|
1563
|
+
pairstyle_args = pairstyle[1:]
|
|
1564
|
+
pairstyle = pairstyle[0].lower()
|
|
1565
|
+
if (
|
|
1566
|
+
'lj' in pairstyle and 'coul' not in pairstyle
|
|
1567
|
+
): # only cover the simplest case
|
|
1568
|
+
sec_force_calculations.vdw_cutoff = (
|
|
1569
|
+
float(pairstyle_args[-1]) * ureg.nanometer
|
|
1570
|
+
)
|
|
1571
|
+
if 'coul' in pairstyle:
|
|
1572
|
+
if 'streitz' in pairstyle:
|
|
1573
|
+
cutoff = float(pairstyle_args[0])
|
|
1574
|
+
else:
|
|
1575
|
+
cutoff = float(pairstyle_args[-1])
|
|
1576
|
+
sec_force_calculations.coulomb_cutoff = cutoff * ureg.nanometer
|
|
1577
|
+
val = self.log_parser.get('kspace_style', None)
|
|
1578
|
+
if val is not None:
|
|
1579
|
+
kspacestyle = val[0][0].lower()
|
|
1580
|
+
if 'ewald' in kspacestyle:
|
|
1581
|
+
sec_force_calculations.coulomb_type = 'ewald'
|
|
1582
|
+
elif 'pppm' in kspacestyle:
|
|
1583
|
+
sec_force_calculations.coulomb_type = (
|
|
1584
|
+
'particle_particle_particle_mesh'
|
|
1585
|
+
)
|
|
1586
|
+
elif 'msm' in kspacestyle:
|
|
1587
|
+
sec_force_calculations.coulomb_type = 'multilevel_summation'
|
|
1588
|
+
|
|
1589
|
+
sec_neighbor_searching = NeighborSearching()
|
|
1590
|
+
sec_force_calculations.neighbor_searching = sec_neighbor_searching
|
|
1591
|
+
val = self.log_parser.get('neighbor', None)
|
|
1592
|
+
if val is not None:
|
|
1593
|
+
neighbor = val[0][0] # just use the first instance for now
|
|
1594
|
+
vdw_cutoff = sec_force_calculations.vdw_cutoff
|
|
1595
|
+
if vdw_cutoff is not None:
|
|
1596
|
+
sec_neighbor_searching.neighbor_update_cutoff = (
|
|
1597
|
+
float(neighbor) * ureg.nanometer
|
|
1598
|
+
)
|
|
1599
|
+
sec_neighbor_searching.neighbor_update_cutoff += vdw_cutoff
|
|
1600
|
+
val = self.log_parser.get('neigh_modify', None)
|
|
1601
|
+
if val is not None:
|
|
1602
|
+
neighmodify = val[0] # just use the first instace for now
|
|
1603
|
+
neighmodify = np.array([str(i).lower() for i in neighmodify])
|
|
1604
|
+
if 'every' in neighmodify:
|
|
1605
|
+
index = np.where(neighmodify == 'every')[0]
|
|
1606
|
+
sec_neighbor_searching.neighbor_update_frequency = int(
|
|
1607
|
+
neighmodify[index + 1]
|
|
1608
|
+
)
|
|
1609
|
+
|
|
1610
|
+
def parse_input(self):
|
|
1611
|
+
sec_run = self.archive.run[-1]
|
|
1612
|
+
sec_input_output_files = x_lammps_section_input_output_files()
|
|
1613
|
+
sec_run.x_lammps_section_input_output_files.append(sec_input_output_files)
|
|
1614
|
+
|
|
1615
|
+
if self.data_parser.mainfile is not None:
|
|
1616
|
+
sec_input_output_files.x_lammps_inout_file_data = os.path.basename(
|
|
1617
|
+
self.data_parser.mainfile
|
|
1618
|
+
)
|
|
1619
|
+
|
|
1620
|
+
if self.traj_parsers[0].mainfile is not None:
|
|
1621
|
+
sec_input_output_files.x_lammps_inout_file_trajectory = os.path.basename(
|
|
1622
|
+
self.traj_parsers[0].mainfile
|
|
1623
|
+
)
|
|
1624
|
+
|
|
1625
|
+
sec_control_parameters = x_lammps_section_control_parameters()
|
|
1626
|
+
sec_run.x_lammps_section_control_parameters.append(sec_control_parameters)
|
|
1627
|
+
keys = self.log_parser._commands
|
|
1628
|
+
for key in keys:
|
|
1629
|
+
val = self.log_parser.get(key, None)
|
|
1630
|
+
if val is None:
|
|
1631
|
+
continue
|
|
1632
|
+
val = val[0] if len(val) == 1 else val
|
|
1633
|
+
key = (
|
|
1634
|
+
'x_lammps_inout_control_%s'
|
|
1635
|
+
% key.replace('_', '').replace('/', '').lower()
|
|
1636
|
+
)
|
|
1637
|
+
if hasattr(sec_control_parameters, key):
|
|
1638
|
+
if isinstance(val, list):
|
|
1639
|
+
val = ' '.join([str(v) for v in val])
|
|
1640
|
+
setattr(sec_control_parameters, key, str(val))
|
|
1641
|
+
|
|
1642
|
+
def write_to_archive(self):
|
|
1643
|
+
self.log_parser.mainfile = self.mainfile
|
|
1644
|
+
self.log_parser.logger = self.logger
|
|
1645
|
+
self._traj_parser.logger = self.logger
|
|
1646
|
+
self._mdanalysistraj_parser.logger = self.logger
|
|
1647
|
+
self._xyztraj_parser.logger = self.logger
|
|
1648
|
+
self.data_parser.logger = self.logger
|
|
1649
|
+
# auxilliary log parser for thermo data
|
|
1650
|
+
self.aux_log_parser.logger = self.logger
|
|
1651
|
+
self.log_parser._units = None
|
|
1652
|
+
self._traj_parser._chemical_symbols = None
|
|
1653
|
+
|
|
1654
|
+
sec_run = Run()
|
|
1655
|
+
self.archive.run.append(sec_run)
|
|
1656
|
+
|
|
1657
|
+
# parse basic
|
|
1658
|
+
sec_run.program = Program(
|
|
1659
|
+
name='LAMMPS', version=self.log_parser.get('program_version', '')
|
|
1660
|
+
)
|
|
1661
|
+
|
|
1662
|
+
# parse data file associated with calculation
|
|
1663
|
+
data_files = self.log_parser.get_data_files()
|
|
1664
|
+
if len(data_files) > 1:
|
|
1665
|
+
self.logger.warning('Multiple data files are specified')
|
|
1666
|
+
if data_files:
|
|
1667
|
+
self.data_parser.mainfile = data_files[0]
|
|
1668
|
+
|
|
1669
|
+
# parse trajectorty file associated with calculation
|
|
1670
|
+
traj_files = self.log_parser.get_traj_files()
|
|
1671
|
+
if len(traj_files) > 1:
|
|
1672
|
+
self.logger.warning('Multiple traj files are specified')
|
|
1673
|
+
|
|
1674
|
+
parsers = []
|
|
1675
|
+
for n, traj_file in enumerate(traj_files):
|
|
1676
|
+
# parser initialization for each traj file cannot be avoided as there are
|
|
1677
|
+
# cases where traj files can share the same parser
|
|
1678
|
+
file_type = self.log_parser.get(
|
|
1679
|
+
'dump', [[1, 'all', traj_file[-3:]]] * (n + 1)
|
|
1680
|
+
)[n][2]
|
|
1681
|
+
if file_type == 'dcd' and data_files:
|
|
1682
|
+
traj_parser = MDAnalysisParser(topology_format='DATA', format='DCD')
|
|
1683
|
+
traj_parser.mainfile = data_files[0]
|
|
1684
|
+
traj_parser.auxilliary_files = [traj_file]
|
|
1685
|
+
self._mdanalysistraj_parser = traj_parser
|
|
1686
|
+
elif file_type == 'xyz' and data_files:
|
|
1687
|
+
traj_parser = MDAnalysisParser(topology_format='DATA', format='XYZ')
|
|
1688
|
+
traj_parser.mainfile = data_files[0]
|
|
1689
|
+
traj_parser.auxilliary_files = [traj_file]
|
|
1690
|
+
self._mdanalysistraj_parser = traj_parser
|
|
1691
|
+
elif file_type == 'custom' and data_files:
|
|
1692
|
+
custom_options = self.log_parser.get('dump')[n][5:]
|
|
1693
|
+
custom_options = [
|
|
1694
|
+
option.replace('xu', 'x') for option in custom_options
|
|
1695
|
+
]
|
|
1696
|
+
custom_options = [
|
|
1697
|
+
option.replace('yu', 'y') for option in custom_options
|
|
1698
|
+
]
|
|
1699
|
+
custom_options = [
|
|
1700
|
+
option.replace('zu', 'z') for option in custom_options
|
|
1701
|
+
]
|
|
1702
|
+
custom_options = ' '.join(custom_options)
|
|
1703
|
+
traj_parser = MDAnalysisParser(
|
|
1704
|
+
topology_format='DATA',
|
|
1705
|
+
format='LAMMPSDUMP',
|
|
1706
|
+
atom_style=custom_options,
|
|
1707
|
+
)
|
|
1708
|
+
if data_files:
|
|
1709
|
+
traj_parser.mainfile = data_files[0]
|
|
1710
|
+
traj_parser.auxilliary_files = [traj_file]
|
|
1711
|
+
# try to check if MDAnalysis can construct the universe or at least parse
|
|
1712
|
+
# the atoms, otherwise will fall back to TrajParser
|
|
1713
|
+
if traj_parser.universe is None or 'X' in traj_parser.get(
|
|
1714
|
+
'atoms_info', {}
|
|
1715
|
+
).get('names', []):
|
|
1716
|
+
# mda necessary to calculate rdf and atomsgroup
|
|
1717
|
+
if n == 0:
|
|
1718
|
+
self._mdanalysistraj_parser = traj_parser
|
|
1719
|
+
traj_parser = TrajParser()
|
|
1720
|
+
traj_parser.mainfile = traj_file
|
|
1721
|
+
else:
|
|
1722
|
+
traj_parser = TrajParser()
|
|
1723
|
+
traj_parser.mainfile = traj_file
|
|
1724
|
+
# TODO provide support for other file types
|
|
1725
|
+
parsers.append(traj_parser)
|
|
1726
|
+
|
|
1727
|
+
self.traj_parsers = TrajParsers(parsers)
|
|
1728
|
+
if self.traj_parsers[0] is None:
|
|
1729
|
+
return
|
|
1730
|
+
|
|
1731
|
+
# parse data from auxiliary log file
|
|
1732
|
+
if self.log_parser.get('log') is not None:
|
|
1733
|
+
self.aux_log_parser.mainfile = os.path.join(
|
|
1734
|
+
self.log_parser.maindir, self.log_parser.get('log')[0]
|
|
1735
|
+
)
|
|
1736
|
+
# we assign units here which is read from log parser
|
|
1737
|
+
self.aux_log_parser._units = self.log_parser.units
|
|
1738
|
+
|
|
1739
|
+
self.parse_method()
|
|
1740
|
+
|
|
1741
|
+
self.parse_system()
|
|
1742
|
+
|
|
1743
|
+
# include input controls from log file
|
|
1744
|
+
self.parse_input()
|
|
1745
|
+
|
|
1746
|
+
# parse thermodynamic data from log file
|
|
1747
|
+
self.parse_thermodynamic_data()
|
|
1748
|
+
|
|
1749
|
+
self.parse_workflow()
|
|
1750
|
+
|
|
1751
|
+
self._mdanalysistraj_parser.close()
|
|
1752
|
+
for parser in parsers:
|
|
1753
|
+
parser.close()
|