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.
Files changed (58) hide show
  1. pydasa/__init__.py +103 -0
  2. pydasa/_version.py +6 -0
  3. pydasa/analysis/__init__.py +0 -0
  4. pydasa/analysis/scenario.py +584 -0
  5. pydasa/analysis/simulation.py +1158 -0
  6. pydasa/context/__init__.py +0 -0
  7. pydasa/context/conversion.py +11 -0
  8. pydasa/context/system.py +17 -0
  9. pydasa/context/units.py +15 -0
  10. pydasa/core/__init__.py +15 -0
  11. pydasa/core/basic.py +287 -0
  12. pydasa/core/cfg/default.json +136 -0
  13. pydasa/core/constants.py +27 -0
  14. pydasa/core/io.py +102 -0
  15. pydasa/core/setup.py +269 -0
  16. pydasa/dimensional/__init__.py +0 -0
  17. pydasa/dimensional/buckingham.py +728 -0
  18. pydasa/dimensional/fundamental.py +146 -0
  19. pydasa/dimensional/model.py +1077 -0
  20. pydasa/dimensional/vaschy.py +633 -0
  21. pydasa/elements/__init__.py +19 -0
  22. pydasa/elements/parameter.py +218 -0
  23. pydasa/elements/specs/__init__.py +22 -0
  24. pydasa/elements/specs/conceptual.py +161 -0
  25. pydasa/elements/specs/numerical.py +469 -0
  26. pydasa/elements/specs/statistical.py +229 -0
  27. pydasa/elements/specs/symbolic.py +394 -0
  28. pydasa/serialization/__init__.py +27 -0
  29. pydasa/serialization/parser.py +133 -0
  30. pydasa/structs/__init__.py +0 -0
  31. pydasa/structs/lists/__init__.py +0 -0
  32. pydasa/structs/lists/arlt.py +578 -0
  33. pydasa/structs/lists/dllt.py +18 -0
  34. pydasa/structs/lists/ndlt.py +262 -0
  35. pydasa/structs/lists/sllt.py +746 -0
  36. pydasa/structs/tables/__init__.py +0 -0
  37. pydasa/structs/tables/htme.py +182 -0
  38. pydasa/structs/tables/scht.py +774 -0
  39. pydasa/structs/tools/__init__.py +0 -0
  40. pydasa/structs/tools/hashing.py +53 -0
  41. pydasa/structs/tools/math.py +149 -0
  42. pydasa/structs/tools/memory.py +54 -0
  43. pydasa/structs/types/__init__.py +0 -0
  44. pydasa/structs/types/functions.py +131 -0
  45. pydasa/structs/types/generics.py +54 -0
  46. pydasa/validations/__init__.py +0 -0
  47. pydasa/validations/decorators.py +510 -0
  48. pydasa/validations/error.py +100 -0
  49. pydasa/validations/patterns.py +32 -0
  50. pydasa/workflows/__init__.py +1 -0
  51. pydasa/workflows/influence.py +497 -0
  52. pydasa/workflows/phenomena.py +529 -0
  53. pydasa/workflows/practical.py +765 -0
  54. pydasa-0.4.7.dist-info/METADATA +320 -0
  55. pydasa-0.4.7.dist-info/RECORD +58 -0
  56. pydasa-0.4.7.dist-info/WHEEL +5 -0
  57. pydasa-0.4.7.dist-info/licenses/LICENSE +674 -0
  58. pydasa-0.4.7.dist-info/top_level.txt +1 -0
