Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove check for n_ quantities #144

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 34 additions & 63 deletions src/nomad_simulations/schema_packages/physical_property.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
logger = utils.get_logger(__name__)


def validate_quantity_wrt_value(name: str = ''):
def validate_quantity_wrt_value(name: str):
"""
Decorator to validate the existence of a quantity and its shape with respect to the `PhysicalProperty.value`
before calling a method. An example can be found in the module `properties/band_structure.py` for the method
Expand All @@ -48,17 +48,6 @@ def wrapper(self, *args, **kwargs):
logger.warning(f'The quantity `{name}` is not defined.')
return False

# Checks if `value` exists and has the same shape as `quantity`
value = getattr(self, 'value', None)
if value is None:
logger.warning('The quantity `value` is not defined.')
return False
if value is not None and value.shape != quantity.shape:
logger.warning(
f'The shape of the quantity `{name}` does not match the shape of the `value`.'
)
return False

return func(self, *args, **kwargs)

return wrapper
Expand Down Expand Up @@ -175,7 +164,7 @@ class PhysicalProperty(ArchiveSection):
Flag indicating whether the physical property is converged or not after a SCF process. This quantity is connected
with `SelfConsistency` defined in the `numerical_settings.py` module.
""",
)
) # ? typically, a calculation is converged as a whole, at least at the level of SCF

self_consistency_ref = Quantity(
type=SelfConsistency,
Expand All @@ -202,16 +191,10 @@ def variables_shape(self) -> Optional[list]:
return []

@property
def full_shape(self) -> list:
def full_shape(self) -> list: # TODO: add support for N-dim variables
"""
Full shape of the physical property. This quantity is calculated as a concatenation of the `variables_shape`
and `rank`:

`full_shape = variables_shape + rank`

where `rank` is passed as an attribute of the `PhysicalProperty` and is related with the order of
the tensor of `value`, and `variables_shape` is obtained from the property-decorated function `variables_shape()`
and is related with the shapes of the `variables` over which the physical property varies.
Full shape of the physical property, defined as the concatenation of the `variables_shape`
and `rank`.

Example: a physical property which is a 3D vector and varies with `variables=[Temperature, ElectricField]`
will have `rank=[3]`, `variables_shape=[n_temperatures, n_electric_fields]`, and thus
Expand All @@ -233,11 +216,10 @@ def _new_value(self) -> Quantity:
(Quantity): The new `Quantity` object for setting the `value` quantity.
"""
value_quantity = self.m_def.all_quantities.get('value')
if value_quantity is None:
return None
return Quantity(
type=value_quantity.type,
unit=value_quantity.unit, # ? this can be moved to __setattr__
shape=self.full_shape,
unit=value_quantity.unit,
description=value_quantity.description,
)

Expand All @@ -252,44 +234,24 @@ def __init__(
'The used property is not defined in the FAIRmat taxonomy (https://fairmat-nfdi.github.io/fairmat-taxonomy/). You can contribute there if you want to extend the list of available materials properties.'
)

# Checking if the quantities `n_` are defined, as this are used to calculate `rank`
for quantity, _ in self.m_def.all_quantities.items():
if quantity.startswith('n_') and getattr(self, quantity) is None:
raise ValueError(
f'`{quantity}` is not defined during initialization of the class.'
)

def __setattr__(self, name: str, val: Any) -> None:
# For the special case of `value`, its `shape` needs to be defined from `_full_shape`
if name == 'value':
if val is None:
raise ValueError(
f'The value of the physical property {self.name} is None. Please provide a finite valid value.'
)
_new_value = self._new_value

# patch for when `val` does not have units and it is passed as a list (instead of np.array)
if isinstance(val, list):
val = np.array(val)

# non-scalar or scalar `val`
try:
value_shape = list(val.shape)
except AttributeError:
value_shape = []

if value_shape != self.full_shape:
raise ValueError(
f'The shape of the stored `value` {value_shape} does not match the full shape {self.full_shape} '
f'extracted from the variables `n_points` and the `shape` defined in `PhysicalProperty`.'
)
_new_value.shape = self.full_shape
if hasattr(val, 'magnitude'):
_new_value = val.magnitude * val.u
else:
_new_value = val
return super().__setattr__(name, _new_value)
return super().__setattr__(name, val)
@property
def _check_value_shape(self) -> int:
"""Provide a code of how the `value` conforms tol `full_shape`."""

if self.value is None:
return 0

if hasattr(self.value, 'shape'):
normalized_shape = list(self.value.shape)
if normalized_shape == [0]:
normalized_shape = []
else:
normalized_shape = []

if normalized_shape != self.full_shape:
return -1

return 1

def _is_derived(self) -> bool:
"""
Expand All @@ -308,6 +270,15 @@ 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()

if (check_value_shape := self._check_value_shape()) == 0:
raise ValueError('`PhysicalProperty.value` is unset.')
elif check_value_shape == -1:
raise ValueError(
f'The shape of the stored `value` does not match the full shape {self.full_shape} '
f'extracted from the variables `n_points` and the `shape` defined in `PhysicalProperty`.'
)

self.m_def.value = self._new_value

class PropertyContribution(PhysicalProperty):
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

class BaseElectronicEigenvalues(PhysicalProperty):
"""
A base section used to define basic quantities for the `ElectronicEigenvalues` and `ElectronicBandStructure` properties.
A base section used to define basic quantities for the `ElectronicEigenvalues` and `ElectronicBandStructure` properties.
"""

iri = ''
Expand All @@ -52,7 +52,13 @@ def __init__(
) -> None:
super().__init__(m_def, m_context, **kwargs)
# ! `n_bands` need to be set up during initialization of the class
self.rank = [int(kwargs.get('n_bands'))]
if (
n_bands := kwargs.get('n_bands')
) is None: # ! alt: derive n_bands from value
raise ValueError(
'`n_bands` is not defined during initialization of the class.'
)
self.rank = [int(n_bands)]

def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None:
super().normalize(archive, logger)
Expand Down
Loading