Commit 0d34d12b authored by Markus Scheidgen's avatar Markus Scheidgen
Browse files

Merge branch 'fhi-vibes-parser' into 'v0.10.2'

Fhi vibes parser

See merge request !306
parents c112c62f 46a0af86
Pipeline #98231 passed with stages
in 27 minutes and 31 seconds
......@@ -205,3 +205,6 @@
[submodule "dependencies/parsers/example"]
path = dependencies/parsers/example
url = https://github.com/nomad-coe/nomad-parser-example.git
[submodule "dependencies/parsers/fhi-vibes"]
path = dependencies/parsers/fhi-vibes
url = https://github.com/nomad-coe/nomad-parser-fhi-vibes.git
Subproject commit cb6d097c7aa1886c2bee157512cabc320c3de3c4
......@@ -1829,6 +1829,64 @@ class EnergyContribution(MSection):
a_legacy=LegacyDefinition(name='energy_contribution_value'))
class StressTensorContribution(MSection):
'''
Section describing the contributions to the stress tensor.
'''
m_def = Section(
aliases=['section_stress_tensor_contribution'],
validate=False,
a_legacy=LegacyDefinition(name='section_stress_tensor_contribution'))
stress_tensor_contribution_kind = Quantity(
type=str,
shape=[],
description='''
The kind of the stress tensor contribution. Can be one of bond, pair, coulomb, etc.
''',
a_legacy=LegacyDefinition(name='stress_tensor_contribution_kind'))
stress_tensor_contribution_value = Quantity(
type=np.dtype(np.float64),
shape=[3, 3],
unit='joule/meter**3',
description='''
Value of the stress tensor contribution.
''',
categories=[EnergyComponent, EnergyValue],
a_legacy=LegacyDefinition(name='stress_tensor_contribution_value'))
class AtomStressContribution(MSection):
'''
Section describing the contributions to the stress tensor.
'''
m_def = Section(
aliases=['section_atom_stress_contribution'],
validate=False,
a_legacy=LegacyDefinition(name='section_atom_stress_contribution'))
atom_stress_contribution_kind = Quantity(
type=str,
shape=[],
description='''
The kind of the stress tensor contribution. Can be one of bond, pair, coulomb, etc.
''',
a_legacy=LegacyDefinition(name='atom_stress_tensor_contribution_kind'))
atom_stress_contribution_value = Quantity(
type=np.dtype(np.float64),
shape=['number_of_atoms', 3, 3],
unit='joule/meter**3',
description='''
Value of the atom stress contribution.
''',
categories=[EnergyComponent, EnergyValue],
a_legacy=LegacyDefinition(name='atom_stress_contribution_value'))
class FrameSequenceUserQuantity(MSection):
'''
Section collecting some user-defined quantities evaluated along a sequence of frame.
......@@ -5361,6 +5419,16 @@ class SingleConfigurationCalculation(MSection):
repeats=True,
a_legacy=LegacyDefinition(name='section_energy_contribution'))
section_stress_tensor_contribution = SubSection(
sub_section=SectionProxy('StressTensorContribution'),
repeats=True,
a_legacy=LegacyDefinition(name='section_stress_tensor_contribution'))
section_atom_stress_contribution = SubSection(
sub_section=SectionProxy('AtomStressContribution'),
repeats=True,
a_legacy=LegacyDefinition(name='section_atom_stress_contribution'))
section_k_band_normalized = SubSection(
sub_section=SectionProxy('KBandNormalized'),
repeats=True,
......@@ -6762,6 +6830,40 @@ class MolecularDynamics(MSection):
a_legacy=LegacyDefinition(name='with_thermodynamics'))
class SinglePoint(MSection):
'''
Section containing results of a single point workflow.
'''
m_def = Section(
validate=False,
a_legacy=LegacyDefinition(name='section_single_point'))
single_point_calculation_method = Quantity(
type=str,
shape=[],
description='''
Calculation method used.
''',
a_legacy=LegacyDefinition(name='single_point_calculation_method'))
scf_steps = Quantity(
type=int,
shape=[],
description='''
Number of self-consistent steps for the calculation
''',
a_legacy=LegacyDefinition(name='number_of_scf_steps'))
is_converged = Quantity(
type=bool,
shape=[],
description='''
Indicates if the convergence criteria were fullfilled
''',
a_legacy=LegacyDefinition(name='is_converged'))
class Workflow(MSection):
'''
Section containing the results of a workflow.
......@@ -6781,6 +6883,14 @@ class Workflow(MSection):
a_search=Search(),
a_legacy=LegacyDefinition(name='workflow_type'))
calculator = Quantity(
type=str,
shape=[],
description='''
Energy and force calculator.
''',
a_legacy=LegacyDefinition(name='calculator'))
calculation_result_ref = Quantity(
type=Reference(SectionProxy('SingleConfigurationCalculation')),
shape=[],
......@@ -6801,6 +6911,12 @@ class Workflow(MSection):
''',
a_legacy=LegacyDefinition(name='calculations_ref'))
section_single_point = SubSection(
sub_section=SectionProxy('SinglePoint'),
repeats=False,
categories=[FastAccess],
a_legacy=LegacyDefinition(name='section_single_point'))
section_geometry_optimization = SubSection(
sub_section=SectionProxy('GeometryOptimization'),
repeats=False,
......
......@@ -21,7 +21,28 @@ import numpy as np
from nomad.normalizing.normalizer import Normalizer
from nomad.datamodel import EntryArchive
from nomad.datamodel.metainfo.public import Workflow, GeometryOptimization, Phonon, Elastic,\
MolecularDynamics
MolecularDynamics, SinglePoint
class SinglePointNormalizer(Normalizer):
def __init__(self, entry_archive):
super().__init__(entry_archive)
def normalize(self):
self.section = self.entry_archive.section_workflow.section_single_point
if not self.section:
self.section = self.entry_archive.section_workflow.m_create(SinglePoint)
if not self.section.single_point_calculation_method:
try:
method = self.section_run.section_method[-1]
self.section.single_point_calculation_method = method.electronic_structure_method
except Exception:
pass
if not self.section.scf_steps:
scc = self.section_run.section_single_configuration_calculation
self.section.scf_steps = len(scc[-1].section_scf_iteration)
class GeometryOptimizationNormalizer(Normalizer):
......@@ -86,16 +107,8 @@ class GeometryOptimizationNormalizer(Normalizer):
except Exception:
pass
scc = self.section_run.section_single_configuration_calculation
if not self.entry_archive.section_workflow.calculation_result_ref:
if scc:
self.entry_archive.section_workflow.calculation_result_ref = scc[-1]
if not self.entry_archive.section_workflow.calculations_ref:
if scc:
self.entry_archive.section_workflow.calculations_ref = scc
if not self.section.optimization_steps:
scc = self.section_run.section_single_configuration_calculation
self.section.optimization_steps = len(scc)
if not self.section.final_energy_difference:
......@@ -137,6 +150,8 @@ class PhononNormalizer(Normalizer):
if not scc:
return
sec_band = scc[0].section_k_band
if not sec_band:
return
result = 0
for band_segment in sec_band[0].section_k_band_segment:
freq = band_segment.band_energies
......@@ -146,15 +161,6 @@ class PhononNormalizer(Normalizer):
def normalize(self):
self.section = self.entry_archive.section_workflow.section_phonon
scc = self.section_run.section_single_configuration_calculation
if not self.entry_archive.section_workflow.calculation_result_ref:
if scc:
self.entry_archive.section_workflow.calculation_result_ref = scc[-1]
if not self.entry_archive.section_workflow.calculations_ref:
if scc:
self.entry_archive.section_workflow.calculations_ref = scc
if not self.section:
self.section = self.entry_archive.section_workflow.m_create(Phonon)
......@@ -235,15 +241,6 @@ class ElasticNormalizer(Normalizer):
def normalize(self):
self.section = self.entry_archive.section_workflow.section_elastic
scc = self.section_run.section_single_configuration_calculation
if not self.entry_archive.section_workflow.calculation_result_ref:
if scc:
self.entry_archive.section_workflow.calculation_result_ref = scc[-1]
if not self.entry_archive.section_workflow.calculations_ref:
if scc:
self.entry_archive.section_workflow.calculations_ref = scc
if not self.section:
self.section = self.entry_archive.section_workflow.m_create(Elastic)
......@@ -275,15 +272,6 @@ class MolecularDynamicsNormalizer(Normalizer):
def normalize(self):
self.section = self.entry_archive.section_workflow.section_molecular_dynamics
scc = self.section_run.section_single_configuration_calculation
if not self.entry_archive.section_workflow.calculation_result_ref:
if scc:
self.entry_archive.section_workflow.calculation_result_ref = scc[-1]
if not self.entry_archive.section_workflow.calculations_ref:
if scc:
self.entry_archive.section_workflow.calculations_ref = scc
if not self.section:
self.section = self.entry_archive.section_workflow.m_create(MolecularDynamics)
......@@ -296,8 +284,7 @@ class MolecularDynamicsNormalizer(Normalizer):
class WorkflowNormalizer(Normalizer):
'''
This normalizer performs all produces a section all data necessary for the Optimade API.
It assumes that the :class:`SystemNormalizer` was run before.
This normalizer produces information specific to a workflow.
'''
def __init__(self, entry_archive):
super().__init__(entry_archive)
......@@ -383,6 +370,18 @@ class WorkflowNormalizer(Normalizer):
elif workflow.workflow_type == 'molecular_dynamics':
MolecularDynamicsNormalizer(self.entry_archive).normalize()
elif workflow.workflow_type == 'single_point':
SinglePointNormalizer(self.entry_archive).normalize()
scc = self.section_run.section_single_configuration_calculation
if not self.entry_archive.section_workflow.calculation_result_ref:
if scc:
self.entry_archive.section_workflow.calculation_result_ref = scc[-1]
if not self.entry_archive.section_workflow.calculations_ref:
if scc:
self.entry_archive.section_workflow.calculations_ref = scc
# remove the section workflow again, if the parser/normalizer could not produce a result
if workflow.calculation_result_ref is None:
self.entry_archive.m_remove_sub_section(EntryArchive.section_workflow, -1)
......@@ -124,6 +124,7 @@ class MatchingParser(Parser):
self, name: str, code_name: str, code_homepage: str = None,
mainfile_contents_re: str = None,
mainfile_binary_header: bytes = None,
mainfile_binary_header_re: bytes = None,
mainfile_mime_re: str = r'text/.*',
mainfile_name_re: str = r'.*',
mainfile_alternative: bool = False,
......@@ -144,6 +145,10 @@ class MatchingParser(Parser):
self._mainfile_contents_re = re.compile(mainfile_contents_re)
else:
self._mainfile_contents_re = None
if mainfile_binary_header_re is not None:
self._mainfile_binary_header_re = re.compile(mainfile_binary_header_re)
else:
self._mainfile_binary_header_re = None
self._supported_compressions = supported_compressions
self._ls = lru_cache(maxsize=16)(lambda directory: os.listdir(directory))
......@@ -156,6 +161,13 @@ class MatchingParser(Parser):
if self._mainfile_binary_header not in buffer:
return False
if self._mainfile_binary_header_re is not None:
if buffer is not None:
if self._mainfile_binary_header_re.search(buffer) is None:
return False
else:
return False
if self._mainfile_contents_re is not None:
if decoded_buffer is not None:
if self._mainfile_contents_re.search(decoded_buffer) is None:
......
......@@ -42,6 +42,7 @@ from gpawparser import GPAWParser
from octopusparser import OctopusParser
from orcaparser import OrcaParser
from cp2kparser import CP2KParser
from fhivibesparser import FHIVibesParser
try:
# these packages are not available without parsing extra, which is ok, if the
......@@ -137,6 +138,7 @@ parsers = [
VASPParser(),
ExcitingParser(),
FHIAimsParser(),
FHIVibesParser(),
CP2KParser(),
CrystalParser(),
# The main contents regex of CPMD was causing a catostrophic backtracking issue
......
......@@ -40,6 +40,7 @@ mdanalysis
nomadcore
nomad_dos_fingerprints
lxml
xarray
# [infrastructure]
optimade
......
......@@ -43,6 +43,7 @@ parser_examples = [
('parsers/vasp', 'tests/data/parsers/vasp_compressed/vasp.xml.gz'),
('parsers/vasp', 'tests/data/parsers/vasp_outcar/OUTCAR'),
('parsers/fhi-aims', 'tests/data/parsers/fhi-aims/aims.out'),
('parsers/fhi-vibes', 'tests/data/parsers/fhi-vibes/molecular_dynamics.nc'),
('parsers/cp2k', 'tests/data/parsers/cp2k/si_bulk8.out'),
('parsers/crystal', 'tests/data/parsers/crystal/si.out'),
('parsers/cpmd', 'tests/data/parsers/cpmd/geo_output.out'),
......@@ -85,7 +86,7 @@ for parser, mainfile in parser_examples:
parser_examples = fixed_parser_examples
correct_num_output_files = 116
correct_num_output_files = 117
class TestBackend(object):
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment