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,798 @@
|
|
|
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
|
+
|
|
20
|
+
import os
|
|
21
|
+
import numpy as np
|
|
22
|
+
from typing import Dict, Any
|
|
23
|
+
|
|
24
|
+
from nomad.units import ureg
|
|
25
|
+
from nomad.parsing.file_parser import TextParser, Quantity
|
|
26
|
+
from runschema.run import Run, Program
|
|
27
|
+
from runschema.method import (
|
|
28
|
+
Method,
|
|
29
|
+
ForceField,
|
|
30
|
+
Model,
|
|
31
|
+
Interaction,
|
|
32
|
+
AtomParameters,
|
|
33
|
+
MoleculeParameters,
|
|
34
|
+
)
|
|
35
|
+
from atomisticparsers.utils import MDParser
|
|
36
|
+
from .metainfo.dl_poly import m_package # pylint: disable=unused-import
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
re_f = r'[-+]?\d*\.\d*(?:[Ee][-+]\d+)?'
|
|
40
|
+
re_n = r'[\n\r]'
|
|
41
|
+
MOL = 6.02214076e23
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class TrajParser(TextParser):
|
|
45
|
+
def init_quantities(self):
|
|
46
|
+
def to_info(val_in):
|
|
47
|
+
val = val_in.strip().split()
|
|
48
|
+
if len(val) == 5:
|
|
49
|
+
return dict(
|
|
50
|
+
step=val[0],
|
|
51
|
+
n_atoms=val[1],
|
|
52
|
+
log_level=val[2],
|
|
53
|
+
pbc_type=val[3],
|
|
54
|
+
dt=val[4],
|
|
55
|
+
)
|
|
56
|
+
elif len(val) == 2:
|
|
57
|
+
return dict(log_level=val[0], pbc_type=val[1])
|
|
58
|
+
elif len(val) > 2:
|
|
59
|
+
return dict(log_level=val[0], pbc_type=val[1], n_atoms=val[2])
|
|
60
|
+
return dict()
|
|
61
|
+
|
|
62
|
+
self._quantities = [
|
|
63
|
+
Quantity(
|
|
64
|
+
'frame',
|
|
65
|
+
rf'(\d+ +\d+.*\s+{re_f}[\s\S]+?)(?:timestep|\Z)',
|
|
66
|
+
repeats=True,
|
|
67
|
+
sub_parser=TextParser(
|
|
68
|
+
quantities=[
|
|
69
|
+
Quantity(
|
|
70
|
+
'info',
|
|
71
|
+
# rf'(\d+ +\d+[ \d]*(?:{re_f})*) *(?:{re_f})*) *{re_n}',
|
|
72
|
+
rf'(\d+ +\d+[ \d\.Ee\-\+]*){re_n}',
|
|
73
|
+
str_operation=to_info,
|
|
74
|
+
),
|
|
75
|
+
Quantity(
|
|
76
|
+
'lattice_vectors',
|
|
77
|
+
rf'({re_f} +{re_f} +{re_f})\s+'
|
|
78
|
+
rf'({re_f} +{re_f} +{re_f})\s+'
|
|
79
|
+
rf'({re_f} +{re_f} +{re_f})',
|
|
80
|
+
dtype=np.dtype(np.float64),
|
|
81
|
+
shape=(3, 3),
|
|
82
|
+
),
|
|
83
|
+
Quantity(
|
|
84
|
+
'atoms',
|
|
85
|
+
rf'({re_n} *[A-Za-z]+\S* *\d*.*(?:\s+{re_f})+)',
|
|
86
|
+
repeats=True,
|
|
87
|
+
sub_parser=TextParser(
|
|
88
|
+
quantities=[
|
|
89
|
+
Quantity(
|
|
90
|
+
'label', rf'{re_n} *([A-Z][a-z]*)\S* *\d*.*'
|
|
91
|
+
),
|
|
92
|
+
Quantity(
|
|
93
|
+
'array',
|
|
94
|
+
rf'({re_f} +{re_f} +{re_f})',
|
|
95
|
+
repeats=True,
|
|
96
|
+
dtype=np.dtype(np.float64),
|
|
97
|
+
),
|
|
98
|
+
]
|
|
99
|
+
),
|
|
100
|
+
),
|
|
101
|
+
]
|
|
102
|
+
),
|
|
103
|
+
)
|
|
104
|
+
]
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class FieldParser(TextParser):
|
|
108
|
+
def __init__(self):
|
|
109
|
+
super().__init__()
|
|
110
|
+
|
|
111
|
+
def init_quantities(self):
|
|
112
|
+
def to_atoms(val_in):
|
|
113
|
+
keys = [
|
|
114
|
+
'label',
|
|
115
|
+
'mass',
|
|
116
|
+
'charge',
|
|
117
|
+
'x_dl_poly_nrept',
|
|
118
|
+
'x_dl_poly_ifrz',
|
|
119
|
+
'x_dl_poly_igrp',
|
|
120
|
+
]
|
|
121
|
+
return [
|
|
122
|
+
{keys[n]: val_n for n, val_n in enumerate(val[: len(keys)])}
|
|
123
|
+
for val in [v.split() for v in val_in.strip().splitlines()]
|
|
124
|
+
]
|
|
125
|
+
|
|
126
|
+
def to_shell(val_in):
|
|
127
|
+
keys = [
|
|
128
|
+
'x_dl_poly_atom_indices_core',
|
|
129
|
+
'x_dl_poly_atom_indices_shell',
|
|
130
|
+
'x_dl_poly_force_constant',
|
|
131
|
+
'x_dl_poly_force_constant_anharmonic',
|
|
132
|
+
]
|
|
133
|
+
return [
|
|
134
|
+
{keys[n]: val_n for n, val_n in enumerate(val[: len(keys)])}
|
|
135
|
+
for val in [v.split() for v in val_in.strip().splitlines()]
|
|
136
|
+
]
|
|
137
|
+
|
|
138
|
+
def to_bonds(val_in):
|
|
139
|
+
val = [v.split() for v in val_in.strip().splitlines()]
|
|
140
|
+
potentials = {
|
|
141
|
+
'harm': 'Harmonic',
|
|
142
|
+
'mors': 'Morse',
|
|
143
|
+
'12-6': '12-6',
|
|
144
|
+
'rhrm': 'Restraint',
|
|
145
|
+
'quar': 'Quartic',
|
|
146
|
+
'buck': 'Buckingham',
|
|
147
|
+
'fene': 'FENE',
|
|
148
|
+
'coul': 'Coulombic',
|
|
149
|
+
}
|
|
150
|
+
interactions = []
|
|
151
|
+
for val_n in val:
|
|
152
|
+
interactions.append(
|
|
153
|
+
dict(
|
|
154
|
+
functional_form=potentials.get(val_n[0], val_n[0]),
|
|
155
|
+
# atom index starts from 1
|
|
156
|
+
atom_indices=[[int(n) - 1 for n in val_n[1:3]]],
|
|
157
|
+
parameters=[float(v) for v in val_n[3:]],
|
|
158
|
+
)
|
|
159
|
+
)
|
|
160
|
+
return interactions
|
|
161
|
+
|
|
162
|
+
def to_angles(val_in):
|
|
163
|
+
val = [v.split() for v in val_in.strip().splitlines()]
|
|
164
|
+
potentials = {
|
|
165
|
+
'harm': 'Harmonic',
|
|
166
|
+
'quar': 'Quartic',
|
|
167
|
+
'thrm': 'Truncated harmonic',
|
|
168
|
+
'sharm': 'Screened harmonic',
|
|
169
|
+
'bvs1': 'Screened Vessa',
|
|
170
|
+
'bvs2': 'Truncated Vessa',
|
|
171
|
+
'bcos': 'Harmonic cosine',
|
|
172
|
+
'cos': 'Cosine',
|
|
173
|
+
'mmsb': 'MM strech-bend',
|
|
174
|
+
'stst': 'Compass stretch-stretch',
|
|
175
|
+
'stbe': 'Compass stretch-bend',
|
|
176
|
+
'cmps': 'Compass all terms',
|
|
177
|
+
}
|
|
178
|
+
interactions = []
|
|
179
|
+
for val_n in val:
|
|
180
|
+
interactions.append(
|
|
181
|
+
dict(
|
|
182
|
+
functional_form=potentials.get(val_n[0], val_n[0]),
|
|
183
|
+
atom_indices=[[int(n) - 1 for n in val_n[1:4]]],
|
|
184
|
+
parameters=[float(v) for v in val_n[4:]],
|
|
185
|
+
)
|
|
186
|
+
)
|
|
187
|
+
return interactions
|
|
188
|
+
|
|
189
|
+
def to_dihedrals(val_in):
|
|
190
|
+
val = [v.split() for v in val_in.strip().splitlines()]
|
|
191
|
+
potentials = {
|
|
192
|
+
'cos': 'Cosine',
|
|
193
|
+
'harm': 'Harmonic',
|
|
194
|
+
'bcos': 'Harmonic cosine',
|
|
195
|
+
'cos3': 'Triple cosine',
|
|
196
|
+
'ryck': 'Ryckaert-Bellemans',
|
|
197
|
+
'rbf': 'Fluorinated Ryckaert-Bellemans',
|
|
198
|
+
'opls': 'OPLS',
|
|
199
|
+
}
|
|
200
|
+
interactions = []
|
|
201
|
+
for val_n in val:
|
|
202
|
+
interactions.append(
|
|
203
|
+
dict(
|
|
204
|
+
functional_form=potentials.get(val_n[0], val_n[0]),
|
|
205
|
+
atom_indices=[[int(n) - 1 for n in val_n[1:5]]],
|
|
206
|
+
parameters=[float(v) for v in val_n[5:]],
|
|
207
|
+
)
|
|
208
|
+
)
|
|
209
|
+
return interactions
|
|
210
|
+
|
|
211
|
+
def to_inversions(val_in):
|
|
212
|
+
val = [v.split() for v in val_in.strip().splitlines()]
|
|
213
|
+
potentials = {
|
|
214
|
+
'harm': 'Harmonic',
|
|
215
|
+
'bcos': 'Harmonic cosine',
|
|
216
|
+
'plan': 'Planar',
|
|
217
|
+
'calc': 'Calcite',
|
|
218
|
+
}
|
|
219
|
+
interactions = []
|
|
220
|
+
for val_n in val:
|
|
221
|
+
interactions.append(
|
|
222
|
+
dict(
|
|
223
|
+
functional_form=potentials.get(val_n[0], val_n[0]),
|
|
224
|
+
atom_indices=[[int(n) - 1 for n in val_n[1:5]]],
|
|
225
|
+
parameters=[float(v) for v in val_n[5:]],
|
|
226
|
+
)
|
|
227
|
+
)
|
|
228
|
+
return interactions
|
|
229
|
+
|
|
230
|
+
def to_teth(val_in):
|
|
231
|
+
val = [v.split() for v in val_in.strip().splitlines()]
|
|
232
|
+
potentials = {'harm': 'Harmonic', 'rhrm': 'Restraint', 'quar': 'Quartic'}
|
|
233
|
+
interactions = []
|
|
234
|
+
for val_n in val:
|
|
235
|
+
interactions.append(
|
|
236
|
+
dict(
|
|
237
|
+
functional_form=potentials.get(val_n[0], val_n[0]),
|
|
238
|
+
atom_indices=[[int(n) - 1 for n in val_n[1:2]]],
|
|
239
|
+
parameters=[float(v) for v in val_n[2:]],
|
|
240
|
+
)
|
|
241
|
+
)
|
|
242
|
+
return interactions
|
|
243
|
+
|
|
244
|
+
def to_rigid(val_in):
|
|
245
|
+
val = [v.split() for v in val_in.strip().splitlines()]
|
|
246
|
+
return [[int(vi) - 1 for vi in v[1 : int(v[0]) + 1]] for v in val]
|
|
247
|
+
|
|
248
|
+
def to_vdw(val_in):
|
|
249
|
+
val = [v.split() for v in val_in.strip().splitlines()]
|
|
250
|
+
potentials = {
|
|
251
|
+
'12-6': '12-6',
|
|
252
|
+
'lj': 'Lennard-Jones',
|
|
253
|
+
'nm': 'n-m',
|
|
254
|
+
'buck': 'Buckingham',
|
|
255
|
+
'bhm': 'Born-Huggins-Meyer',
|
|
256
|
+
'hbnd': '12-10 H-bond',
|
|
257
|
+
'snm': 'Shifted force n-m',
|
|
258
|
+
'mors': 'Morse',
|
|
259
|
+
'wca': 'WCA',
|
|
260
|
+
'tab': 'Table',
|
|
261
|
+
}
|
|
262
|
+
interactions = []
|
|
263
|
+
for val_n in val:
|
|
264
|
+
interactions.append(
|
|
265
|
+
dict(
|
|
266
|
+
functional_form=potentials.get(val_n[2], val_n[2]),
|
|
267
|
+
atom_labels=[val_n[:2]],
|
|
268
|
+
parameters=[float(v) for v in val_n[3:]],
|
|
269
|
+
)
|
|
270
|
+
)
|
|
271
|
+
return interactions
|
|
272
|
+
|
|
273
|
+
def to_tbp(val_in):
|
|
274
|
+
val = [v.split() for v in val_in.strip().splitlines()]
|
|
275
|
+
potentials = {
|
|
276
|
+
'thrm': 'Truncated harmonic',
|
|
277
|
+
'shrm': 'Screened Harmonic',
|
|
278
|
+
'bvs1': 'Screened Vessa',
|
|
279
|
+
'bvs2': 'Truncated Vessa',
|
|
280
|
+
'hbnd': 'H-bond',
|
|
281
|
+
}
|
|
282
|
+
interactions = []
|
|
283
|
+
for val_n in val:
|
|
284
|
+
interactions.append(
|
|
285
|
+
dict(
|
|
286
|
+
functional_form=potentials.get(val_n[3], val_n[3]),
|
|
287
|
+
atom_labels=[val_n[:3]],
|
|
288
|
+
parameters=[float(v) for v in val_n[4:]],
|
|
289
|
+
)
|
|
290
|
+
)
|
|
291
|
+
return interactions
|
|
292
|
+
|
|
293
|
+
def to_fbp(val_in):
|
|
294
|
+
val = [v.split() for v in val_in.strip().splitlines()]
|
|
295
|
+
potentials = {
|
|
296
|
+
'harm': 'Harmonic',
|
|
297
|
+
'hcos': 'Harmonic cosine',
|
|
298
|
+
'plan': 'Planar',
|
|
299
|
+
}
|
|
300
|
+
interactions = []
|
|
301
|
+
for val_n in val:
|
|
302
|
+
interactions.append(
|
|
303
|
+
dict(
|
|
304
|
+
functional_form=potentials.get(val_n[4], val_n[4]),
|
|
305
|
+
atom_labels=[val_n[:4]],
|
|
306
|
+
parameters=[float(v) for v in val_n[5:]],
|
|
307
|
+
)
|
|
308
|
+
)
|
|
309
|
+
return interactions
|
|
310
|
+
|
|
311
|
+
def to_metal(val_in):
|
|
312
|
+
val = [v.split() for v in val_in.strip().splitlines()]
|
|
313
|
+
potentials = {
|
|
314
|
+
'eam': 'EAM',
|
|
315
|
+
'fnsc': 'Finnis-Sinclair',
|
|
316
|
+
'stch': 'Sutton-Chen',
|
|
317
|
+
'gupt': 'Gupta',
|
|
318
|
+
}
|
|
319
|
+
interactions = []
|
|
320
|
+
for val_n in val:
|
|
321
|
+
interactions.append(
|
|
322
|
+
dict(
|
|
323
|
+
functional_form=potentials.get(val_n[2], val_n[2]),
|
|
324
|
+
atom_labels=[val_n[:2]],
|
|
325
|
+
parameters=[float(v) for v in val_n[3:]],
|
|
326
|
+
)
|
|
327
|
+
)
|
|
328
|
+
return interactions
|
|
329
|
+
|
|
330
|
+
self._quantities = [
|
|
331
|
+
Quantity('units', r'[Uu][Nn][Ii][Tt][Ss] +(.+)', dtype=str),
|
|
332
|
+
Quantity(
|
|
333
|
+
'neutral_groups', r'(neutral group)', str_operation=lambda x: True
|
|
334
|
+
),
|
|
335
|
+
Quantity(
|
|
336
|
+
'molecule_types',
|
|
337
|
+
r'[Mm][Oo][Ll][Ee][Cc][Uu][Ll].+ +(\d+)',
|
|
338
|
+
dtype=np.int32,
|
|
339
|
+
),
|
|
340
|
+
Quantity(
|
|
341
|
+
'molecule',
|
|
342
|
+
r'(.+\s*[Nn][Uu][Mm][Mm][Oo][Ll][Ss] +\d+[\s\S]+?[Ff][Ii][Nn][Ii][Ss][Hh])',
|
|
343
|
+
repeats=True,
|
|
344
|
+
sub_parser=TextParser(
|
|
345
|
+
quantities=[
|
|
346
|
+
Quantity(
|
|
347
|
+
'label_nummols',
|
|
348
|
+
rf' *(.+?){re_n} *[Nn][Uu][Mm][Mm][Oo][Ll][Ss] *(\d+)',
|
|
349
|
+
str_operation=lambda x: x.rsplit(' ', 1),
|
|
350
|
+
),
|
|
351
|
+
Quantity(
|
|
352
|
+
'atoms',
|
|
353
|
+
rf'[Aa][Tt][Oo][Mm][Ss] +\d+\s+((?:[A-Z][\w\-\+]* +{re_f}.+\s*)+)',
|
|
354
|
+
str_operation=to_atoms,
|
|
355
|
+
),
|
|
356
|
+
Quantity(
|
|
357
|
+
'shell',
|
|
358
|
+
rf'[Ss][Hh][Ee][Ll][Ll] +\d+ +\d+\s+((?:\d+ +\d+ +{re_f}.+\s*)+)',
|
|
359
|
+
str_operation=to_shell,
|
|
360
|
+
convert=False,
|
|
361
|
+
),
|
|
362
|
+
Quantity(
|
|
363
|
+
'bonds',
|
|
364
|
+
rf'[Bb][Oo][Nn][Dd][Ss] +\d+\s+((?:\w+ +\d+ +\d+ +{re_f}.+\s*)+)',
|
|
365
|
+
str_operation=to_bonds,
|
|
366
|
+
convert=False,
|
|
367
|
+
),
|
|
368
|
+
Quantity(
|
|
369
|
+
'angles',
|
|
370
|
+
rf'[Aa][Nn][Gg][Ll][Ee][Ss] +\d+\s+((?:\w+ +\d+ +\d+ +\d+ +{re_f} +{re_f}.+\s*)+)',
|
|
371
|
+
str_operation=to_angles,
|
|
372
|
+
convert=False,
|
|
373
|
+
),
|
|
374
|
+
Quantity(
|
|
375
|
+
'constraints',
|
|
376
|
+
rf'[Cc][Oo][Nn][Ss][Tt][Rr][Aa][Ii][Nn][Tt][Ss] +\d+\s+((?:\d+ +\d+ +{re_f}.*\s*)+)',
|
|
377
|
+
convert=False,
|
|
378
|
+
str_operation=lambda x: [
|
|
379
|
+
dict(
|
|
380
|
+
atom_indices=[[int(v) - 1 for v in val[:2]]],
|
|
381
|
+
parameters=[float(v) for v in val[2:3]],
|
|
382
|
+
)
|
|
383
|
+
for val in [v.split() for v in x.strip().splitlines()]
|
|
384
|
+
],
|
|
385
|
+
),
|
|
386
|
+
# TODO add pmf constraints
|
|
387
|
+
Quantity(
|
|
388
|
+
'dihedrals',
|
|
389
|
+
rf'[Dd][Ii][Hh][Ee][Dd][Rr][Aa][Ll][Ss] +\d+\s+((?:\w+ +\d+ +\d+ +\d+ +\d+ +{re_f}.+\s*)+)',
|
|
390
|
+
str_operation=to_dihedrals,
|
|
391
|
+
convert=False,
|
|
392
|
+
),
|
|
393
|
+
Quantity(
|
|
394
|
+
'inversions',
|
|
395
|
+
rf'[Ii][Nn][Vv][Ee][Rr][Ss][Ii][Oo][Nn][Ss] +\d+\s+((?:\w+ +\d+ +\d+ +\d+ +\d+ +{re_f}.+\s*)+)',
|
|
396
|
+
str_operation=to_inversions,
|
|
397
|
+
convert=False,
|
|
398
|
+
),
|
|
399
|
+
Quantity(
|
|
400
|
+
'rigid',
|
|
401
|
+
r'[Rr][Ii][Gg][Ii][Dd].+?\d+\s+((?:\d+.+\s+)+)',
|
|
402
|
+
str_operation=to_rigid,
|
|
403
|
+
convert=False,
|
|
404
|
+
),
|
|
405
|
+
Quantity(
|
|
406
|
+
'teth',
|
|
407
|
+
rf'[Tt][Ee][Tt][Hh] +\d+\s+((?:\w+ +\d+ +{re_f}.+\s+)+)',
|
|
408
|
+
str_operation=to_teth,
|
|
409
|
+
convert=False,
|
|
410
|
+
),
|
|
411
|
+
]
|
|
412
|
+
),
|
|
413
|
+
),
|
|
414
|
+
# non-bonded interactions
|
|
415
|
+
Quantity(
|
|
416
|
+
'vdw',
|
|
417
|
+
rf'[Vv][Dd][Ww].+\d+\s+((?:[A-Z][\w\-\+]* +[A-Z][\w\-\+]* +\w+.*\s*)+)',
|
|
418
|
+
str_operation=to_vdw,
|
|
419
|
+
convert=False,
|
|
420
|
+
),
|
|
421
|
+
Quantity(
|
|
422
|
+
'tbp',
|
|
423
|
+
r'[Tt][Bb][Pp].+\d+\s+((?:[A-Z][\w\-\+]* +[A-Z][\w\-\+]*.*\s*)+)',
|
|
424
|
+
str_operation=to_tbp,
|
|
425
|
+
convert=False,
|
|
426
|
+
),
|
|
427
|
+
Quantity(
|
|
428
|
+
'fbp',
|
|
429
|
+
r'[Ff][Bb][Pp].+\d+\s+((?:[A-Z][\w\-\+]* +[A-Z][\w\-\+]*.*\s*)+)',
|
|
430
|
+
str_operation=to_fbp,
|
|
431
|
+
convert=False,
|
|
432
|
+
),
|
|
433
|
+
Quantity(
|
|
434
|
+
'metal',
|
|
435
|
+
r'[Mm][Ee][Tt][Aa][Ll].+\d+\s+((?:[A-Z][\w\-\+]* +[A-Z][\w\-\+]*.*\s*)+)',
|
|
436
|
+
str_operation=to_metal,
|
|
437
|
+
convert=False,
|
|
438
|
+
),
|
|
439
|
+
# TODO implement Tersoff and external fields
|
|
440
|
+
]
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
class MainfileParser(TextParser):
|
|
444
|
+
def __init__(self):
|
|
445
|
+
super().__init__()
|
|
446
|
+
|
|
447
|
+
def init_quantities(self):
|
|
448
|
+
self._quantities = [
|
|
449
|
+
Quantity(
|
|
450
|
+
'program_version_date',
|
|
451
|
+
r'\*\* +version\: +(\S+) +/ +(\w+) +(\d+)',
|
|
452
|
+
dtype=str,
|
|
453
|
+
),
|
|
454
|
+
Quantity(
|
|
455
|
+
'program_name',
|
|
456
|
+
r'when publishing research data obtained using (\S+)',
|
|
457
|
+
dtype=str,
|
|
458
|
+
),
|
|
459
|
+
Quantity(
|
|
460
|
+
'control_parameters',
|
|
461
|
+
r'SIMULATION CONTROL PARAMETERS([\s\S]+?)SYS',
|
|
462
|
+
sub_parser=TextParser(
|
|
463
|
+
quantities=[
|
|
464
|
+
Quantity(
|
|
465
|
+
'parameter',
|
|
466
|
+
r'([ \w]+) \(*\w*\)*((?: |\:) [\w \+\-\.]+)',
|
|
467
|
+
str_operation=lambda x: [
|
|
468
|
+
v.strip() for v in x.replace(':', ' ').rsplit(' ', 1)
|
|
469
|
+
],
|
|
470
|
+
repeats=True,
|
|
471
|
+
)
|
|
472
|
+
]
|
|
473
|
+
),
|
|
474
|
+
),
|
|
475
|
+
Quantity(
|
|
476
|
+
'system_specification',
|
|
477
|
+
r'TEM SPECIFICATION([\s\S]+?system volume.+)',
|
|
478
|
+
sub_parser=TextParser(
|
|
479
|
+
quantities=[
|
|
480
|
+
Quantity(
|
|
481
|
+
'energy_unit',
|
|
482
|
+
r'energy units += +(.+)',
|
|
483
|
+
dtype=str,
|
|
484
|
+
flatten=False,
|
|
485
|
+
)
|
|
486
|
+
]
|
|
487
|
+
),
|
|
488
|
+
),
|
|
489
|
+
Quantity(
|
|
490
|
+
'properties',
|
|
491
|
+
r'(step +eng_tot +temp_tot[\s\S]+?)(?:run terminating|\Z)',
|
|
492
|
+
sub_parser=TextParser(
|
|
493
|
+
quantities=[
|
|
494
|
+
Quantity(
|
|
495
|
+
'names',
|
|
496
|
+
r'(step +eng_tot +temp_tot.+\s+.+\s+.+)',
|
|
497
|
+
str_operation=lambda x: [
|
|
498
|
+
v.strip()
|
|
499
|
+
for v in x.replace('\n', ' ').split(' ')
|
|
500
|
+
if v
|
|
501
|
+
],
|
|
502
|
+
),
|
|
503
|
+
Quantity(
|
|
504
|
+
'instantaneous',
|
|
505
|
+
rf'{re_n} +(\d+ +{re_f}.+\s+.+\s+.+)',
|
|
506
|
+
repeats=True,
|
|
507
|
+
),
|
|
508
|
+
Quantity(
|
|
509
|
+
'average',
|
|
510
|
+
r'rolling(.+)\s+averages(.+)\s+(.+)',
|
|
511
|
+
repeats=True,
|
|
512
|
+
),
|
|
513
|
+
]
|
|
514
|
+
),
|
|
515
|
+
),
|
|
516
|
+
]
|
|
517
|
+
|
|
518
|
+
def get_control_parameters(self):
|
|
519
|
+
return {
|
|
520
|
+
val[0]: val[1]
|
|
521
|
+
for val in self.get('control_parameters', {}).get('parameter', [])
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
class DLPolyParser(MDParser):
|
|
526
|
+
def __init__(self):
|
|
527
|
+
self.mainfile_parser = MainfileParser()
|
|
528
|
+
self.traj_parser = TrajParser()
|
|
529
|
+
self.field_parser = FieldParser()
|
|
530
|
+
self._units = {
|
|
531
|
+
'kj/mol': ureg.kJ / MOL,
|
|
532
|
+
'kj': ureg.kJ / MOL,
|
|
533
|
+
'ev': ureg.eV,
|
|
534
|
+
'kcal/mol': ureg.J * 4184.0 / MOL,
|
|
535
|
+
'kcal': ureg.J * 4184.0 / MOL,
|
|
536
|
+
'dl_poly internal units (10 j/mol)': 10 * ureg.J / MOL,
|
|
537
|
+
}
|
|
538
|
+
self._metainfo_map = {
|
|
539
|
+
'step': 'step',
|
|
540
|
+
'eng_tot': 'energy_total',
|
|
541
|
+
'temp_tot': 'temperature',
|
|
542
|
+
'eng_cfg': 'energy_contribution_configurational',
|
|
543
|
+
'eng_vdw': 'energy_van_der_waals',
|
|
544
|
+
'eng_src': 'energy_contribution_short_range',
|
|
545
|
+
'eng_cou': 'energy_coulomb',
|
|
546
|
+
'eng_bnd': 'energy_contribution_bond',
|
|
547
|
+
'eng_ang': 'energy_contribution_angle',
|
|
548
|
+
'eng_dih': 'energy_contribution_dihedral',
|
|
549
|
+
'eng_tet': 'energy_contribution_tethering',
|
|
550
|
+
'time(ps)': 'time',
|
|
551
|
+
'time': 'time',
|
|
552
|
+
'eng_pv': 'enthalpy',
|
|
553
|
+
'temp_rot': 'x_dl_poly_temperature_rotational',
|
|
554
|
+
# TODO include virial to nomad metainfo
|
|
555
|
+
'vir_cfg': 'x_dl_poly_virial_configurational',
|
|
556
|
+
'vir_src': 'x_dl_poly_virial_short_range',
|
|
557
|
+
'vir_cou': 'x_dl_poly_virial_coulomb',
|
|
558
|
+
'vir_bnd': 'x_dl_poly_virial_bond',
|
|
559
|
+
'vir_ang': 'x_dl_poly_virial_angle',
|
|
560
|
+
'vir_con': 'x_dl_poly_virial_constraint',
|
|
561
|
+
'vir_tet': 'x_dl_poly_virial_tethering',
|
|
562
|
+
'cpu (s)': 'time_physical',
|
|
563
|
+
'cpu time': 'time_physical',
|
|
564
|
+
'volume': 'x_dl_poly_volume',
|
|
565
|
+
'temp_shl': 'x_dl_poly_core_shell',
|
|
566
|
+
'eng_shl': 'energy_contribution_core_shell',
|
|
567
|
+
'vir_shl': 'x_dl_poly_core_shell',
|
|
568
|
+
'vir_pmf': 'x_dl_poly_virial_potential_mean_force',
|
|
569
|
+
'press': 'pressure',
|
|
570
|
+
}
|
|
571
|
+
super().__init__()
|
|
572
|
+
|
|
573
|
+
def write_to_archive(self) -> None:
|
|
574
|
+
self.mainfile_parser.logger = self.logger
|
|
575
|
+
self.mainfile_parser.mainfile = self.mainfile
|
|
576
|
+
self.traj_parser.logger = self.logger
|
|
577
|
+
self.field_parser.logger = self.logger
|
|
578
|
+
self.maindir = os.path.dirname(self.mainfile)
|
|
579
|
+
|
|
580
|
+
sec_run = Run()
|
|
581
|
+
self.archive.run.append(sec_run)
|
|
582
|
+
version_date = self.mainfile_parser.get('program_version_date', [None, None])
|
|
583
|
+
sec_run.program = Program(
|
|
584
|
+
name=self.mainfile_parser.program_name, version=version_date[0]
|
|
585
|
+
)
|
|
586
|
+
sec_run.x_dl_poly_program_version_date = str(version_date[1])
|
|
587
|
+
|
|
588
|
+
def get_system_data(frame_index):
|
|
589
|
+
frame = self.traj_parser.get('frame')[frame_index]
|
|
590
|
+
labels = [
|
|
591
|
+
atom.get('label') if atom.get('label') else 'X'
|
|
592
|
+
for atom in frame.get('atoms', [])
|
|
593
|
+
]
|
|
594
|
+
lattice_vectors = frame.get('lattice_vectors') * ureg.angstrom
|
|
595
|
+
array = np.transpose(
|
|
596
|
+
[atom.get('array') for atom in frame.get('atoms', [])], axes=(1, 0, 2)
|
|
597
|
+
)
|
|
598
|
+
positions = array[0] * ureg.angstrom
|
|
599
|
+
velocities = None
|
|
600
|
+
if len(array) > 1:
|
|
601
|
+
velocities = array[1] * ureg.angstrom / ureg.ps
|
|
602
|
+
return dict(
|
|
603
|
+
atoms=dict(
|
|
604
|
+
labels=labels,
|
|
605
|
+
lattice_vectors=lattice_vectors,
|
|
606
|
+
positions=positions,
|
|
607
|
+
velocities=velocities,
|
|
608
|
+
)
|
|
609
|
+
)
|
|
610
|
+
|
|
611
|
+
# parse initial system from CONFIG
|
|
612
|
+
self.traj_parser.mainfile = os.path.join(self.maindir, 'CONFIG')
|
|
613
|
+
self.parse_trajectory_step(get_system_data(0))
|
|
614
|
+
|
|
615
|
+
# method
|
|
616
|
+
sec_method = Method()
|
|
617
|
+
sec_run.method.append(sec_method)
|
|
618
|
+
control_parameters = self.mainfile_parser.get_control_parameters()
|
|
619
|
+
sec_method.x_dl_poly_control_parameters = control_parameters
|
|
620
|
+
sec_force_field = ForceField()
|
|
621
|
+
sec_method.force_field = sec_force_field
|
|
622
|
+
sec_model = Model()
|
|
623
|
+
sec_force_field.model.append(sec_model)
|
|
624
|
+
# get interactions from FIELD file
|
|
625
|
+
self.field_parser.mainfile = os.path.join(self.maindir, 'FIELD')
|
|
626
|
+
units = {'mass': ureg.amu, 'charge': ureg.elementary_charge}
|
|
627
|
+
for molecule in self.field_parser.get('molecule', []):
|
|
628
|
+
# atom parameters
|
|
629
|
+
sec_molecule_parameters = MoleculeParameters()
|
|
630
|
+
sec_method.molecule_parameters.append(sec_molecule_parameters)
|
|
631
|
+
sec_molecule_parameters.label = molecule.get('label_nummols', [None, None])[
|
|
632
|
+
0
|
|
633
|
+
]
|
|
634
|
+
for atom in molecule.get('atoms', []):
|
|
635
|
+
sec_atom_parameters = AtomParameters()
|
|
636
|
+
sec_molecule_parameters.atom_parameters.append(sec_atom_parameters)
|
|
637
|
+
for key, val in atom.items():
|
|
638
|
+
val = val * units.get(key, 1)
|
|
639
|
+
setattr(sec_atom_parameters, key, val)
|
|
640
|
+
# interactions
|
|
641
|
+
for interaction_type in [
|
|
642
|
+
'bonds',
|
|
643
|
+
'angles',
|
|
644
|
+
'dihedrals',
|
|
645
|
+
'inversions',
|
|
646
|
+
'teth',
|
|
647
|
+
]:
|
|
648
|
+
for interaction in molecule.get(interaction_type, []):
|
|
649
|
+
sec_interaction = Interaction()
|
|
650
|
+
sec_model.contributions.append(sec_interaction)
|
|
651
|
+
for key, val in interaction.items():
|
|
652
|
+
sec_interaction.m_set(
|
|
653
|
+
sec_interaction.m_get_quantity_definition(key), val
|
|
654
|
+
)
|
|
655
|
+
# add constraints to initial system
|
|
656
|
+
constraint_data = []
|
|
657
|
+
for constraint in molecule.get('constraints', []):
|
|
658
|
+
constraint_data.append(
|
|
659
|
+
dict(
|
|
660
|
+
kind='fixed bond length',
|
|
661
|
+
atom_indices=constraint.get('atom_indices'),
|
|
662
|
+
parameters=constraint.get('parameters'),
|
|
663
|
+
)
|
|
664
|
+
)
|
|
665
|
+
# rigid atoms
|
|
666
|
+
for rigid in molecule.get('rigid', []):
|
|
667
|
+
constraint_data.append(dict(kind='static atoms', atom_indices=[rigid]))
|
|
668
|
+
self.parse_section(dict(constraint=constraint_data), sec_run.system[0])
|
|
669
|
+
# TODO add atom groups in system
|
|
670
|
+
|
|
671
|
+
# non-bonded
|
|
672
|
+
for interaction_type in ['vdw', 'tbp', 'fbp', 'metal']:
|
|
673
|
+
for interaction in self.field_parser.get(interaction_type, []):
|
|
674
|
+
sec_interaction = Interaction()
|
|
675
|
+
sec_model.contributions.append(sec_interaction)
|
|
676
|
+
for key, val in interaction.items():
|
|
677
|
+
quantity_def = sec_interaction.m_def.all_quantities.get(key)
|
|
678
|
+
if quantity_def:
|
|
679
|
+
sec_interaction.m_set(quantity_def, val)
|
|
680
|
+
|
|
681
|
+
system_spec = self.mainfile_parser.get('system_specification', {})
|
|
682
|
+
n_atoms = len(sec_run.system[-1].atoms.positions)
|
|
683
|
+
energy_unit = (
|
|
684
|
+
self._units.get(system_spec.get('energy_unit', '').strip().lower(), 1)
|
|
685
|
+
* n_atoms
|
|
686
|
+
)
|
|
687
|
+
properties = self.mainfile_parser.get('properties', {})
|
|
688
|
+
names = [self._metainfo_map.get(name) for name in properties.get('names', [])]
|
|
689
|
+
# parse trajectory from HISTORY
|
|
690
|
+
self.traj_parser.mainfile = os.path.join(self.maindir, 'HISTORY')
|
|
691
|
+
|
|
692
|
+
# set up md parser
|
|
693
|
+
self.n_atoms = n_atoms
|
|
694
|
+
traj_steps = [
|
|
695
|
+
frame.get('info', {}).get('step')
|
|
696
|
+
for frame in self.traj_parser.get('frame', [])
|
|
697
|
+
]
|
|
698
|
+
self.trajectory_steps = traj_steps
|
|
699
|
+
step_n = names.index('step')
|
|
700
|
+
self.thermodynamics_steps = [
|
|
701
|
+
int(val[step_n]) for val in properties.get('instantaneous', [])
|
|
702
|
+
]
|
|
703
|
+
|
|
704
|
+
for step in self.trajectory_steps:
|
|
705
|
+
self.parse_trajectory_step(get_system_data(traj_steps.index(step)))
|
|
706
|
+
|
|
707
|
+
unit_conversion = {'m': 60, 'f': 0.001, 's': 1, 'p': 1}
|
|
708
|
+
for step in self.thermodynamics_steps:
|
|
709
|
+
data = {}
|
|
710
|
+
energy: Dict[str, Any] = {'contributions': []}
|
|
711
|
+
instantaneous = properties.get('instantaneous')[
|
|
712
|
+
self.thermodynamics_steps.index(step)
|
|
713
|
+
]
|
|
714
|
+
# instataneous should be an array of floats, however some outputs may not be all floats and in that
|
|
715
|
+
# case TextParser fails to convert them properly because it reuses the data type from
|
|
716
|
+
# previous parsing run. Convert them manually here
|
|
717
|
+
for n, val in enumerate(instantaneous):
|
|
718
|
+
try:
|
|
719
|
+
if isinstance(val, str):
|
|
720
|
+
instantaneous[n] = float(val[:-1]) * unit_conversion.get(
|
|
721
|
+
val[-1], 1.0
|
|
722
|
+
)
|
|
723
|
+
else:
|
|
724
|
+
instantaneous[n] = float(val)
|
|
725
|
+
except Exception:
|
|
726
|
+
instantaneous[n] = None
|
|
727
|
+
|
|
728
|
+
for n, name in enumerate(names):
|
|
729
|
+
if name is None or instantaneous[n] is None:
|
|
730
|
+
continue
|
|
731
|
+
if name.startswith('energy_contribution_'):
|
|
732
|
+
energy['contributions'].append(
|
|
733
|
+
dict(
|
|
734
|
+
kind=name.replace('energy_contribution_', ''),
|
|
735
|
+
value=instantaneous[n] * energy_unit,
|
|
736
|
+
)
|
|
737
|
+
)
|
|
738
|
+
elif name == 'energy_enthalpy':
|
|
739
|
+
energy['enthalpy'] = instantaneous[n] * energy_unit
|
|
740
|
+
elif name.startswith('energy_'):
|
|
741
|
+
energy[name.replace('energy_', '')] = dict(
|
|
742
|
+
value=instantaneous[n] * energy_unit
|
|
743
|
+
)
|
|
744
|
+
elif name == 'enthalpy':
|
|
745
|
+
data['enthalpy'] = instantaneous[n] * energy_unit
|
|
746
|
+
elif 'temperature' in name:
|
|
747
|
+
data[name] = instantaneous[n] * ureg.kelvin
|
|
748
|
+
elif 'pressure' in name:
|
|
749
|
+
# TODO verify if atmosphere is the unit
|
|
750
|
+
data[name] = instantaneous[n] * ureg.atm
|
|
751
|
+
elif 'volume' in name:
|
|
752
|
+
data[name] = instantaneous[n] * ureg.angstrom**3
|
|
753
|
+
elif name == 'step':
|
|
754
|
+
data[name] = int(instantaneous[n])
|
|
755
|
+
elif name == 'time':
|
|
756
|
+
data[name] = instantaneous[n] * ureg.ps
|
|
757
|
+
elif name == 'time_physical':
|
|
758
|
+
data[name] = instantaneous[n] * ureg.s
|
|
759
|
+
index = self.thermodynamics_steps.index(step)
|
|
760
|
+
time_start = (
|
|
761
|
+
properties.get('instantaneous')[index - 1][n]
|
|
762
|
+
if index > 0
|
|
763
|
+
else 0
|
|
764
|
+
)
|
|
765
|
+
data['time_calculation'] = data[name] - time_start * ureg.s
|
|
766
|
+
else:
|
|
767
|
+
data[name] = instantaneous[n]
|
|
768
|
+
data['energy'] = energy
|
|
769
|
+
if step in traj_steps:
|
|
770
|
+
frame = self.traj_parser.get('frames')[traj_steps.index(step)]
|
|
771
|
+
array = np.transpose(
|
|
772
|
+
[atom.get('array') for atom in frame.get('atoms', [])]
|
|
773
|
+
)
|
|
774
|
+
if len(array) > 2:
|
|
775
|
+
data['forces'] = dict(
|
|
776
|
+
total=dict(
|
|
777
|
+
value=np.transpose(array[2])
|
|
778
|
+
* ureg.amu
|
|
779
|
+
* ureg.angstrom
|
|
780
|
+
/ ureg.ps**2
|
|
781
|
+
)
|
|
782
|
+
)
|
|
783
|
+
self.parse_thermodynamics_step(data)
|
|
784
|
+
|
|
785
|
+
ensemble_type = control_parameters.get('Ensemble')
|
|
786
|
+
self.parse_md_workflow(
|
|
787
|
+
dict(
|
|
788
|
+
method=dict(
|
|
789
|
+
thermodynamic_ensemble=ensemble_type.split()[0]
|
|
790
|
+
if ensemble_type is not None
|
|
791
|
+
else None,
|
|
792
|
+
integration_timestep=control_parameters.get(
|
|
793
|
+
'fixed simulation timestep', 0
|
|
794
|
+
)
|
|
795
|
+
* ureg.ps,
|
|
796
|
+
)
|
|
797
|
+
)
|
|
798
|
+
)
|