From 595843c6e2240327807197931c273fbbfc2298ae Mon Sep 17 00:00:00 2001 From: jrudz Date: Wed, 3 Apr 2024 16:32:22 +0200 Subject: [PATCH] Outputs inherits PhysicalProperty --- src/nomad_simulations/outputs.py | 61 ++++---- src/nomad_simulations/outputs_JFR_temp.py | 168 ++++++++++++++-------- src/nomad_simulations/property.py | 28 ++-- 3 files changed, 151 insertions(+), 106 deletions(-) diff --git a/src/nomad_simulations/outputs.py b/src/nomad_simulations/outputs.py index 5033d122..af41a4af 100644 --- a/src/nomad_simulations/outputs.py +++ b/src/nomad_simulations/outputs.py @@ -24,9 +24,16 @@ from nomad.datamodel.data import ArchiveSection from nomad.metainfo import Quantity, SubSection, MEnum from nomad.datamodel.metainfo.annotations import ELNAnnotation +<<<<<<< HEAD +======= +from nomad.metainfo import Quantity, SubSection, SectionProxy, Reference +# ? are we planning to enforce a dependency on simulationworkflowschema? +from nomad.dependencies.simulationworkflowschema import SimulationWorkflow, MolecularDynamics +>>>>>>> 8399aa9 (Outputs inherits PhysicalProperty) from .model_system import ModelSystem from .numerical_settings import SelfConsistency +<<<<<<< HEAD from .physical_property import PhysicalProperty from .variables import Variables, Temperatures # ? delete these imports @@ -56,9 +63,12 @@ class ElectronicBandGap(PhysicalProperty): def normalize(self, archive, logger) -> None: super().normalize(archive, logger) +======= +from .property import PhysicalProperty +>>>>>>> 8399aa9 (Outputs inherits PhysicalProperty) -class Outputs(ArchiveSection): +class Outputs(PhysicalProperty): """ Output properties of a simulation. This base class can be used for inheritance in any of the output properties defined in this schema. @@ -156,53 +166,36 @@ def normalize(self, archive, logger) -> None: ) class MDOutputs(Outputs): +class WorkflowOutputs(Outputs): """ - This section contains the self-consistent (SCF) steps performed to converge an output property, - as well as the information if the output property `is_converged` or not, depending on the - settings in the `SelfConsistency` base class defined in `numerical_settings.py`. - - For simplicity, we contain the SCF steps of a simulation as part of the minimal workflow defined in NOMAD, - the `SinglePoint`, i.e., we do not split each SCF step in its own entry. Thus, each `SinglePoint` - `Simulation` entry in NOMAD contains the final output properties and all the SCF steps. + This section contains output properties that depend on a single system, but were + calculated as part of a workflow. Examples include geometry optimization and molecular dynamics. """ step = Quantity( type=np.int32, description=""" - Number of self-consistent steps to converge the output property. Note that the SCF steps belong to - the same minimal `Simulation` workflow entry which is known as `SinglePoint`. + The step number with respect to the workflow. """, ) - time = Quantity( - type=np.int32, - description=""" - Number of self-consistent steps to converge the output property. Note that the SCF steps belong to - the same minimal `Simulation` workflow entry which is known as `SinglePoint`. - """, - ) - - scf_step = SubSection( - sub_section=Outputs.m_def, - repeats=True, - description=""" - Self-consistent (SCF) steps performed for converging a given output property. Note that the SCF steps belong to - the same minimal `Simulation` workflow entry which is known as `SinglePoint`. - """, - ) - - ff_ref = Quantity( - type=SelfConsistency, + workflow_ref = Quantity( + type=SimulationWorkflow, description=""" Reference to the `SelfConsistency` section that defines the numerical settings to converge the output property. """, ) - md_ref = Quantity( - type=SelfConsistency, +class TrajectoryOutputs(WorkflowOutputs): + """ + This section contains output properties that depend on a single system, but were + calculated as part of a workflow. Examples include geometry optimization and molecular dynamics. + """ + + time = Quantity( + type=np.float64, description=""" - Reference to the `SelfConsistency` section that defines the numerical settings to converge the - output property. + The elapsed simulated physical time since the start of the trajectory. """, - ) + ) \ No newline at end of file diff --git a/src/nomad_simulations/outputs_JFR_temp.py b/src/nomad_simulations/outputs_JFR_temp.py index 64d9f54f..099b2fdb 100644 --- a/src/nomad_simulations/outputs_JFR_temp.py +++ b/src/nomad_simulations/outputs_JFR_temp.py @@ -43,8 +43,7 @@ from nomad.metainfo.metainfo import DirectQuantity, Dimension from nomad.datamodel.metainfo.annotations import ELNAnnotation -from .outputs import MDOutputs -from .property import BaseProperty +from .outputs import Outputs, SCFOutputs, WorkflowOutputs, TrajectoryOutputs from .atoms_state import AtomsState @@ -69,16 +68,6 @@ from .model_system import ModelSystem -class ScalarProperty(BaseProperty): - """ - Generic section containing the values and information for any scalar property. - """ - - def normalize(self, archive, logger) -> None: - super().normalize(archive, logger) - - # TODO check that all variable and bin quantities are None - class AtomicProperty(BaseProperty): """ Generic section containing the values and information reqarding an atomic quantity @@ -1000,7 +989,7 @@ class MultipolesEntry(Atomic): orbital_projected = SubSection(sub_section=MultipolesValues.m_def, repeats=True) -class Enthalpy(MDOutputs, ScalarProperty): +class Enthalpy(TrajectoryOutputs): """ Section containing the enthalpy (i.e. energy_total + pressure * volume.) of a (sub)system. """ @@ -1013,10 +1002,12 @@ class Enthalpy(MDOutputs, ScalarProperty): def normalize(self, archive, logger) -> None: super().normalize(archive, logger) self.value_unit = 'joule' + self.name = 'enthalpy' + self.is_scalar = True -class Entropy(ScalarProperty): +class Entropy(TrajectoryOutputs): """ - Section containing the entropy of a (sub)system. + Section containing the (scalar) entropy of a (sub)system. """ value = Quantity( @@ -1027,10 +1018,12 @@ class Entropy(ScalarProperty): def normalize(self, archive, logger) -> None: super().normalize(archive, logger) self.value_unit = 'joule / kelvin' + self.name = 'entropy' + self.is_scalar = True -class ChemicalPotential(ScalarProperty): +class ChemicalPotential(TrajectoryOutputs): """ - Section containing the chemical potential of a (sub)system. + Section containing the (scalar) chemical potential of a (sub)system. """ value = Quantity( @@ -1041,10 +1034,11 @@ class ChemicalPotential(ScalarProperty): def normalize(self, archive, logger) -> None: super().normalize(archive, logger) self.value_unit = 'joule' - -class KineticEnergy(ScalarProperty): + self.name = 'chemical_potential' + self.is_scalar = True +class KineticEnergy(TrajectoryOutputs): """ - Section containing the kinetic energy of a (sub)system. + Section containing the (scalar) kinetic energy of a (sub)system. """ value = Quantity( @@ -1055,52 +1049,94 @@ class KineticEnergy(ScalarProperty): def normalize(self, archive, logger) -> None: super().normalize(archive, logger) self.value_unit = 'joule' + self.name = 'kinetic_energy' + self.is_scalar = True - potential_energy = Quantity( - type=np.dtype(np.float64), - shape=[], - unit='joule', - description=""" - Value of the potential energy. - """, - ) +class PotentialEnergy(TrajectoryOutputs): + """ + Section containing the (scalar) potential energy of a (sub)system. + """ - internal_energy = Quantity( - type=np.dtype(np.float64), - shape=[], + value = Quantity( + type=np.float64, unit='joule', - description=""" - Value of the internal energy. - """, ) - vibrational_free_energy_at_constant_volume = Quantity( - type=np.dtype(np.float64), - shape=[], - unit='joule', - description=""" - Value of the vibrational free energy per cell unit at constant volume. - """, + def normalize(self, archive, logger) -> None: + super().normalize(archive, logger) + self.value_unit = 'joule' + self.name = 'potential_energy' + self.is_scalar = True + +class Pressure(TrajectoryOutputs): + """ + Section containing the (scalar) pressure of a (sub)system. + """ + + value = Quantity( + type=np.float64, + unit='pascal', ) - pressure = Quantity( + def normalize(self, archive, logger) -> None: + super().normalize(archive, logger) + self.value_unit = 'pascal' + self.name = 'pressure' + self.is_scalar = True + +class PressureTensor(TrajectoryOutputs): + """ + Section containing the pressure in terms of the x, y, z components of the (sub)system simulation cell. + Typically calculated as the difference between the kinetic energy and the virial. + """ + + value = Quantity( type=np.dtype(np.float64), - shape=[], + shape=[3, 3], unit='pascal', - description=""" - Value of the pressure of the system. - """, ) - temperature = Quantity( + def normalize(self, archive, logger) -> None: + super().normalize(archive, logger) + self.value_unit = 'pascal' + self.name = 'pressure_tensor' + self.variables = [['xx', 'xy', 'xz'], ['yx', 'yy', 'yz'], ['zx', 'zy', 'zz']] + +class VirialTensor(TrajectoryOutputs): + """ + Section containing the virial in terms of the x, y, z components of the (sub)system simulation cell. + Typically calculated as the cross product between positions and forces. + """ + + value = Quantity( type=np.dtype(np.float64), - shape=[], + shape=[3, 3], + unit='joule', + ) + + def normalize(self, archive, logger) -> None: + super().normalize(archive, logger) + self.value_unit = 'joule' + self.name = 'virial_tensor' + self.variables = [['xx', 'xy', 'xz'], ['yx', 'yy', 'yz'], ['zx', 'zy', 'zz']] + +class Temperature(TrajectoryOutputs): + """ + Section containing the (scalar) temperature of a (sub)system. + """ + + value = Quantity( + type=np.float64, unit='kelvin', - description=""" - Value of the temperature of the system at which the properties are calculated. - """, ) + def normalize(self, archive, logger) -> None: + super().normalize(archive, logger) + self.value_unit = 'kelvin' + self.name = 'temperature' + self.is_scalar = True + + volume = Quantity( type=np.dtype(np.float64), shape=[], @@ -1128,14 +1164,28 @@ def normalize(self, archive, logger) -> None: """, ) - time_step = Quantity( - type=int, + + internal_energy = Quantity( + type=np.dtype(np.float64), shape=[], + unit='joule', description=""" - The number of time steps with respect to the start of the calculation. + Value of the internal energy. """, ) + vibrational_free_energy_at_constant_volume = Quantity( + type=np.dtype(np.float64), + shape=[], + unit='joule', + description=""" + Value of the vibrational free energy per cell unit at constant volume. + """, + ) + + + + class RadiusOfGyrationValues(AtomicGroupValues): """ @@ -1377,15 +1427,7 @@ class BaseCalculation(ArchiveSection): """, ) - virial_tensor = Quantity( - type=np.dtype(np.float64), - shape=[3, 3], - unit='joule', - description=""" - Value of the virial in terms of the x, y, z components of the simulation cell. - Typically calculated as the cross product between positions and forces. - """, - ) + enthalpy = Quantity( type=np.dtype(np.float64), diff --git a/src/nomad_simulations/property.py b/src/nomad_simulations/property.py index e0ec8b17..9b4c7dc7 100644 --- a/src/nomad_simulations/property.py +++ b/src/nomad_simulations/property.py @@ -46,7 +46,7 @@ from .model_system import ModelSystem from .outputs import Outputs -class BaseErrors(ArchiveSection): +class Errors(ArchiveSection): """ A base section used to define errors. """ @@ -71,7 +71,7 @@ def normalize(self, archive, logger) -> None: super().normalize(archive, logger) -class BaseSmoothing(ArchiveSection): +class Smoothing(ArchiveSection): """ A base section used to define data smoothing procedures. """ @@ -84,13 +84,13 @@ class BaseSmoothing(ArchiveSection): a_eln=ELNAnnotation(component='StringEditQuantity'), ) - parameters = SubSection(sub_section=BaseErrors.m_def, repeats=True) + parameters = SubSection(sub_section=ParameterEntry.m_def, repeats=True) def normalize(self, archive, logger) -> None: super().normalize(archive, logger) -class BaseProperty(ArchiveSection): +class PhysicalPropery(ArchiveSection): """ A base section used to define properties. """ @@ -193,16 +193,26 @@ def __init__(self, *args, **kwargs): # TODO Add value_per_particle? - system_ref = Quantity( - type=Reference(ModelSystem.m_def), - shape=[1], + is_scalar = Quantity( + type=bool, + default=False, description=""" - References to the (sub)system section containing the atoms relevant for this property. + Flag indicating whether the output property is a scalar. If yes, variable and bin quantities + are ensured to not be populated. """, ) - errors = SubSection(sub_section=BaseErrors.m_def, repeats=True) + errors = SubSection(sub_section=Errors.m_def, repeats=True) + smoothing = SubSection(sub_section=Smoothing.m_def, repeats=True) def normalize(self, archive, logger) -> None: super().normalize(archive, logger) + if self.is_scalar: + if self.variables is not None: + self.variables = None + # TODO throw warning/error + if self.bins is not None: + self.bins = None + # TODO throw warning/error + # TODO ...