Skip to content

Commit

Permalink
- Introduce _base_value as a pre-normalization of value
Browse files Browse the repository at this point in the history
- Remove `__setattr__`
- simplify `__init__`
  • Loading branch information
ndaelman committed Nov 6, 2024
1 parent a398c0a commit 76b6a5d
Showing 1 changed file with 40 additions and 75 deletions.
115 changes: 40 additions & 75 deletions src/nomad_simulations/schema_packages/physical_property.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ class PhysicalProperty(ArchiveSection):
or `'indirect'`.
""",
# ! add more examples in the description to improve the understanding of this quantity
)
) # ?

label = Quantity(
type=str,
Expand All @@ -118,26 +118,14 @@ class PhysicalProperty(ArchiveSection):
can be labeled as `'DFT'` or `'GW'` depending on the methodology used to calculate it.
""",
# ! add more examples in the description to improve the understanding of this quantity
)

rank = DirectQuantity(
type=Dimension,
shape=['0..*'],
default=[],
name='rank',
description="""
Rank of the tensor describing the physical property. This quantity is stored as a Dimension:
- scalars (tensor rank 0) have `rank=[]` (`len(rank) = 0`),
- vectors (tensor rank 1) have `rank=[a]` (`len(rank) = 1`),
- matrices (tensor rank 2), have `rank=[a, b]` (`len(rank) = 2`),
- etc.
""",
)
) # ?

variables = SubSection(sub_section=Variables.m_def, repeats=True)

value: Any = None

# * `value` must be overwritten in the derived classes defining its type, unit, and description
value: Quantity = _placeholder_quantity
_base_value: Quantity = _placeholder_quantity

