@@ -109,7 +109,7 @@ class PhysicalProperty(ArchiveSection):
109
109
or `'indirect'`.
110
110
""" ,
111
111
# ! add more examples in the description to improve the understanding of this quantity
112
- )
112
+ ) # ?
113
113
114
114
label = Quantity (
115
115
type = str ,
@@ -118,26 +118,14 @@ class PhysicalProperty(ArchiveSection):
118
118
can be labeled as `'DFT'` or `'GW'` depending on the methodology used to calculate it.
119
119
""" ,
120
120
# ! 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
+ ) # ?
136
122
137
123
variables = SubSection (sub_section = Variables .m_def , repeats = True )
138
124
125
+ value : Any = None
126
+
139
127
# * `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
141
129
142
130
entity_ref = Quantity (
143
131
type = Entity ,
@@ -175,7 +163,7 @@ class PhysicalProperty(ArchiveSection):
175
163
Flag indicating whether the physical property is converged or not after a SCF process. This quantity is connected
176
164
with `SelfConsistency` defined in the `numerical_settings.py` module.
177
165
""" ,
178
- )
166
+ ) # TODO: move to numerical settings
179
167
180
168
self_consistency_ref = Quantity (
181
169
type = SelfConsistency ,
@@ -186,7 +174,7 @@ class PhysicalProperty(ArchiveSection):
186
174
)
187
175
188
176
@property
189
- def variables_shape (self ) -> Optional [ list ]:
177
+ def variables_shape (self ) -> list [ int ]:
190
178
"""
191
179
Shape of the variables over which the physical property varies. This is extracted from
192
180
`Variables.n_points` and appended in a list.
@@ -197,9 +185,26 @@ def variables_shape(self) -> Optional[list]:
197
185
Returns:
198
186
(list): The shape of the variables over which the physical property varies.
199
187
"""
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
202
190
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.' )
203
208
204
209
@property
205
210
def full_shape (self ) -> list :
@@ -223,21 +228,16 @@ def full_shape(self) -> list:
223
228
return self .variables_shape + self .rank
224
229
225
230
@property
226
- def _new_value (self ) -> Quantity :
231
+ def _new_value (self ) -> Optional [ Quantity ] :
227
232
"""
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`.
234
234
"""
235
- value_quantity = self .m_def .all_quantities .get ('value ' )
235
+ value_quantity = self .m_def .all_quantities .get ('_base_value ' )
236
236
if value_quantity is None :
237
237
return None
238
238
return Quantity (
239
239
type = value_quantity .type ,
240
- unit = value_quantity .unit , # ? this can be moved to __setattr__
240
+ unit = value_quantity .unit ,
241
241
description = value_quantity .description ,
242
242
)
243
243
@@ -247,53 +247,14 @@ def __init__(
247
247
super ().__init__ (m_def , m_context , ** kwargs )
248
248
249
249
# Checking if IRI is defined
250
- if self .iri is None :
250
+ if not self .iri :
251
251
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
+ ) # ?
293
254
294
- def _is_derived (self ) -> bool :
255
+ def _is_derived (self ) -> bool : # ?
295
256
"""
296
- Resolves if the physical property is derived or not.
257
+ Resolves whether the physical property is derived or not.
297
258
298
259
Returns:
299
260
(bool): The flag indicating whether the physical property is derived or not.
@@ -305,8 +266,12 @@ def _is_derived(self) -> bool:
305
266
def normalize (self , archive : 'EntryArchive' , logger : 'BoundLogger' ) -> None :
306
267
super ().normalize (archive , logger )
307
268
308
- # Resolve if the physical property `is_derived` or not from another physical property.
309
269
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
310
275
311
276
312
277
class PropertyContribution (PhysicalProperty ):
0 commit comments