diff --git a/src/nomad_simulations/schema_packages/outputs.py b/src/nomad_simulations/schema_packages/outputs.py index 462d3a12..3c7c9a1e 100644 --- a/src/nomad_simulations/schema_packages/outputs.py +++ b/src/nomad_simulations/schema_packages/outputs.py @@ -16,6 +16,7 @@ # limitations under the License. # +import numpy as np from typing import TYPE_CHECKING, List, Optional from nomad.datamodel.data import ArchiveSection @@ -43,6 +44,11 @@ HoppingMatrix, Permittivity, XASSpectrum, + TotalEnergy, + KineticEnergy, + PotentialEnergy, + TotalForce, + Temperature, ) @@ -70,7 +76,8 @@ class Outputs(ArchiveSection): model_method_ref = Quantity( type=ModelMethod, description=""" - Reference to the `ModelMethod` section in which the output physical properties were calculated. + Reference to the `ModelMethod` section containing the details of the mathematical + model with which the output physical properties were calculated. """, a_eln=ELNAnnotation(component='ReferenceEditQuantity'), ) @@ -111,6 +118,16 @@ class Outputs(ArchiveSection): xas_spectra = SubSection(sub_section=XASSpectrum.m_def, repeats=True) + total_energy = SubSection(sub_section=TotalEnergy.m_def, repeats=True) + + kinetic_energy = SubSection(sub_section=KineticEnergy.m_def, repeats=True) + + potential_energy = SubSection(sub_section=PotentialEnergy.m_def, repeats=True) + + total_force = SubSection(sub_section=TotalForce.m_def, repeats=True) + + temperature = SubSection(sub_section=Temperature.m_def, repeats=True) + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # @@ -310,3 +327,50 @@ def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: physical_property=phys_property, logger=logger, ) + + +class WorkflowOutputs(Outputs): + """ + This section contains output properties that depend on a single system, but were + calculated as part of a workflow (e.g., the energies from a geometry optimization), + and thus may include step information. + """ + + step = Quantity( + type=np.int32, + description=""" + The step number with respect to the workflow. + """, + ) + + # TODO add this in when we link to nomad-simulations-workflow schema + # ? check potential circular imports problems when the nomad-simulations-workflow schema is transferred here + # workflow_ref = Quantity( + # type=SimulationWorkflow, + # description=""" + # Reference to the `SelfConsistency` section that defines the numerical settings to converge the + # output property. + # """, + # ) + + def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: + super().normalize(archive, logger) + + +class TrajectoryOutputs(WorkflowOutputs): + """ + This section contains output properties that depend on a single system, but were + calculated as part of a trajectory (e.g., temperatures from a molecular dynamics + simulation), and thus may include time information. + """ + + time = Quantity( + type=np.float64, + unit='ps', + description=""" + The elapsed simulated physical time since the start of the trajectory. + """, + ) + + def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: + super().normalize(archive, logger) diff --git a/src/nomad_simulations/schema_packages/physical_property.py b/src/nomad_simulations/schema_packages/physical_property.py index 92841b97..5d1ab622 100644 --- a/src/nomad_simulations/schema_packages/physical_property.py +++ b/src/nomad_simulations/schema_packages/physical_property.py @@ -24,6 +24,7 @@ from nomad import utils from nomad.datamodel.data import ArchiveSection from nomad.datamodel.metainfo.basesections import Entity +from nomad.datamodel.metainfo.annotations import ELNAnnotation from nomad.metainfo import ( URL, MEnum, @@ -41,6 +42,7 @@ from nomad_simulations.schema_packages.numerical_settings import SelfConsistency from nomad_simulations.schema_packages.variables import Variables +from nomad_simulations.schema_packages.model_method import BaseModelMethod # We add `logger` for the `validate_quantity_wrt_value` decorator logger = utils.get_logger(__name__) @@ -324,3 +326,27 @@ def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: # Resolve if the physical property `is_derived` or not from another physical property. self.is_derived = self._is_derived() + + +class PropertyContribution(PhysicalProperty): + """ + Abstract physical property section linking a property contribution to a contribution + from some method. + + Abstract class for incorporating specific contributions of a physical property, while + linking this contribution to a specific component (of class `BaseModelMethod`) of the + over `ModelMethod` using the `model_method_ref` quantity. + """ + + model_method_ref = Quantity( + type=BaseModelMethod, + description=""" + Reference to the `ModelMethod` section to which the property is linked to. + """, + a_eln=ELNAnnotation(component='ReferenceEditQuantity'), + ) + + def normalize(self, archive, logger) -> None: + super().normalize(archive, logger) + if not self.name: + self.name = self.get('model_method_ref').get('name') diff --git a/src/nomad_simulations/schema_packages/properties/__init__.py b/src/nomad_simulations/schema_packages/properties/__init__.py index 78e5fe41..a4042294 100644 --- a/src/nomad_simulations/schema_packages/properties/__init__.py +++ b/src/nomad_simulations/schema_packages/properties/__init__.py @@ -18,7 +18,31 @@ from .band_gap import ElectronicBandGap from .band_structure import ElectronicBandStructure, ElectronicEigenvalues -from .energies import ChemicalPotential, FermiLevel +from .energies import ( + FermiLevel, + EnergyContribution, + TotalEnergy, + KineticEnergy, + PotentialEnergy, +) +from .forces import BaseForce, ForceContribution, TotalForce +from .thermodynamics import ( + Pressure, + Volume, + Temperature, + Heat, + Work, + InternalEnergy, + Enthalpy, + Entropy, + GibbsFreeEnergy, + HelmholtzFreeEnergy, + ChemicalPotential, + HeatCapacity, + VirialTensor, + MassDensity, + Hessian, +) from .fermi_surface import FermiSurface from .hopping_matrix import CrystalFieldSplitting, HoppingMatrix from .permittivity import Permittivity diff --git a/src/nomad_simulations/schema_packages/properties/energies.py b/src/nomad_simulations/schema_packages/properties/energies.py index 732d47be..d31f3f09 100644 --- a/src/nomad_simulations/schema_packages/properties/energies.py +++ b/src/nomad_simulations/schema_packages/properties/energies.py @@ -20,33 +20,76 @@ import numpy as np -from nomad.metainfo import Quantity +from nomad.metainfo import Quantity, Section, Context, SubSection if TYPE_CHECKING: from nomad.metainfo import Section, Context from nomad.datamodel.datamodel import EntryArchive from structlog.stdlib import BoundLogger -from nomad_simulations.schema_packages.physical_property import PhysicalProperty +from nomad_simulations.schema_packages.physical_property import ( + PhysicalProperty, + PropertyContribution, +) +################## +# Abstract classes +################## -class FermiLevel(PhysicalProperty): + +class BaseEnergy(PhysicalProperty): """ - Energy required to add or extract a charge from a material at zero temperature. It can be also defined as the chemical potential at zero temperature. + Abstract class used to define a common `value` quantity with the appropriate units + for different types of energies, which avoids repeating the definitions for each + energy class. """ - # ! implement `iri` and `rank` as part of `m_def = Section()` - - iri = 'http://fairmat-nfdi.eu/taxonomy/FermiLevel' - value = Quantity( type=np.float64, unit='joule', description=""" - The value of the Fermi level. """, ) + def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: + super().normalize(archive, logger) + + +class EnergyContribution(BaseEnergy, PropertyContribution): + """ + Abstract class for incorporating specific energy contributions to the `TotalEnergy`. + The inheritance from `PropertyContribution` allows to link this contribution to a + specific component (of class `BaseModelMethod`) of the over `ModelMethod` using the + `model_method_ref` quantity. + + For example, for a force field calculation, the `model_method_ref` may point to a + particular potential type (e.g., a Lennard-Jones potential between atom types X and Y), + while for a DFT calculation, it may point to a particular electronic interaction term + (e.g., 'XC' for the exchange-correlation term, or 'Hartree' for the Hartree term). + Then, the contribution will be named according to this model component and the `value` + quantity will contain the energy contribution from this component evaluated over all + relevant atoms or electrons or as a function of them. + """ + + # TODO address the dual parent normalization explicity + def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: + super().normalize(archive, logger) + + +#################################### +# List of specific energy properties +#################################### + + +class FermiLevel(BaseEnergy): + """ + Energy required to add or extract a charge from a material at zero temperature. It can be also defined as the chemical potential at zero temperature. + """ + + # ! implement `iri` and `rank` as part of `m_def = Section()` + + iri = 'http://fairmat-nfdi.eu/taxonomy/FermiLevel' + def __init__( self, m_def: 'Section' = None, m_context: 'Context' = None, **kwargs ) -> None: @@ -58,29 +101,41 @@ def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: super().normalize(archive, logger) -class ChemicalPotential(PhysicalProperty): +#! The only issue with this structure is that total energy will never be a sum of its contributions, +#! since kinetic energy lives separately, but I think maybe this is ok? +class TotalEnergy(BaseEnergy): """ - Free energy cost of adding or extracting a particle from a thermodynamic system. + The total energy of a system. `contributions` specify individual energetic + contributions to the `TotalEnergy`. """ - # ! implement `iri` and `rank` as part of `m_def = Section()` - - iri = 'http://fairmat-nfdi.eu/taxonomy/ChemicalPotential' - - value = Quantity( - type=np.float64, - unit='joule', - description=""" - The value of the chemical potential. - """, - ) + # ? add a generic contributions quantity to PhysicalProperty + contributions = SubSection(sub_section=EnergyContribution.m_def, repeats=True) def __init__( self, m_def: 'Section' = None, m_context: 'Context' = None, **kwargs ) -> None: super().__init__(m_def, m_context, **kwargs) - self.rank = [] self.name = self.m_def.name def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: super().normalize(archive, logger) + + +# ? Separate quantities for nuclear and electronic KEs? +class KineticEnergy(BaseEnergy): + """ + Physical property section describing the kinetic energy of a (sub)system. + """ + + def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: + super().normalize(archive, logger) + + +class PotentialEnergy(BaseEnergy): + """ + Physical property section describing the potential energy of a (sub)system. + """ + + def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: + super().normalize(archive, logger) diff --git a/src/nomad_simulations/schema_packages/properties/forces.py b/src/nomad_simulations/schema_packages/properties/forces.py new file mode 100644 index 00000000..0ce7b945 --- /dev/null +++ b/src/nomad_simulations/schema_packages/properties/forces.py @@ -0,0 +1,99 @@ +# +# Copyright The NOMAD Authors. +# +# This file is part of NOMAD. +# See https://nomad-lab.eu for further info. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from typing import TYPE_CHECKING + +import numpy as np + +from nomad.metainfo import Quantity, Section, Context, SubSection + +if TYPE_CHECKING: + from nomad.metainfo import Section, Context + from nomad.datamodel.datamodel import EntryArchive + from structlog.stdlib import BoundLogger + +from nomad_simulations.schema_packages.physical_property import ( + PhysicalProperty, + PropertyContribution, +) + +################## +# Abstract classes +################## + + +class BaseForce(PhysicalProperty): + """ + Abstract class used to define a common `value` quantity with the appropriate units + for different types of forces, which avoids repeating the definitions for each + force class. + """ + + value = Quantity( + type=np.dtype(np.float64), + unit='newton', + description=""" + """, + ) + + def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: + super().normalize(archive, logger) + + +class ForceContribution(BaseForce, PropertyContribution): + """ + Abstract class for incorporating specific force contributions to the `TotalForce`. + The inheritance from `PropertyContribution` allows to link this contribution to a + specific component (of class `BaseModelMethod`) of the over `ModelMethod` using the + `model_method_ref` quantity. + + For example, for a force field calculation, the `model_method_ref` may point to a + particular potential type (e.g., a Lennard-Jones potential between atom types X and Y), + while for a DFT calculation, it may point to a particular electronic interaction term + (e.g., 'XC' for the exchange-correlation term, or 'Hartree' for the Hartree term). + Then, the contribution will be named according to this model component and the `value` + quantity will contain the force contribution from this component evaluated over all + relevant atoms or electrons or as a function of them. + """ + + def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: + super().normalize(archive, logger) + + +################################### +# List of specific force properties +################################### + + +class TotalForce(BaseForce): + """ + The total force on a system. `contributions` specify individual force + contributions to the `TotalForce`. + """ + + contributions = SubSection(sub_section=ForceContribution.m_def, repeats=True) + + def __init__( + self, m_def: 'Section' = None, m_context: 'Context' = None, **kwargs + ) -> None: + super().__init__(m_def, m_context, **kwargs) + self.name = self.m_def.name + + def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: + super().normalize(archive, logger) diff --git a/src/nomad_simulations/schema_packages/properties/thermodynamics.py b/src/nomad_simulations/schema_packages/properties/thermodynamics.py new file mode 100644 index 00000000..5d0c4c03 --- /dev/null +++ b/src/nomad_simulations/schema_packages/properties/thermodynamics.py @@ -0,0 +1,278 @@ +# +# Copyright The NOMAD Authors. +# +# This file is part of NOMAD. See https://nomad-lab.eu for further info. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from typing import TYPE_CHECKING + +import numpy as np + +from nomad.metainfo import Quantity + +if TYPE_CHECKING: + from nomad.metainfo import Section, Context + from nomad.datamodel.datamodel import EntryArchive + from structlog.stdlib import BoundLogger + +from nomad_simulations.schema_packages.physical_property import PhysicalProperty +from nomad_simulations.schema_packages.properties.energies import BaseEnergy + +###################################### +# fundamental thermodynamic properties +###################################### + + +class Pressure(PhysicalProperty): + """ + The force exerted per unit area by gas particles as they collide with the walls of + their container. + """ + + # iri = 'http://fairmat-nfdi.eu/taxonomy/Pressure' # ! Does not yet exist in taxonomy + + value = Quantity( + type=np.float64, + unit='pascal', + description=""" + """, + ) + + def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: + super().normalize(archive, logger) + + +class Volume(PhysicalProperty): + """ + the amount of three-dimensional space that a substance or material occupies. + """ + + #! Above description suggested for taxonomy + # TODO check back on definition after first taxonomy version + + iri = 'http://fairmat-nfdi.eu/taxonomy/Volume' + + value = Quantity( + type=np.float64, + unit='m ** 3', + description=""" + """, + ) + + def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: + super().normalize(archive, logger) + + +class Temperature(PhysicalProperty): + """ + a measure of the average kinetic energy of the particles in a system. + """ + + value = Quantity( + type=np.float64, + unit='kelvin', + description=""" + """, + ) + + def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: + super().normalize(archive, logger) + + +class Heat(BaseEnergy): + """ + The transfer of thermal energy **into** a system. + """ + + def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: + super().normalize(archive, logger) + + +class Work(BaseEnergy): + """ + The energy transferred to a system by means of force applied over a distance. + """ + + def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: + super().normalize(archive, logger) + + +class InternalEnergy(BaseEnergy): + """ + The total energy contained within a system, encompassing both kinetic and potential + energies of the particles. The change in `InternalEnergy` for some thermodynamic + process may be expressed as the `Heat` minus the `Work`. + """ + + def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: + super().normalize(archive, logger) + + +class Enthalpy(BaseEnergy): + """ + The total heat content of a system, defined as 'InternalEnergy' + 'Pressure' * 'Volume'. + """ + + def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: + super().normalize(archive, logger) + + +class Entropy(PhysicalProperty): + """ + A measure of the disorder or randomness in a system. + + From a thermodynamic perspective, `Entropy` is a measure of the system's energy + dispersal at a specific temperature, and can be interpreted as the unavailability of + a system's thermal energy for conversion into mechanical work. For a reversible + process, the change in `Entropy` is given mathematically by an integral over the + infinitesimal `Heat` (i.e., thermal energy transfered into the system) divided by the + `Temperature`. + + From a statistical mechanics viewpoint, entropy quantifies the number of microscopic + configurations (microstates) that correspond to a thermodynamic system's macroscopic + state, as given by the Boltzmann equation for entropy. + """ + + value = Quantity( + type=np.float64, + unit='joule / kelvin', + description=""" + """, + ) + + def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: + super().normalize(archive, logger) + + +class GibbsFreeEnergy(BaseEnergy): + """ + The energy available to do work in a system at constant temperature and pressure, + given by `Enthalpy` - `Temperature` * `Entropy`. + """ + + def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: + super().normalize(archive, logger) + + +class HelmholtzFreeEnergy(BaseEnergy): + """ + The energy available to do work in a system at constant volume and temperature, + given by `InternalEnergy` - `Temperature` * `Entropy`. + """ + + def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: + super().normalize(archive, logger) + + +class ChemicalPotential(BaseEnergy): + """ + Free energy cost of adding or extracting a particle from a thermodynamic system. + """ + + # ! implement `iri` and `rank` as part of `m_def = Section()` + + iri = 'http://fairmat-nfdi.eu/taxonomy/ChemicalPotential' + + def __init__( + self, m_def: 'Section' = None, m_context: 'Context' = None, **kwargs + ) -> None: + super().__init__(m_def, m_context, **kwargs) + self.rank = [] + self.name = self.m_def.name + + def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: + super().normalize(archive, logger) + + +class HeatCapacity(PhysicalProperty): + """ + Amount of heat to be supplied to a material to produce a unit change in its temperature. + """ + + value = Quantity( + type=np.float64, + unit='joule / kelvin', + description=""" + """, + ) + + def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: + super().normalize(archive, logger) + + +################################ +# other thermodynamic properties +################################ + + +class VirialTensor(BaseEnergy): + """ + A measure of the distribution of internal forces and the overall stress within + a system of particles. Mathematically, the virial tensor is defined as minus the sum + of the dot product between the position and force vectors for each particle. + The `VirialTensor` can be related to the non-ideal pressure of the system through + the virial theorem. + """ + + def __init__( + self, m_def: 'Section' = None, m_context: 'Context' = None, **kwargs + ) -> None: + super().__init__(m_def, m_context, **kwargs) + self.rank = [3, 3] + self.name = self.m_def.name + + def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: + super().normalize(archive, logger) + + +class MassDensity(PhysicalProperty): + """ + Mass per unit volume of a material. + """ + + value = Quantity( + type=np.float64, + unit='kg / m ** 3', + description=""" + """, + ) + + def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: + super().normalize(archive, logger) + + +# ? fit better elsewhere +class Hessian(PhysicalProperty): + """ + A square matrix of second-order partial derivatives of a potential energy function, + describing the local curvature of the energy surface. + """ + + value = Quantity( + type=np.float64, + unit='joule / m ** 2', + description=""" + """, + ) + + def __init__( + self, m_def: 'Section' = None, m_context: 'Context' = None, **kwargs + ) -> None: + super().__init__(m_def, m_context, **kwargs) + self.rank = [3, 3] + self.name = self.m_def.name + + def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: + super().normalize(archive, logger) diff --git a/tests/test_energies.py b/tests/test_energies.py index 28d3a766..6dc2daeb 100644 --- a/tests/test_energies.py +++ b/tests/test_energies.py @@ -16,7 +16,12 @@ # limitations under the License. # -from nomad_simulations.schema_packages.properties import FermiLevel, ChemicalPotential +from nomad_simulations.schema_packages.properties import ( + FermiLevel, + TotalEnergy, + KineticEnergy, + PotentialEnergy, +) class TestFermiLevel: @@ -35,20 +40,49 @@ def test_default_quantities(self): assert fermi_level.rank == [] -class TestChemicalPotential: +class TestTotalEnergy: """ - Test the `ChemicalPotential` class defined in `properties/energies.py`. + Test the `TotalEnergy` class defined in `properties/energies.py`. """ # ! Include this initial `test_default_quantities` method when testing your PhysicalProperty classes def test_default_quantities(self): """ - Test the default quantities assigned when creating an instance of the `ChemicalPotential` class. + Test the default quantities assigned when creating an instance of the `TotalEnergy` class. """ - chemical_potential = ChemicalPotential() - assert ( - chemical_potential.iri - == 'http://fairmat-nfdi.eu/taxonomy/ChemicalPotential' - ) - assert chemical_potential.name == 'ChemicalPotential' - assert chemical_potential.rank == [] + total_energy = TotalEnergy() + assert total_energy.iri == 'http://fairmat-nfdi.eu/taxonomy/TotalEnergy' + assert total_energy.name == 'TotalEnergy' + assert total_energy.rank == [] + + +class TestKineticEnergy: + """ + Test the `KineticEnergy` class defined in `properties/energies.py`. + """ + + # ! Include this initial `test_default_quantities` method when testing your PhysicalProperty classes + def test_default_quantities(self): + """ + Test the default quantities assigned when creating an instance of the `KineticEnergy` class. + """ + kinetic_energy = KineticEnergy() + assert kinetic_energy.iri == 'http://fairmat-nfdi.eu/taxonomy/KineticEnergy' + assert kinetic_energy.name == 'KineticEnergy' + assert kinetic_energy.rank == [] + + +class TestPotentialEnergy: + """ + Test the `PotentialEnergy` class defined in `properties/energies.py`. + """ + + # ! Include this initial `test_default_quantities` method when testing your PhysicalProperty classes + def test_default_quantities(self): + """ + Test the default quantities assigned when creating an instance of the `PotentialEnergy` class. + """ + potential_energy = PotentialEnergy() + assert potential_energy.iri == 'http://fairmat-nfdi.eu/taxonomy/PotentialEnergy' + assert potential_energy.name == 'PotentialEnergy' + assert potential_energy.rank == [] diff --git a/tests/test_forces.py b/tests/test_forces.py new file mode 100644 index 00000000..c172bb6d --- /dev/null +++ b/tests/test_forces.py @@ -0,0 +1,35 @@ +# +# Copyright The NOMAD Authors. +# +# This file is part of NOMAD. See https://nomad-lab.eu for further info. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from nomad_simulations.schema_packages.properties import TotalForce + + +class TestTotalForce: + """ + Test the `TotalForce` class defined in `properties/forces.py`. + """ + + # ! Include this initial `test_default_quantities` method when testing your PhysicalProperty classes + def test_default_quantities(self): + """ + Test the default quantities assigned when creating an instance of the `TotalForce` class. + """ + total_force = TotalForce() + assert total_force.iri == 'http://fairmat-nfdi.eu/taxonomy/TotalForce' + assert total_force.name == 'TotalForce' + assert total_force.rank == [] diff --git a/tests/test_thermodynamics.py b/tests/test_thermodynamics.py new file mode 100644 index 00000000..887b44b0 --- /dev/null +++ b/tests/test_thermodynamics.py @@ -0,0 +1,283 @@ +# +# Copyright The NOMAD Authors. +# +# This file is part of NOMAD. See https://nomad-lab.eu for further info. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from nomad_simulations.schema_packages.properties import ( + Pressure, + Volume, + Temperature, + Heat, + Work, + InternalEnergy, + Enthalpy, + Entropy, + GibbsFreeEnergy, + HelmholtzFreeEnergy, + ChemicalPotential, + HeatCapacity, + VirialTensor, + MassDensity, + Hessian, +) + + +class TestPressure: + """ + Test the `Pressure` class defined in `properties/thermodynamics.py`. + """ + + # ! Include this initial `test_default_quantities` method when testing your PhysicalProperty classes + def test_default_quantities(self): + """ + Test the default quantities assigned when creating an instance of the `Pressure` class. + """ + pressure = Pressure() + assert pressure.iri == 'http://fairmat-nfdi.eu/taxonomy/Pressure' + assert pressure.name == 'Pressure' + assert pressure.rank == [] + + +class TestVolume: + """ + Test the `Volume` class defined in `properties/thermodynamics.py`. + """ + + # ! Include this initial `test_default_quantities` method when testing your PhysicalProperty classes + def test_default_quantities(self): + """ + Test the default quantities assigned when creating an instance of the `Volume` class. + """ + volume = Volume() + assert volume.iri == 'http://fairmat-nfdi.eu/taxonomy/Volume' + assert volume.name == 'Volume' + assert volume.rank == [] + + +class TestTemperature: + """ + Test the `Temperature` class defined in `properties/thermodynamics.py`. + """ + + # ! Include this initial `test_default_quantities` method when testing your PhysicalProperty classes + def test_default_quantities(self): + """ + Test the default quantities assigned when creating an instance of the `Temperature` class. + """ + temperature = Temperature() + assert temperature.iri == 'http://fairmat-nfdi.eu/taxonomy/Temperature' + assert temperature.name == 'Temperature' + assert temperature.rank == [] + + +class TestHeat: + """ + Test the `Heat` class defined in `properties/thermodynamics.py`. + """ + + # ! Include this initial `test_default_quantities` method when testing your PhysicalProperty classes + def test_default_quantities(self): + """ + Test the default quantities assigned when creating an instance of the `Heat` class. + """ + heat = Heat() + assert heat.iri == 'http://fairmat-nfdi.eu/taxonomy/Heat' + assert heat.name == 'Heat' + assert heat.rank == [] + + +class TestWork: + """ + Test the `Work` class defined in `properties/thermodynamics.py`. + """ + + # ! Include this initial `test_default_quantities` method when testing your PhysicalProperty classes + def test_default_quantities(self): + """ + Test the default quantities assigned when creating an instance of the `Work` class. + """ + work = Work() + assert work.iri == 'http://fairmat-nfdi.eu/taxonomy/Work' + assert work.name == 'Work' + assert work.rank == [] + + +class TestInternalEnergy: + """ + Test the `InternalEnergy` class defined in `properties/thermodynamics.py`. + """ + + # ! Include this initial `test_default_quantities` method when testing your PhysicalProperty classes + def test_default_quantities(self): + """ + Test the default quantities assigned when creating an instance of the `InternalEnergy` class. + """ + internal_energy = InternalEnergy() + assert internal_energy.iri == 'http://fairmat-nfdi.eu/taxonomy/InternalEnergy' + assert internal_energy.name == 'InternalEnergy' + assert internal_energy.rank == [] + + +class TestEnthalpy: + """ + Test the `Enthalpy` class defined in `properties/thermodynamics.py`. + """ + + # ! Include this initial `test_default_quantities` method when testing your PhysicalProperty classes + def test_default_quantities(self): + """ + Test the default quantities assigned when creating an instance of the `Enthalpy` class. + """ + enthalpy = Enthalpy() + assert enthalpy.iri == 'http://fairmat-nfdi.eu/taxonomy/Enthalpy' + assert enthalpy.name == 'Enthalpy' + assert enthalpy.rank == [] + + +class TestEntropy: + """ + Test the `Entropy` class defined in `properties/thermodynamics.py`. + """ + + # ! Include this initial `test_default_quantities` method when testing your PhysicalProperty classes + def test_default_quantities(self): + """ + Test the default quantities assigned when creating an instance of the `Entropy` class. + """ + entropy = Entropy() + assert entropy.iri == 'http://fairmat-nfdi.eu/taxonomy/Entropy' + assert entropy.name == 'Entropy' + assert entropy.rank == [] + + +class TestGibbsFreeEnergy: + """ + Test the `GibbsFreeEnergy` class defined in `properties/thermodynamics.py`. + """ + + # ! Include this initial `test_default_quantities` method when testing your PhysicalProperty classes + def test_default_quantities(self): + """ + Test the default quantities assigned when creating an instance of the `GibbsFreeEnergy` class. + """ + gibbs_free_energy = GibbsFreeEnergy() + assert ( + gibbs_free_energy.iri == 'http://fairmat-nfdi.eu/taxonomy/GibbsFreeEnergy' + ) + assert gibbs_free_energy.name == 'GibbsFreeEnergy' + assert gibbs_free_energy.rank == [] + + +class TestHelmholtzFreeEnergy: + """ + Test the `HelmholtzFreeEnergy` class defined in `properties/thermodynamics.py`. + """ + + # ! Include this initial `test_default_quantities` method when testing your PhysicalProperty classes + def test_default_quantities(self): + """ + Test the default quantities assigned when creating an instance of the `HelmholtzFreeEnergy` class. + """ + helmholtz_free_energy = HelmholtzFreeEnergy() + assert ( + helmholtz_free_energy.iri + == 'http://fairmat-nfdi.eu/taxonomy/HelmholtzFreeEnergy' + ) + assert helmholtz_free_energy.name == 'HelmholtzFreeEnergy' + assert helmholtz_free_energy.rank == [] + + +class TestChemicalPotential: + """ + Test the `ChemicalPotential` class defined in `properties/thermodynamics.py`. + """ + + # ! Include this initial `test_default_quantities` method when testing your PhysicalProperty classes + def test_default_quantities(self): + """ + Test the default quantities assigned when creating an instance of the `ChemicalPotential` class. + """ + chemical_potential = ChemicalPotential() + assert ( + chemical_potential.iri + == 'http://fairmat-nfdi.eu/taxonomy/ChemicalPotential' + ) + assert chemical_potential.name == 'ChemicalPotential' + assert chemical_potential.rank == [] + + +class TestHeatCapacity: + """ + Test the `HeatCapacity` class defined in `properties/thermodynamics.py`. + """ + + # ! Include this initial `test_default_quantities` method when testing your PhysicalProperty classes + def test_default_quantities(self): + """ + Test the default quantities assigned when creating an instance of the `HeatCapacity` class. + """ + heat_capacity = HeatCapacity() + assert heat_capacity.iri == 'http://fairmat-nfdi.eu/taxonomy/HeatCapacity' + assert heat_capacity.name == 'HeatCapacity' + assert heat_capacity.rank == [] + + +class TestVirialTensor: + """ + Test the `VirialTensor` class defined in `properties/thermodynamics.py`. + """ + + # ! Include this initial `test_default_quantities` method when testing your PhysicalProperty classes + def test_default_quantities(self): + """ + Test the default quantities assigned when creating an instance of the `VirialTensor` class. + """ + virial_tensor = VirialTensor() + assert virial_tensor.iri == 'http://fairmat-nfdi.eu/taxonomy/VirialTensor' + assert virial_tensor.name == 'VirialTensor' + assert virial_tensor.rank == [] + + +class TestMassDensity: + """ + Test the `MassDensity` class defined in `properties/thermodynamics.py`. + """ + + # ! Include this initial `test_default_quantities` method when testing your PhysicalProperty classes + def test_default_quantities(self): + """ + Test the default quantities assigned when creating an instance of the `MassDensity` class. + """ + mass_density = MassDensity() + assert mass_density.iri == 'http://fairmat-nfdi.eu/taxonomy/MassDensity' + assert mass_density.name == 'MassDensity' + assert mass_density.rank == [] + + +class TestHessian: + """ + Test the `Hessian` class defined in `properties/thermodynamics.py`. + """ + + # ! Include this initial `test_default_quantities` method when testing your PhysicalProperty classes + def test_default_quantities(self): + """ + Test the default quantities assigned when creating an instance of the `Hessian` class. + """ + hessian = Hessian() + assert hessian.iri == 'http://fairmat-nfdi.eu/taxonomy/Hessian' + assert hessian.name == 'Hessian' + assert hessian.rank == []