pydasa 0.4.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pydasa/__init__.py +103 -0
- pydasa/_version.py +6 -0
- pydasa/analysis/__init__.py +0 -0
- pydasa/analysis/scenario.py +584 -0
- pydasa/analysis/simulation.py +1158 -0
- pydasa/context/__init__.py +0 -0
- pydasa/context/conversion.py +11 -0
- pydasa/context/system.py +17 -0
- pydasa/context/units.py +15 -0
- pydasa/core/__init__.py +15 -0
- pydasa/core/basic.py +287 -0
- pydasa/core/cfg/default.json +136 -0
- pydasa/core/constants.py +27 -0
- pydasa/core/io.py +102 -0
- pydasa/core/setup.py +269 -0
- pydasa/dimensional/__init__.py +0 -0
- pydasa/dimensional/buckingham.py +728 -0
- pydasa/dimensional/fundamental.py +146 -0
- pydasa/dimensional/model.py +1077 -0
- pydasa/dimensional/vaschy.py +633 -0
- pydasa/elements/__init__.py +19 -0
- pydasa/elements/parameter.py +218 -0
- pydasa/elements/specs/__init__.py +22 -0
- pydasa/elements/specs/conceptual.py +161 -0
- pydasa/elements/specs/numerical.py +469 -0
- pydasa/elements/specs/statistical.py +229 -0
- pydasa/elements/specs/symbolic.py +394 -0
- pydasa/serialization/__init__.py +27 -0
- pydasa/serialization/parser.py +133 -0
- pydasa/structs/__init__.py +0 -0
- pydasa/structs/lists/__init__.py +0 -0
- pydasa/structs/lists/arlt.py +578 -0
- pydasa/structs/lists/dllt.py +18 -0
- pydasa/structs/lists/ndlt.py +262 -0
- pydasa/structs/lists/sllt.py +746 -0
- pydasa/structs/tables/__init__.py +0 -0
- pydasa/structs/tables/htme.py +182 -0
- pydasa/structs/tables/scht.py +774 -0
- pydasa/structs/tools/__init__.py +0 -0
- pydasa/structs/tools/hashing.py +53 -0
- pydasa/structs/tools/math.py +149 -0
- pydasa/structs/tools/memory.py +54 -0
- pydasa/structs/types/__init__.py +0 -0
- pydasa/structs/types/functions.py +131 -0
- pydasa/structs/types/generics.py +54 -0
- pydasa/validations/__init__.py +0 -0
- pydasa/validations/decorators.py +510 -0
- pydasa/validations/error.py +100 -0
- pydasa/validations/patterns.py +32 -0
- pydasa/workflows/__init__.py +1 -0
- pydasa/workflows/influence.py +497 -0
- pydasa/workflows/phenomena.py +529 -0
- pydasa/workflows/practical.py +765 -0
- pydasa-0.4.7.dist-info/METADATA +320 -0
- pydasa-0.4.7.dist-info/RECORD +58 -0
- pydasa-0.4.7.dist-info/WHEEL +5 -0
- pydasa-0.4.7.dist-info/licenses/LICENSE +674 -0
- pydasa-0.4.7.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,728 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Module vashchy.py
|
|
4
|
+
===========================================
|
|
5
|
+
|
|
6
|
+
Module for representing Dimensionless Coefficients in Dimensional Analysis for *PyDASA*.
|
|
7
|
+
|
|
8
|
+
This module provides the Coefficient class which models dimensionless numbers used in Vaschy-Buckingham's Pi-theorem for dimensional analysis.
|
|
9
|
+
|
|
10
|
+
Classes:
|
|
11
|
+
|
|
12
|
+
**Coefficient**: Represents a dimensionless coefficient with properties, validation, and symbolic expression.
|
|
13
|
+
|
|
14
|
+
*IMPORTANT:* Based on the theory from:
|
|
15
|
+
|
|
16
|
+
# H.Gorter, *Dimensionalanalyse: Eine Theoririe der physikalischen Dimensionen mit Anwendungen*
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
# native python modules
|
|
20
|
+
# dataclass imports
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
from dataclasses import dataclass, field, fields
|
|
23
|
+
from typing import Optional, List, Dict, Any, Tuple, Union, Sequence
|
|
24
|
+
# numerical imports
|
|
25
|
+
import numpy as np
|
|
26
|
+
|
|
27
|
+
# custom modules
|
|
28
|
+
# basic-core class imports
|
|
29
|
+
from pydasa.core.basic import Foundation
|
|
30
|
+
from pydasa.elements.parameter import Variable
|
|
31
|
+
# import global variables
|
|
32
|
+
from pydasa.core.setup import CoefCardinality
|
|
33
|
+
from pydasa.core.setup import PYDASA_CFG
|
|
34
|
+
# generic error handling and type checking
|
|
35
|
+
from pydasa.validations.error import inspect_var
|
|
36
|
+
# pattern interpreter imports
|
|
37
|
+
from pydasa.serialization.parser import latex_to_python
|
|
38
|
+
# import validation decorators
|
|
39
|
+
from pydasa.validations.decorators import validate_type
|
|
40
|
+
from pydasa.validations.decorators import validate_custom
|
|
41
|
+
from pydasa.validations.decorators import validate_range
|
|
42
|
+
from pydasa.validations.decorators import validate_choices
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass
|
|
46
|
+
class Coefficient(Foundation):
|
|
47
|
+
"""**Coefficient** class for Dimensional Analysis in *PyDASA*.
|
|
48
|
+
|
|
49
|
+
A comprehensive implementation that represents dimensionless coefficients
|
|
50
|
+
(Pi numbers) used in the Vaschy-Buckingham Pi-theorem method.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
Foundation: Foundation class for validation of symbols and frameworks.
|
|
54
|
+
|
|
55
|
+
Attributes:
|
|
56
|
+
# Identification and Classification
|
|
57
|
+
name (str): User-friendly name of the coefficient.
|
|
58
|
+
description (str): Brief summary of the coefficient.
|
|
59
|
+
_idx (int): Index/precedence in the dimensional matrix.
|
|
60
|
+
_sym (str): Symbol representation (LaTeX or alphanumeric).
|
|
61
|
+
_alias (str): Python-compatible alias for use in code.
|
|
62
|
+
_fwk (str): Frameworks context (PHYSICAL, COMPUTATION, SOFTWARE, CUSTOM).
|
|
63
|
+
_cat (str): Category (COMPUTED, DERIVED).
|
|
64
|
+
relevance (bool): Flag indicating if coefficient is relevant for analysis.
|
|
65
|
+
|
|
66
|
+
# Coefficient Construction
|
|
67
|
+
_variables (Dict[str, Variable]): Variables symbols used in coefficient.
|
|
68
|
+
_dim_col (List[int]): Dimensional column for matrix operations.
|
|
69
|
+
_pivot_lt (List[int]): Pivot indices in dimensional matrix.
|
|
70
|
+
_pi_expr (str): Symbolic expression of coefficient.
|
|
71
|
+
var_dims (Dict[str, int]): Dimensional variable exponents.
|
|
72
|
+
|
|
73
|
+
# Value Ranges
|
|
74
|
+
_min (float): Minimum value of the coefficient.
|
|
75
|
+
_max (float): Maximum value of the coefficient.
|
|
76
|
+
_mean (float): Average value of the coefficient.
|
|
77
|
+
_step (float): Step size for simulations.
|
|
78
|
+
_data (np.ndarray): Array of coefficient values for analysis.
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
# Category attribute (COMPUTED, DERIVED)
|
|
82
|
+
# :attr: _cat
|
|
83
|
+
_cat: str = CoefCardinality.COMPUTED.value
|
|
84
|
+
"""Category of the coefficient (COMPUTED, DERIVED)."""
|
|
85
|
+
|
|
86
|
+
# Coefficient construction properties
|
|
87
|
+
# :attr: _variables
|
|
88
|
+
_variables: Dict[str, Variable] = field(default_factory=dict)
|
|
89
|
+
"""Variables symbols used in the coefficient."""
|
|
90
|
+
|
|
91
|
+
# Coefficient calculation related variables
|
|
92
|
+
# :attr: _dim_col
|
|
93
|
+
_dim_col: List[int] = field(default_factory=list)
|
|
94
|
+
"""Dimensional column for matrix operations."""
|
|
95
|
+
|
|
96
|
+
# :attr: var_dims
|
|
97
|
+
var_dims: Dict[str, int] = field(default_factory=dict)
|
|
98
|
+
"""Dimensional variable exponents in coefficient."""
|
|
99
|
+
|
|
100
|
+
# :attr: _pivot_lt
|
|
101
|
+
_pivot_lt: Optional[List[int]] = field(default_factory=list)
|
|
102
|
+
"""Pivot indices in dimensional matrix."""
|
|
103
|
+
|
|
104
|
+
# :attr: _pi_expr
|
|
105
|
+
_pi_expr: Optional[str] = None
|
|
106
|
+
"""Symbolic expression of coefficient."""
|
|
107
|
+
|
|
108
|
+
# Value ranges Variable
|
|
109
|
+
# :attr: _min
|
|
110
|
+
_min: Optional[float] = None
|
|
111
|
+
"""Minimum value of the coefficient, always in standardized units."""
|
|
112
|
+
|
|
113
|
+
# :attr: _max
|
|
114
|
+
_max: Optional[float] = None
|
|
115
|
+
"""Maximum value of the coefficient, always in standardized units."""
|
|
116
|
+
|
|
117
|
+
# :attr: _mean
|
|
118
|
+
_mean: Optional[float] = None
|
|
119
|
+
"""Average value of the coefficient, always in standardized units."""
|
|
120
|
+
|
|
121
|
+
# :attr: _dev
|
|
122
|
+
_dev: Optional[float] = None
|
|
123
|
+
"""Standard deviation of the coefficient, always in standardized units."""
|
|
124
|
+
|
|
125
|
+
# :attr: _step
|
|
126
|
+
_step: Optional[float] = 1e-3
|
|
127
|
+
"""Step size for simulations, always in standardized units."""
|
|
128
|
+
|
|
129
|
+
# :attr: _data
|
|
130
|
+
_data: np.ndarray = field(default_factory=lambda: np.array([]))
|
|
131
|
+
"""Array of coefficient values for analysis."""
|
|
132
|
+
|
|
133
|
+
# Flags
|
|
134
|
+
# :attr: relevance
|
|
135
|
+
relevance: bool = True
|
|
136
|
+
"""Flag indicating if coefficient is relevant for analysis."""
|
|
137
|
+
|
|
138
|
+
def __post_init__(self) -> None:
|
|
139
|
+
"""*__post_init__()* Initializes the coefficient and validates its properties.
|
|
140
|
+
|
|
141
|
+
Performs validation of core properties and builds the coefficient expression
|
|
142
|
+
based on variable symbols and their respective dimensional exponents.
|
|
143
|
+
|
|
144
|
+
Raises:
|
|
145
|
+
ValueError: If variable list and dimensional column have different lengths.
|
|
146
|
+
"""
|
|
147
|
+
# Initialize from base class
|
|
148
|
+
super().__post_init__()
|
|
149
|
+
|
|
150
|
+
# Set the Pi symbol if not specified
|
|
151
|
+
if not self._sym:
|
|
152
|
+
if self._idx >= 0:
|
|
153
|
+
self._sym = f"\\Pi_{{{self._idx}}}"
|
|
154
|
+
else:
|
|
155
|
+
self._sym = "\\Pi_{}"
|
|
156
|
+
# Set the Python alias if not specified
|
|
157
|
+
if not self._alias:
|
|
158
|
+
self._alias = latex_to_python(self._sym)
|
|
159
|
+
|
|
160
|
+
self.cat = self._cat
|
|
161
|
+
self.variables = self._variables
|
|
162
|
+
|
|
163
|
+
# Defaults to empty list
|
|
164
|
+
self.dim_col = self._dim_col or []
|
|
165
|
+
self.pivot_lt = self._pivot_lt or []
|
|
166
|
+
|
|
167
|
+
# Only build expression if parameters and dimensions are provided
|
|
168
|
+
if len(self._variables) > 0 and len(self._dim_col) > 0:
|
|
169
|
+
var_keys = list(self._variables.keys())
|
|
170
|
+
self.pi_expr, self.var_dims = self._build_expression(var_keys,
|
|
171
|
+
self._dim_col)
|
|
172
|
+
|
|
173
|
+
else:
|
|
174
|
+
self.pi_expr = ""
|
|
175
|
+
self.var_dims = {}
|
|
176
|
+
|
|
177
|
+
# Set data
|
|
178
|
+
self.data = self._data
|
|
179
|
+
|
|
180
|
+
# Set up data array if all required values are provided
|
|
181
|
+
if all([self._min, self._max, self._step]):
|
|
182
|
+
self._data = np.arange(self._min, self._max, self._step)
|
|
183
|
+
|
|
184
|
+
def _validate_sequence(self, seq: Sequence,
|
|
185
|
+
exp_type: Union[type, Tuple[type, ...]]) -> bool:
|
|
186
|
+
"""*_validate_sequence()* Validates a list with expected element types.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
seq (Sequence): Sequence to validate.
|
|
190
|
+
exp_type (Union[type, Tuple[type, ...]]): Expected type(s) for sequence elements. Can be a single type or a tuple of types.
|
|
191
|
+
|
|
192
|
+
Raises:
|
|
193
|
+
ValueError: If the object is not a sequence.
|
|
194
|
+
ValueError: If the sequence is empty.
|
|
195
|
+
ValueError: If the sequence contains elements of unexpected types.
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
bool: True if the list is valid.
|
|
199
|
+
"""
|
|
200
|
+
# Explicitly reject strings (they're sequences but not what we want)
|
|
201
|
+
if isinstance(seq, str):
|
|
202
|
+
_msg = f"{inspect_var(seq)} must be a list or tuple, not a string. "
|
|
203
|
+
_msg += f"Provided: {type(seq).__name__}"
|
|
204
|
+
raise ValueError(_msg)
|
|
205
|
+
|
|
206
|
+
if not isinstance(seq, Sequence):
|
|
207
|
+
_msg = f"{inspect_var(seq)} must be from type: '{exp_type}', "
|
|
208
|
+
_msg += f"Provided: {type(seq).__name__}"
|
|
209
|
+
raise ValueError(_msg)
|
|
210
|
+
|
|
211
|
+
if len(seq) == 0:
|
|
212
|
+
_msg = f"{inspect_var(seq)} cannot be empty. Actual sequence: {seq}"
|
|
213
|
+
raise ValueError(_msg)
|
|
214
|
+
|
|
215
|
+
# Convert list to tuple if needed for isinstance(), just in case
|
|
216
|
+
type_check = exp_type if isinstance(exp_type, tuple) else (exp_type,)
|
|
217
|
+
|
|
218
|
+
if not all(isinstance(x, type_check) for x in seq):
|
|
219
|
+
|
|
220
|
+
# Format expected types for error message
|
|
221
|
+
if isinstance(exp_type, tuple):
|
|
222
|
+
type_names = " or ".join(t.__name__ for t in exp_type)
|
|
223
|
+
else:
|
|
224
|
+
type_names = exp_type.__name__
|
|
225
|
+
|
|
226
|
+
_msg = f"{inspect_var(seq)} must contain {type_names} elements."
|
|
227
|
+
_msg += f" Provided: {[type(x).__name__ for x in seq]}"
|
|
228
|
+
raise ValueError(_msg)
|
|
229
|
+
|
|
230
|
+
return True
|
|
231
|
+
|
|
232
|
+
def _build_expression(self,
|
|
233
|
+
var_lt: List[str],
|
|
234
|
+
dim_col: List[int]) -> tuple[str, dict]:
|
|
235
|
+
"""*_build_expression()* Builds LaTeX expression for coefficient.
|
|
236
|
+
|
|
237
|
+
Args:
|
|
238
|
+
var_lt (List[str]): List of variable symbols.
|
|
239
|
+
dim_col (List[int]): List of dimensional exponents.
|
|
240
|
+
|
|
241
|
+
Raises:
|
|
242
|
+
ValueError: If variable list and dimensional column have different lengths.
|
|
243
|
+
|
|
244
|
+
Returns:
|
|
245
|
+
tuple[str, Dict[str, int]]: LaTeX expression and variable exponents.
|
|
246
|
+
"""
|
|
247
|
+
# Validate variable list and dimensional column
|
|
248
|
+
if len(var_lt) != len(dim_col):
|
|
249
|
+
_msg = f"Variables list len ({len(var_lt)}) and "
|
|
250
|
+
_msg += f"dimensional column len ({len(dim_col)}) must be equal."
|
|
251
|
+
raise ValueError(_msg)
|
|
252
|
+
|
|
253
|
+
# Initialize working variables
|
|
254
|
+
numerator = []
|
|
255
|
+
denominator = []
|
|
256
|
+
parameters = {}
|
|
257
|
+
|
|
258
|
+
# Process parameters and their exponents
|
|
259
|
+
for sym, exp in zip(var_lt, dim_col):
|
|
260
|
+
# Add to numerator if exponent is positive
|
|
261
|
+
if exp > 0:
|
|
262
|
+
part = sym if exp == 1 else f"{sym}^{{{exp}}}"
|
|
263
|
+
numerator.append(part)
|
|
264
|
+
# Add to denominator if exponent is negative
|
|
265
|
+
elif exp < 0:
|
|
266
|
+
part = sym if exp == -1 else f"{sym}^{{{-exp}}}"
|
|
267
|
+
denominator.append(part)
|
|
268
|
+
# Skip zero exponents
|
|
269
|
+
else:
|
|
270
|
+
continue
|
|
271
|
+
# Store variable exponent
|
|
272
|
+
parameters[sym] = exp
|
|
273
|
+
|
|
274
|
+
# Build expression
|
|
275
|
+
num_str = "1" if not numerator else "*".join(numerator)
|
|
276
|
+
|
|
277
|
+
# Return expression based on whether denominator exists
|
|
278
|
+
if not denominator:
|
|
279
|
+
return num_str, parameters
|
|
280
|
+
else:
|
|
281
|
+
denom_str = "*".join(denominator)
|
|
282
|
+
return f"\\frac{{{num_str}}}{{{denom_str}}}", parameters
|
|
283
|
+
|
|
284
|
+
# Custom validators for decorators
|
|
285
|
+
|
|
286
|
+
def _validate_variables_dict(self, value: Dict[str, Variable]) -> None:
|
|
287
|
+
"""*_validate_variables_dict()* Custom validator for variables property.
|
|
288
|
+
|
|
289
|
+
Args:
|
|
290
|
+
value (Dict[str, Variable]): Variables dictionary to validate.
|
|
291
|
+
|
|
292
|
+
Raises:
|
|
293
|
+
ValueError: If dict has invalid keys or values.
|
|
294
|
+
"""
|
|
295
|
+
if len(value) > 0:
|
|
296
|
+
self._validate_sequence(list(value.keys()), (str,))
|
|
297
|
+
self._validate_sequence(list(value.values()), (Variable,))
|
|
298
|
+
|
|
299
|
+
def _validate_dim_col_list(self, value: List) -> None:
|
|
300
|
+
"""*_validate_dim_col_list()* Custom validator for dim_col property.
|
|
301
|
+
|
|
302
|
+
Args:
|
|
303
|
+
value (List): Dimensional column list to validate.
|
|
304
|
+
|
|
305
|
+
Raises:
|
|
306
|
+
ValueError: If list has invalid elements.
|
|
307
|
+
"""
|
|
308
|
+
if len(value) > 0:
|
|
309
|
+
self._validate_sequence(value, (int, float))
|
|
310
|
+
|
|
311
|
+
def _validate_pivot_list(self, value: Optional[List[int]]) -> None:
|
|
312
|
+
"""*_validate_pivot_list()* Custom validator for pivot_lt property.
|
|
313
|
+
|
|
314
|
+
Args:
|
|
315
|
+
value (Optional[List[int]]): Pivot list to validate.
|
|
316
|
+
|
|
317
|
+
Raises:
|
|
318
|
+
ValueError: If non-empty list has invalid elements.
|
|
319
|
+
"""
|
|
320
|
+
if value is not None and len(value) > 0:
|
|
321
|
+
self._validate_sequence(value, (int,))
|
|
322
|
+
|
|
323
|
+
def _validate_step_value(self, value: Optional[float]) -> None:
|
|
324
|
+
"""*_validate_step_value()* Custom validator for step property.
|
|
325
|
+
|
|
326
|
+
Args:
|
|
327
|
+
value (Optional[float]): Step value to validate.
|
|
328
|
+
|
|
329
|
+
Raises:
|
|
330
|
+
ValueError: If step is zero or exceeds range.
|
|
331
|
+
"""
|
|
332
|
+
if value == 0:
|
|
333
|
+
raise ValueError("Step cannot be zero.")
|
|
334
|
+
|
|
335
|
+
if value is not None and self._min is not None and self._max is not None:
|
|
336
|
+
range_size = self._max - self._min
|
|
337
|
+
if value >= range_size:
|
|
338
|
+
_msg = f"Step {value} must be less than range: {range_size}."
|
|
339
|
+
raise ValueError(_msg)
|
|
340
|
+
|
|
341
|
+
# Property getters and setters
|
|
342
|
+
|
|
343
|
+
@property
|
|
344
|
+
def cat(self) -> str:
|
|
345
|
+
"""*cat* Get the coefficient category.
|
|
346
|
+
|
|
347
|
+
Returns:
|
|
348
|
+
str: Category (COMPUTED, DERIVED).
|
|
349
|
+
"""
|
|
350
|
+
return self._cat
|
|
351
|
+
|
|
352
|
+
@cat.setter
|
|
353
|
+
@validate_choices(PYDASA_CFG.coefficient_cardinality)
|
|
354
|
+
def cat(self, val: str) -> None:
|
|
355
|
+
"""*cat* Set the coefficient category.
|
|
356
|
+
|
|
357
|
+
Args:
|
|
358
|
+
val (str): Category value.
|
|
359
|
+
|
|
360
|
+
Raises:
|
|
361
|
+
ValueError: If category is not supported.
|
|
362
|
+
"""
|
|
363
|
+
self._cat = val.upper()
|
|
364
|
+
|
|
365
|
+
@property
|
|
366
|
+
def variables(self) -> Dict[str, Variable]:
|
|
367
|
+
"""*variables* Get the variable symbols list.
|
|
368
|
+
|
|
369
|
+
Returns:
|
|
370
|
+
Dict[str, Variable]: Variables symbols list.
|
|
371
|
+
"""
|
|
372
|
+
return self._variables
|
|
373
|
+
|
|
374
|
+
@variables.setter
|
|
375
|
+
@validate_type(dict, allow_none=False)
|
|
376
|
+
@validate_custom(lambda self, val: self._validate_variables_dict(val))
|
|
377
|
+
def variables(self, val: Dict[str, Variable]) -> None:
|
|
378
|
+
"""*variables* Set the variable symbols list.
|
|
379
|
+
|
|
380
|
+
Args:
|
|
381
|
+
val (Dict[str, Variable]): Variables symbols list.
|
|
382
|
+
|
|
383
|
+
Raises:
|
|
384
|
+
ValueError: If variables list is invalid.
|
|
385
|
+
"""
|
|
386
|
+
self._variables = val
|
|
387
|
+
|
|
388
|
+
@property
|
|
389
|
+
def dim_col(self) -> List[int]:
|
|
390
|
+
"""*dim_col* Get the dimensional column.
|
|
391
|
+
|
|
392
|
+
Returns:
|
|
393
|
+
List[int]: Dimensional column.
|
|
394
|
+
"""
|
|
395
|
+
return self._dim_col
|
|
396
|
+
|
|
397
|
+
@dim_col.setter
|
|
398
|
+
@validate_type(list, allow_none=False)
|
|
399
|
+
@validate_custom(lambda self, val: self._validate_dim_col_list(val))
|
|
400
|
+
def dim_col(self, val: Union[List[int], List[float]]) -> None:
|
|
401
|
+
"""*dim_col* Set the dimensional column.
|
|
402
|
+
|
|
403
|
+
Args:
|
|
404
|
+
val (Union[List[int], List[float]]): Dimensional column.
|
|
405
|
+
Raises:
|
|
406
|
+
ValueError: If dimensional column is invalid.
|
|
407
|
+
"""
|
|
408
|
+
# Convert to integers
|
|
409
|
+
self._dim_col = [int(x) for x in val]
|
|
410
|
+
|
|
411
|
+
@property
|
|
412
|
+
def pivot_lt(self) -> Optional[List[int]]:
|
|
413
|
+
"""*pivot_lt* Get the pivot indices list.
|
|
414
|
+
|
|
415
|
+
Returns:
|
|
416
|
+
Optional[List[int]]: Pivot indices list.
|
|
417
|
+
"""
|
|
418
|
+
return self._pivot_lt
|
|
419
|
+
|
|
420
|
+
@pivot_lt.setter
|
|
421
|
+
@validate_custom(lambda self, val: self._validate_pivot_list(val))
|
|
422
|
+
def pivot_lt(self, val: List[int]) -> None:
|
|
423
|
+
"""*pivot_lt* Set the pivot indices list.
|
|
424
|
+
|
|
425
|
+
Args:
|
|
426
|
+
val (List[int]): Pivot indices list.
|
|
427
|
+
|
|
428
|
+
Raises:
|
|
429
|
+
ValueError: If pivot list is invalid.
|
|
430
|
+
"""
|
|
431
|
+
self._pivot_lt = val
|
|
432
|
+
|
|
433
|
+
@property
|
|
434
|
+
def pi_expr(self) -> Optional[str]:
|
|
435
|
+
"""*pi_expr* Get the coefficient expression.
|
|
436
|
+
|
|
437
|
+
Returns:
|
|
438
|
+
Optional[str]: Coefficient expression.
|
|
439
|
+
"""
|
|
440
|
+
return self._pi_expr
|
|
441
|
+
|
|
442
|
+
@pi_expr.setter
|
|
443
|
+
@validate_type(str, allow_none=False)
|
|
444
|
+
def pi_expr(self, val: str) -> None:
|
|
445
|
+
"""*pi_expr* Set the coefficient expression.
|
|
446
|
+
|
|
447
|
+
Args:
|
|
448
|
+
val (str): Coefficient expression.
|
|
449
|
+
|
|
450
|
+
Raises:
|
|
451
|
+
ValueError: If the coefficient expression is not a string.
|
|
452
|
+
"""
|
|
453
|
+
self._pi_expr = val
|
|
454
|
+
|
|
455
|
+
# Value range properties
|
|
456
|
+
|
|
457
|
+
@property
|
|
458
|
+
def min(self) -> Optional[float]:
|
|
459
|
+
"""*min* Get minimum range value.
|
|
460
|
+
|
|
461
|
+
Returns:
|
|
462
|
+
Optional[float]: Minimum range value.
|
|
463
|
+
"""
|
|
464
|
+
return self._min
|
|
465
|
+
|
|
466
|
+
@min.setter
|
|
467
|
+
@validate_type(int, float)
|
|
468
|
+
@validate_range(max_attr='_max')
|
|
469
|
+
def min(self, val: Optional[float]) -> None:
|
|
470
|
+
"""*min* Sets minimum range value.
|
|
471
|
+
|
|
472
|
+
Args:
|
|
473
|
+
val (Optional[float]): Minimum range value.
|
|
474
|
+
|
|
475
|
+
Raises:
|
|
476
|
+
ValueError: If value is not a number.
|
|
477
|
+
ValueError: If value is greater than max.
|
|
478
|
+
"""
|
|
479
|
+
self._min = val
|
|
480
|
+
|
|
481
|
+
# Update range if all values are available
|
|
482
|
+
if all([self._min is not None,
|
|
483
|
+
self._max is not None,
|
|
484
|
+
self._step is not None]):
|
|
485
|
+
self._range = np.arange(self._min,
|
|
486
|
+
self._max,
|
|
487
|
+
self._step)
|
|
488
|
+
|
|
489
|
+
@property
|
|
490
|
+
def max(self) -> Optional[float]:
|
|
491
|
+
"""*max* Get the maximum range value.
|
|
492
|
+
|
|
493
|
+
Returns:
|
|
494
|
+
Optional[float]: Maximum range value.
|
|
495
|
+
"""
|
|
496
|
+
return self._max
|
|
497
|
+
|
|
498
|
+
@max.setter
|
|
499
|
+
@validate_type(int, float)
|
|
500
|
+
@validate_range(min_attr='_min')
|
|
501
|
+
def max(self, val: Optional[float]) -> None:
|
|
502
|
+
"""*max* Sets the maximum range value.
|
|
503
|
+
|
|
504
|
+
Args:
|
|
505
|
+
val (Optional[float]): Maximum range value.
|
|
506
|
+
|
|
507
|
+
Raises:
|
|
508
|
+
ValueError: If value is not a number.
|
|
509
|
+
ValueError: If value is less than min.
|
|
510
|
+
"""
|
|
511
|
+
self._max = val
|
|
512
|
+
|
|
513
|
+
# Update range if all values are available
|
|
514
|
+
if all([self._min is not None,
|
|
515
|
+
self._max is not None,
|
|
516
|
+
self._step is not None]):
|
|
517
|
+
self._range = np.arange(self._min,
|
|
518
|
+
self._max,
|
|
519
|
+
self._step)
|
|
520
|
+
|
|
521
|
+
@property
|
|
522
|
+
def mean(self) -> Optional[float]:
|
|
523
|
+
"""*mean* Get the average value.
|
|
524
|
+
|
|
525
|
+
Returns:
|
|
526
|
+
Optional[float]: average value.
|
|
527
|
+
"""
|
|
528
|
+
return self._mean
|
|
529
|
+
|
|
530
|
+
@mean.setter
|
|
531
|
+
@validate_type(int, float)
|
|
532
|
+
@validate_range(min_attr='_min', max_attr='_max')
|
|
533
|
+
def mean(self, val: Optional[float]) -> None:
|
|
534
|
+
"""*mean* Sets the average value.
|
|
535
|
+
|
|
536
|
+
Args:
|
|
537
|
+
val (Optional[float]): average value.
|
|
538
|
+
|
|
539
|
+
Raises:
|
|
540
|
+
ValueError: If value is not a number.
|
|
541
|
+
ValueError: If value is outside min-max range.
|
|
542
|
+
"""
|
|
543
|
+
self._mean = val
|
|
544
|
+
|
|
545
|
+
@property
|
|
546
|
+
def dev(self) -> Optional[float]:
|
|
547
|
+
"""*dev* Get the Variable standard deviation.
|
|
548
|
+
|
|
549
|
+
Returns:
|
|
550
|
+
Optional[float]: Variable standard deviation.
|
|
551
|
+
"""
|
|
552
|
+
return self._dev
|
|
553
|
+
|
|
554
|
+
@dev.setter
|
|
555
|
+
@validate_type(int, float)
|
|
556
|
+
def dev(self, val: Optional[float]) -> None:
|
|
557
|
+
"""*dev* Sets the Variable standard deviation.
|
|
558
|
+
|
|
559
|
+
Args:
|
|
560
|
+
val (Optional[float]): Variable standard deviation.
|
|
561
|
+
Raises:
|
|
562
|
+
ValueError: If value not a valid number.
|
|
563
|
+
"""
|
|
564
|
+
self._dev = val
|
|
565
|
+
|
|
566
|
+
@property
|
|
567
|
+
def step(self) -> Optional[float]:
|
|
568
|
+
"""*step* Get standardized step size.
|
|
569
|
+
|
|
570
|
+
Returns:
|
|
571
|
+
Optional[float]: Step size (always standardized).
|
|
572
|
+
"""
|
|
573
|
+
return self._step
|
|
574
|
+
|
|
575
|
+
@step.setter
|
|
576
|
+
@validate_type(int, float)
|
|
577
|
+
@validate_custom(lambda self, val: self._validate_step_value(val))
|
|
578
|
+
def step(self, val: Optional[float]) -> None:
|
|
579
|
+
"""*step* Set standardized step size.
|
|
580
|
+
|
|
581
|
+
Args:
|
|
582
|
+
val (Optional[float]): Step size (always standardized).
|
|
583
|
+
|
|
584
|
+
Raises:
|
|
585
|
+
ValueError: If step is not a valid number
|
|
586
|
+
ValueError: If step is zero.
|
|
587
|
+
ValueError: If step is greater than range.
|
|
588
|
+
"""
|
|
589
|
+
self._step = val
|
|
590
|
+
|
|
591
|
+
# Update range if all values are available
|
|
592
|
+
if all([self._min is not None,
|
|
593
|
+
self._max is not None,
|
|
594
|
+
self._step is not None]):
|
|
595
|
+
self._range = np.arange(self._min,
|
|
596
|
+
self._max,
|
|
597
|
+
self._step)
|
|
598
|
+
|
|
599
|
+
@property
|
|
600
|
+
def data(self) -> np.ndarray:
|
|
601
|
+
"""*data* Get the data array.
|
|
602
|
+
|
|
603
|
+
Returns:
|
|
604
|
+
np.ndarray: Data array. If not explicitly set, generates from min, max, step.
|
|
605
|
+
"""
|
|
606
|
+
# If data was explicitly set, return it
|
|
607
|
+
if len(self._data) > 0:
|
|
608
|
+
return self._data
|
|
609
|
+
|
|
610
|
+
# Otherwise, generate from range parameters
|
|
611
|
+
if self._min is not None and self._max is not None and self._step != 0:
|
|
612
|
+
return np.arange(self._min, self._max, self._step)
|
|
613
|
+
|
|
614
|
+
# Default to empty array
|
|
615
|
+
return np.array([], dtype=float)
|
|
616
|
+
|
|
617
|
+
@data.setter
|
|
618
|
+
@validate_type(np.ndarray, list, allow_none=False)
|
|
619
|
+
def data(self, val: Union[np.ndarray, list]) -> None:
|
|
620
|
+
"""*data* Set the data array.
|
|
621
|
+
|
|
622
|
+
Args:
|
|
623
|
+
val (Union[np.ndarray, list]): Data array or list.
|
|
624
|
+
|
|
625
|
+
Raises:
|
|
626
|
+
ValueError: If data cannot be converted to numpy array.
|
|
627
|
+
"""
|
|
628
|
+
# Convert list to numpy array if needed
|
|
629
|
+
if isinstance(val, list):
|
|
630
|
+
self._data = np.array(val, dtype=float)
|
|
631
|
+
else:
|
|
632
|
+
self._data = val
|
|
633
|
+
|
|
634
|
+
def clear(self) -> None:
|
|
635
|
+
"""*clear()* Reset all attributes to default values.
|
|
636
|
+
|
|
637
|
+
Resets all coefficient properties to their initial state.
|
|
638
|
+
"""
|
|
639
|
+
# Reset base class attributes
|
|
640
|
+
self._idx = -1
|
|
641
|
+
self._sym = ""
|
|
642
|
+
self._alias = ""
|
|
643
|
+
self._fwk = "PHYSICAL"
|
|
644
|
+
self.name = ""
|
|
645
|
+
self.description = ""
|
|
646
|
+
|
|
647
|
+
# Reset coefficient-specific attributes
|
|
648
|
+
self._cat = "COMPUTED"
|
|
649
|
+
self._variables = {}
|
|
650
|
+
self._dim_col = []
|
|
651
|
+
self._pivot_lt = None
|
|
652
|
+
self._pi_expr = None
|
|
653
|
+
self.var_dims = {}
|
|
654
|
+
self._min = None
|
|
655
|
+
self._max = None
|
|
656
|
+
self._mean = None
|
|
657
|
+
self._step = 1e-3
|
|
658
|
+
self._data = np.array([])
|
|
659
|
+
self.relevance = True
|
|
660
|
+
|
|
661
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
662
|
+
"""*to_dict()* Convert variable to dictionary representation.
|
|
663
|
+
|
|
664
|
+
Returns:
|
|
665
|
+
Dict[str, Any]: Dictionary representation of variable.
|
|
666
|
+
"""
|
|
667
|
+
result = {}
|
|
668
|
+
|
|
669
|
+
# Get all dataclass fields
|
|
670
|
+
for f in fields(self):
|
|
671
|
+
attr_name = f.name
|
|
672
|
+
attr_value = getattr(self, attr_name)
|
|
673
|
+
|
|
674
|
+
# Skip numpy arrays (not JSON serializable without special handling)
|
|
675
|
+
if isinstance(attr_value, np.ndarray):
|
|
676
|
+
# Convert to list for JSON compatibility
|
|
677
|
+
attr_value = attr_value.tolist()
|
|
678
|
+
|
|
679
|
+
# Skip callables (can't be serialized)
|
|
680
|
+
if callable(attr_value) and attr_name == "_dist_func":
|
|
681
|
+
continue
|
|
682
|
+
|
|
683
|
+
# Remove leading underscore from private attributes
|
|
684
|
+
if attr_name.startswith("_"):
|
|
685
|
+
clean_name = attr_name[1:] # Remove first character
|
|
686
|
+
else:
|
|
687
|
+
clean_name = attr_name
|
|
688
|
+
|
|
689
|
+
result[clean_name] = attr_value
|
|
690
|
+
|
|
691
|
+
return result
|
|
692
|
+
|
|
693
|
+
@classmethod
|
|
694
|
+
def from_dict(cls, data: Dict[str, Any]) -> Coefficient:
|
|
695
|
+
"""*from_dict()* Create variable from dictionary representation.
|
|
696
|
+
|
|
697
|
+
Args:
|
|
698
|
+
data (Dict[str, Any]): Dictionary representation of variable.
|
|
699
|
+
|
|
700
|
+
Returns:
|
|
701
|
+
Variable: New variable instance.
|
|
702
|
+
"""
|
|
703
|
+
# Get all valid field names from the dataclass
|
|
704
|
+
field_names = {f.name for f in fields(cls)}
|
|
705
|
+
|
|
706
|
+
# Map keys without underscores to keys with underscores
|
|
707
|
+
mapped_data = {}
|
|
708
|
+
|
|
709
|
+
for key, value in data.items():
|
|
710
|
+
# Try the key as-is first (handles both _idx and name)
|
|
711
|
+
if key in field_names:
|
|
712
|
+
mapped_data[key] = value
|
|
713
|
+
# Try adding underscore prefix (handles idx -> _idx)
|
|
714
|
+
elif f"_{key}" in field_names:
|
|
715
|
+
mapped_data[f"_{key}"] = value
|
|
716
|
+
# Try removing underscore prefix (handles _name -> name if needed)
|
|
717
|
+
elif key.startswith("_") and key[1:] in field_names:
|
|
718
|
+
mapped_data[key[1:]] = value
|
|
719
|
+
else:
|
|
720
|
+
# Use as-is for unknown keys (will be validated by dataclass)
|
|
721
|
+
mapped_data[key] = value
|
|
722
|
+
|
|
723
|
+
# Convert lists back to numpy arrays for range attributes
|
|
724
|
+
for range_key in ["std_range", "_std_range"]:
|
|
725
|
+
if range_key in mapped_data and isinstance(mapped_data[range_key], list):
|
|
726
|
+
mapped_data[range_key] = np.array(mapped_data[range_key])
|
|
727
|
+
|
|
728
|
+
return cls(**mapped_data)
|