pydasa/__init__.py ADDED
@@ -0,0 +1,103 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ PyDASA Package
4
+ ===========================================
5
+ Main package initializer for PyDASA library, exposing core functionalities, data structures, dimensional analysis tools, unit conversion utilities, and workflow management components.
6
+ """
7
+ # expose version
8
+ from ._version import __version__
9
+
10
+ # expose imports
11
+ # exposing analytics modules
12
+ from .analysis.scenario import Sensitivity
13
+ from .analysis.simulation import MonteCarlo
14
+
15
+ # TODO conversion still in development
16
+ # exposing unit conversion modules
17
+ # from .context.conversion import UnitStandarizer
18
+ # from .context.system import MeasureSystem
19
+ # from .context.measurements import Unit
20
+
21
+ # exposing pi-theorem/dimensional analysis modules
22
+ from .dimensional.buckingham import Coefficient
23
+ from .dimensional.fundamental import Dimension
24
+ from .dimensional.vaschy import Schema
25
+ from .dimensional.model import Matrix
26
+
27
+ # exposing core modules
28
+ # exposing basic elements/variables modules
29
+ from .elements.parameter import Variable
30
+ # exposing parser/io modules
31
+ from .core.io import load, save
32
+
33
+ # exposing custom data structure modules
34
+ # TODO measurement still in development
35
+ # lists
36
+ from .structs.lists.arlt import ArrayList
37
+ from .structs.lists.sllt import SingleLinkedList
38
+ from .structs.lists.ndlt import Node, SLNode, DLNode
39
+ # from .structs.lists.dllt import DoubleLinkedList
40
+ # tables
41
+ from .structs.tables.htme import MapEntry
42
+ from .structs.tables.scht import Bucket, SCHashTable
43
+
44
+ # exposing validation, error and decorator modules
45
+ # exposing workflow modules
46
+ from .workflows.influence import SensitivityAnalysis
47
+ from .workflows.practical import MonteCarloSimulation
48
+ # from .workflows.phenomena import AnalysisEngine
49
+
50
+ # asserting all imports
51
+ # asserting analytics modules
52
+ assert Sensitivity
53
+ assert MonteCarlo
54
+
55
+ # asserting unit conversion modules
56
+ # assert UnitStandarizer
57
+ # assert MeasureSystem
58
+ # assert Unit
59
+ # asserting pi-theorem/dimensional analysis modules
60
+ assert Coefficient
61
+ assert Dimension
62
+ assert Schema
63
+ assert Matrix
64
+ # asserting elements/variables modules
65
+ assert Variable
66
+ # asserting parser/io modules
67
+ assert load
68
+ assert save
69
+ # asserting custom data structure modules
70
+ # lists
71
+ assert ArrayList
72
+ assert SingleLinkedList
73
+ assert Node
74
+ assert SLNode
75
+ assert DLNode
76
+ # tables
77
+ assert MapEntry
78
+ assert Bucket
79
+ assert SCHashTable
80
+ # asserting workflow modules
81
+ assert SensitivityAnalysis
82
+ assert MonteCarloSimulation
83
+ # assert AnalysisEngine
84
+
85
+ # Define __all__ for wildcard imports
86
+ __all__ = [
87
+ "__version__",
88
+ "Sensitivity",
89
+ "MonteCarlo",
90
+ # "UnitStandarizer",
91
+ # "MeasureSystem",
92
+ # "Unit",
93
+ "Coefficient",
94
+ "Dimension",
95
+ "Schema",
96
+ "Matrix",
97
+ "Variable",
98
+ "load",
99
+ "save",
100
+ "SensitivityAnalysis",
101
+ "MonteCarloSimulation",
102
+ # "AnalysisEngine",
103
+ ]
pydasa/_version.py ADDED
@@ -0,0 +1,6 @@
1
+ # Version information
2
+ # version format: [MAYOR].[MINOR].[PATCH]
3
+ # PATCH: for bug fixes and minor improvements, third-party library updates, etc.
4
+ # MINOR: for new features and enhancements, backward-compatible changes
5
+ # MAYOR: for incompatible API changes
6
+ __version__ = "0.4.7"
File without changes
@@ -0,0 +1,584 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Module scenario.py
4
+ ===========================================
5
+
6
+ Module for sensitivity analysis in *PyDASA*.
7
+
8
+ This module provides the Sensitivity class for performing sensitivity analysis on dimensional coefficients derived from dimensional analysis.
9
+
10
+ Classes:
11
+
12
+ **Sensitivity**: Performs sensitivity analysis on dimensional coefficients in *PyDASA*.
13
+
14
+ *IMPORTANT:* Based on the theory from:
15
+ # H.Gorter, *Dimensionalanalyse: Eine Theoririe der physikalischen Dimensionen mit Anwendungen*
16
+ """
17
+
18
+ from __future__ import annotations
19
+ from dataclasses import dataclass, field
20
+ from typing import Optional, List, Dict, Any, Callable
21
+
22
+ # Third-party modules
23
+ import numpy as np
24
+ # import sympy as sp
25
+ from sympy import diff, lambdify # , symbols
26
+ from SALib.sample.fast_sampler import sample
27
+ from SALib.analyze.fast import analyze
28
+
29
+ # Import validation base classes
30
+ from pydasa.core.basic import Foundation
31
+
32
+ # Import validation decorators
33
+ from pydasa.validations.decorators import validate_choices, validate_pattern, validate_custom
34
+
35
+ # Import related classes
36
+ from pydasa.dimensional.buckingham import Coefficient
37
+ # from pydasa.core.parameter import Variable
38
+ from pydasa.core.setup import Frameworks
39
+ from pydasa.core.setup import AnaliticMode
40
+
41
+
42
+ # Import utils
43
+ from pydasa.serialization.parser import parse_latex
44
+ from pydasa.serialization.parser import create_latex_mapping
45
+ from pydasa.serialization.parser import latex_to_python
46
+
47
+ # Import configuration
48
+ from pydasa.core.setup import PYDASA_CFG
49
+ from pydasa.validations.patterns import LATEX_RE
50
+
51
+
52
+ @dataclass
53
+ class Sensitivity(Foundation):
54
+ # FIXME clean code, some vars and types are inconsistent
55
+ """**Sensitivity** class for analyzing variable impacts in *PyDASA*.
56
+
57
+ Performs sensitivity analysis on dimensionless coefficients to determine which variables have the most significant impact on the system behavior.
58
+
59
+ Args:
60
+ Foundation: Foundation class for validation of symbols and frameworks.
61
+
62
+ Attributes:
63
+ # Identification and Classification
64
+ name (str): User-friendly name of the sensitivity analysis.
65
+ description (str): Brief summary of the sensitivity analysis.
66
+ _idx (int): Index/precedence of the sensitivity analysis.
67
+ _sym (str): Symbol representation (LaTeX or alphanumeric).
68
+ _alias (str): Python-compatible alias for use in code.
69
+ _fwk (str): Frameworks context (PHYSICAL, COMPUTATION, SOFTWARE, CUSTOM).
70
+ _cat (str): Category of analysis (SYM, NUM).
71
+
72
+ # Expression Management
73
+ _pi_expr (str): LaTeX expression to analyze.
74
+ _sym_func (Callable): Sympy function of the sensitivity.
75
+ _exe_func (Callable): Executable function for numerical evaluation.
76
+ _variables (Dict[str, Variable]): Variable symbols in the expression.
77
+ _symbols (Dict[str, Any]): Python symbols for the variables.
78
+ _aliases (Dict[str, Any]): Variable aliases for use in code.
79
+
80
+ # Analysis Configuration
81
+ var_bounds (List[List[float]]): Min/max bounds for each variable.
82
+ var_values (Dict[str, float]): Values for symbolic analysis.
83
+ var_ranges (np.ndarray): Sample value range for numerical analysis.
84
+ n_samples (int): Number of samples for analysis.
85
+
86
+ # Results
87
+ results (Dict[str, Any]): Analysis results.
88
+ """
89
+
90
+ # Category attribute
91
+ # :attr: _cat
92
+ _cat: str = AnaliticMode.SYM.value
93
+ """Category of sensitivity analysis (SYM, NUM)."""
94
+
95
+ # Expression properties
96
+ # :attr: _pi_expr
97
+ _pi_expr: Optional[str] = None
98
+ """LaTeX expression to analyze."""
99
+
100
+ # :attr: _sym_func
101
+ _sym_func: Optional[Callable] = None
102
+ """Sympy function of the sensitivity."""
103
+
104
+ # :attr: _exe_func
105
+ _exe_func: Optional[Callable] = None
106
+ """Executable function for numerical evaluation."""
107
+
108
+ # :attr: _variables
109
+ _variables: Dict[str, Any] = field(default_factory=dict)
110
+ """Variable symbols in the expression."""
111
+
112
+ # :attr: _symbols
113
+ _symbols: Dict[str, Any] = field(default_factory=dict)
114
+ """Python symbols for the variables."""
115
+
116
+ # :attr: _aliases
117
+ _aliases: Dict[str, Any] = field(default_factory=dict)
118
+ """Variable aliases for use in code."""
119
+
120
+ # :attr: _latex_to_py
121
+ _latex_to_py: Dict[str, str] = field(default_factory=dict)
122
+ """Mapping from LaTeX symbols to Python-compatible names."""
123
+
124
+ # :attr: _py_to_latex
125
+ _py_to_latex: Dict[str, str] = field(default_factory=dict)
126
+ """Mapping from Python-compatible names to LaTeX symbols."""
127
+
128
+ # Analysis configuration
129
+ # :attr: var_bounds
130
+ var_bounds: List[List[float]] = field(default_factory=list)
131
+ """Min/max bounds for each variable."""
132
+
133
+ # :attr: var_values
134
+ var_values: Dict[str, float] = field(default_factory=dict)
135
+ """Values for symbolic analysis."""
136
+
137
+ # :attr: var_domains
138
+ var_domains: Optional[np.ndarray] = None
139
+ """Sample domain (inputs) for numerical analysis."""
140
+
141
+ # :attr: var_ranges
142
+ var_ranges: Optional[np.ndarray] = None
143
+ """Sample value range (results) for numerical analysis."""
144
+
145
+ # :attr: n_samples
146
+ n_samples: int = 1000
147
+ """Number of samples for analysis."""
148
+
149
+ # Results
150
+ # :attr: results
151
+ results: Dict[str, Any] = field(default_factory=dict)
152
+ """Analysis results."""
153
+
154
+ def _validate_callable(self, value: Any, field_name: str) -> None:
155
+ """Custom validator to ensure value is callable.
156
+
157
+ Args:
158
+ value: The value to validate.
159
+ field_name: Name of the field being validated.
160
+
161
+ Raises:
162
+ ValueError: If value is not callable.
163
+ """
164
+ if not callable(value):
165
+ raise ValueError(f"Sympy function must be callable. Provided: {type(value)}")
166
+
167
+ def __post_init__(self) -> None:
168
+ """*__post_init__()* Initializes the sensitivity analysis. Validates basic properties, sets default values, and processes the expression if provided.
169
+ """
170
+ # Initialize from base class
171
+ super().__post_init__()
172
+
173
+ # Set default symbol if not specified
174
+ if not self._sym:
175
+ self._sym = f"SANSYS_\\Pi_{{{self._idx}}}" if self._idx >= 0 else "SANSYS_\\Pi_{}"
176
+ # Set default Python alias if not specified
177
+ if not self._alias:
178
+ self._alias = latex_to_python(self._sym)
179
+
180
+ # Set name and description if not already set
181
+ if not self.name:
182
+ self.name = f"{self._sym} Sensitivity"
183
+ if not self.description:
184
+ self.description = f"Sensitivity analysis for {self._sym}"
185
+
186
+ if self._pi_expr:
187
+ # Parse the expression
188
+ self._parse_expression(self._pi_expr)
189
+
190
+ def _validate_analysis_ready(self) -> None:
191
+ """*_validate_analysis_ready()* Checks if the analysis can be performed.
192
+
193
+ Raises:
194
+ ValueError: If the variables are missing.
195
+ ValueError: If the python-compatible variables are missing.
196
+ ValueError: If the symbolic expression is missing.
197
+ """
198
+ if not self._variables:
199
+ raise ValueError("No variables found in the expression.")
200
+ if not self._aliases:
201
+ raise ValueError("No Python aliases found for variables.")
202
+ if not self._sym_func:
203
+ raise ValueError("No expression has been defined for analysis.")
204
+
205
+ def set_coefficient(self, coef: Coefficient) -> None:
206
+ """*set_coefficient()* Configure analysis from a coefficient.
207
+
208
+ Args:
209
+ coef (Coefficient): Dimensionless coefficient to analyze.
210
+
211
+ Raises:
212
+ ValueError: If the coefficient doesn't have a valid expression.
213
+ """
214
+ if not coef.pi_expr:
215
+ raise ValueError("Coefficient does not have a valid expression.")
216
+
217
+ # Set expression
218
+ self._pi_expr = coef.pi_expr
219
+ # parse coefficient expresion
220
+ if coef._pi_expr:
221
+ self._parse_expression(self._pi_expr)
222
+
223
+ def _parse_expression(self, expr: str) -> None:
224
+ """*_parse_expression()* Parse the LaTeX expression into a sympy function.
225
+
226
+ Args:
227
+ expr (str): LaTeX expression to parse.
228
+
229
+ Raises:
230
+ ValueError: If the expression cannot be parsed.
231
+ """
232
+ try:
233
+ # Parse the expression
234
+ self._sym_func = parse_latex(expr)
235
+
236
+ # Create symbol mapping
237
+ maps = create_latex_mapping(expr)
238
+ self._symbols = maps[0]
239
+ self._aliases = maps[1]
240
+ self._latex_to_py = maps[2]
241
+ self._py_to_latex = maps[3]
242
+
243
+ # Substitute LaTeX symbols with Python symbols
244
+ for latex_sym, py_sym in self._symbols.items():
245
+ self._sym_func = self._sym_func.subs(latex_sym, py_sym)
246
+
247
+ # Get Python variable names
248
+ # self._variables = sorted(self._aliases.keys())
249
+ fsyms = self._sym_func.free_symbols
250
+ self._variables = sorted([str(s) for s in fsyms])
251
+
252
+ # """
253
+ # # OLD code, first version, keep for reference!!!
254
+ # self.results = {
255
+ # var: lambdify(self._variables, diff(self._sym_fun, var), "numpy")(
256
+ # *[vals[v] for v in self.variables]
257
+ # )
258
+ # for var in self._variables
259
+ # }
260
+ # """
261
+ except Exception as e:
262
+ _msg = f"Failed to parse expression: {str(e)}"
263
+ raise ValueError(_msg)
264
+
265
+ def analyze_symbolically(self,
266
+ vals: Dict[str, float]) -> Dict[str, float]:
267
+ """*analyze_symbolically()* Perform symbolic sensitivity analysis.
268
+
269
+ Args:
270
+ vals (Dict[str, float]): Dictionary mapping variable names to values.
271
+
272
+ Returns:
273
+ Dict[str, float]: Sensitivity results for each variable.
274
+ """
275
+ # # parse the coefficient expression
276
+ # self._parse_expression(self._pi_expr)
277
+
278
+ # save variable values for the analysis
279
+ self.var_values = vals
280
+
281
+ # Check that all required variables are provided
282
+ var_lt = [str(v) for v in self._latex_to_py]
283
+ missing_vars = set(var_lt) - set(list(vals.keys()))
284
+ if missing_vars:
285
+ _msg = f"Missing values for variables: {missing_vars}"
286
+ raise ValueError(_msg)
287
+
288
+ # Validate analysis readiness
289
+ self._validate_analysis_ready()
290
+
291
+ # trying symbolic coefficient sensitivity analysis
292
+ try:
293
+ py_to_latex = self._py_to_latex
294
+ results = dict()
295
+ functions = dict()
296
+ if self._variables:
297
+ for var in self._variables:
298
+ # Create lambdify function using Python symbols
299
+ expr = diff(self._sym_func, var)
300
+ aliases = [self._aliases[v] for v in self._variables]
301
+ # self._exe_func = lambdify(aliases, expr, "numpy")
302
+ func = lambdify(aliases, expr, "numpy")
303
+ functions[py_to_latex[var]] = func
304
+
305
+ # Convert back to LaTeX variables for result keys
306
+ val_args = [vals[py_to_latex[v]] for v in self._variables]
307
+ res = functions[py_to_latex[var]](*val_args)
308
+ results[py_to_latex[var]] = res
309
+
310
+ self._exe_func = functions
311
+ self.results = results
312
+ return self.results
313
+
314
+ except Exception as e:
315
+ _msg = f"Error calculating sensitivity for {var}: {str(e)}"
316
+ raise ValueError(_msg)
317
+
318
+ def analyze_numerically(self,
319
+ vals: List[str],
320
+ bounds: List[List[float]],
321
+ n_samples: int = 1000) -> Dict[str, Any]:
322
+ """*analyze_numerically()* Perform numerical sensitivity analysis.
323
+
324
+ Args:
325
+ vals (List[str]): List of variable names to analyze.
326
+ bounds (List[List[float]]): Bounds for each variable [min, max].
327
+ n_samples (int, optional): Number of samples to use. Defaults to 1000.
328
+
329
+ Returns:
330
+ Dict[str, Any]: Detailed sensitivity analysis results.
331
+ """
332
+ # # parse the coefficient expression
333
+ # self._parse_expression(self._pi_expr)
334
+
335
+ # Validate analysis readiness
336
+ self._validate_analysis_ready()
337
+
338
+ # trying numeric coefficient sensitivity analysis
339
+ try:
340
+ # Validate bounds length matches number of variables
341
+ if len(bounds) != len(self._variables):
342
+ _msg = f"Expected {len(self._variables)} "
343
+ _msg += f"bounds (one per variable), got {len(bounds)}"
344
+ raise ValueError(_msg)
345
+
346
+ # Set number of samples
347
+ self.n_samples = n_samples
348
+ # Store bounds
349
+ self.var_bounds = bounds
350
+
351
+ results = dict()
352
+ if self._variables:
353
+
354
+ # Set up problem definition for SALib
355
+ problem = {
356
+ "num_vars": len(vals),
357
+ "names": self.variables,
358
+ "bounds": bounds,
359
+ }
360
+
361
+ # Generate samples (domain)
362
+ self.var_domains = sample(problem, n_samples)
363
+ _len = len(self._variables)
364
+ self.var_domains = self.var_domains.reshape(-1, _len)
365
+
366
+ # Create lambdify function using Python symbols
367
+ aliases = [self._aliases[v] for v in self._variables]
368
+ self._exe_func = lambdify(aliases, self._sym_func, "numpy")
369
+
370
+ # Evaluate function at sample points
371
+ Y = np.apply_along_axis(lambda v: self._exe_func(*v),
372
+ 1, self.var_domains)
373
+ self.var_ranges = Y.reshape(-1, 1)
374
+
375
+ # Perform FAST (Fourier Amplitude Sensitivity Test) analysis
376
+ results = analyze(problem, Y)
377
+
378
+ # Convert back to LaTeX variables for result keys
379
+ if "names" in results:
380
+ py_to_latex = self._py_to_latex
381
+ results["names"] = [py_to_latex.get(v, v) for v in results["names"]]
382
+
383
+ self.results = results
384
+ return self.results
385
+
386
+ except Exception as e:
387
+ _msg = f"Error calculating sensitivity: {str(e)}"
388
+ raise ValueError(_msg)
389
+
390
+ # Property getters and setters
391
+
392
+ @property
393
+ def cat(self) -> str:
394
+ """*cat* Get the analysis category.
395
+
396
+ Returns:
397
+ str: Category (SYM, NUM).
398
+ """
399
+ return self._cat
400
+
401
+ @cat.setter
402
+ @validate_choices(PYDASA_CFG.analitic_modes, case_sensitive=False)
403
+ def cat(self, val: str) -> None:
404
+ """*cat* Set the analysis category.
405
+
406
+ Args:
407
+ val (str): Category value.
408
+
409
+ Raises:
410
+ ValueError: If category is invalid.
411
+ """
412
+ self._cat = val.upper()
413
+
414
+ @property
415
+ def pi_expr(self) -> Optional[str]:
416
+ """*pi_expr* Get the expression to analyze.
417
+
418
+ Returns:
419
+ Optional[str]: LaTeX expression.
420
+ """
421
+ return self._pi_expr
422
+
423
+ @pi_expr.setter
424
+ @validate_pattern(LATEX_RE, allow_alnum=True)
425
+ def pi_expr(self, val: str) -> None:
426
+ """*pi_expr* Set the expression to analyze.
427
+
428
+ Args:
429
+ val (str): LaTeX expression.
430
+
431
+ Raises:
432
+ ValueError: If expression is invalid.
433
+ """
434
+ # Update expression
435
+ self._pi_expr = val
436
+
437
+ # Parse expression
438
+ self._parse_expression(self._pi_expr)
439
+
440
+ @property
441
+ def sym_func(self) -> Optional[Callable]:
442
+ """*sym_func* Get the symbolic function.
443
+
444
+ Returns:
445
+ Optional[Callable]: Symbolic expression.
446
+ """
447
+ return self._sym_func
448
+
449
+ @sym_func.setter
450
+ @validate_custom(lambda self, val: self._validate_callable(val, "sym_func"))
451
+ def sym_func(self, val: Callable) -> None:
452
+ """*sym_func* Set the symbolic function.
453
+
454
+ Args:
455
+ val (Callable): Symbolic function.
456
+
457
+ Raises:
458
+ ValueError: If function is not callable.
459
+ """
460
+ self._sym_func = val
461
+
462
+ @property
463
+ def exe_func(self) -> Optional[Callable]:
464
+ """*exe_func* Get the executable function.
465
+
466
+ Returns:
467
+ Optional[Callable]: Executable function for numerical evaluation.
468
+ """
469
+ return self._exe_func
470
+
471
+ @property
472
+ def variables(self) -> Dict[str, Any]:
473
+ """*variables* Get the variables in the expression.
474
+
475
+ Returns:
476
+ Dict[str, Any]:: Variable symbols.
477
+ """
478
+ return self._variables
479
+
480
+ @property
481
+ def symbols(self) -> Dict[str, Any]:
482
+ """*symbols* Get the Python symbols for the variables.
483
+
484
+ Returns:
485
+ Dict[str, Any]: Dictionary mapping variable names to sympy symbols.
486
+ """
487
+ return self._symbols
488
+
489
+ @property
490
+ def aliases(self) -> Dict[str, Any]:
491
+ """*aliases* Get the Python aliases for the variables.
492
+
493
+ Returns:
494
+ Dict[str, Any]:: Python-compatible variable names.
495
+ """
496
+ return self._aliases
497
+
498
+ def clear(self) -> None:
499
+ """*clear()* Reset all attributes to default values. Resets all sensitivity analysis properties to their initial state.
500
+ """
501
+ # Reset base class attributes
502
+ self._idx = -1
503
+ self._sym = "SANSYS_\\Pi_{}"
504
+ self._alias = ""
505
+ self._fwk = Frameworks.PHYSICAL.value
506
+ self.name = ""
507
+ self.description = ""
508
+
509
+ # Reset sensitivity-specific attributes
510
+ self._cat = AnaliticMode.SYM.value
511
+ self._pi_expr = None
512
+ self._sym_func = None
513
+ self._exe_func = None
514
+ self._variables = []
515
+ self._symbols = {}
516
+ self._aliases = {}
517
+ self.var_bounds = []
518
+ self.var_values = {}
519
+ self.var_domains = None
520
+ self.var_ranges = None
521
+ self.n_samples = 1000
522
+ self.results = {}
523
+
524
+ def to_dict(self) -> Dict[str, Any]:
525
+ """*to_dict()* Convert sensitivity analysis to dictionary representation.
526
+
527
+ Returns:
528
+ Dict[str, Any]: Dictionary representation of sensitivity analysis.
529
+ """
530
+ return {
531
+ "name": self.name,
532
+ "description": self.description,
533
+ "idx": self._idx,
534
+ "sym": self._sym,
535
+ "alias": self._alias,
536
+ "fwk": self._fwk,
537
+ "cat": self._cat,
538
+ "pi_expr": self._pi_expr,
539
+ "variables": self._variables,
540
+ "symbols": self._symbols,
541
+ "aliases": self._aliases,
542
+ "var_bounds": self.var_bounds,
543
+ "var_values": self.var_values,
544
+ "var_domains": self.var_domains,
545
+ "var_ranges": self.var_ranges,
546
+ "n_samples": self.n_samples,
547
+ "results": self.results
548
+ }
549
+
550
+ @classmethod
551
+ def from_dict(cls, data: Dict[str, Any]) -> Sensitivity:
552
+ """*from_dict()* Create sensitivity analysis from dictionary representation.
553
+
554
+ Args:
555
+ data (Dict[str, Any]): Dictionary representation of sensitivity analysis.
556
+
557
+ Returns:
558
+ Sensitivity: New sensitivity analysis instance.
559
+ """
560
+ # Create basic instance
561
+ instance = cls(
562
+ _name=data.get("name", ""),
563
+ description=data.get("description", ""),
564
+ _idx=data.get("idx", -1),
565
+ _sym=data.get("sym", ""),
566
+ _cat=data.get("cat", AnaliticMode.SYM.value),
567
+ _fwk=data.get("fwk", Frameworks.PHYSICAL.value),
568
+ _alias=data.get("alias", ""),
569
+ _pi_expr=data.get("pi_expr", None),
570
+ _variables=data.get("variables", {}),
571
+ _symbols=data.get("symbols", {}),
572
+ _aliases=data.get("aliases", {}),
573
+ var_bounds=data.get("var_bounds", []),
574
+ var_values=data.get("var_values", {}),
575
+ var_domains=data.get("var_domains", None),
576
+ var_ranges=data.get("var_ranges", None),
577
+ n_samples=data.get("n_samples", 1000),
578
+ # results=data.get("results", {})
579
+ )
580
+
581
+ # Set additional properties if available
582
+ if "results" in data:
583
+ instance.results = data["results"]
584
+ return instance