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,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
+ import os
20
+ import numpy as np
21
+ import logging
22
+ from ase import Atoms as aseAtoms
23
+
24
+ from nomad.units import ureg
25
+ from nomad.parsing.file_parser import Quantity, TextParser
26
+
27
+ from runschema.run import Run, Program
28
+ from runschema.method import Method
29
+ from runschema.system import System, Atoms
30
+ from runschema.calculation import Calculation
31
+ from simulationworkflowschema import Elastic, ElasticMethod, ElasticResults
32
+ from simulationworkflowschema.elastic import StrainDiagrams
33
+ from .metainfo.elastic import x_elastic_section_fitting_parameters
34
+
35
+
36
+ class InfoParser(TextParser):
37
+ def __init__(self):
38
+ super().__init__(None)
39
+
40
+ def init_quantities(self):
41
+ self._quantities = [
42
+ Quantity(
43
+ 'order',
44
+ r'\s*Order of elastic constants\s*=\s*([0-9]+)',
45
+ repeats=False,
46
+ dtype=int,
47
+ ),
48
+ Quantity(
49
+ 'calculation_method',
50
+ r'\s*Method of calculation\s*=\s*([-a-zA-Z]+)\s*',
51
+ repeats=False,
52
+ ),
53
+ Quantity(
54
+ 'code_name', r'\s*DFT code name\s*=\s*([-a-zA-Z]+)', repeats=False
55
+ ),
56
+ Quantity(
57
+ 'space_group_number',
58
+ r'\s*Space-group number\s*=\s*([0-9]+)',
59
+ repeats=False,
60
+ ),
61
+ Quantity(
62
+ 'equilibrium_volume',
63
+ r'\s*Volume of equilibrium unit cell\s*=\s*([0-9.]+)\s*',
64
+ unit='angstrom ** 3',
65
+ ),
66
+ Quantity(
67
+ 'max_strain',
68
+ r'\s*Maximum Lagrangian strain\s*=\s*([0-9.]+)',
69
+ repeats=False,
70
+ ),
71
+ Quantity(
72
+ 'n_strains',
73
+ r'\s*Number of distorted structures\s*=\s*([0-9]+)',
74
+ repeats=False,
75
+ ),
76
+ ]
77
+
78
+
79
+ class StructureParser(TextParser):
80
+ def __init__(self):
81
+ super().__init__(None)
82
+
83
+ def init_quantities(self):
84
+ def get_sym_pos(val):
85
+ val = val.strip().replace('\n', '').split()
86
+ sym = []
87
+ pos = []
88
+ for i in range(0, len(val), 4):
89
+ sym.append(val[i + 3].strip())
90
+ pos.append([float(val[j]) for j in range(i, i + 3)])
91
+ sym_pos = dict(symbols=sym, positions=pos)
92
+ return sym_pos
93
+
94
+ self._quantities = [
95
+ Quantity(
96
+ 'cellpar',
97
+ r'a\s*b\s*c\n([\d\.\s]+)\n\s*alpha\s*beta\s*gamma\n([\d\.\s]+)\n+',
98
+ repeats=False,
99
+ ),
100
+ Quantity(
101
+ 'sym_pos',
102
+ r'Atom positions:\n\n([\s\d\.A-Za-z]+)\n\n',
103
+ str_operation=get_sym_pos,
104
+ repeats=False,
105
+ convert=False,
106
+ ),
107
+ ]
108
+
109
+
110
+ class DistortedParametersParser(TextParser):
111
+ def __init__(self):
112
+ super().__init__(None)
113
+
114
+ def init_quantities(self):
115
+ self._quantities = [
116
+ Quantity(
117
+ 'deformation',
118
+ r'Lagrangian strain\s*=\s*\(([eta\s\d\.,]+)\)',
119
+ str_operation=lambda x: x.replace(',', '').split(),
120
+ repeats=True,
121
+ dtype=str,
122
+ )
123
+ ]
124
+
125
+
126
+ class FitParser(TextParser):
127
+ def __init__(self):
128
+ super().__init__(None)
129
+
130
+ def init_quantities(self):
131
+ def split_eta_val(val):
132
+ order, val = val.strip().split(' order fit.')
133
+ val = [float(v) for v in val.strip().split()]
134
+ return order, val[0::2], val[1::2]
135
+
136
+ self._quantities = [
137
+ Quantity(
138
+ 'fit',
139
+ r'(\w+ order fit\.\n[\d.\s\neE\-\+]+)\n',
140
+ repeats=True,
141
+ convert=False,
142
+ str_operation=split_eta_val,
143
+ )
144
+ ]
145
+
146
+
147
+ class ElasticConstant2Parser(TextParser):
148
+ def __init__(self):
149
+ super().__init__(None)
150
+
151
+ def init_quantities(self):
152
+ self._quantities = [
153
+ Quantity(
154
+ 'voigt',
155
+ r'Symmetry[\s\S]+\n\s*\n([C\d\s\n\(\)\-\+\/\*]+)\n',
156
+ shape=(6, 6),
157
+ dtype=str,
158
+ repeats=False,
159
+ ),
160
+ Quantity(
161
+ 'elastic_constant',
162
+ r'Elastic constant[\s\S]+in GPa\s*:\s*\n\n([\-\d\.\s\n]+)\n',
163
+ shape=(6, 6),
164
+ dtype=float,
165
+ unit='GPa',
166
+ repeats=False,
167
+ ),
168
+ Quantity(
169
+ 'compliance',
170
+ r'Elastic compliance[\s\S]+in 1/GPa\s*:\s*\n\n([\-\d\.\s\n]+)\n',
171
+ shape=(6, 6),
172
+ dtype=float,
173
+ unit='1/GPa',
174
+ repeats=False,
175
+ ),
176
+ ]
177
+
178
+ def str_to_modulus(val_in):
179
+ val_in = val_in.strip().split()
180
+ key = val_in[0]
181
+ unit = val_in[-1] if len(val_in) == 3 else None
182
+ val = float(val_in[1])
183
+ val = val * ureg.GPa if unit is not None else val
184
+ return key, val
185
+
186
+ self._quantities.append(
187
+ Quantity(
188
+ 'modulus',
189
+ r',\s*(\w+)\s*=\s*([\-\+\w\. ]+?)\n',
190
+ str_operation=str_to_modulus,
191
+ repeats=True,
192
+ )
193
+ )
194
+
195
+ self._quantities.append(
196
+ Quantity(
197
+ 'eigenvalues',
198
+ r'Eigenvalues of elastic constant \(stiffness\) matrix:\s*\n+([\-\d\.\n\s]+)\n',
199
+ unit='GPa',
200
+ repeats=False,
201
+ )
202
+ )
203
+
204
+
205
+ class ElasticConstant3Parser(TextParser):
206
+ def __init__(self):
207
+ super().__init__(None)
208
+
209
+ def init_quantities(self):
210
+ def arrange_matrix(val):
211
+ val = val.strip().split('\n')
212
+ matrix = [v.strip().split() for v in val if v.strip()]
213
+ matrix = np.array(matrix).reshape((12, 18))
214
+ arranged = []
215
+ for i in range(2):
216
+ for j in range(3):
217
+ arranged.append(
218
+ matrix[i * 6 : (i + 1) * 6, j * 6 : (j + 1) * 6].tolist()
219
+ )
220
+ return arranged
221
+
222
+ self._quantities = [
223
+ Quantity(
224
+ 'elastic_constant',
225
+ r'\%\s*\n([\s0-6A-L]*)[\n\s\%1-6\-ij]*([\s0-6A-L]*)\n',
226
+ str_operation=arrange_matrix,
227
+ dtype=str,
228
+ repeats=False,
229
+ convert=False,
230
+ ),
231
+ Quantity(
232
+ 'cijk',
233
+ r'(C\d\d\d)\s*=\s*([\-\d\.]+)\s*GPa',
234
+ repeats=True,
235
+ convert=False,
236
+ ),
237
+ ]
238
+
239
+
240
+ class ElasticParser:
241
+ def __init__(self):
242
+ self._mainfile = None
243
+ self.logger = None
244
+ self._deform_dirs = None
245
+ self._deform_dir_prefix = 'Dst'
246
+ self._dirs = []
247
+ self.info = InfoParser()
248
+ self.structure = StructureParser()
249
+ self.distorted_parameters = DistortedParametersParser()
250
+ self.fit = FitParser()
251
+ self.elastic_constant_2 = ElasticConstant2Parser()
252
+ self.elastic_constant_3 = ElasticConstant3Parser()
253
+
254
+ @property
255
+ def deformation_dirs(self):
256
+ if self._deform_dirs is None:
257
+ self._deform_dirs = [
258
+ os.path.join(self.maindir, d)
259
+ for d in self._dirs
260
+ if d.startswith(self._deform_dir_prefix)
261
+ ]
262
+ self._deform_dirs.sort()
263
+
264
+ return self._deform_dirs
265
+
266
+ def get_elastic_files(self, filename, extension, dirname=None):
267
+ dirs = self._dirs if dirname is None else os.listdir(dirname)
268
+ dirname = self.maindir if dirname is None else dirname
269
+ filenames = [d for d in dirs if filename in d and d.endswith(extension)]
270
+ if len(filenames) > 1:
271
+ filenames = [d for d in filenames if d.startswith(filename)]
272
+ if len(filenames) == 0:
273
+ return
274
+ return os.path.join(dirname, filenames[0])
275
+
276
+ def get_references_to_calculations(self):
277
+ def output_file(dirname):
278
+ code = self.info.get('code_name', '').lower()
279
+ if code == 'exciting':
280
+ return os.path.join(dirname, 'INFO.OUT')
281
+ elif code == 'wien':
282
+ return os.path.join(
283
+ dirname, '%s_Converged.scf' % os.path.basename(dirname)
284
+ )
285
+ elif code == 'quantum':
286
+ return os.path.join(dirname, '%s.out' % os.path.basename(dirname))
287
+ else:
288
+ return None
289
+
290
+ references = []
291
+ for deform_dir in self.deformation_dirs:
292
+ sub_dirs = os.listdir(deform_dir)
293
+ for sub_dir in sub_dirs:
294
+ calc_dir = os.path.join(deform_dir, sub_dir)
295
+ out_file = output_file(calc_dir)
296
+ if out_file is not None and os.path.isfile(out_file):
297
+ references.append(out_file)
298
+
299
+ return references
300
+
301
+ def get_structure_info(self):
302
+ path = os.path.join(self.maindir, 'sgroup.out')
303
+ if not os.path.isfile(path):
304
+ return
305
+
306
+ self.structure.mainfile = path
307
+
308
+ cellpar = self.structure.get('cellpar', None)
309
+ sym_pos = self.structure.get('sym_pos', {})
310
+
311
+ sym = sym_pos.get('symbols', None)
312
+ pos = sym_pos.get('positions', None)
313
+
314
+ if cellpar is None or sym is None or pos is None:
315
+ return
316
+
317
+ structure = aseAtoms(cell=cellpar, scaled_positions=pos, symbols=sym, pbc=True)
318
+
319
+ positions = structure.get_positions()
320
+ positions = positions * ureg.angstrom
321
+ cell = structure.get_cell()
322
+ cell = cell * ureg.angstrom
323
+
324
+ return sym, positions, cell
325
+
326
+ def get_strain_energy(self):
327
+ strains, energies = [], []
328
+
329
+ for deform_dir in self.deformation_dirs:
330
+ filenames = [d for d in os.listdir(deform_dir) if d.endswith('Energy.dat')]
331
+ if not filenames:
332
+ continue
333
+
334
+ path = os.path.join(deform_dir, filenames[-1])
335
+ data = np.loadtxt(path).T
336
+ strains.append(list(data[0]))
337
+ # the peculiarity of the x_elastic_strain_diagram_values metainfo that it does
338
+ # not have the energy unit
339
+ energies.append((data[1] * ureg.hartree).to('J').magnitude)
340
+ try:
341
+ energies = np.array(energies)
342
+ if len(np.shape(energies)) != 2:
343
+ strains, energies = [], []
344
+ except Exception:
345
+ strains, energies = [], []
346
+ return strains, energies
347
+
348
+ def get_strain_stress(self):
349
+ strains = {'Lagrangian-stress': [], 'Physical-stress': []}
350
+ stresses = {'Lagrangian-stress': [], 'Physical-stress': []}
351
+
352
+ for deform_dir in self.deformation_dirs:
353
+ filenames = [d for d in os.listdir(deform_dir) if d.endswith('stress.dat')]
354
+
355
+ for filename in filenames:
356
+ path = os.path.join(deform_dir, filename)
357
+ if not os.path.isfile(path):
358
+ continue
359
+
360
+ with open(path) as f:
361
+ lines = f.readlines()
362
+
363
+ strain, stress = [], []
364
+ for line in lines:
365
+ val = line.strip().split()
366
+ if not val[0].strip().replace('.', '').isdecimal():
367
+ continue
368
+
369
+ strain.append(float(val[0]))
370
+ stress.append([float(v) for v in val[1:7]])
371
+
372
+ stype = filename.rstrip('.dat').split('_')[-1]
373
+ strains[stype].append(strain)
374
+ stresses[stype].append(stress)
375
+
376
+ return strains, stresses
377
+
378
+ def get_deformation_types(self):
379
+ path = os.path.join(self.maindir, 'Distorted_Parameters')
380
+ self.distorted_parameters.mainfile = path
381
+ return self.distorted_parameters.get('deformation')
382
+
383
+ def _get_fit(self, path_dir, file_ext):
384
+ path_dir = os.path.join(self.maindir, path_dir)
385
+
386
+ if not os.path.isdir(path_dir):
387
+ return
388
+
389
+ paths = [p for p in os.listdir(path_dir) if p.endswith(file_ext)]
390
+ paths.sort()
391
+
392
+ if not paths:
393
+ return
394
+
395
+ eta, val = {}, {}
396
+ for path in paths:
397
+ self.fit.mainfile = os.path.join(path_dir, path)
398
+ fit_results = self.fit.get('fit', [])
399
+ for result in fit_results:
400
+ eta.setdefault(result[0], [])
401
+ val.setdefault(result[0], [])
402
+ eta[result[0]].append(result[1])
403
+ val[result[0]].append(result[2])
404
+
405
+ return [eta, val]
406
+
407
+ def get_energy_fit(self):
408
+ energy_fit = dict()
409
+
410
+ for file_ext in ['d2E.dat', 'd3E.dat', 'ddE.dat']:
411
+ result = self._get_fit('Energy-vs-Strain', file_ext)
412
+ if result is None:
413
+ continue
414
+
415
+ result = list(result)
416
+ result[1] = {
417
+ key: (val * ureg.GPa).to('Pa').magnitude
418
+ for key, val in result[1].items()
419
+ }
420
+ energy_fit['d2e'] = result
421
+
422
+ result = self._get_fit('Energy-vs-Strain', 'CVe.dat')
423
+ if result is not None:
424
+ result = list(result)
425
+ result[1] = {
426
+ key: (val * ureg.hartree).to('J').magnitude
427
+ for key, val in result[1].items()
428
+ }
429
+ energy_fit['cross-validation'] = result
430
+
431
+ return energy_fit
432
+
433
+ def get_stress_fit(self):
434
+ stress_fit = dict()
435
+ stress_fit['dtn'] = [[]] * 6
436
+ stress_fit['cross-validation'] = [[]] * 6
437
+
438
+ for strain_index in range(1, 7):
439
+ result = self._get_fit('Stress-vs-Strain', '%d_dS.dat' % strain_index)
440
+ if result is not None:
441
+ result[1] = {key: val * ureg.GPa for key, val in result[1].items()}
442
+ stress_fit['dtn'][strain_index - 1] = result
443
+
444
+ result = self._get_fit('Stress-vs-Strain', '%d_CVe.dat' % strain_index)
445
+ if result is not None:
446
+ result[1] = {key: val * ureg.hartree for key, val in result[1].items()}
447
+ stress_fit['cross-validation'][strain_index - 1] = result
448
+
449
+ return stress_fit
450
+
451
+ def get_input(self):
452
+ paths = os.listdir(self.maindir)
453
+ path = None
454
+ order = self.info.get('order', 2)
455
+ for p in paths:
456
+ if 'ElaStic_' in p and p.endswith('.in') and str(order) in p:
457
+ path = p
458
+ break
459
+
460
+ if path is None:
461
+ return
462
+
463
+ calc_method = self.info.get('calculation_method')
464
+
465
+ eta_ec = []
466
+ fit_ec = []
467
+
468
+ def _is_number(var):
469
+ try:
470
+ float(var)
471
+ return True
472
+ except Exception:
473
+ return False
474
+
475
+ path = os.path.join(self.maindir, path)
476
+
477
+ with open(path) as f:
478
+ while True:
479
+ line = f.readline()
480
+ if not line:
481
+ break
482
+
483
+ if calc_method.lower() == 'energy':
484
+ _, eta, fit = line.strip().split()
485
+ eta_ec.append(float(eta))
486
+ fit_ec.append(int(fit))
487
+
488
+ elif calc_method.lower() == 'stress':
489
+ val = line.strip().split()
490
+ if not _is_number(val[0]):
491
+ eta_ec.append([float(val[i + 1]) for i in range(6)])
492
+ else:
493
+ fit_ec.append([int(val[i]) for i in range(6)])
494
+
495
+ else:
496
+ pass
497
+
498
+ return eta_ec, fit_ec
499
+
500
+ def get_elastic_constants_order2(self):
501
+ path = self.get_elastic_files('ElaStic_2nd', 'out')
502
+ self.elastic_constant_2.mainfile = path
503
+
504
+ matrices = dict()
505
+ for key in ['voigt', 'elastic_constant', 'compliance']:
506
+ val = self.elastic_constant_2.get(key, None)
507
+ if val is not None:
508
+ matrices[key] = val
509
+
510
+ moduli = dict()
511
+ for modulus in self.elastic_constant_2.get('modulus', []):
512
+ moduli[modulus[0]] = modulus[1]
513
+
514
+ eigenvalues = self.elastic_constant_2.get('eigenvalues')
515
+
516
+ return matrices, moduli, eigenvalues
517
+
518
+ def get_elastic_constants_order3(self):
519
+ path = self.get_elastic_files('ElaStic_3rd.out', 'out')
520
+ self.elastic_constant_3.mainfile = path
521
+
522
+ elastic_constant_str = self.elastic_constant_3.get('elastic_constant')
523
+ if elastic_constant_str is None:
524
+ return
525
+
526
+ cijk = dict()
527
+ for element in self.elastic_constant_3.get('cijk', []):
528
+ cijk[element[0]] = float(element[1])
529
+
530
+ # formulas for the coefficients
531
+ coeff_A = cijk.get('C111', 0) + cijk.get('C112', 0) - cijk.get('C222', 0)
532
+ coeff_B = -(cijk.get('C115', 0) + 3 * cijk.get('C125', 0)) / 2
533
+ coeff_C = (cijk.get('C114', 0) + 3 * cijk.get('C124', 0)) / 2
534
+ coeff_D = (
535
+ -(2 * cijk.get('C111', 0) + cijk.get('C112', 0) - 3 * cijk.get('C222', 0))
536
+ / 4
537
+ )
538
+ coeff_E = -cijk.get('C114', 0) - 2 * cijk.get('C124', 0)
539
+ coeff_F = -cijk.get('C115', 0) - 2 * cijk.get('C125', 0)
540
+ coeff_G = -(cijk.get('C115', 0) - cijk.get('C125', 0)) / 2
541
+ coeff_H = (cijk.get('C114', 0) - cijk.get('C124', 0)) / 2
542
+ coeff_I = (
543
+ 2 * cijk.get('C111', 0) - cijk.get('C112', 0) - cijk.get('C222', 0)
544
+ ) / 4
545
+ coeff_J = (cijk.get('C113', 0) - cijk.get('C123', 0)) / 2
546
+ coeff_K = -(cijk.get('C144', 0) - cijk.get('C155', 0)) / 2
547
+
548
+ space_group_number = self.info.get('space_group_number')
549
+
550
+ if space_group_number <= 148: # rhombohedral II
551
+ coefficients = dict(
552
+ A=coeff_A,
553
+ B=coeff_B,
554
+ C=coeff_C,
555
+ D=coeff_D,
556
+ E=coeff_E,
557
+ F=coeff_F,
558
+ G=coeff_G,
559
+ H=coeff_H,
560
+ I=coeff_I,
561
+ J=coeff_J,
562
+ K=coeff_K,
563
+ )
564
+ elif space_group_number <= 167: # rhombohedral I
565
+ coefficients = dict(
566
+ A=coeff_A,
567
+ B=coeff_C,
568
+ C=coeff_D,
569
+ D=coeff_E,
570
+ E=coeff_H,
571
+ F=coeff_I,
572
+ G=coeff_J,
573
+ H=coeff_K,
574
+ )
575
+ elif space_group_number <= 176: # hexagonal II
576
+ coefficients = dict(A=coeff_A, B=coeff_D, C=coeff_I, D=coeff_J, E=coeff_K)
577
+ elif space_group_number <= 194: # hexagonal I
578
+ coefficients = dict(A=coeff_A, B=coeff_D, C=coeff_I, D=coeff_J, E=coeff_K)
579
+ else:
580
+ coefficients = dict()
581
+
582
+ # assign values to the elastic constant matrix from the independent ec and coefficients
583
+ elastic_constant = np.zeros((6, 6, 6))
584
+ for i in range(6):
585
+ for j in range(6):
586
+ for k in range(6):
587
+ val = elastic_constant_str[i][j][k]
588
+ if val == '0':
589
+ elastic_constant[i][j][k] = 0
590
+ elif val.isdigit():
591
+ elastic_constant[i][j][k] = cijk['C%s' % val]
592
+ else:
593
+ elastic_constant[i][j][k] = coefficients.get(val, 0)
594
+
595
+ return elastic_constant
596
+
597
+ def parse_strain(self):
598
+ method = self.info['calculation_method'].lower()
599
+
600
+ n_strains = self.info['n_strains']
601
+ poly_fit_2 = int((n_strains - 1) / 2)
602
+ poly_fit = {
603
+ '2nd': poly_fit_2,
604
+ '3rd': poly_fit_2 - 1,
605
+ '4th': poly_fit_2 - 1,
606
+ '5th': poly_fit_2 - 2,
607
+ '6th': poly_fit_2 - 2,
608
+ '7th': poly_fit_2 - 3,
609
+ }
610
+
611
+ if method == 'energy':
612
+ strain, energy = self.get_strain_energy()
613
+ if not strain:
614
+ self.logger.warn('Error getting strain and energy data')
615
+ return
616
+
617
+ sec_strain_diagram2 = StrainDiagrams()
618
+ self.archive.workflow2.results.strain_diagrams.append(sec_strain_diagram2)
619
+ sec_strain_diagram2.type = 'energy'
620
+ sec_strain_diagram2.n_eta = len(strain[0])
621
+ sec_strain_diagram2.eta = strain
622
+ sec_strain_diagram2.value = energy
623
+
624
+ energy_fit = self.get_energy_fit()
625
+ if not energy_fit:
626
+ self.logger.warn('Error getting energy fit data')
627
+ return
628
+
629
+ for diagram_type in ['cross-validation', 'd2e']:
630
+ for fit_order in energy_fit[diagram_type][0].keys():
631
+ sec_strain_diagram = StrainDiagrams()
632
+ self.archive.workflow2.results.strain_diagrams.append(
633
+ sec_strain_diagram
634
+ )
635
+ sec_strain_diagram.type = diagram_type
636
+ sec_strain_diagram.polynomial_fit_order = int(fit_order[:-2])
637
+ sec_strain_diagram.n_eta = poly_fit.get(fit_order, None)
638
+ sec_strain_diagram.eta = energy_fit[diagram_type][0][fit_order]
639
+ sec_strain_diagram.value = energy_fit[diagram_type][1][fit_order]
640
+
641
+ elif method == 'stress':
642
+ strain, stress = self.get_strain_stress()
643
+ for diagram_type in ['Lagrangian-stress', 'Physical-stress']:
644
+ strain_i = strain[diagram_type]
645
+ if not strain_i:
646
+ continue
647
+ stress_i = np.transpose(np.array(stress[diagram_type]), axes=(2, 0, 1))
648
+
649
+ for si in range(6):
650
+ sec_strain_diagram = StrainDiagrams()
651
+ self.archive.workflow2.results.strain_diagrams.append(
652
+ sec_strain_diagram
653
+ )
654
+ sec_strain_diagram.type = diagram_type
655
+ sec_strain_diagram.stress_voigt_component = si + 1
656
+ sec_strain_diagram.n_eta = len(strain_i[0])
657
+ sec_strain_diagram.eta = strain_i
658
+ sec_strain_diagram.value = stress_i[si]
659
+
660
+ stress_fit = self.get_stress_fit()
661
+ for diagram_type in ['cross-validation', 'dtn']:
662
+ for si in range(6):
663
+ if len(stress_fit[diagram_type][si]) == 0:
664
+ continue
665
+ for fit_order in stress_fit[diagram_type][si][0].keys():
666
+ sec_strain_diagram = StrainDiagrams()
667
+ self.archive.workflow2.results.strain_diagrams.append(
668
+ sec_strain_diagram
669
+ )
670
+ sec_strain_diagram.type = diagram_type
671
+ sec_strain_diagram.stress_voigt_component = si + 1
672
+ sec_strain_diagram.polynomial_fit_order = int(fit_order[:-2])
673
+ sec_strain_diagram.n_eta = poly_fit.get(fit_order, None)
674
+ sec_strain_diagram.eta = stress_fit[diagram_type][si][0][
675
+ fit_order
676
+ ]
677
+ sec_strain_diagram.value = np.array(
678
+ stress_fit[diagram_type][si][1][fit_order]
679
+ )
680
+
681
+ def parse_elastic_constant(self):
682
+ sec_results = self.archive.workflow2.results
683
+
684
+ order = self.info['order']
685
+
686
+ if order == 2:
687
+ matrices, moduli, eigenvalues = self.get_elastic_constants_order2()
688
+ sec_results.elastic_constants_notation_matrix_second_order = matrices.get(
689
+ 'voigt'
690
+ )
691
+ sec_results.elastic_constants_matrix_second_order = matrices.get(
692
+ 'elastic_constant'
693
+ )
694
+ sec_results.compliance_matrix_second_order = matrices.get('compliance')
695
+
696
+ sec_results.bulk_modulus_voigt = moduli.get('B_V', moduli.get('K_V'))
697
+ sec_results.shear_modulus_voigt = moduli.get('G_V')
698
+
699
+ sec_results.bulk_modulus_reuss = moduli.get('B_R', moduli.get('K_R'))
700
+ sec_results.shear_modulus_reuss = moduli.get('G_R')
701
+
702
+ sec_results.bulk_modulus_hill = moduli.get('B_H', moduli.get('K_H'))
703
+ sec_results.shear_modulus_hill = moduli.get('G_H')
704
+
705
+ sec_results.young_modulus_voigt = moduli.get('E_V')
706
+ sec_results.poisson_ratio_voigt = moduli.get('nu_V')
707
+ sec_results.young_modulus_reuss = moduli.get('E_R')
708
+ sec_results.poisson_ratio_reuss = moduli.get('nu_R')
709
+ sec_results.young_modulus_hill = moduli.get('E_H')
710
+ sec_results.poisson_ratio_hill = moduli.get('nu_H')
711
+
712
+ sec_results.eigenvalues_elastic = eigenvalues
713
+
714
+ elif order == 3:
715
+ elastic_constant = self.get_elastic_constants_order3()
716
+ if elastic_constant is not None:
717
+ sec_results.elastic_constants_matrix_third_order = (
718
+ elastic_constant * ureg.GPa
719
+ )
720
+
721
+ def init_parser(self):
722
+ self._deform_dirs = None
723
+ self.maindir = os.path.dirname(self.filepath)
724
+ self.info.mainfile = self.filepath
725
+ self.info.logger = self.logger
726
+ self.structure.logger = self.logger
727
+ self.distorted_parameters.logger = self.logger
728
+ self.fit.logger = self.logger
729
+ self.elastic_constant_2.logger = self.logger
730
+ self.elastic_constant_3.logger = self.logger
731
+ self._dirs = os.listdir(self.maindir)
732
+
733
+ def reuse_parser(self, parser):
734
+ self.info.quantities = parser.info.quantities
735
+ self.structure.quantities = parser.structure.quantities
736
+ self.distorted_parameters.quantities = parser.distorted_parameters.quantities
737
+ self.fit.quantities = parser.fit.quantities
738
+ self.elastic_constant_2.quantities = parser.elastic_constant_2.quantities
739
+ self.elastic_constant_3.quantities = parser.elastic_constant_3.quantities
740
+
741
+ def parse(self, filepath, archive, logger):
742
+ self.filepath = os.path.abspath(filepath)
743
+ self.archive = archive
744
+ self.logger = logger if logger is not None else logging
745
+
746
+ self.init_parser()
747
+
748
+ sec_run = Run()
749
+ self.archive.run.append(sec_run)
750
+
751
+ sec_run.program = Program(name='elastic', version='1.0')
752
+
753
+ sec_system = System()
754
+ sec_run.system.append(sec_system)
755
+
756
+ symbols_positions_cell = self.get_structure_info()
757
+ volume = self.info['equilibrium_volume']
758
+
759
+ sec_atoms = Atoms()
760
+ sec_system.atoms = sec_atoms
761
+ if symbols_positions_cell is not None:
762
+ sec_atoms.labels = symbols_positions_cell[0]
763
+ sec_atoms.positions = symbols_positions_cell[1]
764
+ sec_atoms.lattice_vectors = symbols_positions_cell[2]
765
+ sec_atoms.periodic = [True, True, True]
766
+ sec_system.x_elastic_space_group_number = self.info['space_group_number']
767
+ sec_system.x_elastic_unit_cell_volume = volume
768
+
769
+ sec_method = Method()
770
+ sec_run.method.append(sec_method)
771
+
772
+ sec_scc = Calculation()
773
+ sec_run.calculation.append(sec_scc)
774
+ sec_scc.calculations_path = self.get_references_to_calculations()
775
+
776
+ fit_input = self.get_input()
777
+ if fit_input is not None:
778
+ sec_fit_par = x_elastic_section_fitting_parameters()
779
+ sec_method.x_elastic_section_fitting_parameters.append(sec_fit_par)
780
+ sec_fit_par.x_elastic_fitting_parameters_eta = fit_input[0]
781
+ sec_fit_par.x_elastic_fitting_parameters_polynomial_order = fit_input[1]
782
+
783
+ sec_scc.method_ref = sec_method
784
+ sec_scc.system_ref = sec_system
785
+
786
+ deformation_types = self.get_deformation_types()
787
+ workflow = Elastic(method=ElasticMethod(), results=ElasticResults())
788
+ workflow.method.energy_stress_calculator = self.info['code_name']
789
+ workflow.method.calculation_method = self.info['calculation_method'].lower()
790
+ workflow.method.elastic_constants_order = self.info['order']
791
+ workflow.method.strain_maximum = self.info['max_strain']
792
+ workflow.results.n_strains = self.info['n_strains']
793
+ workflow.results.n_deformations = len(self.deformation_dirs)
794
+ workflow.results.deformation_types = deformation_types
795
+ self.archive.workflow2 = workflow
796
+
797
+ self.parse_strain()
798
+ self.parse_elastic_constant()