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,583 @@
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
+ import phonopy
24
+ from phonopy.units import THzToEv
25
+ from phonopy.structure.atoms import PhonopyAtoms
26
+
27
+ from .calculator import PhononProperties
28
+
29
+ from nomad import config # type: ignore
30
+ from nomad.units import ureg
31
+ from nomad.parsing.file_parser import TextParser, Quantity
32
+ from runschema.run import Run, Program
33
+ from runschema.method import Method, Electronic
34
+ from runschema.system import System, Atoms
35
+ from runschema.calculation import (
36
+ Calculation,
37
+ BandStructure,
38
+ BandEnergies,
39
+ Dos,
40
+ DosValues,
41
+ Thermodynamics,
42
+ )
43
+ from simulationworkflowschema import Phonon, PhononMethod, PhononResults
44
+ from nomad.datamodel.metainfo.workflow import Link, Task
45
+ from nomad.datamodel import EntryArchive
46
+
47
+ from .metainfo import phonopy as phonopymetainfo # pylint: disable=unused-import
48
+
49
+
50
+ def read_aims(filename):
51
+ """Method to read FHI-aims geometry files in phonopy context."""
52
+ cell = []
53
+ positions = []
54
+ fractional = []
55
+ symbols = []
56
+ magmoms = []
57
+ if not os.path.isfile(filename):
58
+ return
59
+ with open(filename) as f:
60
+ while True:
61
+ line = f.readline()
62
+ if not line:
63
+ break
64
+ line = line.split()
65
+ if len(line) == 0:
66
+ continue
67
+ if line[0] == 'lattice_vector':
68
+ cell.append([float(x) for x in line[1:4]])
69
+ elif line[0].startswith('atom'):
70
+ fractional.append(line[0] == 'atom_frac')
71
+ positions.append([float(x) for x in line[1:4]])
72
+ symbols.append(line[4])
73
+ elif line[0] == 'initial_moment':
74
+ magmoms.append(float(line[1]))
75
+
76
+ for n, pos in enumerate(positions):
77
+ if fractional[n]:
78
+ positions[n] = [
79
+ sum([pos[j] * cell[j][i] for j in range(3)]) for i in range(3)
80
+ ]
81
+ if len(magmoms) == len(positions):
82
+ return PhonopyAtoms(
83
+ cell=cell, symbols=symbols, positions=positions, magmoms=magmoms
84
+ )
85
+ else:
86
+ return PhonopyAtoms(cell=cell, symbols=symbols, positions=positions)
87
+
88
+
89
+ class Atoms_with_forces(PhonopyAtoms):
90
+ """Hack to phonopy.atoms to maintain ASE compatibility also for forces."""
91
+
92
+ def get_forces(self):
93
+ return self.forces
94
+
95
+
96
+ def read_aims_output(filename):
97
+ """Read FHI-aims output
98
+ returns geometry with forces from last self-consistency iteration"""
99
+ cell = []
100
+ symbols = []
101
+ positions = []
102
+ forces = []
103
+ N = 0
104
+
105
+ with open(filename) as f:
106
+ while True:
107
+ line = f.readline()
108
+ if not line:
109
+ break
110
+ if 'Number of atoms' in line:
111
+ N = int(line.split()[5])
112
+ elif '| Unit cell:' in line:
113
+ cell = [[float(x) for x in f.readline().split()[1:4]] for _ in range(3)]
114
+ elif 'Atomic structure:' in line or 'Updated atomic structure:' in line:
115
+ positions = []
116
+ symbols = []
117
+ symbol_index = 3 if 'Atomic' in line else 4
118
+ position_index = 4 if 'Atomic' in line else 1
119
+ while len(positions) != N:
120
+ line = f.readline()
121
+ if 'Species' in line or 'atom ' in line:
122
+ line = line.split()
123
+ positions.append(
124
+ [
125
+ float(x)
126
+ for x in line[position_index : position_index + 3]
127
+ ]
128
+ )
129
+ symbols.append(line[symbol_index])
130
+ elif 'Total atomic forces' in line:
131
+ forces = [
132
+ [float(x) for x in f.readline().split()[2:5]] for _ in range(N)
133
+ ]
134
+
135
+ atoms = Atoms_with_forces(cell=cell, symbols=symbols, positions=positions)
136
+ atoms.forces = forces
137
+
138
+ return atoms
139
+
140
+
141
+ def read_forces_aims(reference_supercells, tolerance=1e-6, logger=None):
142
+ """
143
+ Collect the pre calculated forces for each of the supercells
144
+ """
145
+
146
+ def get_aims_output_file(directory):
147
+ files = [f for f in os.listdir(directory) if f.endswith('.out')]
148
+ output = None
149
+ for f in files:
150
+ try:
151
+ output = read_aims_output(os.path.join(directory, f))
152
+ break
153
+ except Exception:
154
+ pass
155
+ return output, f
156
+
157
+ def is_equal(reference, calculated):
158
+ if len(reference) != len(calculated):
159
+ logger.warning('Inconsistent number of atoms.')
160
+ return False
161
+ if (reference.get_atomic_numbers() != calculated.get_atomic_numbers()).any():
162
+ logger.warning('Inconsistent species.')
163
+ return False
164
+ if (abs(reference.get_cell() - calculated.get_cell()) > tolerance).any():
165
+ logger.warning('Inconsistent cell.')
166
+ return False
167
+ # get normalized positions, wrapped to the bounding cell
168
+ ref_pos = reference.get_scaled_positions() % 1.0
169
+ cal_pos = calculated.get_scaled_positions() % 1.0
170
+ # resolve coordinates at the boundary
171
+ ref_pos = np.where(ref_pos != 1.0, ref_pos, 0.0)
172
+ cal_pos = np.where(cal_pos != 1.0, cal_pos, 0.0)
173
+ if (abs(ref_pos - cal_pos) > tolerance).any():
174
+ logger.warning('Inconsistent positions.')
175
+ return False
176
+ return True
177
+
178
+ reference_paths, forces_sets = [], []
179
+
180
+ n_pad = int(np.ceil(np.log10(len(reference_supercells) + 1))) + 1
181
+ for n, reference_supercell in enumerate(reference_supercells):
182
+ directory = 'phonopy-FHI-aims-displacement-%s' % (str(n + 1).zfill(n_pad))
183
+ filename = os.path.join(directory, '%s.out' % directory)
184
+ if os.path.isfile(filename):
185
+ calculated_supercell = read_aims_output(filename)
186
+ else:
187
+ # try reading out files
188
+ calculated_supercell, filename = get_aims_output_file(directory)
189
+ filename = os.path.join(directory, filename)
190
+
191
+ # compare if calculated cell really corresponds to supercell
192
+ if not is_equal(reference_supercell, calculated_supercell):
193
+ logger.error('Supercells do not match')
194
+
195
+ forces = np.array(calculated_supercell.get_forces())
196
+ drift_force = forces.sum(axis=0)
197
+ for force in forces:
198
+ force -= drift_force / forces.shape[0]
199
+ forces_sets.append(forces)
200
+ reference_paths.append(filename)
201
+ return forces_sets, reference_paths
202
+
203
+
204
+ class ControlParser(TextParser):
205
+ def __init__(self):
206
+ super().__init__()
207
+
208
+ def init_quantities(self):
209
+ def str_to_nac(val_in):
210
+ val = val_in.strip().split()
211
+ nac = dict(file=val[0], method=val[1].lower())
212
+ if len(val) > 2:
213
+ nac['delta'] = [float(v) for v in val[3:6]]
214
+ return nac
215
+
216
+ def str_to_supercell(val_in):
217
+ val = [int(v) for v in val_in.strip().split()]
218
+ if len(val) == 3:
219
+ return np.diag(val)
220
+ else:
221
+ return np.reshape(val, (3, 3))
222
+
223
+ self._quantities = [
224
+ Quantity(
225
+ 'displacement', r'\n *phonon displacement\s*([\d\.]+)', dtype=float
226
+ ),
227
+ Quantity(
228
+ 'symmetry_thresh',
229
+ r'\n *phonon symmetry_thresh\s*([\d\.]+)',
230
+ dtype=float,
231
+ ),
232
+ Quantity('frequency_unit', r'\n *phonon frequency_unit\s*(\S+)'),
233
+ Quantity(
234
+ 'supercell',
235
+ r'\n *phonon supercell\s*(.+)',
236
+ str_operation=str_to_supercell,
237
+ ),
238
+ Quantity('nac', r'\n *phonon nac\s*(.+)', str_operation=str_to_nac),
239
+ ]
240
+
241
+
242
+ def phonopy_obj_to_archive(
243
+ phonopy_obj, references=[], archive=None, filename=None, logger=None, **kwargs
244
+ ):
245
+ """
246
+ Executes Phonopy starting from a phonopy object and write the results on a nomad archive.
247
+ """
248
+
249
+ def parse_bandstructure():
250
+ freqs, bands, bands_labels = properties.get_bandstructure()
251
+ if freqs is None:
252
+ return
253
+
254
+ # convert THz to eV
255
+ freqs = freqs * THzToEv
256
+
257
+ # convert eV to J
258
+ freqs = (freqs * ureg.eV).to('joules').magnitude
259
+
260
+ sec_scc = archive.run[0].calculation[0]
261
+
262
+ sec_k_band = BandStructure()
263
+ sec_scc.band_structure_phonon.append(sec_k_band)
264
+
265
+ for i in range(len(freqs)):
266
+ sec_k_band_segment = BandEnergies()
267
+ sec_k_band.segment.append(sec_k_band_segment)
268
+ sec_k_band_segment.kpoints = bands[i]
269
+ sec_k_band_segment.endpoints_labels = [
270
+ str(label) for label in bands_labels[i]
271
+ ]
272
+ sec_k_band_segment.energies = [freqs[i]]
273
+
274
+ def parse_dos():
275
+ f, dos = properties.get_dos()
276
+
277
+ # convert THz to eV to Joules
278
+ f = f * THzToEv
279
+ f = (f * ureg.eV).to('joules').magnitude
280
+
281
+ sec_scc = archive.run[0].calculation[0]
282
+ sec_dos = Dos()
283
+ sec_scc.dos_phonon.append(sec_dos)
284
+ sec_dos.energies = f
285
+ sec_dos_values = DosValues()
286
+ sec_dos.total.append(sec_dos_values)
287
+ sec_dos_values.value = dos
288
+
289
+ def parse_thermodynamical_properties():
290
+ T, fe, _, cv = properties.get_thermodynamical_properties()
291
+
292
+ n_atoms = len(phonopy_obj.unitcell)
293
+ n_atoms_supercell = len(phonopy_obj.supercell)
294
+
295
+ fe = fe / n_atoms
296
+
297
+ # The thermodynamic properties are reported by phonopy for the base
298
+ # system. Since the values in the metainfo are stored per the referenced
299
+ # system, we need to multiple by the size factor between the base system
300
+ # and the supersystem used in the calculations.
301
+ cv = cv * (n_atoms_supercell / n_atoms)
302
+
303
+ # convert to SI units
304
+ fe = (fe * ureg.eV).to('joules').magnitude
305
+
306
+ cv = (cv * ureg.eV / ureg.K).to('joules/K').magnitude
307
+
308
+ sec_run = archive.run[0]
309
+ sec_scc = sec_run.calculation[0]
310
+
311
+ for n, Tn in enumerate(T):
312
+ sec_thermo_prop = Thermodynamics()
313
+ sec_scc.thermodynamics.append(sec_thermo_prop)
314
+ sec_thermo_prop.temperature = Tn
315
+ sec_thermo_prop.vibrational_free_energy_at_constant_volume = fe[n]
316
+ sec_thermo_prop.heat_capacity_c_v = cv[n]
317
+
318
+ # TODO create a taylor_expansion workflow?
319
+ # sampling_method = 'taylor_expansion'
320
+ # expansion_order = 2
321
+
322
+ logger = logger if logger is not None else logging
323
+ archive = archive if archive else EntryArchive()
324
+
325
+ pbc = np.array((1, 1, 1), bool)
326
+
327
+ unit_cell = phonopy_obj.unitcell.get_cell()
328
+ unit_pos = phonopy_obj.unitcell.get_positions()
329
+ unit_sym = np.array(phonopy_obj.unitcell.get_chemical_symbols())
330
+
331
+ super_cell = phonopy_obj.supercell.get_cell()
332
+ super_pos = phonopy_obj.supercell.get_positions()
333
+ super_sym = np.array(phonopy_obj.supercell.get_chemical_symbols())
334
+
335
+ unit_cell = (unit_cell * ureg.angstrom).to('meter').magnitude
336
+ unit_pos = (unit_pos * ureg.angstrom).to('meter').magnitude
337
+
338
+ super_cell = (super_cell * ureg.angstrom).to('meter').magnitude
339
+ super_pos = (super_pos * ureg.angstrom).to('meter').magnitude
340
+
341
+ try:
342
+ displacement = np.linalg.norm(phonopy_obj.displacements[0][1:])
343
+ displacement = displacement * ureg.angstrom
344
+ except Exception:
345
+ displacement = None
346
+
347
+ supercell_matrix = phonopy_obj.supercell_matrix
348
+ sym_tol = phonopy_obj.symmetry.tolerance
349
+
350
+ sec_run = Run()
351
+ archive.run.append(sec_run)
352
+ sec_run.program = Program(name='Phonopy', version=phonopy.__version__)
353
+
354
+ sec_system_unit = System()
355
+ sec_run.system.append(sec_system_unit)
356
+ sec_atoms = Atoms()
357
+ sec_system_unit.atoms = sec_atoms
358
+ sec_atoms.periodic = pbc
359
+ sec_atoms.labels = unit_sym
360
+ sec_atoms.positions = unit_pos
361
+ sec_atoms.lattice_vectors = unit_cell
362
+
363
+ sec_system = System()
364
+ sec_run.system.append(sec_system)
365
+ sec_system.sub_system_ref = sec_system_unit
366
+ sec_system.systems_ref = [sec_system_unit]
367
+ sec_atoms = Atoms()
368
+ sec_system.atoms = sec_atoms
369
+ sec_atoms.periodic = pbc
370
+ sec_atoms.labels = super_sym
371
+ sec_atoms.positions = super_pos
372
+ sec_atoms.lattice_vectors = super_cell
373
+ sec_atoms.supercell_matrix = supercell_matrix
374
+ sec_system.x_phonopy_original_system_ref = sec_system_unit
375
+
376
+ sec_method = Method()
377
+ sec_run.method.append(sec_method)
378
+ # TODO I put this so as to have a recognizable section method, but metainfo
379
+ # should be expanded to include phonon related method parameters
380
+ sec_method.electronic = Electronic(method='DFT')
381
+ sec_method.x_phonopy_symprec = sym_tol
382
+ if displacement is not None:
383
+ sec_method.x_phonopy_displacement = displacement
384
+
385
+ try:
386
+ force_constants = phonopy_obj.get_force_constants()
387
+ force_constants = (
388
+ (force_constants * ureg.eV / ureg.angstrom**2).to('J/(m**2)').magnitude
389
+ )
390
+ except Exception:
391
+ logger.error('Error producing force constants.')
392
+ return
393
+
394
+ sec_scc = Calculation()
395
+ sec_run.calculation.append(sec_scc)
396
+ sec_scc.system_ref = sec_system
397
+ sec_scc.method_ref = sec_method
398
+ sec_scc.hessian_matrix = force_constants
399
+
400
+ # run Phonopy
401
+ properties = PhononProperties(phonopy_obj, logger, **kwargs)
402
+
403
+ parse_bandstructure()
404
+ parse_dos()
405
+ parse_thermodynamical_properties()
406
+
407
+ # create workflow section
408
+ vol = np.dot(unit_cell[0], np.cross(unit_cell[1], unit_cell[2]))
409
+ n_imaginary = np.count_nonzero(properties.frequencies < 0)
410
+
411
+ workflow = Phonon(method=PhononMethod(), results=PhononResults())
412
+ workflow.method.force_calculator = phonopy_obj.calculator
413
+ workflow.method.mesh_density = np.prod(properties.mesh) / vol
414
+ workflow.results.n_imaginary_frequencies = n_imaginary
415
+ if phonopy_obj.nac_params:
416
+ workflow.method.with_non_analytic_correction = True
417
+ inputs = []
418
+ for path in references:
419
+ try:
420
+ archive = archive.m_context.resolve_archive(
421
+ f'../upload/archive/mainfile/{path}'
422
+ )
423
+ section = archive.run[0].calculation[0]
424
+ except Exception as e:
425
+ section = None
426
+ logger.error(
427
+ 'Could not resolve referenced calculations.', exc_info=e, path=path
428
+ )
429
+ if section is not None:
430
+ inputs.append(Link(name='Input calculation', section=section))
431
+ workflow.inputs = inputs
432
+ workflow.outputs = [Link(name='Phonon results', section=f'/workflow2/results')]
433
+ workflow.tasks = [
434
+ Task(
435
+ name='Phonon calculation', inputs=workflow.inputs, outputs=workflow.outputs
436
+ )
437
+ ]
438
+ archive.workflow2 = workflow
439
+
440
+ if filename:
441
+ with open(filename, 'w') as f:
442
+ json.dump(archive.m_to_dict(), f, indent=4)
443
+
444
+ return archive
445
+
446
+
447
+ class PhonopyParser:
448
+ level = 1
449
+
450
+ def __init__(self, **kwargs):
451
+ # super().__init__(
452
+ # name='parsers/phonopy', code_name='Phonopy', code_homepage='https://phonopy.github.io/phonopy/',
453
+ # mainfile_name_re=(r'(.*/phonopy-FHI-aims-displacement-0*1/control.in$)|(.*/phon.+yaml)')
454
+ # )
455
+ self._kwargs = kwargs
456
+ self.control_parser = ControlParser()
457
+
458
+ @property
459
+ def mainfile(self):
460
+ return self._filepath
461
+
462
+ @mainfile.setter
463
+ def mainfile(self, val):
464
+ self._phonopy_obj = None
465
+ self.references = []
466
+ self._filepath = os.path.abspath(val)
467
+
468
+ @property
469
+ def phonopy_obj(self):
470
+ if self._phonopy_obj is None:
471
+ if 'control.in' in self.mainfile:
472
+ self._build_phonopy_object_fhi_aims()
473
+ elif self.mainfile.endswith('.yaml'):
474
+ self._build_phonopy_object_yaml()
475
+ return self._phonopy_obj
476
+
477
+ def _build_phonopy_object_yaml(self):
478
+ cwd = os.getcwd()
479
+ os.chdir(os.path.dirname(self.mainfile))
480
+
481
+ try:
482
+ phonopy_obj = phonopy.load(self.mainfile)
483
+ except Exception:
484
+ self.logger.error('Error loading phonopy file.')
485
+ phonopy_obj = None
486
+ finally:
487
+ os.chdir(cwd)
488
+
489
+ self._phonopy_obj = phonopy_obj
490
+
491
+ def _build_phonopy_object_fhi_aims(self):
492
+ cwd = os.getcwd()
493
+ os.chdir(os.path.dirname(os.path.dirname(self.mainfile)))
494
+ try:
495
+ cell_obj = read_aims('geometry.in')
496
+ self.control_parser.mainfile = 'control.in'
497
+ supercell_matrix = self.control_parser.get('supercell')
498
+ displacement = self.control_parser.get('displacement', 0.001)
499
+ sym = self.control_parser.get('symmetry_thresh', 1e-6)
500
+ try:
501
+ phonopy_obj = phonopy.Phonopy(
502
+ cell_obj, supercell_matrix, symprec=sym, calculator='fhi-aims'
503
+ )
504
+ phonopy_obj.generate_displacements(distance=displacement)
505
+ supercells = phonopy_obj.get_supercells_with_displacements()
506
+ set_of_forces, relative_paths = read_forces_aims(
507
+ supercells, logger=self.logger
508
+ )
509
+ except Exception:
510
+ self.logger.error('Error generating phonopy object.')
511
+ set_of_forces = []
512
+ phonopy_obj = None
513
+ relative_paths = []
514
+
515
+ prep_path = self.mainfile.split('phonopy-FHI-aims-displacement-')
516
+ # Try to resolve references as paths relative to the upload root.
517
+ try:
518
+ for path in relative_paths:
519
+ abs_path = '%s%s' % (prep_path[0], path)
520
+ rel_path = abs_path.split(config.fs.staging + '/')[1].split('/', 3)[
521
+ 3
522
+ ]
523
+ self.references.append(rel_path)
524
+ except Exception:
525
+ self.logger.warning(
526
+ 'Could not resolve path to a referenced calculation within the upload.'
527
+ )
528
+
529
+ finally:
530
+ os.chdir(cwd)
531
+
532
+ if set_of_forces:
533
+ try:
534
+ phonopy_obj.set_forces(set_of_forces)
535
+ phonopy_obj.produce_force_constants()
536
+ except Exception:
537
+ self.logger.error('Error producing force constants.')
538
+ pass
539
+
540
+ self._phonopy_obj = phonopy_obj
541
+
542
+ def parse(self, filepath, archive, logger, **kwargs):
543
+ self.mainfile = os.path.abspath(filepath)
544
+ self.archive = archive
545
+ self.logger = logger if logger is not None else logging
546
+ self._kwargs.update(kwargs)
547
+
548
+ # get bandstructure configuration file
549
+ maindir = os.path.dirname(self.mainfile)
550
+ # TODO read band configuration from phonopy.yaml
551
+ files = [f for f in os.listdir(maindir) if f.endswith('.conf')]
552
+ self._kwargs.update(
553
+ {'band_conf': os.path.join(maindir, files[0]) if files else None}
554
+ )
555
+
556
+ phonopy_obj = self.phonopy_obj
557
+ if phonopy_obj is None:
558
+ self.logger.error('Error running phonopy.')
559
+ return
560
+
561
+ phonopy_obj_to_archive(
562
+ phonopy_obj, references=self.references, archive=archive, logger=logger
563
+ )
564
+
565
+ def after_normalization(self, archive, logger=None) -> None:
566
+ # Overwrite the result method with method details taken from the first referenced
567
+ # calculation. The program name and version are kept.
568
+ self.logger = logger if logger is not None else logging
569
+ try:
570
+ first_referenced_calculation = archive.workflow2.results.calculations_ref[0]
571
+ referenced_archive = first_referenced_calculation.m_root()
572
+ except Exception:
573
+ self.logger.warn('Error getting referenced calculation.')
574
+ return
575
+
576
+ new_method = referenced_archive.results.method.m_copy()
577
+ new_method.simulation.program_name = (
578
+ self.archive.results.method.simulation.program_name
579
+ )
580
+ new_method.simulation.program_version = (
581
+ self.archive.results.method.simulation.program_version
582
+ )
583
+ archive.results.method = new_method
@@ -0,0 +1,19 @@
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
+ from .parser import QuantumEspressoEPWParser
@@ -0,0 +1,31 @@
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 sys
20
+ import json
21
+ import logging
22
+
23
+ from nomad.utils import configure_logging
24
+ from nomad.datamodel import EntryArchive
25
+ from workflowparsers.quantum_espresso_epw import QuantumEspressoEPWParser
26
+
27
+ if __name__ == '__main__':
28
+ configure_logging(console_log_level=logging.DEBUG)
29
+ archive = EntryArchive()
30
+ QuantumEspressoEPWParser().parse(sys.argv[1], archive, logging)
31
+ json.dump(archive.m_to_dict(), sys.stdout, indent=2)
@@ -0,0 +1,19 @@
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
+ from . import quantum_espresso_epw