diff --git a/src/nomad_simulations/outputs.py b/src/nomad_simulations/outputs.py index 42fd2035..c9aef22e 100644 --- a/src/nomad_simulations/outputs.py +++ b/src/nomad_simulations/outputs.py @@ -41,7 +41,8 @@ AbsorptionSpectrum, XASSpectrum, Permittivity, - TotalEnergy + TotalEnergy, + TotalForce, ) @@ -112,7 +113,9 @@ class Outputs(ArchiveSection): xas_spectra = SubSection(sub_section=XASSpectrum.m_def, repeats=True) - total_energy = SubSection(SubSection=TotalEnergy.m_def, repeats=True) + total_energy = SubSection(sub_section=TotalEnergy.m_def, repeats=True) + + total_force = SubSection(sub_section=TotalForce.m_def, repeats=True) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # @@ -296,6 +299,7 @@ def normalize(self, archive, logger) -> None: logger=logger, ) + class WorkflowOutputs(Outputs): """ This section contains output properties that depend on a single system, but were @@ -320,6 +324,7 @@ class WorkflowOutputs(Outputs): def normalize(self, archive, logger) -> None: super().normalize(archive, logger) + class TrajectoryOutputs(WorkflowOutputs): """ This section contains output properties that depend on a single system, but were @@ -328,6 +333,7 @@ class TrajectoryOutputs(WorkflowOutputs): time = Quantity( type=np.float64, + unit='ps', description=""" The elapsed simulated physical time since the start of the trajectory. """, diff --git a/src/nomad_simulations/physical_property.py b/src/nomad_simulations/physical_property.py index e879a593..39c1d926 100644 --- a/src/nomad_simulations/physical_property.py +++ b/src/nomad_simulations/physical_property.py @@ -320,4 +320,4 @@ def normalize(self, archive, logger) -> None: super().normalize(archive, logger) # Resolve if the physical property `is_derived` or not from another physical property. - self.is_derived = self._is_derived() \ No newline at end of file + self.is_derived = self._is_derived() diff --git a/src/nomad_simulations/properties/__init__.py b/src/nomad_simulations/properties/__init__.py index a8834033..0146ca18 100644 --- a/src/nomad_simulations/properties/__init__.py +++ b/src/nomad_simulations/properties/__init__.py @@ -23,6 +23,7 @@ ClassicalEnergyContributions, QuantumEnergyContributions, ) +from .forces import TotalForce, ForceContributions from .band_gap import ElectronicBandGap from .spectral_profile import ( SpectralProfile, diff --git a/src/nomad_simulations/properties/energies.py b/src/nomad_simulations/properties/energies.py index d7d4c4b3..9b7f2fe0 100644 --- a/src/nomad_simulations/properties/energies.py +++ b/src/nomad_simulations/properties/energies.py @@ -19,9 +19,11 @@ import numpy as np from nomad.metainfo import Quantity, Section, Context, SubSection, MEnum +from nomad.datamodel.data import ArchiveSection from nomad_simulations.physical_property import PhysicalProperty + class FermiLevel(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. @@ -82,6 +84,7 @@ def normalize(self, archive, logger) -> None: # List of classical energy contribuions #################################################### + class PotentialEnergy(PhysicalProperty): """ Section containing the potential energy of a (sub)system. @@ -98,6 +101,7 @@ class PotentialEnergy(PhysicalProperty): def normalize(self, archive, logger) -> None: super().normalize(archive, logger) + class KineticEnergy(PhysicalProperty): """ Section containing the kinetic energy of a (sub)system. @@ -152,6 +156,7 @@ class VDWEnergy(PhysicalProperty): def normalize(self, archive, logger) -> None: super().normalize(archive, logger) + class ElectrostaticEnergy(PhysicalProperty): """ Section containing all electrostatic contributions to the potential energy of a (sub)system. @@ -168,6 +173,7 @@ class ElectrostaticEnergy(PhysicalProperty): def normalize(self, archive, logger) -> None: super().normalize(archive, logger) + class ElectrostaticShortRangeEnergy(PhysicalProperty): """ Section containing short-range electrostatic contributions to the potential energy of a (sub)system. @@ -184,6 +190,7 @@ class ElectrostaticShortRangeEnergy(PhysicalProperty): def normalize(self, archive, logger) -> None: super().normalize(archive, logger) + class ElectrostaticLongRangeEnergy(PhysicalProperty): """ Section containing long-range electrostatic contributions to the potential energy of a (sub)system. @@ -200,6 +207,7 @@ class ElectrostaticLongRangeEnergy(PhysicalProperty): def normalize(self, archive, logger) -> None: super().normalize(archive, logger) + class BondedEnergy(PhysicalProperty): """ Section containing all bonded (i.e., intramolecular) contributions to the potential energy of a (sub)system. @@ -216,6 +224,7 @@ class BondedEnergy(PhysicalProperty): def normalize(self, archive, logger) -> None: super().normalize(archive, logger) + class BondEnergy(PhysicalProperty): """ Section containing contributions to the potential energy from bond interactions of a (sub)system. @@ -232,6 +241,7 @@ class BondEnergy(PhysicalProperty): def normalize(self, archive, logger) -> None: super().normalize(archive, logger) + class AngleEnergy(PhysicalProperty): """ Section containing contributions to the potential energy from angle interactions of a (sub)system. @@ -248,6 +258,7 @@ class AngleEnergy(PhysicalProperty): def normalize(self, archive, logger) -> None: super().normalize(archive, logger) + class DihedralEnergy(PhysicalProperty): """ Section containing contributions to the potential energy from dihedral interactions of a (sub)system. @@ -264,6 +275,7 @@ class DihedralEnergy(PhysicalProperty): def normalize(self, archive, logger) -> None: super().normalize(archive, logger) + class ImproperDihedralEnergy(PhysicalProperty): """ Section containing contributions to the potential energy from improper dihedral interactions of a (sub)system. @@ -280,6 +292,7 @@ class ImproperDihedralEnergy(PhysicalProperty): def normalize(self, archive, logger) -> None: super().normalize(archive, logger) + class ExternalEnergy(PhysicalProperty): """ Section containing contributions to the potential energy from external interactions of a (sub)system. @@ -296,6 +309,7 @@ class ExternalEnergy(PhysicalProperty): def normalize(self, archive, logger) -> None: super().normalize(archive, logger) + class ClassicalEnergyContributions(ArchiveSection): """ Section containing contributions to the potential energy from a classical force field. @@ -309,9 +323,13 @@ class ClassicalEnergyContributions(ArchiveSection): electrostatic = SubSection(sub_section=ElectrostaticEnergy.m_def, repeats=False) - electrostatic_short_range = SubSection(sub_section=ElectrostaticShortRangeEnergy.m_def, repeats=False) + electrostatic_short_range = SubSection( + sub_section=ElectrostaticShortRangeEnergy.m_def, repeats=False + ) - electrostatic_long_range = SubSection(sub_section=ElectrostaticLongRangeEnergy.m_def, repeats=False) + electrostatic_long_range = SubSection( + sub_section=ElectrostaticLongRangeEnergy.m_def, repeats=False + ) bonded = SubSection(sub_section=BondedEnergy.m_def, repeats=False) @@ -321,7 +339,9 @@ class ClassicalEnergyContributions(ArchiveSection): dihedral = SubSection(sub_section=DihedralEnergy.m_def, repeats=False) - improper_dihedral = SubSection(sub_section=ImproperDihedralEnergy.m_def, repeats=False) + improper_dihedral = SubSection( + sub_section=ImproperDihedralEnergy.m_def, repeats=False + ) external = SubSection(sub_section=ExternalEnergy.m_def, repeats=False) @@ -330,10 +350,12 @@ def normalize(self, archive, logger) -> None: # Set the name of the section self.name = self.m_def.name + ###################################### # List of quantum energy contributions ###################################### + class ElectronicEnergy(PhysicalProperty): """ Section containing the electronic energy of a (sub)system. @@ -347,12 +369,11 @@ class ElectronicEnergy(PhysicalProperty): """, ) - contributions = SubSection(sub_section=EnergyContributions.m_def, repeats=True) - def normalize(self, archive, logger) -> None: super().normalize(archive, logger) allowed_contributions = ['PotKin'] + class ElectronicKineticEnergy(PhysicalProperty): """ Section containing the electronic kinetic energy of a (sub)system. @@ -369,11 +390,13 @@ class ElectronicKineticEnergy(PhysicalProperty): def normalize(self, archive, logger) -> None: super().normalize(archive, logger) + class XCEnergy(PhysicalProperty): """ Section containing the exchange-correlation (XC) energy of a (sub)system, calculated using the functional stored in XC_functional. """ + # ! Someone check this description! # ? Do we really want to specify the method here? This can't be user-defined? @@ -388,6 +411,7 @@ class XCEnergy(PhysicalProperty): def normalize(self, archive, logger) -> None: super().normalize(archive, logger) + class XCPotentialEnergy(PhysicalProperty): """ Section containing the potential energy contribution to the exchange-correlation (XC) energy, @@ -396,6 +420,7 @@ class XCPotentialEnergy(PhysicalProperty): of XC that is in the sum of the eigenvalues. Value associated with the configuration, should be the most converged value. | """ + # ! Someone check this description! # ? Do we really want to specify the method here? This can't be user-defined? @@ -410,12 +435,14 @@ class XCPotentialEnergy(PhysicalProperty): def normalize(self, archive, logger) -> None: super().normalize(archive, logger) + # ? XCCorrelationEnergy? class CorrelationEnergy(PhysicalProperty): """ Section containing the correlation energy of a (sub)system, calculated using the method described in XC_functional. """ + # ! Someone check this description! # ? Do we really want to specify the method here? This can't be user-defined? @@ -430,12 +457,14 @@ class CorrelationEnergy(PhysicalProperty): def normalize(self, archive, logger) -> None: super().normalize(archive, logger) + # ? XCExchangeEnergy? class ExchangeEnergy(PhysicalProperty): """ Section containing the exchange energy of a (sub)system, calculated using the method described in XC_functional. """ + # ! Someone check this description! # ? Do we really want to specify the method here? This can't be user-defined? @@ -468,11 +497,13 @@ class ZeroTemperatureEnergy(PhysicalProperty): def normalize(self, archive, logger) -> None: super().normalize(archive, logger) + class ZeroPointEnergy(PhysicalProperty): """ Section containing the zero-point vibrational energy of a (sub)system, calculated using the method described in zero_point_method. """ + # ! Someone check this description! # ? Do we really want to specify the method here? This can't be user-defined? @@ -487,6 +518,7 @@ class ZeroPointEnergy(PhysicalProperty): def normalize(self, archive, logger) -> None: super().normalize(archive, logger) + class ElectrostaticEnergy(PhysicalProperty): """ Section containing the electrostatic energy (nuclei + electrons) of a (sub)system. @@ -503,6 +535,7 @@ class ElectrostaticEnergy(PhysicalProperty): def normalize(self, archive, logger) -> None: super().normalize(archive, logger) + class NuclearRepulsionEnergy(PhysicalProperty): """ Section containing the nuclear-nuclear repulsion energy of a (sub)system. @@ -524,11 +557,14 @@ class QuantumEnergyContributions(ArchiveSection): """ Section containing contributions to the potential energy from a DFT calculation. """ -... + + ... electronic = SubSection(sub_section=ElectronicEnergy.m_def, repeats=False) - electronic_kinetic = SubSection(sub_section=ElectronicKineticEnergy.m_def, repeats=False) + electronic_kinetic = SubSection( + sub_section=ElectronicKineticEnergy.m_def, repeats=False + ) xc = SubSection(sub_section=XCEnergy.m_def, repeats=False) @@ -538,18 +574,24 @@ class QuantumEnergyContributions(ArchiveSection): exchange = SubSection(sub_section=ExchangeEnergy.m_def, repeats=False) - zero_temperature = SubSection(sub_section=ZeroTemperatureEnergy.m_def, repeats=False) + zero_temperature = SubSection( + sub_section=ZeroTemperatureEnergy.m_def, repeats=False + ) zero_point = SubSection(sub_section=ZeroPointEnergy.m_def, repeats=False) electrostatic = SubSection(sub_section=ElectrostaticEnergy.m_def, repeats=False) - nuclear_repulsion = SubSection(sub_section=NuclearRepulsionEnergy.m_def, repeats=False) + nuclear_repulsion = SubSection( + sub_section=NuclearRepulsionEnergy.m_def, repeats=False + ) + ########################## # Other / General energies ########################## + class TotalEnergy(PhysicalProperty): """ Section containing the total energy of a (sub)system. @@ -564,12 +606,17 @@ class TotalEnergy(PhysicalProperty): ) # ? Do we need these descriptions under value? It ends up simply duplicating the section info to some extent. - classical_contributions = SubSection(sub_section=ClassicalEnergyContributions.m_def, repeats=False) + classical_contributions = SubSection( + sub_section=ClassicalEnergyContributions.m_def, repeats=False + ) - quantum_contributions = SubSection(sub_section=QuantumEnergyContributions.m_def, repeats=False) + quantum_contributions = SubSection( + sub_section=QuantumEnergyContributions.m_def, repeats=False + ) def normalize(self, archive, logger) -> None: super().normalize(archive, logger) + #? Should we set total energy value as sum of all contributions if not set explicitly? # madelung = SubSection( # sub_section=EnergyEntry.m_def, @@ -709,7 +756,6 @@ def normalize(self, archive, logger) -> None: # """, # ) - # # TODO this should be removed and replaced by correction in EnergyEntry # current = SubSection( # sub_section=EnergyEntry.m_def, @@ -723,8 +769,6 @@ def normalize(self, archive, logger) -> None: # ) - - # # ? Do we want to allow this? # class EnergyCustom(PhysicalProperty): # """ diff --git a/src/nomad_simulations/properties/forces.py b/src/nomad_simulations/properties/forces.py index 71721933..11676584 100644 --- a/src/nomad_simulations/properties/forces.py +++ b/src/nomad_simulations/properties/forces.py @@ -34,45 +34,19 @@ # limitations under the License. # -import re import numpy as np -from nomad.units import ureg +from nomad.metainfo import Quantity, Section, Context, SubSection, MEnum from nomad.datamodel.data import ArchiveSection -from nomad.metainfo import Quantity, SubSection, SectionProxy, MEnum -from nomad.metainfo.metainfo import DirectQuantity, Dimension -from nomad.datamodel.metainfo.annotations import ELNAnnotation - -from .outputs import Outputs, SCFOutputs, WorkflowOutputs, TrajectoryOutputs -from .atoms_state import AtomsState -from .physical_property import PhysicalProperty - - -from nomad.datamodel.metainfo.common import ( - FastAccess, - PropertySection, - ProvenanceTracker, -) -from nomad.metainfo import ( - Category, - HDF5Reference, - MCategory, - MEnum, - MSection, - Package, - Quantity, - Reference, - Section, - SectionProxy, - SubSection, -) - -from .model_system import ModelSystem + +from nomad_simulations.physical_property import PhysicalProperty + ################################ # List of Forces Contributions # ################################ + class FreeForce(PhysicalProperty): """ Contains the value and information regarding the forces on the atoms @@ -94,6 +68,7 @@ class FreeForce(PhysicalProperty): def normalize(self, archive, logger) -> None: super().normalize(archive, logger) + class ZeroTemperatureForce(PhysicalProperty): """ Contains the value and information regarding the forces on the atoms @@ -112,9 +87,9 @@ class ZeroTemperatureForce(PhysicalProperty): def normalize(self, archive, logger) -> None: super().normalize(archive, logger) + class RawForce(PhysicalProperty): - """ - """ + """ """ value = Quantity( type=np.dtype(np.float64), @@ -130,8 +105,7 @@ class RawForce(PhysicalProperty): def normalize(self, archive, logger) -> None: super().normalize(archive, logger) - - #? Do we want to support custom contributions? + # ? Do we want to support custom contributions? # contributions = SubSection( # sub_section=ForcesEntry.m_def, # description=""" @@ -149,6 +123,7 @@ def normalize(self, archive, logger) -> None: # repeats=True, # ) + class ForceContributions(ArchiveSection): """ Section containing contributions to the potential energy from a classical force field. @@ -156,7 +131,9 @@ class ForceContributions(ArchiveSection): free = SubSection(sub_section=FreeForce.m_def, repeats=False) - zero_temperature_force = SubSection(sub_section=ZeroTemperatureForce.m_def, repeats=False) + zero_temperature_force = SubSection( + sub_section=ZeroTemperatureForce.m_def, repeats=False + ) raw = SubSection(sub_section=RawForce.m_def, repeats=False) @@ -166,12 +143,12 @@ def normalize(self, archive, logger) -> None: # Old version of the Forces description - # Value of the forces acting on the atoms. This is calculated as minus gradient of - # the corresponding energy type or contribution **including** constraints, if - # present. The derivatives with respect to displacements of nuclei are evaluated in - # Cartesian coordinates. In addition, these are obtained by filtering out the - # unitary transformations (center-of-mass translations and rigid rotations for - # non-periodic systems, see value_raw for the unfiltered counterpart). +# Value of the forces acting on the atoms. This is calculated as minus gradient of +# the corresponding energy type or contribution **including** constraints, if +# present. The derivatives with respect to displacements of nuclei are evaluated in +# Cartesian coordinates. In addition, these are obtained by filtering out the +# unitary transformations (center-of-mass translations and rigid rotations for +# non-periodic systems, see value_raw for the unfiltered counterpart). class TotalForce(PhysicalProperty): """ Section containing the total force of a (sub)system. @@ -179,6 +156,7 @@ class TotalForce(PhysicalProperty): Contains the value and information regarding the total forces on the atoms calculated as minus gradient of energy_total. """ + # ! We need to avoid giving the precise method of calculation without also providing context, this is not necessarily true in general! value = Quantity( @@ -189,8 +167,10 @@ class TotalForce(PhysicalProperty): """, ) - force_contributions = SubSection(sub_section=ForceContributions.m_def, repeats=False) - #? Do we need to separate classical and quantum contributions here? + contributions = SubSection( + sub_section=ForceContributions.m_def, repeats=False + ) + # ? Do we need to separate classical and quantum contributions here? def normalize(self, archive, logger) -> None: super().normalize(archive, logger) diff --git a/src/nomad_simulations/properties/mechanics.py b/src/nomad_simulations/properties/mechanics.py index bbe418ba..d9a1078a 100644 --- a/src/nomad_simulations/properties/mechanics.py +++ b/src/nomad_simulations/properties/mechanics.py @@ -126,4 +126,4 @@ class Stress(MSection): Contains other types of stress. """, repeats=True, - ) \ No newline at end of file + ) diff --git a/src/nomad_simulations/properties/molecular.py b/src/nomad_simulations/properties/molecular.py index 931e0930..3905a0a9 100644 --- a/src/nomad_simulations/properties/molecular.py +++ b/src/nomad_simulations/properties/molecular.py @@ -49,12 +49,12 @@ class RadiusOfGyration(PhysicalProperty): - value = Quantity( type=np.float64, unit='m', ) + class Hessian(TrajectoryOutputs): """ Section containing the Hessian matrix, i.e., 2nd derivatives with respect to atomic displacements, @@ -71,7 +71,12 @@ def normalize(self, archive, logger) -> None: super().normalize(archive, logger) self.value_unit = 'joule / m ** 2' self.name = 'hessian' - self.variables = [['atom_index'], ['atom_index'], ['x', 'y', 'z'], ['x', 'y', 'z']] + self.variables = [ + ['atom_index'], + ['atom_index'], + ['x', 'y', 'z'], + ['x', 'y', 'z'], + ] self.bins = [np.range(self.n_atoms), np.range(self.n_atoms)] diff --git a/src/nomad_simulations/properties/thermodynamics.py b/src/nomad_simulations/properties/thermodynamics.py index ecd1ec62..4598c4f9 100644 --- a/src/nomad_simulations/properties/thermodynamics.py +++ b/src/nomad_simulations/properties/thermodynamics.py @@ -38,6 +38,7 @@ from nomad.metainfo import Quantity, SubSection, MEnum from .physical_property import PhysicalProperty + class Enthalpy(PhysicalProperty): """ Section containing the enthalpy (i.e. energy_total + pressure * volume.) of a (sub)system. @@ -54,6 +55,7 @@ class Enthalpy(PhysicalProperty): def normalize(self, archive, logger) -> None: super().normalize(archive, logger) + class Entropy(PhysicalProperty): """ Section containing the entropy of a (sub)system. @@ -70,6 +72,7 @@ class Entropy(PhysicalProperty): def normalize(self, archive, logger) -> None: super().normalize(archive, logger) + class ChemicalPotential(PhysicalProperty): """ Section containing the chemical potential of a (sub)system. @@ -86,6 +89,7 @@ class ChemicalPotential(PhysicalProperty): def normalize(self, archive, logger) -> None: super().normalize(archive, logger) + class Pressure(PhysicalProperty): """ Section containing the pressure of a (sub)system. @@ -102,6 +106,7 @@ class Pressure(PhysicalProperty): def normalize(self, archive, logger) -> None: super().normalize(archive, logger) + class Virial(PhysicalProperty): """ Section containing the virial (cross product between positions and forces) of a (sub)system. @@ -118,6 +123,7 @@ class Virial(PhysicalProperty): def normalize(self, archive, logger) -> None: super().normalize(archive, logger) + class Temperature(PhysicalProperty): """ Section containing the temperature of a (sub)system. @@ -134,6 +140,7 @@ class Temperature(PhysicalProperty): def normalize(self, archive, logger) -> None: super().normalize(archive, logger) + class Volume(PhysicalProperty): """ Section containing the volume of a (sub)system. @@ -150,6 +157,7 @@ class Volume(PhysicalProperty): def normalize(self, archive, logger) -> None: super().normalize(archive, logger) + class Density(PhysicalProperty): """ Section containing the density of a (sub)system. @@ -166,6 +174,7 @@ class Density(PhysicalProperty): def normalize(self, archive, logger) -> None: super().normalize(archive, logger) + # ? Does this go here or in energies? # ? Naming specific to Potential Energy? class Hessian(PhysicalProperty): @@ -199,6 +208,7 @@ class HeatCapacityCV(PhysicalProperty): """, ) + class HeatCapacityCP(PhysicalProperty): """ Section containing the heat capacity at constant volume for a (sub)system. @@ -215,7 +225,6 @@ class HeatCapacityCP(PhysicalProperty): def normalize(self, archive, logger) -> None: super().normalize(archive, logger) - # ? Is this ever used? # internal_energy = Quantity( # type=np.dtype(np.float64), @@ -234,4 +243,3 @@ def normalize(self, archive, logger) -> None: # Value of the vibrational free energy per cell unit at constant volume. # """, # ) - diff --git a/src/nomad_simulations/test_direct.py b/src/nomad_simulations/test_direct.py index 88eec4cf..096ac6d5 100644 --- a/src/nomad_simulations/test_direct.py +++ b/src/nomad_simulations/test_direct.py @@ -27,10 +27,14 @@ # Playing with `PhysicalProperty` band_gap = ElectronicBandGap(source='simulation', type='direct', label='DFT') n_grid_points = 3 -temperature = Temperature(n_grid_points=n_grid_points, grid_points=np.linspace(0, 100, n_grid_points)) +temperature = Temperature( + n_grid_points=n_grid_points, grid_points=np.linspace(0, 100, n_grid_points) +) band_gap.variables.append(temperature) n_grid_points = 2 -custom_bins = Variables(n_grid_points=n_grid_points, grid_points=np.linspace(0, 100, n_grid_points)) +custom_bins = Variables( + n_grid_points=n_grid_points, grid_points=np.linspace(0, 100, n_grid_points) +) band_gap.variables.append(custom_bins) # band_gap.value_unit = 'joule' # band_gap.shape = [3]