nomad-parser-plugins-workflow 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 (58) hide show
  1. nomad_parser_plugins_workflow-1.0.dist-info/LICENSE +202 -0
  2. nomad_parser_plugins_workflow-1.0.dist-info/METADATA +319 -0
  3. nomad_parser_plugins_workflow-1.0.dist-info/RECORD +58 -0
  4. nomad_parser_plugins_workflow-1.0.dist-info/WHEEL +5 -0
  5. nomad_parser_plugins_workflow-1.0.dist-info/entry_points.txt +11 -0
  6. nomad_parser_plugins_workflow-1.0.dist-info/top_level.txt +1 -0
  7. workflowparsers/__init__.py +314 -0
  8. workflowparsers/aflow/__init__.py +19 -0
  9. workflowparsers/aflow/__main__.py +31 -0
  10. workflowparsers/aflow/metainfo/__init__.py +19 -0
  11. workflowparsers/aflow/metainfo/aflow.py +1240 -0
  12. workflowparsers/aflow/parser.py +741 -0
  13. workflowparsers/asr/__init__.py +19 -0
  14. workflowparsers/asr/__main__.py +31 -0
  15. workflowparsers/asr/metainfo/__init__.py +19 -0
  16. workflowparsers/asr/metainfo/asr.py +306 -0
  17. workflowparsers/asr/parser.py +266 -0
  18. workflowparsers/atomate/__init__.py +19 -0
  19. workflowparsers/atomate/__main__.py +31 -0
  20. workflowparsers/atomate/metainfo/__init__.py +19 -0
  21. workflowparsers/atomate/metainfo/atomate.py +395 -0
  22. workflowparsers/atomate/parser.py +357 -0
  23. workflowparsers/elastic/__init__.py +19 -0
  24. workflowparsers/elastic/__main__.py +31 -0
  25. workflowparsers/elastic/metainfo/__init__.py +19 -0
  26. workflowparsers/elastic/metainfo/elastic.py +364 -0
  27. workflowparsers/elastic/parser.py +798 -0
  28. workflowparsers/fhivibes/__init__.py +19 -0
  29. workflowparsers/fhivibes/__main__.py +31 -0
  30. workflowparsers/fhivibes/metainfo/__init__.py +19 -0
  31. workflowparsers/fhivibes/metainfo/fhi_vibes.py +898 -0
  32. workflowparsers/fhivibes/parser.py +566 -0
  33. workflowparsers/lobster/__init__.py +19 -0
  34. workflowparsers/lobster/__main__.py +31 -0
  35. workflowparsers/lobster/metainfo/__init__.py +19 -0
  36. workflowparsers/lobster/metainfo/lobster.py +446 -0
  37. workflowparsers/lobster/parser.py +618 -0
  38. workflowparsers/phonopy/__init__.py +19 -0
  39. workflowparsers/phonopy/__main__.py +31 -0
  40. workflowparsers/phonopy/calculator.py +260 -0
  41. workflowparsers/phonopy/metainfo/__init__.py +19 -0
  42. workflowparsers/phonopy/metainfo/phonopy.py +83 -0
  43. workflowparsers/phonopy/parser.py +583 -0
  44. workflowparsers/quantum_espresso_epw/__init__.py +19 -0
  45. workflowparsers/quantum_espresso_epw/__main__.py +31 -0
  46. workflowparsers/quantum_espresso_epw/metainfo/__init__.py +19 -0
  47. workflowparsers/quantum_espresso_epw/metainfo/quantum_espresso_epw.py +579 -0
  48. workflowparsers/quantum_espresso_epw/parser.py +583 -0
  49. workflowparsers/quantum_espresso_phonon/__init__.py +19 -0
  50. workflowparsers/quantum_espresso_phonon/__main__.py +31 -0
  51. workflowparsers/quantum_espresso_phonon/metainfo/__init__.py +19 -0
  52. workflowparsers/quantum_espresso_phonon/metainfo/quantum_espresso_phonon.py +389 -0
  53. workflowparsers/quantum_espresso_phonon/parser.py +483 -0
  54. workflowparsers/quantum_espresso_xspectra/__init__.py +19 -0
  55. workflowparsers/quantum_espresso_xspectra/__main__.py +31 -0
  56. workflowparsers/quantum_espresso_xspectra/metainfo/__init__.py +19 -0
  57. workflowparsers/quantum_espresso_xspectra/metainfo/quantum_espresso_xspectra.py +290 -0
  58. workflowparsers/quantum_espresso_xspectra/parser.py +586 -0
