Skip to content

Commit cebad11

Browse files
cecizagladinesaJFRudzinskiMicheleMatteucci
authored
Populate Outputs section for nmr (#116)
* Add vscode settings * Move remove_annotations utils * Add qe gipaw parser * Use add_mapping_annotations util * Implement support for xml mainfile * Update qe parser spec * Fix generic workflow * Fix workflow for xml * Remove exec time constraint * Parse gipaw xml for v7.4.1 * populate Outputs section for nmr * mapped Section with add_mapping_annotations * removed simulation.outputs with remove_mapping_annotations * replaced .@ with .magnetic_shieldings * fixed get_magnetic_shieldings * added annotation_keys for gipaw * added magnetic susceptibilities * fixed names * updated tests and test data * parse mag sus in xml 1st attempt * moved custom workflow props to h5md * removed old schema dependencies, working h5md tests * generalize get_output() step 1 for rdfs * generalized to msd outputs * reduced test data file * corrected small test file and updated tests * removed unused output list function * updated get_ouput_data function, removed observable_type labeling * revert nomad-simulations changes to pyproject * ruff * update CustomOutputs * working up to custom ensemble outputs * fully working custom MD results * code clean * ruff * clean prints * updated test data * h5md attribute annotation broken * ruff format * save before comment test * non-commented annot working except rdf.value * working tests again * code clean and formatting * new cond dep approach * Add vscode settings * Move remove_annotations utils * Add qe gipaw parser * Use add_mapping_annotations util * Implement support for xml mainfile * Update qe parser spec * Fix generic workflow * Fix workflow for xml * Remove exec time constraint * Parse gipaw xml for v7.4.1 * Fix rename * Fix rebase * Fix rebase * moved up anotation key assegnation * refactored annotation_key assegnation * added magnetic_shieldings for xml * Include EFG in new version * Trivia * removed useless test * Add test for efg output files * added xml magnetic_susceptibilities * Fix last * adapted xml test * handled key not found in get_nmr_text * Fix attribute debug in efg test * Include EPR in new version * Add non-dummy attribute debugging in EPR tests * nmr cleanup * fixed delta_g * gipaw cleanup * added test files * moved up `parser.filepath = self.mainfile` * cleanup nmr xml * Cache mainfile parser * test changes to mainfile_parser * fixed delta_g parsing * get job for xml gipaw files * added efg for xml * added hyperfine xml * moved custom workflow props to h5md * removed old schema dependencies, working h5md tests * generalize get_output() step 1 for rdfs * generalized to msd outputs * reduced test data file * corrected small test file and updated tests * removed unused output list function * updated get_ouput_data function, removed observable_type labeling * revert nomad-simulations changes to pyproject * ruff * update CustomOutputs * working up to custom ensemble outputs * fully working custom MD results * code clean * ruff * clean prints * h5md attribute annotation broken * ruff format * save before comment test * non-commented annot working except rdf.value * working tests again * code clean and formatting * new cond dep approach * Add vscode settings * Move remove_annotations utils * Add qe gipaw parser * added xml g-tensor * Use add_mapping_annotations util * Implement support for xml mainfile * Update qe parser spec * Fix generic workflow * Fix workflow for xml * Remove exec time constraint * Parse gipaw xml for v7.4.1 * Fix rename * Fix rebase * Fix rebase * Cache mainfile parser * added debugging for test * moved custom workflow props to h5md * removed old schema dependencies, working h5md tests * generalize get_output() step 1 for rdfs * generalized to msd outputs * reduced test data file * corrected small test file and updated tests * removed unused output list function * updated get_ouput_data function, removed observable_type labeling * revert nomad-simulations changes to pyproject * ruff * update CustomOutputs * working up to custom ensemble outputs * fully working custom MD results * code clean * ruff * clean prints * h5md attribute annotation broken * ruff format * save before comment test * non-commented annot working except rdf.value * working tests again * code clean and formatting * new cond dep approach * Add vscode settings * Move remove_annotations utils * Add qe gipaw parser * Use add_mapping_annotations util * Implement support for xml mainfile * Update qe parser spec * Fix generic workflow * Fix workflow for xml * Remove exec time constraint * Parse gipaw xml for v7.4.1 * Fix rename * Fix rebase * Fix rebase * Cache mainfile parser * removed suggested line --------- Co-authored-by: Alvin Noe Ladines <ladinesalvinnoe@gmail.com> Co-authored-by: jrudz <rudzinski@mpip-mainz.mpg.de> Co-authored-by: Michele Matteucci <michelematteucci98@gmail.com>
1 parent 1342ecf commit cebad11

File tree

29 files changed

+20344
-14
lines changed

29 files changed

+20344
-14
lines changed

src/nomad_simulation_parsers/parsers/quantumespresso/gipaw/file_parser.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,57 @@ def str_to_chi_tensor(val_in):
4444

4545
return res
4646

47+
def parse_tensor_block(val_in: str):
48+
lines = [
49+
line.strip()
50+
for line
51+
in val_in.strip().splitlines()
52+
if line.strip()
53+
]
54+
result = []
55+
for i in range(0, len(lines), 3):
56+
block = lines[i:i+3]
57+
if len(block) < 3:
58+
continue
59+
60+
values = []
61+
atom_type = None
62+
atom_index = None
63+
64+
for row in block:
65+
parts = row.split()
66+
if atom_type is None:
67+
atom_type = parts[0]
68+
atom_index = int(parts[1])
69+
values.extend([float(p) for p in parts[2:]])
70+
71+
result.append([atom_type, atom_index] + values)
72+
return result
73+
74+
def parse_scalar_block(val):
75+
lines = [
76+
line.strip()
77+
for line
78+
in val.strip().splitlines()
79+
if line.strip()
80+
]
81+
results = []
82+
for line in lines:
83+
parts = line.split()
84+
if len(parts) != 6:
85+
continue
86+
results.append([parts[0], int(parts[1]), float(parts[-1])])
87+
return results
88+
89+
def str_to_gtensor(val_in):
90+
lines = val_in.strip().splitlines()
91+
tensor = []
92+
for line in lines:
93+
if line.strip():
94+
row = [float(x) for x in line.strip().split()]
95+
tensor.append(row)
96+
return tensor
97+
4798
self._quantities = [
4899
Quantity(
49100
'header',
@@ -75,4 +126,38 @@ def str_to_chi_tensor(val_in):
75126
str_operation=str_to_chi_tensor,
76127
convert=False,
77128
),
129+
Quantity(
130+
'efg',
131+
r'----- total EFG \(symmetrized\) -----\n((?:.*?\n)*?)\s+NQR/NMR SPECTROSCOPIC PARAMETERS:',
132+
str_operation=parse_tensor_block,
133+
convert=False,
134+
),
135+
Quantity(
136+
'hyperfine_dipolar',
137+
r'----- total dipolar -----\n((?:.*?\n)*?)\s+----- total dipolar \(symmetrized\) -----',
138+
str_operation=parse_tensor_block,
139+
convert=False,
140+
),
141+
Quantity(
142+
'hyperfine_fermi_contact',
143+
r'----- Fermi contact in G -----\n((?:.*?\n)*?)\s+Initialization:',
144+
str_operation=parse_scalar_block,
145+
convert=False,
146+
),
147+
Quantity(
148+
"delta_g_total_paratec",
149+
rf"Delta_g total \(SOO a la Paratec\):\s*-+\s*\n"
150+
rf"((?:\s*{re_float}\s+{re_float}\s+{re_float}\s*\n){{3}})",
151+
repeats=False,
152+
str_operation=str_to_gtensor,
153+
convert=False,
154+
),
155+
Quantity(
156+
"delta_g_total",
157+
rf"Delta_g total \(SOO as in Eq\.\(7\)\):\s*-+\s*\n"
158+
rf"((?:\s*{re_float}\s+{re_float}\s+{re_float}\s*\n){{3}})",
159+
repeats=False,
160+
str_operation=str_to_gtensor,
161+
convert=False,
162+
),
78163
]

src/nomad_simulation_parsers/parsers/quantumespresso/gipaw/parser.py

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1+
from typing import Any
2+
3+
import numpy as np
14
from nomad.datamodel import EntryArchive
25
from nomad.utils import get_logger
6+
from nomad.units import ureg
37

48
from nomad_simulation_parsers.schema_packages.quantumespresso import gipaw
59

@@ -15,13 +19,133 @@ class GIPAWMainfileTextParser(MainfileTextParser):
1519
def logger(self):
1620
return LOGGER
1721

22+
def get_gipaw_text(self, source: dict[str, Any]) -> list[dict[str, Any]]:
23+
out = {}
24+
# magnetic shieldings
25+
ms_list = source.get('ms_list', None)
26+
if ms_list is not None:
27+
magnetic_shieldings = []
28+
for atom_data in ms_list:
29+
values = np.reshape(atom_data[2:], (3, 3))
30+
FACTOR = 1e-6
31+
magnetic_shieldings.append(values * FACTOR * ureg("dimensionless"))
32+
out["magnetic_shieldings"] = [dict(value=m) for m in magnetic_shieldings ]
33+
34+
# magnetic_susceptibilities
35+
chi_bare_pGv = source.get("chi_bare_pGv", None)
36+
chi_bare_vGv = source.get("chi_bare_vGv", None)
37+
38+
if chi_bare_pGv is not None and chi_bare_pGv is not None:
39+
sus = (chi_bare_pGv + chi_bare_vGv) / 2
40+
out["magnetic_susceptibilities"] = dict(
41+
value=sus,
42+
value_vgv_approx = chi_bare_vGv,
43+
value_pgv_approx = chi_bare_pGv
44+
)
45+
46+
# electric_field_gradient
47+
efg = source.get('efg', None)
48+
if efg is not None:
49+
electric_field_gradients = []
50+
for atom_data in efg:
51+
values = np.reshape(atom_data[2:], (3, 3))
52+
electric_field_gradients.append(values)
53+
out['electric_field_gradients'] = [dict(value=item) for item in electric_field_gradients]
54+
55+
# hyperfine_dipolar
56+
hd = source.get('hyperfine_dipolar', None)
57+
if hd is not None:
58+
hyperfine_dipolar = []
59+
for atom_data in hd:
60+
values = np.reshape(atom_data[2:], (3, 3))
61+
hyperfine_dipolar.append(values)
62+
out['hyperfine_dipolar'] = [dict(value=item) for item in hyperfine_dipolar]
63+
64+
# hyperfine_fermi_contact
65+
hfc = source.get('hyperfine_fermi_contact', None)
66+
if hfc is not None:
67+
hyperfine_fermi_contact = []
68+
for atom_data in hfc:
69+
values = atom_data[-1]
70+
hyperfine_fermi_contact.append(values)
71+
out['hyperfine_fermi_contact'] = [dict(value=item) for item in hyperfine_fermi_contact]
72+
73+
# delta_g_paratec
74+
delta_g_paratec = source.get('delta_g_total_paratec', None)
75+
if delta_g_paratec is not None:
76+
out['delta_g_paratec'] = dict(value=delta_g_paratec)
77+
78+
# delta_g
79+
delta_g = source.get('delta_g_total', None)
80+
if delta_g is not None:
81+
out['delta_g'] = dict(value=delta_g)
82+
83+
return [out]
84+
85+
1886

1987
class GIPAWMainfileXMLParser(MainfileXMLParser):
88+
_job: str | None = None
89+
2090
# TODO temporary fix for structlog unable to propagate logger
2191
@property
2292
def logger(self):
2393
return LOGGER
2494

95+
@property
96+
def job(self) -> str | None:
97+
if self._job is None:
98+
try:
99+
self._job = self.data["input"]["job"]
100+
except Exception as exc:
101+
self.logger.warning("Unable to get job from data: %s", exc)
102+
return self._job
103+
104+
105+
def get_magnetic_shieldings(self, atom: dict[str, Any]) -> Any:
106+
if self.job != "nmr":
107+
return
108+
value = np.reshape(atom.get("__value"), (3, 3))
109+
FACTOR = 1e-6
110+
return value * FACTOR * ureg("dimensionless")
111+
112+
def get_magnetic_susceptibilities(self, source: dict[str, Any], **kwargs) -> Any:
113+
if self.job != "nmr":
114+
return
115+
name = kwargs.get("name")
116+
if name != "value":
117+
value = source.get(name, None)
118+
return np.reshape(value.get("__value", None), (3, 3))
119+
120+
value_vgv = source.get("susceptibility_low", None)
121+
vgv = np.reshape(value_vgv.get("__value", None), (3, 3))
122+
value_pgv = source.get("susceptibility_high", None)
123+
pgv = np.reshape(value_pgv.get("__value", None), (3, 3))
124+
sus = (vgv + pgv) / 2
125+
return sus
126+
127+
def get_efg(self, atom: dict[str, Any]) -> Any:
128+
if self.job != "efg":
129+
return
130+
return np.reshape(atom.get("__value"), (3, 3))
131+
132+
def get_hyperfine_dipolar(self, atom: dict[str, Any]) -> Any:
133+
if self.job != "hyperfine":
134+
return
135+
return np.reshape(atom.get("__value"), (3, 3))
136+
137+
def get_hyperfine_fermi_contact(self, atom: dict[str, Any]) -> Any:
138+
if self.job != "hyperfine":
139+
return
140+
return atom.get("__value")
141+
142+
def get_delta_g(self, source: dict[str, Any]) -> Any:
143+
if self.job != "g-tensor":
144+
return
145+
return np.reshape(source.get("__value"), (3, 3))
146+
147+
148+
25149

26150
class GIPAWArchiveWriter(QuantumEspressoArchiveWriter):
27151
schema = gipaw

src/nomad_simulation_parsers/parsers/quantumespresso/parser.py

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,8 @@ def parse_program(self, archive: EntryArchive, index: int) -> None:
329329
program=Program(name='Quantum Espresso')
330330
)
331331
# convert
332+
from devtools import debug
333+
debug(self.simulation_parser.annotation_key)
332334
self.mainfile_parser.convert(self.simulation_parser)
333335
# set the parsed data to archive
334336
archive.data = self.simulation_parser.data_object
@@ -414,21 +416,26 @@ def parse_workflow(self) -> None:
414416
def mainfile_parser(self) -> MainfileTextParser | MainfileXMLParser:
415417
if self._mainfile_parser is None:
416418
basename, ext = self.mainfile.rsplit('.', 1)
417-
self.simulation_parser.annotation_key = ext
418-
self._mainfile_parser = dict(
419-
out=self._text_parser, xml=self._xml_parser
420-
).get(ext)
421-
if ext == 'xml':
422-
return self._mainfile_parser
423-
424-
# special handling for GIPAW to parse xml if available for version >= 7.4.1
425-
# check version
419+
self._mainfile_parser = dict(out=self._text_parser, xml=self._xml_parser).get(ext)
426420
self._mainfile_parser.filepath = self.mainfile
427-
program = self._mainfile_parser.data.get('program')
428-
if not program:
421+
keys = list(self._mainfile_parser.data.keys())
422+
423+
if "gipaw" not in self.simulation_parser.annotation_key:
424+
self.simulation_parser.annotation_key = ext
425+
426+
if "program" not in keys:
427+
if len(keys) == 1 and "gipaw" in keys[0]:
428+
self.simulation_parser.annotation_key = 'gipaw_xml'
429429
return self._mainfile_parser
430430

