-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Add
PhysicalPropertyDecomposition
to standardize contributions
- Rework energies, forces, thermodynamics
- Loading branch information
ndaelman
committed
Jan 14, 2025
1 parent
430bfb8
commit 17cf3bb
Showing
5 changed files
with
231 additions
and
478 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
49 changes: 18 additions & 31 deletions
49
src/nomad_simulations/schema_packages/properties/energies.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,45 +1,32 @@ | ||
from typing import TYPE_CHECKING | ||
|
||
import numpy as np | ||
from nomad.metainfo import MEnum, Quantity, Reference | ||
from nomad.metainfo.dataset import MDataset, Dataset | ||
from nomad.datamodel.metainfo.model import ModelMethod | ||
|
||
from ..physical_property import PhysicalPropertyDecomposition | ||
from ..variables import Energy, EnergyType, MethodReference | ||
|
||
if TYPE_CHECKING: | ||
from nomad.datamodel.datamodel import EntryArchive | ||
from structlog.stdlib import BoundLogger | ||
|
||
EnergyTemplateGenerator = PhysicalPropertyDecomposition( | ||
Energy, | ||
reference_type=MethodReference, | ||
) | ||
|
||
class Energy(MDataset): | ||
m_def = Dataset( | ||
type=np.float64, | ||
unit='joule', | ||
description="""A base section used to define basic quantities for the `TotalEnergy` property.""", | ||
default_variables=['Energy'], # ? does this require variables of this shape | ||
) | ||
|
||
# ? origin_reference | ||
|
||
kind = Quantity( | ||
type=MEnum('kinetic', 'potential', 'total'), | ||
) | ||
class ModelEnergySection('ArchiveSection'): | ||
energy = EnergyTemplateGenerator()() # ? suggest energy_origin or kind | ||
|
||
method_reference = Quantity( | ||
type=Reference(ModelMethod), | ||
description=""" | ||
Reference to a `ModelMethod` definition, according to which the energy was calculated. | ||
""", | ||
) | ||
type = EnergyType() | ||
|
||
def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: | ||
super().normalize(archive, logger) | ||
|
||
energy_sums = np.sum( | ||
[var.data for var in self.variables if isinstance(var, Energy)], axis=0 | ||
) | ||
if self.data is None or self.data == []: | ||
self.data = energy_sums | ||
elif not np.allclose(self.data, energy_sums): | ||
logger.warning( | ||
f'The sum of the energies in the variables is different from the total energy: {energy_sums} != {self.data}' | ||
) | ||
# check that the contributions do not outgrow the total energy | ||
for field in self.energy.fields: | ||
if (total_energy := field[0]) < (energy_contributions := np.sum(field[1])): | ||
logger.warning( | ||
f'The contributions outweigh the total energy', | ||
energy_contributions, | ||
total_energy, | ||
) |
82 changes: 12 additions & 70 deletions
82
src/nomad_simulations/schema_packages/properties/forces.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,79 +1,21 @@ | ||
from typing import TYPE_CHECKING | ||
|
||
import numpy as np | ||
from nomad.metainfo import Context, Quantity, Section, SubSection | ||
from ..physical_property import PhysicalPropertyDecomposition | ||
from ..variables import Force, MethodReference | ||
from nomad.metainfo.datasets import DatasetTemplate | ||
|
||
if TYPE_CHECKING: | ||
from nomad.datamodel.datamodel import EntryArchive | ||
from nomad.metainfo import Context, Section | ||
from structlog.stdlib import BoundLogger | ||
|
||
from nomad_simulations.schema_packages.physical_property import ( | ||
PhysicalProperty, | ||
PropertyContribution, | ||
ForceTemplateGenerator = PhysicalPropertyDecomposition( | ||
Force, | ||
reference_type=MethodReference, | ||
) | ||
|
||
################## | ||
# 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`. | ||
""" | ||
class ModelForceSection('ArchiveSection'): | ||
force = ForceTemplateGenerator()() | ||
|
||
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 | ||
# OR | ||
|
||
def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None: | ||
super().normalize(archive, logger) | ||
ModelForceSection = DatasetTemplate( | ||
mandatory_fields=[Force], | ||
)() |
Oops, something went wrong.