Commit 84b3546d authored by Alvin Noe Ladines's avatar Alvin Noe Ladines
Browse files

Implemented fixes and tests to workflow

parent a77d1e1f
Pipeline #82332 passed with stages
in 22 minutes and 47 seconds
Subproject commit 2b9ee49d74cf89bb07e2b798e6e391fd5ac724c6
Subproject commit c17dbecac0f395b48fc65b0c7dcc2ba4de1be271
Subproject commit 84602c2c62462b93274dab91fce38a72b249cb49
Subproject commit e1f873f104d4ce64815636251c8e30971a9fcbda
......@@ -5646,20 +5646,20 @@ class section_XC_functionals(MSection):
a_legacy=LegacyDefinition(name='XC_functional_weight'))
class Relaxation(MSection):
class GeometryOptimization(MSection):
'''
Section containing the results of a relaxation workflow.
Section containing the results of a geometry_optimization workflow.
'''
m_def = Section(validate=False, a_legacy=LegacyDefinition(name='section_relaxation'))
m_def = Section(validate=False, a_legacy=LegacyDefinition(name='section_geometry_optimization'))
relaxation_type = Quantity(
geometry_optimization_type = Quantity(
type=str,
shape=[],
description='''
The type of relaxation ionic, cell_shape, cell_volume.
The type of geometry optimization can either be ionic, cell_shape, cell_volume.
''',
a_legacy=LegacyDefinition(name='relaxation_type')
a_legacy=LegacyDefinition(name='geometry_optimization_type')
)
input_energy_difference_tolerance = Quantity(
......@@ -5685,7 +5685,7 @@ class Relaxation(MSection):
shape=[],
unit='joule',
description='''
The difference in the energy between the last two steps during relaxation.
The difference in the energy between the last two steps during optimization.
''',
a_legacy=LegacyDefinition(name='final_energy_difference'),
a_search=Search())
......@@ -5695,35 +5695,17 @@ class Relaxation(MSection):
shape=[],
unit='newton',
description='''
The maximum net force in the last relaxation step.
The maximum net force in the last optimization step.
''',
a_legacy=LegacyDefinition(name='final_force_maximum'))
final_calculation_ref = Quantity(
type=Reference(SectionProxy('section_single_configuration_calculation')),
categories=[fast_access],
shape=[],
description='''
Reference to last calculation step.
''',
a_legacy=LegacyDefinition(name='final_calculation_ref'))
n_relaxation_steps = Quantity(
optimization_steps = Quantity(
type=int,
shape=[],
description='''
Number of relaxation steps.
Number of optimization steps.
''',
a_legacy=LegacyDefinition(name='n_relaxation_steps'))
calculations_ref = Quantity(
type=Reference(SectionProxy('section_single_configuration_calculation')),
shape=['n_relaxation_steps'],
description='''
List of references to each section_single_configuration_calculation corresponding
to each step in the relaxation.
''',
a_legacy=LegacyDefinition(name='calculations_ref'))
a_legacy=LegacyDefinition(name='optimization_steps'))
class Phonon(MSection):
......@@ -5794,6 +5776,14 @@ class Elastic(MSection):
m_def = Section(validate=False, a_legacy=LegacyDefinition(name='section_elastic'))
energy_stress_calculator = Quantity(
type=str,
shape=[],
description='''
Name of program used to calculate energy or stress.
''',
a_legacy=LegacyDefinition(name='energy_stress_calculator'))
elastic_calculation_method = Quantity(
type=str,
shape=[],
......@@ -5886,15 +5876,35 @@ class Workflow(MSection):
type=str,
shape=[],
description='''
The type of calculation workflow. Can be one of relaxation, elastic, phonon,
molecular dynamics.
The type of calculation workflow. Can be one of geometry_optimization, elastic,
phonon, molecular_dynamics.
''',
a_legacy=LegacyDefinition(name='workflow_type'),
a_search=Search(statistic_size=4, statistic_order='_count'))
section_relaxation = SubSection(
sub_section=SectionProxy('Relaxation'), categories=[fast_access],
a_legacy=LegacyDefinition(name='section_relaxation'))
calculation_result_ref = Quantity(
type=Reference(SectionProxy('section_single_configuration_calculation')),
categories=[fast_access],
shape=[],
description='''
Reference to calculation result. In the case of geometry_optimization and
molecular dynamics, this corresponds to the final step in the simulation. For the
rest of the workflow types, it refers to the original system.
''',
a_legacy=LegacyDefinition(name='calculation_result_ref'))
calculations_ref = Quantity(
type=Reference(SectionProxy('section_single_configuration_calculation')),
shape=['optimization_steps'],
description='''
List of references to each section_single_configuration_calculation in the
simulation.
''',
a_legacy=LegacyDefinition(name='calculations_ref'))
section_geometry_optimization = SubSection(
sub_section=SectionProxy('GeometryOptimization'), categories=[fast_access],
a_legacy=LegacyDefinition(name='section_geometry_optimization'))
section_phonon = SubSection(
sub_section=SectionProxy('Phonon'), categories=[fast_access],
......
......@@ -15,17 +15,18 @@
import numpy as np
from nomad.normalizing.normalizer import Normalizer
from nomad.datamodel.metainfo.public import Workflow, Relaxation, Phonon, Elastic
from nomad.datamodel.metainfo.public import Workflow, GeometryOptimization, Phonon, Elastic,\
MolecularDynamics
class RelaxationNormalizer(Normalizer):
class GeometryOptimizationNormalizer(Normalizer):
def __init__(self, entry_archive):
super().__init__(entry_archive)
def _to_numpy_array(self, quantity):
return np.array(quantity.m if quantity is not None else quantity)
def _get_relaxation_type(self):
def _get_geometry_optimization_type(self):
sec_system = self.section_run.section_system
if not sec_system:
return
......@@ -45,8 +46,15 @@ class RelaxationNormalizer(Normalizer):
return 'static'
else:
cell_init = self._to_numpy_array(sec_system[0].lattice_vectors)
cell_final = self._to_numpy_array(sec_system[-1].lattice_vectors)
cell_init = sec_system[0].lattice_vectors
cell_final = sec_system[-1].lattice_vectors
if cell_init is None:
cell_init = sec_system[0].simulation_cell
if cell_final is None:
cell_final = sec_system[-1].simulation_cell
cell_init = self._to_numpy_array(cell_init)
cell_final = self._to_numpy_array(cell_final)
cell_relaxation = compare_cell(cell_init, cell_final)
......@@ -62,23 +70,28 @@ class RelaxationNormalizer(Normalizer):
return 'ionic'
def normalize(self):
self.section = self.entry_archive.section_workflow.section_relaxation
self.section = self.entry_archive.section_workflow.section_geometry_optimization
if not self.section:
self.section = self.entry_archive.section_workflow.m_create(Relaxation)
self.section = self.entry_archive.section_workflow.m_create(GeometryOptimization)
if not self.section.relaxation_type:
self.section.relaxation_type = self._get_relaxation_type()
if not self.section.geometry_optimization_type:
try:
geometry_optimization_type = self._get_geometry_optimization_type()
self.section.geometry_optimization_type = geometry_optimization_type
except Exception:
pass
scc = self.section_run.section_single_configuration_calculation
if not self.section.final_calculation_ref:
if not self.entry_archive.section_workflow.calculation_result_ref:
if scc:
self.section.final_calculation_ref = scc[-1]
self.entry_archive.section_workflow.calculation_result_ref = scc[-1]
if not self.section.n_relaxation_steps:
self.section.n_relaxation_steps = len(scc)
if not self.entry_archive.section_workflow.calculations_ref:
if scc:
self.entry_archive.section_workflow.calculations_ref = scc
if not self.section.calculations_ref:
self.section.calculations_ref = scc
if not self.section.optimization_steps:
self.section.optimization_steps = len(scc)
if not self.section.final_energy_difference:
energies = []
......@@ -115,7 +128,7 @@ class PhononNormalizer(Normalizer):
super().__init__(entry_archive)
def _get_n_imaginary_frequencies(self):
scc = self.entry_archive.section_run[0].section_single_configuration_calculation
scc = self.section_run.section_single_configuration_calculation
sec_band = scc[0].section_k_band
result = 0
for band_segment in sec_band[0].section_k_band_segment:
......@@ -125,6 +138,16 @@ 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)
......@@ -144,9 +167,12 @@ class ElasticNormalizer(Normalizer):
if spacegroup is None or order != 2:
return
scc = self.entry_archive.section_run[-1].section_single_configuration_calculation[-1]
scc = self.section_run.section_single_configuration_calculation[-1]
c = scc.x_elastic_2nd_order_constants_matrix
if c is None:
return
# see Phys. Rev B 90, 224104 (2014)
res = False
if spacegroup <= 2: # Triclinic
......@@ -189,7 +215,7 @@ class ElasticNormalizer(Normalizer):
return res
def _get_maximum_fit_error(self):
scc = self.entry_archive.section_run[-1].section_single_configuration_calculation[-1]
scc = self.section_run.section_single_configuration_calculation[-1]
max_error = 0.0
for diagram in scc.x_elastic_section_strain_diagrams:
......@@ -201,14 +227,66 @@ 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)
if self.section.is_mechanically_stable is None:
self.section.is_mechanically_stable = bool(self._resolve_mechanical_stability())
if self.section.fitting_error_maximum is None:
self.section.fitting_error_maximum = self._get_maximum_fit_error()
class MolecularDynamicsNormalizer(Normalizer):
def __init__(self, entry_archive):
super().__init__(entry_archive)
def _is_with_thermodynamics(self):
scc = self.section_run.section_single_configuration_calculation
res = False
if scc:
res = scc[-1].temperature is not None
return res
def _is_with_trajectory(self):
sec_system = self.section_run.section_system
res = False
if sec_system:
res = sec_system[-1].atom_positions is not None
return res
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)
if self.section.with_thermodynamics is None:
self.section.with_thermodynamics = self._is_with_thermodynamics()
if self.section.with_trajectory is None:
self.section.with_trajectory = self._is_with_trajectory()
class WorkflowNormalizer(Normalizer):
'''
This normalizer performs all produces a section all data necessary for the Optimade API.
......@@ -216,6 +294,43 @@ class WorkflowNormalizer(Normalizer):
'''
def __init__(self, entry_archive):
super().__init__(entry_archive)
self._elastic_programs = ['elastic']
self._phonon_programs = ['phonopy']
def _resolve_workflow_type_vasp(self):
ibrion = self.section_run.section_method[0].x_vasp_incarOut_IBRION
if ibrion == 0:
workflow_type = "molecular_dynamics"
else:
workflow_type = "geometry_optimization"
return workflow_type
def _resolve_workflow_type(self):
# first get it from section_sampling_method
workflow_type = None
sec_sampling_method = self.section_run.section_sampling_method
if sec_sampling_method:
workflow_type = sec_sampling_method[-1].sampling_method
# resolve it from parser
if not workflow_type:
program_name = self.section_run.program_name
if program_name:
program_name = program_name.lower()
if program_name == 'vasp':
workflow_type = self._resolve_workflow_type_vasp()
elif program_name == 'elastic':
workflow_type = 'elastic'
elif program_name == 'lammps':
workflow_type = 'molecular_dynamics'
elif program_name == 'phonopy':
workflow_type = 'phonon'
return workflow_type
def normalize(self, logger=None) -> None:
# Setup logger
......@@ -226,31 +341,30 @@ class WorkflowNormalizer(Normalizer):
if self.section_run is None:
return
workflow = self.entry_archive.section_workflow
if not workflow:
workflow = self.entry_archive.m_create(Workflow)
workflow_type = None
if self.entry_archive.section_workflow:
workflow_type = self.entry_archive.section_workflow.workflow_type
if not workflow_type:
sec_sampling_method = self.section_run.section_sampling_method
if sec_sampling_method:
# TODO imho geometry_optimization is not an appropriate name
# if workflow_type == 'geometry_optimization':
# workflow_type = 'relaxation'
workflow_type = sec_sampling_method[-1].sampling_method
workflow_type = self._resolve_workflow_type()
if not workflow_type:
return
workflow = self.entry_archive.section_workflow
if not workflow:
workflow = self.entry_archive.m_create(Workflow)
workflow.workflow_type = workflow_type
if workflow.workflow_type in ['geometry_optimization', 'relaxation']:
RelaxationNormalizer(self.entry_archive).normalize()
if workflow.workflow_type == 'geometry_optimization':
GeometryOptimizationNormalizer(self.entry_archive).normalize()
elif workflow.workflow_type == 'phonon':
PhononNormalizer(self.entry_archive).normalize()
elif workflow.workflow_type == 'elastic':
ElasticNormalizer(self.entry_archive).normalize()
elif workflow.workflow_type == 'molecular_dynamics':
MolecularDynamicsNormalizer(self.entry_archive).normalize()
......@@ -28,7 +28,7 @@ from hashlib import md5
from nomad.app.common import rfc3339DateTime
from nomad.app.api.auth import generate_upload_token
from nomad import search, parsing, files, config, utils, infrastructure
from nomad import search, files, config, utils, infrastructure
from nomad.metainfo import search_extension
from nomad.files import UploadFiles, PublicUploadFiles
from nomad.processing import Upload, Calc, SUCCESS
......@@ -781,7 +781,7 @@ class TestArchive(UploadFilesBasedTests):
'$and': [
{'dft.code_name': 'VASP'},
{'$gte': {'n_atoms': 3}},
{'$lte': {'dft.workflow.section_relaxation.final_energy_difference': 1e-24}}
{'$lte': {'dft.workflow.section_geometry_optimization.final_energy_difference': 1e-24}}
]}, 0, id='client-example')
])
def test_post_archive_query(self, api, example_upload, query_expression, nresults):
......@@ -848,7 +848,7 @@ class TestMetainfo():
class TestRepo():
@pytest.fixture(scope='class')
def example_elastic_calcs(
self, elastic_infra, raw_files_infra, normalized: parsing.Backend,
self, elastic_infra, raw_files_infra, normalized,
test_user: User, other_test_user: User):
clear_elastic(elastic_infra)
......
Dst01, Lagrangian strain = ( eta, eta, eta, 0.0, 0.0, 0.0)
Dst01_01, eta = -0.05
V1 --=> 0.4743416490258656 0.4743416490258656 0.0000000000000000
V2 --=> 0.4743416490258656 0.0000000000000000 0.4743416490258656
V3 --=> 0.0000000000000000 0.4743416490258656 0.4743416490258656
Dst01_02, eta = -0.04
V1 --=> 0.4795831523313500 0.4795831523313500 0.0000000000000000
V2 --=> 0.4795831523313500 0.0000000000000000 0.4795831523313500
V3 --=> 0.0000000000000000 0.4795831523313500 0.4795831523313500
Dst01_03, eta = -0.03
V1 --=> 0.4847679857418169 0.4847679857418169 0.0000000000000000
V2 --=> 0.4847679857418169 0.0000000000000000 0.4847679857418169
V3 --=> 0.0000000000000000 0.4847679857418169 0.4847679857418169
Dst01_04, eta = -0.02
V1 --=> 0.4898979485569774 0.4898979485569774 0.0000000000000000
V2 --=> 0.4898979485569774 0.0000000000000000 0.4898979485569774
V3 --=> 0.0000000000000000 0.4898979485569774 0.4898979485569774
Dst01_05, eta = -0.01
V1 --=> 0.4949747468308403 0.4949747468308403 0.0000000000000000
V2 --=> 0.4949747468308403 0.0000000000000000 0.4949747468308403
V3 --=> 0.0000000000000000 0.4949747468308403 0.4949747468308403
Dst01_06, eta = 0.0001
V1 --=> 0.5000499975002500 0.5000499975002500 0.0000000000000000
V2 --=> 0.5000499975002500 0.0000000000000000 0.5000499975002500
V3 --=> 0.0000000000000000 0.5000499975002500 0.5000499975002500
Dst01_07, eta = 0.01
V1 --=> 0.5049752469183472 0.5049752469183472 0.0000000000000000
V2 --=> 0.5049752469183472 0.0000000000000000 0.5049752469183472
V3 --=> 0.0000000000000000 0.5049752469183472 0.5049752469183472
Dst01_08, eta = 0.02
V1 --=> 0.5099019513589783 0.5099019513589783 0.0000000000000000
V2 --=> 0.5099019513589783 0.0000000000000000 0.5099019513589783
V3 --=> 0.0000000000000000 0.5099019513589783 0.5099019513589783
Dst01_09, eta = 0.03
V1 --=> 0.5147815070494969 0.5147815070494969 0.0000000000000000
V2 --=> 0.5147815070494969 0.0000000000000000 0.5147815070494969
V3 --=> 0.0000000000000000 0.5147815070494969 0.5147815070494969
Dst01_10, eta = 0.04
V1 --=> 0.5196152422706076 0.5196152422706076 0.0000000000000000
V2 --=> 0.5196152422706076 0.0000000000000000 0.5196152422706076
V3 --=> 0.0000000000000000 0.5196152422706076 0.5196152422706076
Dst01_11, eta = 0.05
V1 --=> 0.5244044240846781 0.5244044240846781 0.0000000000000000
V2 --=> 0.5244044240846781 0.0000000000000000 0.5244044240846781
V3 --=> 0.0000000000000000 0.5244044240846781 0.5244044240846781
Dst02, Lagrangian strain = ( eta, eta, 0.0, 0.0, 0.0, 0.0)
Dst02_01, eta = -0.05
V1 --=> 0.4743416490258656 0.4743416490258656 0.0000000000000000
V2 --=> 0.4743416490258656 0.0000000000000000 0.5000000000000000
V3 --=> 0.0000000000000000 0.4743416490258656 0.5000000000000000
Dst02_02, eta = -0.04
V1 --=> 0.4795831523313500 0.4795831523313500 0.0000000000000000
V2 --=> 0.4795831523313500 0.0000000000000000 0.5000000000000000
V3 --=> 0.0000000000000000 0.4795831523313500 0.5000000000000000
Dst02_03, eta = -0.03
V1 --=> 0.4847679857418169 0.4847679857418169 0.0000000000000000
V2 --=> 0.4847679857418169 0.0000000000000000 0.5000000000000000
V3 --=> 0.0000000000000000 0.4847679857418169 0.5000000000000000
Dst02_04, eta = -0.02
V1 --=> 0.4898979485569774 0.4898979485569774 0.0000000000000000
V2 --=> 0.4898979485569774 0.0000000000000000 0.5000000000000000
V3 --=> 0.0000000000000000 0.4898979485569774 0.5000000000000000
Dst02_05, eta = -0.01
V1 --=> 0.4949747468308403 0.4949747468308403 0.0000000000000000
V2 --=> 0.4949747468308403 0.0000000000000000 0.5000000000000000
V3 --=> 0.0000000000000000 0.4949747468308403 0.5000000000000000
Dst02_06, eta = 0.0001
V1 --=> 0.5000499975002500 0.5000499975002500 0.0000000000000000
V2 --=> 0.5000499975002500 0.0000000000000000 0.5000000000000000
V3 --=> 0.0000000000000000 0.5000499975002500 0.5000000000000000
Dst02_07, eta = 0.01
V1 --=> 0.5049752469183472 0.5049752469183472 0.0000000000000000
V2 --=> 0.5049752469183472 0.0000000000000000 0.5000000000000000
V3 --=> 0.0000000000000000 0.5049752469183472 0.5000000000000000
Dst02_08, eta = 0.02
V1 --=> 0.5099019513589783 0.5099019513589783 0.0000000000000000
V2 --=> 0.5099019513589783 0.0000000000000000 0.5000000000000000
V3 --=> 0.0000000000000000 0.5099019513589783 0.5000000000000000
Dst02_09, eta = 0.03
V1 --=> 0.5147815070494969 0.5147815070494969 0.0000000000000000
V2 --=> 0.5147815070494969 0.0000000000000000 0.5000000000000000
V3 --=> 0.0000000000000000 0.5147815070494969 0.5000000000000000
Dst02_10, eta = 0.04
V1 --=> 0.5196152422706076 0.5196152422706076 0.0000000000000000
V2 --=> 0.5196152422706076 0.0000000000000000 0.5000000000000000
V3 --=> 0.0000000000000000 0.5196152422706076 0.5000000000000000
Dst02_11, eta = 0.05
V1 --=> 0.5244044240846781 0.5244044240846781 0.0000000000000000
V2 --=> 0.5244044240846781 0.0000000000000000 0.5000000000000000
V3 --=> 0.0000000000000000 0.5244044240846781 0.5000000000000000
Dst03, Lagrangian strain = ( 0.0, 0.0, 0.0, 2eta, 2eta, 2eta)
Dst03_01, eta = -0.05
V1 --=> 0.4729438716979345 0.4729438716979345 -0.0514605523871404
V2 --=> 0.4729438716979345 -0.0514605523871404 0.4729438716979345
V3 --=> -0.0514605523871404 0.4729438716979345 0.4729438716979345
Dst03_02, eta = -0.04
V1 --=> 0.4787101270899567 0.4787101270899567 -0.0409051151807087
V2 --=> 0.4787101270899567 -0.0409051151807087 0.4787101270899567
V3 --=> -0.0409051151807087 0.4787101270899567 0.4787101270899567
Dst03_03, eta = -0.03
V1 --=> 0.4842882196735362 0.4842882196735362 -0.0304932873758094
V2 --=> 0.4842882196735362 -0.0304932873758094 0.4842882196735362
V3 --=> -0.0304932873758094 0.4842882196735362 0.4842882196735362
Dst03_04, eta = -0.02
V1 --=> 0.4896894186752173 0.4896894186752173 -0.0202125326840671
V2 --=> 0.4896894186752173 -0.0202125326840671 0.4896894186752173
V3 --=> -0.0202125326840671 0.4896894186752173 0.4896894186752173
Dst03_05, eta = -0.01
V1 --=> 0.4949237146773521 0.4949237146773521 -0.0100515322407494
V2 --=> 0.4949237146773521 -0.0100515322407494 0.4949237146773521
V3 --=> -0.0100515322407494 0.4949237146773521 0.4949237146773521
Dst03_06, eta = 0.0001
V1 --=> 0.5000499925012499 0.5000499925012499 0.0000999950014999
V2 --=> 0.5000499925012499 0.0000999950014999 0.5000499925012499
V3 --=> 0.0000999950014999 0.5000499925012499 0.5000499925012499
Dst03_07, eta = 0.01
V1 --=> 0.5049262165161809 0.5049262165161809 0.0099514696855950
V2 --=> 0.5049262165161809 0.0099514696855950 0.5049262165161809
V3 --=> 0.0099514696855950 0.5049262165161809 0.5049262165161809
Dst03_08, eta = 0.02
V1 --=> 0.5097094777002669 0.5097094777002669 0.0198115291436243
V2 --=> 0.5097094777002669 0.0198115291436243 0.5097094777002669
V3 --=> 0.0198115291436243 0.5097094777002669 0.5097094777002669
Dst03_09, eta = 0.03
V1 --=> 0.5143561700545102 0.5143561700545102 0.0295881843128717
V2 --=> 0.5143561700545102 0.0295881843128717 0.5143561700545102
V3 --=> 0.0295881843128717 0.5143561700545102 0.5143561700545102
Dst03_10, eta = 0.04
V1 --=> 0.5188720379206402 0.5188720379206402 0.0392888855893651
V2 --=> 0.5188720379206402 0.0392888855893651 0.5188720379206402