Skip to content

Commit 95f7088

Browse files
committed
Merge remote-tracking branch 'origin/develop' into gaussian-parser
2 parents 76e6fcc + 3193ea3 commit 95f7088

File tree

243 files changed

+157397
-158
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

243 files changed

+157397
-158
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,3 +128,7 @@ dmypy.json
128128

129129
# Pyre type checker
130130
.pyre/
131+
132+
# MDAnalysis temporary/cache artifacts under tests data
133+
tests/data/**/*.lock
134+
tests/data/**/*.npz

pyproject.toml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,14 @@ license = { file = "LICENSE" }
3434
dependencies = [
3535
"nomad-lab>=1.3.16.dev100",
3636
"nomad-simulations@git+https://github.com/nomad-coe/nomad-simulations.git@develop",
37+
"nomad-schema-plugin-simulation-workflow>=1.0.10",
3738
"python-magic-bin; sys_platform == 'win32'",
3839
"phonopy>=2.35",
40+
"mdanalysis>=2.8.0,<3.0.0 ; python_full_version >= '3.10'",
41+
"mdanalysis<2.8 ; python_full_version < '3.10'",
42+
"panedr>=0.2",
43+
# issue with reading nc file in 1.7.3: https://github.com/Unidata/netcdf4-python/issues/1438
44+
"netcdf4<=1.7.2",
3945
]
4046

4147
[project.urls]
@@ -145,6 +151,8 @@ gaussian_parser = "nomad_simulation_parsers.parsers:gaussian_parser"
145151
gaussian_schema_package = "nomad_simulation_parsers.schema_packages:gaussian_schema_package"
146152
gpaw_parser = "nomad_simulation_parsers.parsers:gpaw_parser"
147153
gpaw_schema_package = "nomad_simulation_parsers.schema_packages:gpaw_schema_package"
154+
gromacs_parser = "nomad_simulation_parsers.parsers:gromacs_parser"
155+
gromacs_schema_package = "nomad_simulation_parsers.schema_packages:gromacs_schema_package"
148156
h5md_parser = "nomad_simulation_parsers.parsers:h5md_parser"
149157
h5md_schema_package = "nomad_simulation_parsers.schema_packages:h5md_schema_package"
150158
lammps_parser = "nomad_simulation_parsers.parsers:lammps_parser"
@@ -158,6 +166,8 @@ vasp_parser = "nomad_simulation_parsers.parsers:vasp_parser"
158166
vasp_schema_package = "nomad_simulation_parsers.schema_packages:vasp_schema_package"
159167
wannier90_parser = "nomad_simulation_parsers.parsers:wannier90_parser"
160168
wannier90_schema_package = "nomad_simulation_parsers.schema_packages:wannier90_schema_package"
169+
yambo_parser = "nomad_simulation_parsers.parsers:yambo_parser"
170+
yambo_schema_package = "nomad_simulation_parsers.schema_packages:yambo_schema_package"
161171

162172

163173
[tool.cruft]

src/nomad_simulation_parsers/parsers/__init__.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,18 @@ class Wannier90ParserEntryPoint(EntryPoint):
131131
code_category='Atomistic code',
132132
)
133133

134+
gromacs_parser = EntryPoint(
135+
name='parsers/gromacs',
136+
aliases=['parsers/gromacs'],
137+
description='NOMAD parser for GROMACS.',
138+
python_package='nomad_simulation_parsers',
139+
mainfile_contents_re=r'gmx mdrun, (VERSION|version)[\s\S]*Input Parameters:',
140+
parser_class_name='nomad_simulation_parsers.parsers.gromacs.parser.GromacsParser',
141+
code_name='GROMACS',
142+
code_homepage='http://www.gromacs.org/',
143+
code_category='Atomistic code',
144+
)
145+
134146
h5md_parser = EntryPoint(
135147
name='parsers/h5md',
136148
aliases=['parsers/h5md'],
@@ -223,3 +235,15 @@ class Wannier90ParserEntryPoint(EntryPoint):
223235
code_homepage='http://www.wannier.org/',
224236
code_category='Atomistic code',
225237
)
238+
239+
yambo_parser = EntryPoint(
240+
name='parsers/yambo',
241+
aliases=['parsers/yambo'],
242+
description='NOMAD parser for YAMBO.',
243+
parser_class_name='nomad_simulation_parsers.parsers.yambo.parser.YamboParser',
244+
python_package='nomad_simulation_parsers',
245+
mainfile_contents_re=r'Build[\s\S]+?http://www\.yambo-code\.org',
246+
code_name='YAMBO',
247+
code_homepage='http://www.yambo-code.org/',
248+
code_category='Atomistic code',
249+
)