431+
program = self._mainfile_parser.data.get('program')
431432
name_version = get_program_name_version(program[0][:30])
433+
if name_version[0] == 'gipaw':
434+
if ext == 'out':
435+
self.simulation_parser.annotation_key = 'gipaw_out'
436+
437+
# special handling for GIPAW to parse xml if available for version >= 7.4.1
438+
# check version
432439
if name_version[0] != 'gipaw':
433440
return self._mainfile_parser
434441

@@ -437,7 +444,7 @@ def mainfile_parser(self) -> MainfileTextParser | MainfileXMLParser:
437444
# check if xml file exists
438445
xml_file = f'{basename}.xml'
439446
if os.path.isfile(xml_file):
440-
self.simulation_parser.annotation_key = 'xml'
447+
self.simulation_parser.annotation_key = 'gipaw_xml'
441448
self._mainfile_parser = self._xml_parser
442449
self._mainfile_parser.filepath = xml_file
443450
return self._mainfile_parser

src/nomad_simulation_parsers/schema_packages/quantumespresso/common.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212

1313
OUT_KEY = 'out'
1414
XML_KEY = 'xml'
15+
GIPAW_OUT_KEY = 'gipaw_out'
16+
GIPAW_XML_KEY = 'gipaw_xml'
1517

1618

1719
class Program(general.Program):

0 commit comments

Comments
 (0)