entity_ref = Quantity(
type=Entity,
Expand Down Expand Up @@ -175,7 +163,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.
""",
)
) # TODO: move to numerical settings

self_consistency_ref = Quantity(
type=SelfConsistency,
Expand All @@ -186,7 +174,7 @@ class PhysicalProperty(ArchiveSection):
)

@property
def variables_shape(self) -> Optional[list]:
def variables_shape(self) -> list[int]:
"""
Shape of the variables over which the physical property varies. This is extracted from
`Variables.n_points` and appended in a list.
Expand All @@ -197,9 +185,26 @@ def variables_shape(self) -> Optional[list]:
Returns:
(list): The shape of the variables over which the physical property varies.
"""
if self.variables is not None:
return [v.get_n_points(logger) for v in self.variables]
if self.variables:
return [v.get_n_points(logger) for v in self.variables] # ! TODO: support any variable shape, not just vectors
return []

@property
def rank(self) -> list[int]:
"""
Rank of the physical property. This quantity is related with the order of the tensor of `value`.
Example: a physical property which is a 3D vector will have `rank=[3]`.
Returns:
(list): The rank of the physical property.
"""
if self._base_value:
if isinstance(shape := self._base_value.shape, list):
return shape
else:
return []
raise ValueError('The `_base_value` quantity is not defined.')

@property
def full_shape(self) -> list:
Expand All @@ -223,21 +228,16 @@ def full_shape(self) -> list:
return self.variables_shape + self.rank

@property
def _new_value(self) -> Quantity:
def _new_value(self) -> Optional[Quantity]:
"""
Initialize a new `Quantity` object for the `value` quantity with the correct `shape` extracted from
the `full_shape` attribute. This copies the main attributes from `value` (`type`, `description`, `unit`).
It is used in the `__setattr__` method.
Returns:
(Quantity): The new `Quantity` object for setting the `value` quantity.
Generate a new `Quantity` object for the `value` quantity based on `base_value` and with `shape=full_shape`.
"""
value_quantity = self.m_def.all_quantities.get('value')
value_quantity = self.m_def.all_quantities.get('_base_value')
if value_quantity is None:
return None
return Quantity(
type=value_quantity.type,
unit=value_quantity.unit, # ? this can be moved to __setattr__
unit=value_quantity.unit,
description=value_quantity.description,
)

Expand All @@ -247,53 +247,14 @@ def __init__(
super().__init__(m_def, m_context, **kwargs)

# Checking if IRI is defined
if self.iri is None:
if not self.iri:
logger.warning(
'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)
'The used property is not defined in the FAIRmat taxonomy (https://fairmat-nfdi.github.io/fairmat-taxonomy/).'
) # ?

def _is_derived(self) -> bool:
def _is_derived(self) -> bool: # ?
"""
Resolves if the physical property is derived or not.
Resolves whether the physical property is derived or not.
Returns:
(bool): The flag indicating whether the physical property is derived or not.
Expand All @@ -305,8 +266,12 @@ def _is_derived(self) -> bool:
def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None:
super().normalize(archive, logger)

# Resolve if the physical property `is_derived` or not from another physical property.
self.is_derived = self._is_derived()
if (new_value := self._new_value):
value_data = self.get('value')
# ? is it necessary to store the value data or can I set it right away?
self.m_def.value = new_value
self.value = value_data


class PropertyContribution(PhysicalProperty):
Expand Down

1 comment on commit 76b6a5d

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage

Coverage Report
FileStmtsMissCoverMissing
src/nomad_simulations
   __init__.py4250%3–4
   _version.py11282%5–6
src/nomad_simulations/schema_packages
   __init__.py15287%39–41
   atoms_state.py19012932%13–15, 159–179, 197–232, 245–276, 279–297, 344–355, 358–372, 491–506, 518–526, 529–550, 608–615, 627–634, 637–643
   basis_set.py24015336%8–9, 78–79, 115–119, 122–133, 162–165, 168–169, 172–185, 208, 264, 270–271, 275–279, 290–294, 298–302, 306–327, 335, 370–381, 385–397, 417–418, 453–466, 470, 475–476, 513–521, 530, 562–589, 595–600, 603–618, 647–698
   general.py894253%4–7, 51–54, 121, 185, 214–216, 228–286, 289–311
   model_method.py26915443%10–12, 73, 92, 125, 171–174, 177–184, 276–277, 297, 318–339, 355–381, 384–401, 493, 516–559, 562–587, 637–645, 704–706, 732–753, 756–762, 780, 791, 833–840, 878, 897, 977, 1034, 1109, 1223
   model_system.py34822037%45–51, 207–215, 219–225, 234–236, 243, 246, 250, 254, 258, 261, 264, 273–282, 286–291, 374–377, 381, 385, 389, 393, 397, 401, 404, 449–455, 466–475, 489–520, 531–548, 551–554, 685–712, 730–812, 815–831, 897–901, 904–925, 1131–1169, 1172–1211
   numerical_settings.py25919525%12–14, 36, 147, 175–191, 213–284, 374–394, 410–425, 444–466, 469–496, 561–582, 594–610, 632–662, 680–742, 745–766, 792–794, 809–827, 830–835, 884–886, 889
   outputs.py1206843%9–10, 153–159, 169–173, 183–187, 190–198, 241–259, 276–309, 312–329, 362, 381
   physical_property.py914155%20–22, 46–62, 188–190, 202–207, 228, 235–238, 251, 262–264, 267–274, 296–298
   variables.py862176%8–10, 63–70, 73–76, 98, 121, 145, 167, 189, 211, 233, 256, 272–273, 276
src/nomad_simulations/schema_packages/properties
   band_gap.py513237%8–10, 79–92, 104–126, 129–144
   band_structure.py1237638%9–11, 53–55, 58, 131–132, 144–159, 174–199, 209–218, 232–265, 274–286, 289–308, 321–322, 325, 372–373, 378
   energies.py421857%7–9, 36, 57, 77–79, 82, 99–100, 103, 115–116, 119, 130–131, 134
   fermi_surface.py17759%7–9, 34–37, 40
   forces.py22864%7–9, 36, 56, 75–76, 79
   greens_function.py995247%7–9, 135–137, 149–179, 182–189, 210–211, 214, 235–236, 239, 260–261, 264, 349–355, 365–367, 376–384, 387–403
   hopping_matrix.py291162%7–9, 50–53, 58, 88–91, 94
   permittivity.py483233%7–9, 53–56, 59–62, 71–94, 97–105
   spectral_profile.py26021517%9–11, 39–40, 49–51, 54–60, 80, 94–106, 109–112, 176–177, 199–300, 313–345, 356–368, 382–400, 415–463, 466–502, 521–523, 526, 555–557, 568–593, 598–604
   thermodynamics.py752764%7–9, 35, 56, 72, 81, 90, 101, 110, 137, 147, 157, 172–174, 177, 193, 213–215, 218, 234, 254–256, 259
src/nomad_simulations/schema_packages/utils
   utils.py795629%8–11, 48–58, 65–74, 78–84, 87–92, 96, 100, 117–122, 139–145, 154–156, 165–170
TOTAL2578156339% 

Tests Skipped Failures Errors Time
1 0 💤 0 ❌ 1 🔥 1.335s ⏱️

Please sign in to comment.