@@ -0,0 +1,741 @@
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 os
20
+ import numpy as np
21
+ import logging
22
+ import json
23
+ from io import StringIO
24
+ from ase.cell import Cell
25
+ from ase.io import vasp
26
+
27
+ from nomad.units import ureg
28
+ from nomad.parsing.file_parser import TextParser, Quantity
29
+ from runschema.run import Run, Program
30
+ from runschema.calculation import (
31
+ Calculation,
32
+ Energy,
33
+ EnergyEntry,
34
+ Forces,
35
+ ForcesEntry,
36
+ Stress,
37
+ StressEntry,
38
+ Thermodynamics,
39
+ Dos,
40
+ DosValues,
41
+ BandStructure,
42
+ BandEnergies,
43
+ )
44
+ from runschema.method import Method
45
+ from runschema.system import System, Atoms
46
+ from simulationworkflowschema import (
47
+ Elastic,
48
+ ElasticMethod,
49
+ ElasticResults,
50
+ Phonon,
51
+ PhononMethod,
52
+ PhononResults,
53
+ Thermodynamics as WorkflowThermodynamics,
54
+ ThermodynamicsResults,
55
+ )
56
+ from .metainfo import aflow # noqa
57
+
58
+
59
+ class AflowOutParser(TextParser):
60
+ def __init__(self, **kwargs):
61
+ super().__init__(**kwargs)
62
+
63
+ def init_quantities(self):
64
+ def str_to_property(val_in):
65
+ val = val_in.split('=')
66
+ return val[0].strip().replace(' ', '_').lower(), val[-1].split('//')[
67
+ 0
68
+ ].strip()
69
+
70
+ self._quantities = [
71
+ Quantity(
72
+ 'property',
73
+ r'\n *\[(.+)\](.+?=.+)',
74
+ str_operation=str_to_property,
75
+ repeats=True,
76
+ ),
77
+ Quantity(
78
+ 'section',
79
+ r'(\[.+?\]START[\s\S]+?\]STOP)',
80
+ repeats=True,
81
+ sub_parser=TextParser(
82
+ quantities=[
83
+ Quantity(
84
+ 'name',
85
+ r'\[(.+)\]START',
86
+ str_operation=lambda x: x.lower(),
87
+ dtype=str,
88
+ ),
89
+ Quantity('key_value', r'\n *([^#]\S+)=(\S+)', repeats=True),
90
+ Quantity(
91
+ 'array',
92
+ rf'\n *(\d[\s\S]+?\d\s*)\[.+?STOP',
93
+ dtype=np.dtype(np.float64),
94
+ ),
95
+ ]
96
+ ),
97
+ ),
98
+ ]
99
+
100
+ def parse(self, key=None):
101
+ super().parse(key)
102
+ for property in self._results.get('property', []):
103
+ self._results[property[0]] = property[1]
104
+ for section in self._results.get('section', []):
105
+ if section.key_value is not None:
106
+ result = dict()
107
+ for k, v in section.get('key_value', []):
108
+ result[k] = v
109
+ self._results[section.name] = result
110
+ elif section.array is not None:
111
+ self._results[section.name] = section.array
112
+
113
+
114
+ class AflowInParser(AflowOutParser):
115
+ def __init__(self, **kwargs):
116
+ super().__init__(**kwargs)
117
+
118
+ def init_quantities(self):
119
+ super().init_quantities()
120
+ self._quantities += [
121
+ Quantity('aflow_version', r'Stefano Curtarolo \- \(AFLOW V([\d\.]+)\)'),
122
+ Quantity(
123
+ 'poscar',
124
+ r'\[VASP_POSCAR_MODE_EXPLICIT\]START\s*([\s\S]+?)\[VASP_POSCAR_MODE_EXPLICIT\]STOP',
125
+ str_operation=lambda x: x,
126
+ convert=False,
127
+ repeats=True,
128
+ ),
129
+ Quantity(
130
+ 'aflow_composition',
131
+ r'\[AFLOW\] COMPOSITION=(\S+)',
132
+ sub_parser=TextParser(
133
+ quantities=[
134
+ Quantity('species', r'([A-Z][a-z]*)', repeats=True, dtype=str),
135
+ Quantity(
136
+ 'composition',
137
+ r'(\d+)\|',
138
+ repeats=True,
139
+ dtype=np.dtype(np.int32),
140
+ ),
141
+ ]
142
+ ),
143
+ ),
144
+ ] + [
145
+ Quantity(
146
+ module.lower(),
147
+ r'\n *\[AFLOW\_%s\]CALC([\s\S]+?)\[AFLOW\] \*' % module,
148
+ sub_parser=TextParser(
149
+ quantities=[
150
+ Quantity(
151
+ 'parameters',
152
+ r'\[AFLOW\_%s\](.+?)=(\S+)' % module,
153
+ repeats=True,
154
+ )
155
+ ]
156
+ ),
157
+ )
158
+ for module in ['AEL', 'AGL', 'APL', 'QHA', 'AAPL']
159
+ ]
160
+
161
+ def parse(self, key=None):
162
+ super().parse(key)
163
+
164
+ if (
165
+ self._results.get('poscar') is not None
166
+ and self._results.get('geometry') is None
167
+ ):
168
+ try:
169
+ atoms = vasp.read_vasp(StringIO(self._results['poscar'][-1]))
170
+ self._results['cell'] = atoms.get_cell()
171
+ self._results['geometry'] = atoms.get_cell().cellpar()
172
+ composition = self._results['aflow_composition']
173
+ self._results['species'] = composition.species
174
+ self._results['composition'] = [
175
+ int(c) for c in composition._results['composition']
176
+ ]
177
+ self._results['positions_cartesian'] = atoms.get_positions()
178
+ except Exception:
179
+ pass
180
+
181
+ if self._results.get('loop') is None:
182
+ self._results['loop'] = [
183
+ module
184
+ for module in ['ael', 'agl', 'apl', 'qha', 'aapl']
185
+ if module in self._results
186
+ ]
187
+
188
+
189
+ class AFLOWParser:
190
+ def __init__(self):
191
+ self.ael_parser = AflowOutParser()
192
+ self.agl_parser = AflowOutParser()
193
+ self.apl_parser = AflowOutParser()
194
+ self.aflowin_parser = AflowInParser()
195
+
196
+ self._metainfo_map = {
197
+ 'stiffness_tensor': 'elastic_constants_matrix_second_order',
198
+ 'compliance_tensor': 'compliance_matrix_second_order',
199
+ 'poisson_ratio': 'poisson_ratio_hill',
200
+ 'bulk_modulus_vrh': 'bulk_modulus_hill',
201
+ 'shear_modulus_vrh': 'shear_modulus_hill',
202
+ 'youngs_modulus_vrh': 'Young_modulus_hill',
203
+ 'pughs_modulus_ratio': 'pugh_ratio_hill',
204
+ 'applied_pressure': 'x_aflow_ael_applied_pressure',
205
+ 'average_external_pressure': 'x_aflow_ael_average_external_pressure',
206
+ }
207
+
208
+ def init_parser(self):
209
+ if '.json' in self.filepath:
210
+ self.aflow_data = json.load(open(self.filepath))
211
+ else:
212
+ self.aflowin_parser.mainfile = self.filepath
213
+ self.aflow_data = self.aflowin_parser
214
+
215
+ def get_aflow_file(self, filename):
216
+ files = [f for f in os.listdir(self.maindir) if filename in f]
217
+ if not files:
218
+ files = ['']
219
+ return os.path.join(self.maindir, files[0])
220
+
221
+ def parse_structures(self, module):
222
+ try:
223
+ structures = json.load(
224
+ open(os.path.join(self.maindir, '%s_energy_structures.json' % module))
225
+ ).get('%s_energy_structures' % module, [])
226
+ except Exception:
227
+ structures = []
228
+
229
+ for structure in structures:
230
+ sec_calc = Calculation()
231
+ self.archive.run[-1].calculation.append(sec_calc)
232
+ sec_thermo = Thermodynamics()
233
+ sec_calc.thermodynamics.append(sec_thermo)
234
+ if structure.get('energy') is not None:
235
+ sec_calc.energy = Energy(
236
+ total=EnergyEntry(value=structure.get('energy') * ureg.eV)
237
+ )
238
+ if structure.get('pressure') is not None:
239
+ sec_thermo.pressure = structure.get('pressure') * ureg.kbar
240
+ if structure.get('stress_tensor') is not None:
241
+ sec_calc.stress = Stress(
242
+ total=StressEntry(value=structure.get('stress_tensor') * ureg.kbar)
243
+ )
244
+ if structure.get('structure') is not None:
245
+ sec_system = System()
246
+ self.archive.run[-1].system.append(sec_system)
247
+ sec_system.atoms = Atoms()
248
+ struc = structure.get('structure')
249
+ sec_system.atoms.labels = [
250
+ atom.get('name') for atom in struc.get('atoms', [])
251
+ ]
252
+ sec_system.atoms.concentrations = [
253
+ atom.get('occupancy') for atom in struc.get('atoms', [])
254
+ ]
255
+ if struc.get('lattice') is not None:
256
+ sec_system.atoms.lattice_vectors = (
257
+ struc.get('lattice') * ureg.angstrom * struc.get('scale', 1)
258
+ )
259
+ positions = [atom.get('position') for atom in struc.get('atoms', [])]
260
+ if struc.get('coordinates_type', 'direct').lower().startswith('d'):
261
+ if sec_system.atoms.lattice_vectors is not None:
262
+ positions = np.dot(positions, sec_system.atoms.lattice_vectors)
263
+ sec_system.atoms.positions = positions
264
+
265
+ def parse_agl(self):
266
+ sec_run = Run()
267
+ self.archive.run.append(sec_run)
268
+ sec_run.program = Program(
269
+ name='AFlow', version=self.aflow_data.get('aflow_version', 'unknown')
270
+ )
271
+
272
+ self.parse_structures('AGL')
273
+
274
+ self.agl_parser.mainfile = self.get_aflow_file('aflow.agl.out')
275
+ thermal_properties = self.agl_parser.get('agl_thermal_properties_temperature')
276
+ if thermal_properties is None:
277
+ return
278
+
279
+ workflow = WorkflowThermodynamics(results=ThermodynamicsResults())
280
+
281
+ thermal_properties = np.reshape(
282
+ thermal_properties, (len(thermal_properties) // 9, 9)
283
+ )
284
+ thermal_properties = np.transpose(thermal_properties)
285
+ energies = self.agl_parser.get('agl_energies_temperature')
286
+ energies = np.reshape(energies, (len(energies) // 9, 9))
287
+ energies = np.transpose(energies)
288
+
289
+ workflow.results.temperature = thermal_properties[0] * ureg.K
290
+ workflow.results.gibbs_free_energy = energies[1] * ureg.eV
291
+ workflow.results.vibrational_free_energy = energies[2] * ureg.meV
292
+ workflow.results.vibrational_internal_energy = energies[3] * ureg.meV
293
+ workflow.results.vibrational_entropy = energies[4] * ureg.meV / ureg.K
294
+ workflow.results.heat_capacity_c_v = (
295
+ thermal_properties[4] * ureg.boltzmann_constant
296
+ )
297
+ workflow.results.heat_capacity_c_p = (
298
+ thermal_properties[5] * ureg.boltzmann_constant
299
+ )
300
+ # TODO add these to metainfo def
301
+ # workflow.results.thermal_conductivity = thermal_properties[1] * ureg.watt / ureg.m * ureg.K
302
+ # sec_debye.debye_temperature = thermal_properties[2] * ureg.K
303
+ # sec_debye.gruneisen_parameter = thermal_properties[3]
304
+ # sec_debye.thermal_expansion = thermal_properties[6] / ureg.K
305
+ # sec_debye.bulk_modulus_static = thermal_properties[7] * ureg.GPa
306
+ # sec_debye.bulk_modulus_isothermal = thermal_properties[8] * ureg.GPa
307
+ self.archive.workflow2 = workflow
308
+
309
+ def parse_ael(self):
310
+ sec_run = Run()
311
+ self.archive.run.append(sec_run)
312
+ sec_run.program = Program(
313
+ name='AFlow', version=self.aflow_data.get('aflow_version', 'unknown')
314
+ )
315
+
316
+ self.parse_structures('AEL')
317
+
318
+ self.ael_parser.mainfile = self.get_aflow_file('aflow.ael.out')
319
+ workflow = Elastic(method=ElasticMethod(), results=ElasticResults())
320
+ workflow.method.energy_stress_calculator = 'vasp'
321
+ workflow.method.calculation_method = 'stress'
322
+ workflow.method.elastic_constants_order = 2
323
+
324
+ paths = [
325
+ d for d in self.aflow_data.get('files', []) if d.startswith('ARUN.AEL')
326
+ ]
327
+ deforms = np.array(
328
+ [d.split('_')[-2:] for d in paths], dtype=np.dtype(np.float64)
329
+ )
330
+ strains = [d[1] for d in deforms if d[0] == 1]
331
+ workflow.results.n_deformations = int(max(np.transpose(deforms)[0]))
332
+ workflow.results.n_strains = len(strains)
333
+ workflow.method.strain_maximum = max(strains) - 1.0
334
+
335
+ for key, val in self.ael_parser.get('ael_results', {}).items():
336
+ key = key.replace('ael_', '')
337
+ key = self._metainfo_map.get(key, key)
338
+ if 'modulus' in key or 'pressure' in key:
339
+ val = val * ureg.GPa
340
+ elif 'speed' in key:
341
+ val = val * (ureg.m / ureg.s)
342
+ elif 'temperature' in key:
343
+ val = val * ureg.K
344
+ setattr(workflow.results, key, val)
345
+
346
+ if self.ael_parser.ael_stiffness_tensor is not None:
347
+ workflow.results.elastic_constants_matrix_second_order = (
348
+ np.reshape(self.ael_parser.ael_stiffness_tensor, (6, 6)) * ureg.GPa
349
+ )
350
+
351
+ if self.ael_parser.ael_compliance_tensor is not None:
352
+ workflow.results.compliance_matrix_second_order = np.reshape(
353
+ self.ael_parser.ael_compliance_tensor, (6, 6)
354
+ )
355
+
356
+ self.archive.workflow2 = workflow
357
+
358
+ def parse_apl(self):
359
+ sec_run = Run()
360
+ self.archive.run.append(sec_run)
361
+ sec_run.program = Program(
362
+ name='AFlow', version=self.aflow_data.get('aflow_version', 'unknown')
363
+ )
364
+ sec_scc = Calculation()
365
+ sec_run.calculation.append(sec_scc)
366
+
367
+ try:
368
+ dos = np.transpose(
369
+ np.loadtxt(self.get_aflow_file('flow.apl.phonon_dos.out.xz'))
370
+ )
371
+ except Exception:
372
+ dos = None
373
+
374
+ if dos is not None:
375
+ sec_dos = Dos()
376
+ sec_scc.dos_phonon.append(sec_dos)
377
+ sec_dos.energies = dos[2] * ureg.millielectron_volt
378
+ sec_dos.total.append(
379
+ DosValues(value=dos[3] * (1 / ureg.millielectron_volt))
380
+ )
381
+
382
+ try:
383
+ kpoints = np.transpose(
384
+ np.loadtxt(self.get_aflow_file('aflow.apl.hskpoints.out.xz'))
385
+ )
386
+ n_kpoints = int(max(kpoints[3])) + 1
387
+ kpoints = kpoints[:3]
388
+ kpoints = np.reshape(kpoints, (3, len(kpoints[0]) // n_kpoints, n_kpoints))
389
+ kpoints = np.transpose(kpoints, axes=(1, 2, 0))
390
+
391
+ bandstructure = np.transpose(
392
+ np.loadtxt(self.get_aflow_file('aflow.apl.phonon_dispersion.out.xz'))
393
+ )
394
+ bandstructure = bandstructure[2:]
395
+ bandstructure = np.reshape(
396
+ bandstructure,
397
+ (len(bandstructure), len(bandstructure[0]) // n_kpoints, n_kpoints),
398
+ )
399
+ bandstructure = np.transpose(bandstructure, axes=(1, 2, 0))
400
+ except Exception:
401
+ kpoints = None
402
+
403
+ if kpoints is not None:
404
+ sec_bandstructure = BandStructure()
405
+ sec_scc.band_structure_phonon.append(sec_bandstructure)
406
+ for n_segment in range(len(kpoints)):
407
+ sec_segment = BandEnergies()
408
+ sec_bandstructure.segment.append(sec_segment)
409
+ sec_segment.kpoints = kpoints[n_segment]
410
+ sec_segment.energies = (
411
+ np.reshape(
412
+ bandstructure[n_segment],
413
+ (1, *np.shape(bandstructure[n_segment])),
414
+ )
415
+ * ureg.millielectron_volt
416
+ )
417
+
418
+ self.apl_parser.mainfile = self.get_aflow_file(
419
+ 'aflow.apl.thermodynamic_properties.out'
420
+ )
421
+
422
+ workflow = Phonon(method=PhononMethod(), results=PhononResults())
423
+
424
+ workflow.method.force_calculator = 'vasp'
425
+ mesh = self.aflowin_parser.get('aflow_apl_dosmesh')
426
+ if mesh is not None:
427
+ try:
428
+ cell = Cell.fromcellpar(self.aflowin_parser.geometry)
429
+ workflow.method.mesh_density = (
430
+ np.product([int(m) for m in mesh.split('x')]) / cell.volume
431
+ )
432
+ except Exception:
433
+ pass
434
+
435
+ self.apl_parser.mainfile = self.get_aflow_file('aflow.apl.group_velocities.out')
436
+ group_velocity = self.apl_parser.get('apl_group_velocity')
437
+ if group_velocity is not None:
438
+ try:
439
+ qpoints = self.apl_parser.apl_qpoints
440
+ qpoints = np.reshape(qpoints, (len(qpoints) // 4, 4))
441
+ group_velocity = np.reshape(
442
+ group_velocity, (len(qpoints), len(group_velocity) // len(qpoints))
443
+ )
444
+ group_velocity = np.transpose(np.transpose(group_velocity)[1:])
445
+ workflow.results.qpoints = np.transpose(np.transpose(qpoints)[1:])
446
+ workflow.results.group_velocity = (
447
+ np.reshape(
448
+ group_velocity,
449
+ (len(group_velocity), len(group_velocity[0]) // 3, 3),
450
+ )
451
+ * ureg.kilometer
452
+ / ureg.second
453
+ )
454
+ except Exception:
455
+ pass
456
+
457
+ self.apl_parser.mainfile = self.get_aflow_file(
458
+ 'aflow.apl.thermodynamic_properties.out.xz'
459
+ )
460
+ apl_thermo = self.apl_parser.get('apl_thermo')
461
+ # TODO handle multiple workflows
462
+ if apl_thermo is not None:
463
+ apl_thermo = np.transpose(np.reshape(apl_thermo, (len(apl_thermo) // 6, 6)))
464
+ sec_thermo = WorkflowThermodynamics(results=ThermodynamicsResults())
465
+ sec_thermo.results.temperature = apl_thermo[0] * ureg.kelvin
466
+ sec_thermo.results.internal_energy = apl_thermo[2] * ureg.millielectron_volt
467
+ sec_thermo.results.helmholtz_free_energy = (
468
+ apl_thermo[3] * ureg.millielectron_volt
469
+ )
470
+ sec_thermo.results.entropy = apl_thermo[4] * ureg.boltzmann_constant
471
+ sec_thermo.results.heat_capacity_c_v = (
472
+ apl_thermo[5] * ureg.boltzmann_constant
473
+ )
474
+
475
+ self.archive.workflow2 = workflow
476
+
477
+ # TODO parse systems for each displacements
478
+
479
+ # TODO parse displacements, force constants, dynamical matrix
480
+
481
+ def parse(self, filepath, archive, logger):
482
+ self.filepath = os.path.abspath(filepath)
483
+ self.archive = archive
484
+ self.maindir = os.path.dirname(self.filepath)
485
+ self.logger = logger if logger is not None else logging
486
+
487
+ self.init_parser()
488
+
489
+ sec_run = Run()
490
+ self.archive.run.append(sec_run)
491
+ sec_run.program = Program(
492
+ name='AFlow', version=self.aflow_data.get('aflow_version', 'unknown')
493
+ )
494
+
495
+ # parse run metadata
496
+ run_quantities = ['aurl', 'auid', 'data_api', 'data_source', 'loop']
497
+ for key in run_quantities:
498
+ val = self.aflow_data.get(key)
499
+ if val is not None:
500
+ setattr(sec_run, 'x_aflow_%s' % key, val)
501
+
502
+ # TODO The OUTCAR file will be read by the vasp parser and so the complete
503
+ # metadata for both system and method should be filled in by vasp parser.
504
+ # parse structure from aflow_data
505
+ sec_system = System()
506
+ sec_run.system.append(sec_system)
507
+ sec_system.atoms = Atoms()
508
+ lattice_parameters = self.aflow_data.get('geometry')
509
+ if lattice_parameters is not None:
510
+ cell = self.aflow_data.get('cell', Cell.fromcellpar(lattice_parameters))
511
+ sec_system.atoms.lattice_vectors = cell.array * ureg.angstrom
512
+ sec_system.atoms.periodic = [True, True, True]
513
+ species = self.aflow_data.get('species', [])
514
+ atom_labels = []
515
+ for n, specie in enumerate(species):
516
+ atom_labels += [specie] * self.aflow_data['composition'][n]
517
+ sec_system.atoms.labels = atom_labels
518
+ if self.aflow_data.get('positions_cartesian') is not None:
519
+ sec_system.atoms.positions = (
520
+ self.aflow_data.get('positions_cartesian') * ureg.angstrom
521
+ )
522
+
523
+ # parse system metadata from aflow_data
524
+ system_quantities = [
525
+ 'compound',
526
+ 'prototype',
527
+ 'nspecies',
528
+ 'natoms',
529
+ 'natoms_orig',
530
+ 'composition',
531
+ 'density',
532
+ 'density_orig',
533
+ 'scintillation_attenuation_length',
534
+ 'stoichiometry',
535
+ 'species',
536
+ 'geometry',
537
+ 'geometry_orig',
538
+ 'volume_cell',
539
+ 'volume_atom',
540
+ 'volume_cell_orig',
541
+ 'volume_atom_orig',
542
+ 'n_sg',
543
+ 'sg',
544
+ 'sg2',
545
+ 'spacegroup_orig',
546
+ 'spacegroup_relax',
547
+ 'Bravais_lattice_orig',
548
+ 'lattice_variation_orig',
549
+ 'lattice_system_orig',
550
+ 'Pearson_symbol_orig',
551
+ 'Bravais_lattice_relax',
552
+ 'lattice_variation_relax',
553
+ 'lattice_system_relax',
554
+ 'Pearson_symbol_relax',
555
+ 'crystal_family_orig',
556
+ 'crystal_system_orig',
557
+ 'crystal_class_orig',
558
+ 'point_group_Hermann_Mauguin_orig',
559
+ 'point_group_Schoenflies_orig',
560
+ 'point_group_orbifold_orig',
561
+ 'point_group_type_orig',
562
+ 'point_group_order_orig',
563
+ 'point_group_structure_orig',
564
+ 'Bravais_lattice_lattice_type_orig',
565
+ 'Bravais_lattice_lattice_variation_type_orig',
566
+ 'Bravais_lattice_lattice_system_orig',
567
+ 'Bravais_superlattice_lattice_type_orig',
568
+ 'Bravais_superlattice_lattice_variation_type_orig',
569
+ 'Bravais_superlattice_lattice_system_orig',
570
+ 'Pearson_symbol_superlattice_orig',
571
+ 'reciprocal_geometry_orig',
572
+ 'reciprocal_volume_cell_orig',
573
+ 'reciprocal_lattice_type_orig',
574
+ 'reciprocal_lattice_variation_type_orig',
575
+ 'Wyckoff_letters_orig',
576
+ 'Wyckoff_multiplicities_orig',
577
+ 'Wyckoff_site_symmetries_orig',
578
+ 'crystal_family',
579
+ 'crystal_system',
580
+ 'crystal_class',
581
+ 'point_group_Hermann_Mauguin',
582
+ 'point_group_Schoenflies',
583
+ 'point_group_orbifold',
584
+ 'point_group_type',
585
+ 'point_group_order',
586
+ 'point_group_structure',
587
+ 'Bravais_lattice_lattice_type',
588
+ 'Bravais_lattice_lattice_variation_type',
589
+ 'Bravais_lattice_lattice_system',
590
+ 'Bravais_superlattice_lattice_type',
591
+ 'Bravais_superlattice_lattice_variation_type',
592
+ 'Bravais_superlattice_lattice_system',
593
+ 'Pearson_symbol_superlattice',
594
+ 'reciprocal_geometry',
595
+ 'reciprocal_volume_cell',
596
+ 'reciprocal_lattice_type',
597
+ 'reciprocal_lattice_variation_type',
598
+ 'Wyckoff_letters',
599
+ 'Wyckoff_multiplicities',
600
+ 'Wyckoff_site_symmetries',
601
+ 'prototype_label_orig',
602
+ 'prototype_params_list_orig',
603
+ 'prototype_params_values_orig',
604
+ 'prototype_label_relax',
605
+ 'prototype_params_list_relax',
606
+ 'prototype_params_values_relax',
607
+ ]
608
+ for key in system_quantities:
609
+ val = self.aflow_data.get(key)
610
+ if val is not None:
611
+ sec_system.m_set(
612
+ sec_system.m_get_quantity_definition(f'x_aflow_{key}'), val
613
+ )
614
+
615
+ # parse method metadata from self.aflow_data
616
+ method_quantities = [
617
+ 'code',
618
+ 'species_pp',
619
+ 'n_dft_type',
620
+ 'dft_type',
621
+ 'dft_type',
622
+ 'species_pp_version',
623
+ 'species_pp_ZVAL',
624
+ 'species_pp_AUID',
625
+ 'ldau_type',
626
+ 'ldau_l',
627
+ 'ldau_u',
628
+ 'ldau_j',
629
+ 'valence_cell_iupac',
630
+ 'valence_cell_std',
631
+ 'energy_cutoff',
632
+ 'delta_electronic_energy_convergence',
633
+ 'delta_electronic_energy_threshold',
634
+ 'kpoints_relax',
635
+ 'kpoints_static',
636
+ 'n_kpoints_bands_path',
637
+ 'kpoints_bands_path',
638
+ 'kpoints_bands_nkpts',
639
+ ]
640
+ sec_method = Method()
641
+ sec_run.method.append(sec_method)
642
+ for key in method_quantities:
643
+ val = self.aflow_data.get(key)
644
+ if val is not None:
645
+ sec_method.m_set(
646
+ sec_method.m_get_quantity_definition(f'x_aflow_{key}'), val
647
+ )
648
+
649
+ # parse basic calculation quantities from self.aflow_data
650
+ sec_scc = Calculation()
651
+ sec_run.calculation.append(sec_scc)
652
+ sec_scc.energy = Energy()
653
+ sec_scc.forces = Forces()
654
+ sec_thermo = Thermodynamics()
655
+ sec_scc.thermodynamics.append(sec_thermo)
656
+ if self.aflow_data.get('energy_cell') is not None:
657
+ sec_scc.energy.total = EnergyEntry(
658
+ value=self.aflow_data['energy_cell'] * ureg.eV
659
+ )
660
+ if self.aflow_data.get('forces') is not None:
661
+ sec_scc.forces.total = ForcesEntry(
662
+ value=self.aflow_data['forces'] * ureg.eV / ureg.angstrom
663
+ )
664
+ if self.aflow_data.get('enthalpy_cell') is not None:
665
+ sec_thermo.enthalpy = self.aflow_data['enthalpy_cell'] * ureg.eV
666
+ if self.aflow_data.get('entropy_cell') is not None:
667
+ sec_thermo.entropy = self.aflow_data['entropy_cell'] * ureg.eV / ureg.K
668
+ if self.aflow_data.get('calculation_time') is not None:
669
+ sec_scc.time_calculation = self.aflow_data['calculation_time'] * ureg.s
670
+ calculation_quantities = [
671
+ 'stress_tensor',
672
+ 'pressure_residual',
673
+ 'Pulay_stress',
674
+ 'Egap',
675
+ 'Egap_fit',
676
+ 'Egap_type',
677
+ 'enthalpy_formation_cell',
678
+ 'entropic_temperature',
679
+ 'PV',
680
+ 'spin_cell',
681
+ 'spinD',
682
+ 'spinF',
683
+ 'calculation_memory',
684
+ 'calculation_cores',
685
+ 'nbondxx',
686
+ 'agl_thermal_conductivity_300K',
687
+ 'agl_debye',
688
+ 'agl_acoustic_debye',
689
+ 'agl_gruneisen',
690
+ 'agl_heat_capacity_Cv_300K',
691
+ 'agl_heat_capacity_Cp_300K',
692
+ 'agl_thermal_expansion_300K',
693
+ 'agl_bulk_modulus_static_300K',
694
+ 'agl_bulk_modulus_isothermal_300K',
695
+ 'agl_poisson_ratio_source',
696
+ 'agl_vibrational_free_energy_300K_cell',
697
+ 'agl_vibrational_free_energy_300K_atom',
698
+ 'agl_vibrational_entropy_300K_cell',
699
+ 'agl_vibrational_entropy_300K_atom',
700
+ 'ael_poisson_ratio',
701
+ 'ael_bulk_modulus_voigt',
702
+ 'ael_bulk_modulus_reuss',
703
+ 'ael_shear_modulus_voigt',
704
+ 'ael_shear_modulus_reuss',
705
+ 'ael_bulk_modulus_vrh',
706
+ 'ael_shear_modulus_vrh',
707
+ 'ael_elastic_anisotropy',
708
+ 'ael_youngs_modulus_vrh',
709
+ 'ael_speed_sound_transverse',
710
+ 'ael_speed_sound_longitudinal',
711
+ 'ael_speed_sound_average',
712
+ 'ael_pughs_modulus_ratio',
713
+ 'ael_debye_temperature',
714
+ 'ael_applied_pressure',
715
+ 'ael_average_external_pressure',
716
+ 'ael_stiffness_tensor',
717
+ 'ael_compliance_tensor',
718
+ 'bader_net_charges',
719
+ 'bader_atomic_volumes',
720
+ 'n_files',
721
+ 'files',
722
+ 'node_CPU_Model',
723
+ 'node_CPU_Cores',
724
+ 'node_CPU_MHz',
725
+ 'node_RAM_GB',
726
+ 'catalog',
727
+ 'aflowlib_version',
728
+ 'aflowlib_date',
729
+ ]
730
+ for key in calculation_quantities:
731
+ val = self.aflow_data.get(key)
732
+ if val is not None:
733
+ setattr(sec_scc, 'x_aflow_%s' % key, val)
734
+
735
+ for module in self.aflow_data.get('loop', []):
736
+ if module == 'ael':
737
+ self.parse_ael()
738
+ elif module == 'agl':
739
+ self.parse_agl()
740
+ elif module == 'apl':
741
+ self.parse_apl()