Commit c35fcf44 authored by Lauri Himanen's avatar Lauri Himanen Committed by Markus Scheidgen
Browse files

Resolve "Workflow search update"

parent 11858c6b
......@@ -44,12 +44,12 @@ export const labelElectronic = 'Electronic'
export const labelVibrational = 'Vibrational'
export const labelMechanical = 'Mechanical'
export const labelSpectroscopy = 'Spectroscopy'
export const labelGeometryOptimization = 'Geometry optimization'
export const labelAuthor = 'Author / Origin'
export const labelAccess = 'Access'
export const labelDataset = 'Dataset'
export const labelIDs = 'IDs'
export const labelArchive = 'Archive'
export const labelWorkflow = 'Workflow'
/**
* This function is used to register a new filter within the SearchContext.
......@@ -249,6 +249,7 @@ registerFilter('results.material.symmetry.point_group', labelSymmetry, termQuant
registerFilter('results.material.symmetry.hall_symbol', labelSymmetry, termQuantity)
registerFilter('results.material.symmetry.prototype_aflow_id', labelSymmetry, termQuantity)
registerFilter('results.method.method_name', labelMethod, {...termQuantity, scale: 1 / 4})
registerFilter('results.method.workflow_name', labelMethod, {...termQuantity, scale: 1 / 4})
registerFilter('results.method.simulation.program_name', labelSimulation, {...termQuantity, scale: 1 / 4})
registerFilter('results.method.simulation.program_version', labelSimulation, termQuantity)
registerFilter('results.method.simulation.dft.basis_set_type', labelDFT, {...termQuantity, scale: 1 / 4})
......@@ -360,6 +361,11 @@ registerFilter(
{name: 'value', ...rangeQuantity}
]
)
registerFilter(
'results.properties.available_properties',
labelProperties,
noAggQuantity
)
registerFilter(
'results.properties.mechanical.energy_volume_curve',
labelMechanical,
......@@ -370,7 +376,7 @@ registerFilter(
)
registerFilter(
'results.properties.geometry_optimization',
labelWorkflow,
labelGeometryOptimization,
nestedQuantity,
[
{name: 'final_energy_difference', ...rangeQuantity},
......@@ -475,22 +481,6 @@ registerFilterOptions(
}
)
// Workflow properties: subset of metadata.quantities
registerFilterOptions(
'workflow',
labelWorkflow,
'quantities',
'Workflow',
'The workflows present in an entry.',
{
'workflow.geometry_optimization': {label: 'Geometry optimization'},
'workflow.molecular_dynamics': {label: 'Molecular dynamics'},
'workflow.elastic': {label: 'Elastic constants'},
'workflow.phonon': {label: 'Phonons'},
'workflow.single_point': {label: 'Single point'}
}
)
// The filter abbreviation mapping has to be done only after all filters have
// been registered.
const abbreviations = {}
......
......@@ -41,7 +41,6 @@ import FilterSubMenuAccess from './FilterSubMenuAccess'
import FilterSubMenuDataset from './FilterSubMenuDataset'
import FilterSubMenuIDs from './FilterSubMenuIDs'
import FilterSubMenuArchive from './FilterSubMenuArchive'
import FilterSubMenuWorkflow from './FilterSubMenuWorkflow'
import {
labelMaterial,
labelElements,
......@@ -62,11 +61,12 @@ import {
labelAccess,
labelSpectroscopy,
labelArchive,
labelWorkflow
labelGeometryOptimization
} from '../FilterRegistry'
import { useSearchContext } from '../SearchContext'
import InputCheckbox from '../input/InputCheckbox'
import { delay } from '../../../utils'
import FilterSubMenuGeometryOptimization from './FilterSubMenuGeometryOptimization'
/**
* Swipable menu that shows the available filters on the left side of the
......@@ -117,7 +117,7 @@ const FilterMainMenu = React.memo(({
<FilterMenuItem value={labelVibrational} depth={1}/>
<FilterMenuItem value={labelMechanical} depth={1}/>
<FilterMenuItem value={labelSpectroscopy} depth={1}/>
<FilterMenuItem value={labelWorkflow} depth={1}/>
<FilterMenuItem value={labelGeometryOptimization} depth={1}/>
<FilterMenuItem value={labelAuthor} depth={0}/>
<FilterMenuItem value={labelDataset} depth={0}/>
<FilterMenuItem value={labelAccess} depth={0}/>
......@@ -148,7 +148,7 @@ const FilterMainMenu = React.memo(({
<FilterSubMenuVibrational value={labelVibrational}/>
<FilterSubMenuMechanical value={labelMechanical}/>
<FilterSubMenuSpectroscopy value={labelSpectroscopy}/>
<FilterSubMenuWorkflow value={labelWorkflow}/>
<FilterSubMenuGeometryOptimization value={labelGeometryOptimization}/>
<FilterSubMenuAuthor value={labelAuthor}/>
<FilterSubMenuDataset value={labelDataset}/>
<FilterSubMenuAccess value={labelAccess}/>
......
......@@ -34,7 +34,6 @@ const FilterSubMenuDFT = React.memo(({
actions={<InputCheckboxValue
quantity="results.method.method_name"
value="DFT"
label=""
description="Search DFT entries"
/>}
{...rest}
......
......@@ -38,7 +38,6 @@ const FilterSubMenuEELS = React.memo(({
actions={<InputCheckboxValue
quantity="results.method.method_name"
value="EELS"
label=""
description="Search EELS entries"
/>}
{...rest}>
......
......@@ -34,7 +34,6 @@ const FilterSubMenuGW = React.memo(({
actions={<InputCheckboxValue
quantity="results.method.method_name"
value="GW"
label=""
description="Search GW entries"
/>}
{...rest}>
......
......@@ -18,29 +18,29 @@
import React, { useContext } from 'react'
import PropTypes from 'prop-types'
import { FilterSubMenu, filterMenuContext } from './FilterMenu'
import { InputGrid, InputGridItem } from '../input/InputGrid'
import InputSlider from '../input/InputSlider'
import InputSection from '../input/InputSection'
import InputField from '../input/InputField'
import { InputCheckboxValue } from '../input/InputCheckbox'
import { InputGrid, InputGridItem } from '../input/InputGrid'
import { useUnits } from '../../../units'
const FilterSubMenuWorkflow = React.memo(({
const FilterSubMenuGeometryOptimization = React.memo(({
value,
...rest
}) => {
const units = useUnits()
const {selected} = useContext(filterMenuContext)
const visible = value === selected
const units = useUnits()
return <FilterSubMenu value={value} {...rest}>
return <FilterSubMenu
value={value}
actions={<InputCheckboxValue
quantity="results.properties.available_properties"
value="geometry_optimization"
description="Search entries with geometry optimization results"
/>}
{...rest}>
<InputGrid>
<InputGridItem xs={12}>
<InputField
quantity="workflow"
visible={visible}
disableSearch
/>
</InputGridItem>
<InputGridItem xs={12}>
<InputSection
section="results.properties.geometry_optimization"
......@@ -66,8 +66,8 @@ const FilterSubMenuWorkflow = React.memo(({
</InputGrid>
</FilterSubMenu>
})
FilterSubMenuWorkflow.propTypes = {
FilterSubMenuGeometryOptimization.propTypes = {
value: PropTypes.string
}
export default FilterSubMenuWorkflow
export default FilterSubMenuGeometryOptimization
......@@ -25,7 +25,7 @@ import { InputGrid, InputGridItem } from '../input/InputGrid'
import { Quantity, useUnits } from '../../../units'
const step = new Quantity(10, 'gigapascal')
const FilterSubMenuElectronic = React.memo(({
const FilterSubMenuMechanical = React.memo(({
value,
...rest
}) => {
......@@ -94,8 +94,8 @@ const FilterSubMenuElectronic = React.memo(({
</InputGrid>
</FilterSubMenu>
})
FilterSubMenuElectronic.propTypes = {
FilterSubMenuMechanical.propTypes = {
value: PropTypes.string
}
export default FilterSubMenuElectronic
export default FilterSubMenuMechanical
......@@ -38,6 +38,14 @@ const FilterSubMenuMethod = React.memo(({
disableSearch
/>
</InputGridItem>
<InputGridItem xs={12}>
<InputField
quantity="results.method.workflow_name"
visible={visible}
xs={12}
disableSearch
/>
</InputGridItem>
</InputGrid>
</FilterSubMenu>
})
......
......@@ -2,7 +2,7 @@ import numpy as np # pylint: disable=unused-import
import typing # pylint: disable=unused-import
from nptyping import NDArray
from nomad.metainfo import ( # pylint: disable=unused-import
MSection, Quantity, Section, SubSection, SectionProxy,
MSection, MEnum, Quantity, Section, SubSection, SectionProxy,
Reference, derived)
from nomad.datamodel.metainfo.simulation.calculation import Calculation
from nomad.datamodel.metainfo.simulation.run import Run
......@@ -1463,13 +1463,25 @@ class Workflow(MSection):
validate=False)
type = Quantity(
type=str,
shape=[],
description='''
The type of calculation workflow. Can be one of the following
single_point, geometry_optimization, elastic, phonon, molecular_dynamics,
debye_model, equation_of_state, nudged_elastic_band, adsorption, raman,
thermodyanamics, magnetic_ordering
type=MEnum([
"single_point",
"geometry_optimization",
"phonon",
"elastic",
"molecular_dynamics",
"debye_model",
"equation_of_state",
"nudged_elastic_band",
"convex_hull",
"adsorption",
"magnetic_ordering",
"raman",
"interface",
"thermodynamics"
]),
shape=[],
description='''
The workflow type.
''')
initial_structure = Quantity(
......
......@@ -25,7 +25,7 @@ from ase.formula import Formula
from nomad import config
from nomad import atomutils
from nomad.datamodel.metainfo.measurements import Spectrum
from nomad.datamodel.metainfo.workflow import EquationOfState, EOSFit
from nomad.datamodel.metainfo.workflow import EquationOfState, EOSFit, Workflow
from nomad.metainfo.elasticsearch_extension import (
Elasticsearch,
material_type,
......@@ -56,7 +56,7 @@ from nomad.datamodel.metainfo.simulation.method import (
BasisSet, Scf, Electronic, Smearing, GW as GWMethod
) # noqa
from nomad.datamodel.metainfo.workflow import (
GeometryOptimization, Phonon, Elastic, Thermodynamics
GeometryOptimization as MGeometryOptimization, Thermodynamics
) # noqa
......@@ -814,56 +814,6 @@ class GW(MSection):
)
class GeometryOptimizationMethod(MSection):
m_def = Section(
description='''
Geometry optimization methodology. This methodology applies to the
properties presented in results.properties.geometry_optimization.
''',
)
type = GeometryOptimization.type.m_copy()
convergence_tolerance_energy_difference = GeometryOptimization.convergence_tolerance_energy_difference.m_copy()
convergence_tolerance_energy_difference.m_annotations['elasticsearch'] = Elasticsearch(material_entry_type)
convergence_tolerance_force_maximum = GeometryOptimization.convergence_tolerance_force_maximum.m_copy()
convergence_tolerance_force_maximum.m_annotations['elasticsearch'] = Elasticsearch(material_entry_type)
class MolecularDynamicsMethod(MSection):
m_def = Section(
description='''
Molecular dynamics methodology. This methodology applies to the
properties presented in results.properties.molecular_dynamics.
''',
)
class VibrationalMethod(MSection):
m_def = Section(
description='''
Vibrational properties methodology. This methodology applies to the
properties presented in results.properties.vibrational.
''',
)
force_calculator = Phonon.force_calculator.m_copy()
mesh_density = Phonon.mesh_density.m_copy()
random_displacements = Phonon.random_displacements.m_copy()
with_non_analytic_correction = Phonon.with_non_analytic_correction.m_copy()
class ElasticMethod(MSection):
m_def = Section(
description='''
Elastic properties methodology. This methodology applies to the
properties presented in results.properties.elastic.
''',
)
energy_stress_calculator = Elastic.energy_stress_calculator.m_copy()
elastic_calculation_method = Elastic.calculation_method.m_copy()
elastic_constants_order = Elastic.elastic_constants_order.m_copy()
fitting_error_maximum = Elastic.fitting_error_maximum.m_copy()
strain_maximum = Elastic.strain_maximum.m_copy()
class QuantumCircuit(MSection):
processors = Quantity(type=str, shape=['0..*'])
number_of_registers = Quantity(type=int)
......@@ -906,10 +856,6 @@ class Simulation(MSection):
)
dft = SubSection(sub_section=DFT.m_def, repeats=False)
gw = SubSection(sub_section=GW.m_def, repeats=False)
geometry_optimization = SubSection(sub_section=GeometryOptimizationMethod.m_def, repeats=False)
molecular_dynamics = SubSection(sub_section=MolecularDynamicsMethod.m_def, repeats=False)
vibrational = SubSection(sub_section=VibrationalMethod.m_def, repeats=False)
elastic = SubSection(sub_section=ElasticMethod.m_def, repeats=False)
quantum_cms = SubSection(sub_section=QuantumCMS.m_def, repeats=False)
......@@ -958,6 +904,13 @@ class Method(MSection):
Elasticsearch(suggestion='default')
],
)
workflow_name = Workflow.type.m_copy()
workflow_name.shape = ['*']
workflow_name.m_annotations['elasticsearch'] = [
Elasticsearch(material_entry_type),
Elasticsearch(suggestion='default')
]
simulation = SubSection(sub_section=Simulation.m_def, repeats=False)
......@@ -1136,55 +1089,6 @@ class EnergyFreeHelmholtz(MSection):
)
class GeometryOptimizationProperties(MSection):
m_def = Section(
description='''
Properties from a geometry optimization.
''',
)
trajectory = Quantity(
type=Calculation,
shape=['0..*'],
description='''
List of references to each section_single_configuration_calculation in
the optimization trajectory.
''',
)
energies = Quantity(
type=GeometryOptimization.energies,
description='''
List of energy_total values gathered from the single configuration
calculations that are a part of the optimization trajectory.
''',
)
structure_optimized = SubSection(
sub_section=StructureOptimized.m_def,
repeats=False,
)
final_force_maximum = GeometryOptimization.final_force_maximum.m_copy()
final_force_maximum.m_annotations['elasticsearch'] = Elasticsearch(material_entry_type)
final_energy_difference = GeometryOptimization.final_energy_difference.m_copy()
final_energy_difference.m_annotations['elasticsearch'] = Elasticsearch(material_entry_type)
final_displacement_maximum = GeometryOptimization.final_displacement_maximum.m_copy()
final_displacement_maximum.m_annotations['elasticsearch'] = Elasticsearch(material_entry_type)
class MolecularDynamicsProperties(MSection):
m_def = Section(
description='''
Properties from molecular_dynamics.
''',
)
trajectory = Quantity(
type=Calculation,
shape=['0..*'],
description='''
List of references to each section_single_configuration_calculation in
the molecular dynamics trajectory.
''',
)
class VibrationalProperties(MSection):
m_def = Section(
description='''
......@@ -1285,6 +1189,44 @@ class ShearModulus(MSection):
)
class GeometryOptimization(MSection):
m_def = Section(
description='''
Geometry optimization results and settings.
''',
)
trajectory = Quantity(
type=Calculation,
shape=['0..*'],
description='''
List of references to each section_single_configuration_calculation in
the optimization trajectory.
''',
)
energies = Quantity(
type=MGeometryOptimization.energies,
description='''
List of energy_total values gathered from the single configuration
calculations that are a part of the optimization trajectory.
''',
)
structure_optimized = SubSection(
sub_section=StructureOptimized.m_def,
repeats=False,
)
type = MGeometryOptimization.type.m_copy()
convergence_tolerance_energy_difference = MGeometryOptimization.convergence_tolerance_energy_difference.m_copy()
convergence_tolerance_energy_difference.m_annotations['elasticsearch'] = Elasticsearch(material_entry_type)
convergence_tolerance_force_maximum = MGeometryOptimization.convergence_tolerance_force_maximum.m_copy()
convergence_tolerance_force_maximum.m_annotations['elasticsearch'] = Elasticsearch(material_entry_type)
final_force_maximum = MGeometryOptimization.final_force_maximum.m_copy()
final_force_maximum.m_annotations['elasticsearch'] = Elasticsearch(material_entry_type)
final_energy_difference = MGeometryOptimization.final_energy_difference.m_copy()
final_energy_difference.m_annotations['elasticsearch'] = Elasticsearch(material_entry_type)
final_displacement_maximum = MGeometryOptimization.final_displacement_maximum.m_copy()
final_displacement_maximum.m_annotations['elasticsearch'] = Elasticsearch(material_entry_type)
class MechanicalProperties(MSection):
m_def = Section(
description='''
......@@ -1331,12 +1273,11 @@ class Properties(MSection):
'''
)
structures = SubSection(sub_section=Structures.m_def, repeats=False)
geometry_optimization = SubSection(sub_section=GeometryOptimizationProperties.m_def, repeats=False)
molecular_dynamics = SubSection(sub_section=MolecularDynamicsProperties.m_def, repeats=False)
vibrational = SubSection(sub_section=VibrationalProperties.m_def, repeats=False)
electronic = SubSection(sub_section=ElectronicProperties.m_def, repeats=False)
mechanical = SubSection(sub_section=MechanicalProperties.m_def, repeats=False)
spectroscopy = SubSection(sub_section=SpectroscopyProperties.m_def, repeats=False)
geometry_optimization = SubSection(sub_section=GeometryOptimization.m_def, repeats=False)
n_calculations = Quantity(
type=int,
......@@ -1348,7 +1289,7 @@ class Properties(MSection):
available_properties = Quantity(
type=str,
shape=['0..*'],
description='List of all properties which are present in this section_results',
description='Subset of the property names that are present in this entry.',
a_elasticsearch=Elasticsearch(material_entry_type),
)
......
......@@ -104,6 +104,7 @@ class MethodNormalizer():
self.method_name = method_name
settings_basis_set = get_basis_set(self.entry_archive, self.repr_method, self.repr_system, self.logger)
functional_long_name = self.functional_long_name()
method.workflow_name = self.workflow_name()
if method_name == "GW":
method.method_name = "GW"
......@@ -139,6 +140,12 @@ class MethodNormalizer():
method.simulation = simulation
return method
def workflow_name(self):
workflow_name = None
if self.entry_archive.workflow:
workflow_name = set(filter(lambda x: x, map(lambda x: x.type, self.entry_archive.workflow)))
return workflow_name
def method_id_dft(self, settings_basis_set, functional_long_name: str):
"""Creates a method id for DFT calculations if all required data is
present.
......
......@@ -27,6 +27,7 @@ import matid.geometry
from nomad import config
from nomad.units import ureg
from nomad import atomutils
from nomad.utils import deep_get
from nomad.normalizing.normalizer import Normalizer
from nomad.normalizing.method import MethodNormalizer
from nomad.normalizing.material import MaterialNormalizer
......@@ -37,8 +38,7 @@ from nomad.datamodel.results import (
Results,
Material,
Method,
GeometryOptimizationProperties,
GeometryOptimizationMethod,
GeometryOptimization,
Properties,
Structures,
Structure,
......@@ -102,24 +102,26 @@ class ResultsNormalizer(Normalizer):
for measurement in self.entry_archive.measurement:
self.normalize_measurement(measurement, logger=self.logger)
# Add the present quantities. The full path will be saved for each
# property, and if the leaf quantity/section name is unambiguous, also
# it will be saved. Each repeating section will be investigated as well
# to find all present properties.
available_properties = set()
for section, m_def, _ in results.properties.m_traverse():
parent_path = section.m_path()
path = "{}/{}".format(parent_path if parent_path != "/" else "", m_def.name)[20:]
parts = filter(lambda x: not isint(x), path.split("/"))
path = ".".join(parts)
available_properties.add(path)
shorthand_prop = list()
for prop in available_properties:
name = prop.rsplit(".", 1)[-1]
shorthand_prop.append(name)
u, c = np.unique(shorthand_prop, return_counts=True)
shorthand_prop = u[c == 1]
available_properties |= set([str(x) for x in shorthand_prop])
# Add the list of available_properties: it is a selected subset of the
# stored properties.
available_property_names = {
"properties.electronic.band_structure_electronic.band_gap": "electronic.band_structure_electronic.band_gap",
"properties.electronic.band_structure_electronic": "band_structure_electronic",
"properties.electronic.dos_electronic": "dos_electronic",
"properties.vibrational.dos_phonon": "dos_phonon",
"properties.vibrational.band_structure_phonon": "band_structure_phonon",
"properties.vibrational.energy_free_helmholtz": "energy_free_helmholtz",
"properties.vibrational.heat_capacity_constant_volume": "heat_capacity_constant_volume",
"properties.geometry_optimization": "geometry_optimization",
"properties.mechanical.bulk_modulus": "bulk_modulus",
"properties.mechanical.shear_modulus": "shear_modulus",
"properties.mechanical.energy_volume_curve": "energy_volume_curve",
"properties.spectroscopy.eels": "eels",
}
available_properties: List[str] = []
for path, shortcut in available_property_names.items():
if deep_get(results, *path.split(".")) is not None:
available_properties.append(shortcut)
results.properties.available_properties = sorted(available_properties)
def normalize_measurement(self, measurement, logger) -> None:
......@@ -191,7 +193,6 @@ class ResultsNormalizer(Normalizer):
logger
).material()
results.method = MethodNormalizer(self.entry_archive, repr_system, results.material, logger).method()
self.geometry_optimization(results.method, results.properties)
def species(self, labels: List[str], atomic_numbers: List[int], struct: Structure) -> None:
"""Given a list of species labels, creates the corresponding Species
......@@ -367,7 +368,7 @@ class ResultsNormalizer(Normalizer):
return None
def geometry_optimization(self, method: Method, properties: Properties) -> None:
def geometry_optimization(self) -> Union[GeometryOptimization, None]:
"""Populates both geometry optimization methodology and calculated
properties based on the first found geometry optimization workflow.
"""
......@@ -376,31 +377,25 @@ class ResultsNormalizer(Normalizer):
# Check validity
if workflow.type == "geometry_optimization" and workflow.calculations_ref:
# Method
geo_opt = GeometryOptimization()
geo_opt_wf = workflow.geometry_optimization
if geo_opt_wf is not None:
geo_opt_meth = GeometryOptimizationMethod()
geo_opt_meth.type = geo_opt_wf.type
geo_opt_meth.convergence_tolerance_energy_difference = geo_opt_wf.convergence_tolerance_energy_difference
geo_opt_meth.convergence_tolerance_force_maximum = geo_opt_wf.convergence_tolerance_force_maximum
method.simulation.geometry_optimization = geo_opt_meth
# Properties
geo_opt_prop = GeometryOptimizationProperties()
geo_opt_prop.trajectory = workflow.calculations_ref
geo_opt.trajectory = workflow.calculations_ref