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.
- nomad_parser_plugins_workflow-1.0.dist-info/LICENSE +202 -0
- nomad_parser_plugins_workflow-1.0.dist-info/METADATA +319 -0
- nomad_parser_plugins_workflow-1.0.dist-info/RECORD +58 -0
- nomad_parser_plugins_workflow-1.0.dist-info/WHEEL +5 -0
- nomad_parser_plugins_workflow-1.0.dist-info/entry_points.txt +11 -0
- nomad_parser_plugins_workflow-1.0.dist-info/top_level.txt +1 -0
- workflowparsers/__init__.py +314 -0
- workflowparsers/aflow/__init__.py +19 -0
- workflowparsers/aflow/__main__.py +31 -0
- workflowparsers/aflow/metainfo/__init__.py +19 -0
- workflowparsers/aflow/metainfo/aflow.py +1240 -0
- workflowparsers/aflow/parser.py +741 -0
- workflowparsers/asr/__init__.py +19 -0
- workflowparsers/asr/__main__.py +31 -0
- workflowparsers/asr/metainfo/__init__.py +19 -0
- workflowparsers/asr/metainfo/asr.py +306 -0
- workflowparsers/asr/parser.py +266 -0
- workflowparsers/atomate/__init__.py +19 -0
- workflowparsers/atomate/__main__.py +31 -0
- workflowparsers/atomate/metainfo/__init__.py +19 -0
- workflowparsers/atomate/metainfo/atomate.py +395 -0
- workflowparsers/atomate/parser.py +357 -0
- workflowparsers/elastic/__init__.py +19 -0
- workflowparsers/elastic/__main__.py +31 -0
- workflowparsers/elastic/metainfo/__init__.py +19 -0
- workflowparsers/elastic/metainfo/elastic.py +364 -0
- workflowparsers/elastic/parser.py +798 -0
- workflowparsers/fhivibes/__init__.py +19 -0
- workflowparsers/fhivibes/__main__.py +31 -0
- workflowparsers/fhivibes/metainfo/__init__.py +19 -0
- workflowparsers/fhivibes/metainfo/fhi_vibes.py +898 -0
- workflowparsers/fhivibes/parser.py +566 -0
- workflowparsers/lobster/__init__.py +19 -0
- workflowparsers/lobster/__main__.py +31 -0
- workflowparsers/lobster/metainfo/__init__.py +19 -0
- workflowparsers/lobster/metainfo/lobster.py +446 -0
- workflowparsers/lobster/parser.py +618 -0
- workflowparsers/phonopy/__init__.py +19 -0
- workflowparsers/phonopy/__main__.py +31 -0
- workflowparsers/phonopy/calculator.py +260 -0
- workflowparsers/phonopy/metainfo/__init__.py +19 -0
- workflowparsers/phonopy/metainfo/phonopy.py +83 -0
- workflowparsers/phonopy/parser.py +583 -0
- workflowparsers/quantum_espresso_epw/__init__.py +19 -0
- workflowparsers/quantum_espresso_epw/__main__.py +31 -0
- workflowparsers/quantum_espresso_epw/metainfo/__init__.py +19 -0
- workflowparsers/quantum_espresso_epw/metainfo/quantum_espresso_epw.py +579 -0
- workflowparsers/quantum_espresso_epw/parser.py +583 -0
- workflowparsers/quantum_espresso_phonon/__init__.py +19 -0
- workflowparsers/quantum_espresso_phonon/__main__.py +31 -0
- workflowparsers/quantum_espresso_phonon/metainfo/__init__.py +19 -0
- workflowparsers/quantum_espresso_phonon/metainfo/quantum_espresso_phonon.py +389 -0
- workflowparsers/quantum_espresso_phonon/parser.py +483 -0
- workflowparsers/quantum_espresso_xspectra/__init__.py +19 -0
- workflowparsers/quantum_espresso_xspectra/__main__.py +31 -0
- workflowparsers/quantum_espresso_xspectra/metainfo/__init__.py +19 -0
- workflowparsers/quantum_espresso_xspectra/metainfo/quantum_espresso_xspectra.py +290 -0
- 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()
|