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.
Files changed (80) hide show
  1. atomisticparsers/__init__.py +400 -0
  2. atomisticparsers/amber/__init__.py +19 -0
  3. atomisticparsers/amber/__main__.py +31 -0
  4. atomisticparsers/amber/metainfo/__init__.py +19 -0
  5. atomisticparsers/amber/metainfo/amber.py +495 -0
  6. atomisticparsers/amber/parser.py +42 -0
  7. atomisticparsers/asap/__init__.py +19 -0
  8. atomisticparsers/asap/__main__.py +31 -0
  9. atomisticparsers/asap/metainfo/__init__.py +19 -0
  10. atomisticparsers/asap/metainfo/asap.py +75 -0
  11. atomisticparsers/asap/parser.py +197 -0
  12. atomisticparsers/bopfox/__init__.py +19 -0
  13. atomisticparsers/bopfox/__main__.py +31 -0
  14. atomisticparsers/bopfox/metainfo/__init__.py +19 -0
  15. atomisticparsers/bopfox/metainfo/bopfox.py +225 -0
  16. atomisticparsers/bopfox/parser.py +808 -0
  17. atomisticparsers/dftbplus/__init__.py +19 -0
  18. atomisticparsers/dftbplus/__main__.py +31 -0
  19. atomisticparsers/dftbplus/metainfo/__init__.py +19 -0
  20. atomisticparsers/dftbplus/metainfo/dftbplus.py +217 -0
  21. atomisticparsers/dftbplus/parser.py +500 -0
  22. atomisticparsers/dlpoly/__init__.py +19 -0
  23. atomisticparsers/dlpoly/__main__.py +31 -0
  24. atomisticparsers/dlpoly/metainfo/__init__.py +19 -0
  25. atomisticparsers/dlpoly/metainfo/dl_poly.py +312 -0
  26. atomisticparsers/dlpoly/parser.py +798 -0
  27. atomisticparsers/gromacs/__init__.py +19 -0
  28. atomisticparsers/gromacs/__main__.py +31 -0
  29. atomisticparsers/gromacs/metainfo/__init__.py +19 -0
  30. atomisticparsers/gromacs/metainfo/gromacs.py +2388 -0
  31. atomisticparsers/gromacs/parser.py +1581 -0
  32. atomisticparsers/gromos/__init__.py +19 -0
  33. atomisticparsers/gromos/__main__.py +31 -0
  34. atomisticparsers/gromos/metainfo/__init__.py +19 -0
  35. atomisticparsers/gromos/metainfo/gromos.py +1995 -0
  36. atomisticparsers/gromos/parser.py +58 -0
  37. atomisticparsers/gulp/__init__.py +19 -0
  38. atomisticparsers/gulp/__main__.py +31 -0
  39. atomisticparsers/gulp/metainfo/__init__.py +19 -0
  40. atomisticparsers/gulp/metainfo/gulp.py +1117 -0
  41. atomisticparsers/gulp/parser.py +1316 -0
  42. atomisticparsers/h5md/__init__.py +19 -0
  43. atomisticparsers/h5md/__main__.py +31 -0
  44. atomisticparsers/h5md/metainfo/__init__.py +19 -0
  45. atomisticparsers/h5md/metainfo/h5md.py +239 -0
  46. atomisticparsers/h5md/parser.py +901 -0
  47. atomisticparsers/lammps/__init__.py +19 -0
  48. atomisticparsers/lammps/__main__.py +31 -0
  49. atomisticparsers/lammps/metainfo/__init__.py +19 -0
  50. atomisticparsers/lammps/metainfo/lammps.py +1417 -0
  51. atomisticparsers/lammps/parser.py +1753 -0
  52. atomisticparsers/libatoms/__init__.py +19 -0
  53. atomisticparsers/libatoms/__main__.py +31 -0
  54. atomisticparsers/libatoms/metainfo/__init__.py +19 -0
  55. atomisticparsers/libatoms/metainfo/lib_atoms.py +251 -0
  56. atomisticparsers/libatoms/parser.py +38 -0
  57. atomisticparsers/namd/__init__.py +19 -0
  58. atomisticparsers/namd/__main__.py +31 -0
  59. atomisticparsers/namd/metainfo/__init__.py +19 -0
  60. atomisticparsers/namd/metainfo/namd.py +1605 -0
  61. atomisticparsers/namd/parser.py +312 -0
  62. atomisticparsers/tinker/__init__.py +19 -0
  63. atomisticparsers/tinker/__main__.py +31 -0
  64. atomisticparsers/tinker/metainfo/__init__.py +18 -0
  65. atomisticparsers/tinker/metainfo/tinker.py +1363 -0
  66. atomisticparsers/tinker/parser.py +685 -0
  67. atomisticparsers/utils/__init__.py +22 -0
  68. atomisticparsers/utils/mdanalysis.py +662 -0
  69. atomisticparsers/utils/parsers.py +226 -0
  70. atomisticparsers/xtb/__init__.py +19 -0
  71. atomisticparsers/xtb/__main__.py +32 -0
  72. atomisticparsers/xtb/metainfo/__init__.py +19 -0
  73. atomisticparsers/xtb/metainfo/xtb.py +256 -0
  74. atomisticparsers/xtb/parser.py +979 -0
  75. nomad_parser_plugins_atomistic-1.0.dist-info/LICENSE +202 -0
  76. nomad_parser_plugins_atomistic-1.0.dist-info/METADATA +327 -0
  77. nomad_parser_plugins_atomistic-1.0.dist-info/RECORD +80 -0
  78. nomad_parser_plugins_atomistic-1.0.dist-info/WHEEL +5 -0
  79. nomad_parser_plugins_atomistic-1.0.dist-info/entry_points.txt +15 -0
  80. 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
+ )