src/nomad_simulation_parsers/parsers/fhiaims/parser.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -372,8 +372,8 @@ def get_forces(
372372
f'../upload/archive/mainfile/{mainfile}'
373373
)
374374
# check if supercell match calculation cell
375-
calc_cell: Atoms = (
376-
archive.data.model_system[-1].cell[-1].to_ase_atoms(self.logger)
375+
calc_cell: Atoms = archive.data.model_system[-1].to_ase_atoms(
376+
logger=self.logger
377377
)
378378
supercell_atoms = Atoms(
379379
positions=supercell.positions,
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
RE_FLOAT = r'[-+]?\d+\.*\d*(?:[Ee][-+]\d+)?'
2+
RE_N = r'[\n\r]'
3+
4+
5+
def to_float(string: str | None) -> float | None:
6+
if string is None:
7+
return None
8+
try:
9+
value = float(string)
10+
except ValueError:
11+
value = None
12+
return value
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import numpy as np
2+
import panedr
3+
from nomad.parsing.file_parser import FileParser
4+
5+
6+
class GromacsEDRParser(FileParser):
7+
@property
8+
def fileedr(self):
9+
if self._file_handler is None:
10+
try:
11+
self._file_handler = panedr.edr_to_df(self.mainfile)
12+
except Exception:
13+
self.logger.error('Error reading edr file.')
14+
15+
return self._file_handler
16+
17+
def parse(self, key: str):
18+
if self.fileedr is None:
19+
return
20+
21+
val = self.fileedr.get(key, None)
22+
if self._results is None:
23+
self._results = dict()
24+
25+
if val is not None:
26+
val = np.asarray(val)
27+
28+
self._results[key] = val
29+
30+
def keys(self) -> list[str]:
31+
if self.fileedr is None:
32+
return []
33+
return list(self.fileedr.keys())
34+
35+
@property
36+
def length(self) -> int:
37+
return self.fileedr.shape[0]
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
import re
2+
from typing import Any
3+
4+
from nomad.parsing.file_parser.text_parser import Quantity, TextParser
5+
6+
from .common import RE_N, to_float
7+
8+
9+
def str_to_header(block: str) -> dict[str, Any]:
10+
n_val = 2
11+
val = [v.split(':', 1) for v in block.strip().splitlines()]
12+
return {v[0].strip(): v[1].strip() for v in val if len(v) == n_val}
13+
14+
15+
def str_to_input_parameters(block: str) -> dict[str, Any]:
16+
re_section = re.compile(r'^\s*([\w\-]+):\s*$')
17+
re_subsection = re.compile(r'^\s*([\w\-]+\s[\d]+):\s*$')
18+
re_scalar = re.compile(r'\s*([\w\-]+)\s*[=:]\s*(.+)')
19+
re_array = re.compile(r'\s*([\w\-]+)\[[\d ]+\]\s*=\s*\{*(.+)')
20+
re_shorthand_array = re.compile(
21+
r'\s*([\w\-]+)\[\d+,\.\.\.,\d+\]\s*=\s*\{(\d+),\.\.\.,(\d+)\}'
22+
)
23+
24+
parameters = dict()
25+
stack = [parameters] # Stack to track the current context
26+
indent_levels = [] # To track the indentation levels
27+
28+
for line in block.strip().splitlines():
29+
val_n = line.rstrip() # Remove trailing spaces
30+
if not val_n:
31+
continue
32+
33+
# Calculate the indentation level
34+
current_indent = len(val_n) - len(val_n.lstrip())
35+
36+
# Handle end of section based on indentation
37+
while indent_levels and current_indent <= indent_levels[-1]:
38+
stack.pop()
39+
indent_levels.pop()
40+
41+
# Check for section or subsection
42+
if match := (re_section.match(val_n) or re_subsection.match(val_n)):
43+
key = match.group(1)
44+
stack[-1][key] = {}
45+
stack.append(stack[-1][key])
46+
indent_levels.append(current_indent)
47+
# Check for scalar
48+
elif match := re_scalar.match(val_n):
49+
key = match.group(1)
50+
value = match.group(2)
51+
if value.lower() in ['true', 'false']:
52+
value = value.lower() == 'true'
53+
elif value.replace('.', '', 1).isdigit():
54+
value = float(value) if '.' in value else int(value)
55+
stack[-1][key] = value
56+
# Check for shorthand array
57+
elif match := re_shorthand_array.match(val_n):
58+
array_key = match.group(1)
59+
start = int(match.group(2))
60+
end = int(match.group(3))
61+
stack[-1][array_key] = list(range(start, end + 1))
62+
# Check for array
63+
elif match := re_array.match(val_n):
64+
array_key = match.group(1)
65+
value = [float(v) for v in match.group(2).rstrip('}').split(',')]
66+
stack[-1].setdefault(array_key, [])
67+
stack[-1][array_key].append(value[0] if len(value) == 1 else value)
68+
return parameters
69+
70+
71+
def str_to_energies(block: str) -> dict[str, float]:
72+
thermo_common = [
73+
r'Total Energy',
74+
r'Potential',
75+
r'Kinetic En.',
76+
r'Temperature',
77+
r'Pressure \(bar\)',
78+
r'LJ \(SR\)',
79+
r'Coulomb \(SR\)',
80+
r'Proper Dih.',
81+
]
82+
n_chars_val = re.search(rf'( +{"| +".join(thermo_common)})', block)
83+
n_chars_val = len(n_chars_val.group(1)) if n_chars_val is not None else None
84+
if n_chars_val is None:
85+
n_chars_val = 15
86+
energies = {}
87+
rows = [v for v in block.splitlines() if v]
88+
for n in range(0, len(rows), 2):
89+
pointer = 0
90+
while pointer < len(rows[n]):
91+
key = rows[n][pointer : pointer + n_chars_val].strip()
92+
value = rows[n + 1][pointer : pointer + n_chars_val]
93+
float_value = to_float(value)
94+
if float_value is not None:
95+
energies[key] = to_float(value)
96+
pointer += n_chars_val
97+
return energies
98+
99+
100+
def str_to_step_info(block: str) -> dict[str, float]:
101+
val = block.strip().splitlines()
102+
keys = val[0].split()
103+
values = [to_float(v) for v in val[1].split()]
104+
return {key: values[n] for n, key in enumerate(keys) if values[n] is not None}
105+
106+
107+
class GromacsLogParser(TextParser):
108+
def init_quantities(self):
109+
thermo_quantities = [
110+
Quantity(
111+
'energies',
112+
r'Energies \(kJ/mol\).*\n(\s*[\s\S]+?)(?:\n.*step.* load imb.*|\n\n)',
113+
str_operation=str_to_energies,
114+
convert=False,
115+
),
116+
Quantity(
117+
'step_info',
118+
rf'{RE_N}\s*(Step.+\n[\d\.\- ]+)',
119+
str_operation=str_to_step_info,
120+
convert=False,
121+
),
122+
]
123+
124+
self._quantities = [
125+
Quantity('time_start', r'Log file opened on (.+)', flatten=False),
126+
Quantity(
127+
'host_info',
128+
r'Host:\s*(\S+)\s*pid:\s*(\d+)\s*'
129+
r'rank ID:\s*(\d+)\s*number of ranks:\s*(\d*)',
130+
),
131+
Quantity(
132+
'module_version', r'GROMACS:\s*(.+?),\s*VERSION\s*(\S+)', flatten=False
133+
),
134+
Quantity('execution_path', r'Executable:\s*(.+)'),
135+
Quantity('working_path', r'Data prefix:\s*(.+)'),
136+
# TODO cannot understand treatment of the command line in the old parser
137+
Quantity(
138+
'header',
139+
r'(?:GROMACS|Gromacs) (20[\s\S]+?)\n\n',
140+
str_operation=str_to_header,
141+
),
142+
Quantity(
143+
'header',
144+
r'(?:GROMACS|Gromacs) (version:[\s\S]+?)\n\n',
145+
str_operation=str_to_header,
146+
),
147+
Quantity(
148+
'input_parameters',
149+
r'Input Parameters:\s*\n([\s\S]+?)\n\n',
150+
str_operation=str_to_input_parameters,
151+
),
152+
Quantity('maximum_force', r'Norm of force\s*([\s\S]+?)\n\n', flatten=False),
153+
Quantity(
154+
'step',
155+
r'(Step\s*Time[\s\S]+?Energies[\s\S]+?\n\n)',
156+
repeats=True,
157+
sub_parser=TextParser(quantities=thermo_quantities),
158+
),
159+
Quantity(
160+
'averages',
161+
r'A V E R A G E S ====>([\s\S]+?\n\n\n)',
162+
sub_parser=TextParser(quantities=thermo_quantities),
163+
),
164+
Quantity('time_end', r'Finished \S+ on rank \d+ (.+)', flatten=False),
165+
]

0 commit comments

Comments
 (0)