Skip to content

Commit 76b6a5d

Browse files
author
ndaelman
committed
- Introduce _base_value as a pre-normalization of value
- Remove `__setattr__` - simplify `__init__`
1 parent a398c0a commit 76b6a5d

File tree

1 file changed

+40
-75
lines changed

1 file changed

+40
-75
lines changed

src/nomad_simulations/schema_packages/physical_property.py

Lines changed: 40 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ class PhysicalProperty(ArchiveSection):
109109
or `'indirect'`.
110110
""",
111111
# ! add more examples in the description to improve the understanding of this quantity
112-
)
112+
) # ?
113113

114114
label = Quantity(
115115
type=str,
@@ -118,26 +118,14 @@ class PhysicalProperty(ArchiveSection):
118118
can be labeled as `'DFT'` or `'GW'` depending on the methodology used to calculate it.
119119
""",
120120
# ! add more examples in the description to improve the understanding of this quantity
121-
)
122-
123-
rank = DirectQuantity(
124-
type=Dimension,
125-
shape=['0..*'],
126-
default=[],
127-
name='rank',
128-
description="""
129-
Rank of the tensor describing the physical property. This quantity is stored as a Dimension:
130-
- scalars (tensor rank 0) have `rank=[]` (`len(rank) = 0`),
131-
- vectors (tensor rank 1) have `rank=[a]` (`len(rank) = 1`),
132-
- matrices (tensor rank 2), have `rank=[a, b]` (`len(rank) = 2`),
133-
- etc.
134-
""",
135-
)
121+
) # ?
136122

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

125+
value: Any = None
126+
139127
# * `value` must be overwritten in the derived classes defining its type, unit, and description
140-
value: Quantity = _placeholder_quantity
128+
_base_value: Quantity = _placeholder_quantity
141129

142130
entity_ref = Quantity(
143131
type=Entity,
@@ -175,7 +163,7 @@ class PhysicalProperty(ArchiveSection):
175163
Flag indicating whether the physical property is converged or not after a SCF process. This quantity is connected
176164
with `SelfConsistency` defined in the `numerical_settings.py` module.
177165
""",
178-
)
166+
) # TODO: move to numerical settings
179167

180168
self_consistency_ref = Quantity(
181169
type=SelfConsistency,
@@ -186,7 +174,7 @@ class PhysicalProperty(ArchiveSection):
186174
)
187175

188176
@property
189-
def variables_shape(self) -> Optional[list]:
177+
def variables_shape(self) -> list[int]:
190178
"""
191179
Shape of the variables over which the physical property varies. This is extracted from
192180
`Variables.n_points` and appended in a list.
@@ -197,9 +185,26 @@ def variables_shape(self) -> Optional[list]:
197185
Returns:
198186
(list): The shape of the variables over which the physical property varies.
199187
"""
200-
if self.variables is not None:
201-
return [v.get_n_points(logger) for v in self.variables]
188+
if self.variables:
189+
return [v.get_n_points(logger) for v in self.variables] # ! TODO: support any variable shape, not just vectors
202190
return []
191+
192+
@property
193+
def rank(self) -> list[int]:
194+
"""
195+
Rank of the physical property. This quantity is related with the order of the tensor of `value`.
196+
197+
Example: a physical property which is a 3D vector will have `rank=[3]`.
198+
199+
Returns:
200+
(list): The rank of the physical property.
201+
"""
202+
if self._base_value:
203+
if isinstance(shape := self._base_value.shape, list):
204+
return shape
205+
else:
206+
return []
207+
raise ValueError('The `_base_value` quantity is not defined.')
203208

204209
@property
205210
def full_shape(self) -> list:
@@ -223,21 +228,16 @@ def full_shape(self) -> list:
223228
return self.variables_shape + self.rank
224229

225230
@property
226-
def _new_value(self) -> Quantity:
231+
def _new_value(self) -> Optional[Quantity]:
227232
"""
228-
Initialize a new `Quantity` object for the `value` quantity with the correct `shape` extracted from
229-
the `full_shape` attribute. This copies the main attributes from `value` (`type`, `description`, `unit`).
230-
It is used in the `__setattr__` method.
231-
232-
Returns:
233-
(Quantity): The new `Quantity` object for setting the `value` quantity.
233+
Generate a new `Quantity` object for the `value` quantity based on `base_value` and with `shape=full_shape`.
234234
"""
235-
value_quantity = self.m_def.all_quantities.get('value')
235+
value_quantity = self.m_def.all_quantities.get('_base_value')
236236
if value_quantity is None:
237237
return None
238238
return Quantity(
239239
type=value_quantity.type,
240-
unit=value_quantity.unit, # ? this can be moved to __setattr__
240+
unit=value_quantity.unit,
241241
description=value_quantity.description,
242242
)
243243

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

249249
# Checking if IRI is defined
250-
if self.iri is None:
250+
if not self.iri:
251251
logger.warning(
252-
'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.'
253-
)
254-
255-
# Checking if the quantities `n_` are defined, as this are used to calculate `rank`
256-
for quantity, _ in self.m_def.all_quantities.items():
257-
if quantity.startswith('n_') and getattr(self, quantity) is None:
258-
raise ValueError(
259-
f'`{quantity}` is not defined during initialization of the class.'
260-
)
261-
262-
def __setattr__(self, name: str, val: Any) -> None:
263-
# For the special case of `value`, its `shape` needs to be defined from `_full_shape`
264-
if name == 'value':
265-
if val is None:
266-
raise ValueError(
267-
f'The value of the physical property {self.name} is None. Please provide a finite valid value.'
268-
)
269-
_new_value = self._new_value
270-
271-
# patch for when `val` does not have units and it is passed as a list (instead of np.array)
272-
if isinstance(val, list):
273-
val = np.array(val)
274-
275-
# non-scalar or scalar `val`
276-
try:
277-
value_shape = list(val.shape)
278-
except AttributeError:
279-
value_shape = []
280-
281-
if value_shape != self.full_shape:
282-
raise ValueError(
283-
f'The shape of the stored `value` {value_shape} does not match the full shape {self.full_shape} '
284-
f'extracted from the variables `n_points` and the `shape` defined in `PhysicalProperty`.'
285-
)
286-
_new_value.shape = self.full_shape
287-
if hasattr(val, 'magnitude'):
288-
_new_value = val.magnitude * val.u
289-
else:
290-
_new_value = val
291-
return super().__setattr__(name, _new_value)
292-
return super().__setattr__(name, val)
252+
'The used property is not defined in the FAIRmat taxonomy (https://fairmat-nfdi.github.io/fairmat-taxonomy/).'
253+
) # ?
293254

294-
def _is_derived(self) -> bool:
255+
def _is_derived(self) -> bool: # ?
295256
"""
296-
Resolves if the physical property is derived or not.
257+
Resolves whether the physical property is derived or not.
297258
298259
Returns:
299260
(bool): The flag indicating whether the physical property is derived or not.
@@ -305,8 +266,12 @@ def _is_derived(self) -> bool:
305266
def normalize(self, archive: 'EntryArchive', logger: 'BoundLogger') -> None:
306267
super().normalize(archive, logger)
307268

308-
# Resolve if the physical property `is_derived` or not from another physical property.
309269
self.is_derived = self._is_derived()
270+
if (new_value := self._new_value):
271+
value_data = self.get('value')
272+
# ? is it necessary to store the value data or can I set it right away?
273+
self.m_def.value = new_value
274+
self.value = value_data
310275

311276

312277
class PropertyContribution(PhysicalProperty):

0 commit comments

Comments
 (0)