Commit 4a8d6da1 authored by Alvin Noe Ladines's avatar Alvin Noe Ladines
Browse files

Implemented phonon workflow

parent c6956c48
Pipeline #81056 passed with stages
in 33 minutes and 52 seconds
Subproject commit 99f576323efc2aa8ede5b63b108d9fe6901a8795
Subproject commit 6af93535078cb43d11b3d5b950525e76b78acb74
......@@ -1221,7 +1221,7 @@ class section_dos_fingerprint(MSection):
indices = Quantity(
type=np.dtype(np.int16),
shape=[2],
shape=['first_index_of_DOS_grid', 'last_index_of_DOS_grid'],
description='''
Indices used to compare DOS fingerprints of different energy ranges.
''',
......@@ -5639,6 +5639,67 @@ class Relaxation(MSection):
a_legacy=LegacyDefinition(name='final_calculation_ref'))
class Phonon(MSection):
'''
Section containing the results of a phonon workflow.
'''
m_def = Section(validate=False, a_legacy=LegacyDefinition(name='section_phonon'))
force_calculator = Quantity(
type=str,
shape=[],
description='''
Name of the program used to calculate the forces.
''',
a_legacy=LegacyDefinition(name='force_calculator'))
mesh_density = Quantity(
type=np.dtype(np.float64),
shape=[],
unit='1 / m ** 3',
description='''
Density of the k-mesh for sampling.
''',
a_legacy=LegacyDefinition(name='mesh_density'),
a_search=Search())
n_imaginary_frequencies = Quantity(
type=int,
shape=[],
description='''
Number of modes with imaginary frequencies.
''',
a_legacy=LegacyDefinition(name='n_imaginary_frequencies'),
a_search=Search())
random_displacements = Quantity(
type=bool,
shape=[],
description='''
Identifies if displacements are made randomly.
''',
a_legacy=LegacyDefinition(name='random_displacements'))
with_non_analytic_correction = Quantity(
type=bool,
shape=[],
description='''
Identifies if non-analytical term corrections are applied to dynamical matrix.
''',
a_legacy=LegacyDefinition(name='with_non_analytic_correction'),
a_search=Search())
with_grueneisen_parameters = Quantity(
type=bool,
shape=[],
description='''
Identifies if Grueneisen parameters are calculated.
''',
a_legacy=LegacyDefinition(name='with_grueneisen_parameters'),
a_search=Search())
class Workflow(MSection):
'''
Section containing the results of a workflow.
......@@ -5660,5 +5721,9 @@ class Workflow(MSection):
sub_section=SectionProxy('Relaxation'),
a_legacy=LegacyDefinition(name='section_relaxation'))
section_phonon = SubSection(
sub_section=SectionProxy('Phonon'),
a_legacy=LegacyDefinition(name='section_phonon'))
m_package.__init_metainfo__()
......@@ -15,14 +15,13 @@
import numpy as np
from nomad.normalizing.normalizer import Normalizer
from nomad.datamodel.metainfo.public import Workflow, Relaxation
from nomad.datamodel.metainfo.public import Workflow, Relaxation, Phonon
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.
'''
class RelaxationNormalizer(Normalizer):
def __init__(self, entry_archive):
super().__init__(entry_archive)
def _get_relaxation_type(self):
sec_system = self.section_run.section_system
if not sec_system:
......@@ -59,20 +58,20 @@ class WorkflowNormalizer(Normalizer):
return 'ionic'
def normalize_relaxation(self):
sec_relaxation = self.entry_archive.section_workflow.section_relaxation
if not sec_relaxation:
sec_relaxation = self.entry_archive.section_workflow.m_create(Relaxation)
def normalize(self):
self.section = self.entry_archive.section_workflow.section_relaxation
if not self.section:
self.section = self.entry_archive.section_workflow.m_create(Relaxation)
if not sec_relaxation.relaxation_type:
sec_relaxation.relaxation_type = self._get_relaxation_type()
if not self.section.relaxation_type:
self.section.relaxation_type = self._get_relaxation_type()
if not sec_relaxation.final_calculation_ref:
if not self.section.final_calculation_ref:
scc = self.section_run.section_single_configuration_calculation
if scc:
sec_relaxation.final_calculation_ref = scc[-1]
self.section.final_calculation_ref = scc[-1]
if not sec_relaxation.final_energy_difference:
if not self.section.final_energy_difference:
energies = []
for scc in self.section_run.section_single_configuration_calculation:
if scc.energy_total_T0:
......@@ -89,9 +88,9 @@ class WorkflowNormalizer(Normalizer):
pass
if delta_energy:
sec_relaxation.final_energy_difference = delta_energy
self.section.final_energy_difference = delta_energy
if not sec_relaxation.final_force_maximum:
if not self.section.final_force_maximum:
scc = self.section_run.section_single_configuration_calculation
max_force = None
if scc:
......@@ -99,7 +98,39 @@ class WorkflowNormalizer(Normalizer):
forces = np.array(scc[-1].atom_forces)
max_force = np.max(np.linalg.norm(forces, axis=1))
if max_force is not None:
sec_relaxation.final_force_maximum = max_force
self.section.final_force_maximum = max_force
class PhononNormalizer(Normalizer):
def __init__(self, entry_archive):
super().__init__(entry_archive)
def _get_n_imaginary_frequencies(self):
scc = self.entry_archive.section_run[0].section_single_configuration_calculation
sec_band = scc[0].section_k_band
result = 0
for band_segment in sec_band[0].section_k_band_segment:
freq = band_segment.band_energies
result += np.count_nonzero(np.array(freq) < 0)
return result
def normalize(self):
self.section = self.entry_archive.section_workflow.section_phonon
if not self.section:
self.entry_archive.section_workflow.m_create(Phonon)
if not self.section.n_imaginary_frequencies:
# get number from bands (not complete as this is not the whole mesh)
self.section.n_imaginary_frequencies = self._get_n_imaginary_frequencies()
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.
'''
def __init__(self, entry_archive):
super().__init__(entry_archive)
def normalize(self, logger=None) -> None:
# Setup logger
......@@ -124,4 +155,7 @@ class WorkflowNormalizer(Normalizer):
sec_workflow.workflow_type = workflow_type
if sec_workflow.workflow_type in ['geometry_optimization', 'relaxation']:
self.normalize_relaxation()
RelaxationNormalizer(self.entry_archive).normalize()
elif sec_workflow.workflow_type == 'phonon':
PhononNormalizer(self.entry_archive).normalize()
......@@ -25,6 +25,7 @@ from eelsparser import EelsParser
from mpesparser import MPESParser
from aptfimparser import APTFIMParser
from vaspparser import VASPParser
from phonopyparser import PhonopyParser
try:
# these packages are not available without parsing extra, which is ok, if the
......@@ -116,12 +117,7 @@ parsers = [
GenerateRandomParser(),
TemplateParser(),
ChaosParser(),
LegacyParser(
name='parsers/phonopy', code_name='Phonopy', code_homepage='https://phonopy.github.io/phonopy/',
parser_class_name='phonopyparser.PhonopyParserWrapper',
# mainfile_contents_re=r'', # Empty regex since this code calls other DFT codes.
mainfile_name_re=(r'.*/phonopy-FHI-aims-displacement-0*1/control.in$')
),
PhonopyParser(),
VASPParser(),
VaspOutcarParser(
name='parsers/vasp-outcar', code_name='VASP', code_homepage='https://www.vasp.at/',
......
Supports Markdown
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