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,529 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Module phenomena.py
|
|
4
|
+
===========================================
|
|
5
|
+
|
|
6
|
+
Module for **AnalysisEngine** to orchestrate dimensional analysis workflows in *PyDASA*.
|
|
7
|
+
|
|
8
|
+
This module provides the **AnalysisEngine** class serves as the main entry point and workflow for *PyDASA's* dimensional analysis capabilities setting up the dimensional domain, solving the dimensional matrix, and coefficient generation.
|
|
9
|
+
|
|
10
|
+
Classes:
|
|
11
|
+
**AnalysisEngine**: Main workflow class for dimensional analysis and coefficient generation.
|
|
12
|
+
|
|
13
|
+
*IMPORTANT:* Based on the theory from:
|
|
14
|
+
|
|
15
|
+
# H.Gorter, *Dimensionalanalyse: Eine Theoririe der physikalischen Dimensionen mit Anwendungen*
|
|
16
|
+
"""
|
|
17
|
+
# Standard library imports
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
from dataclasses import dataclass, field
|
|
20
|
+
from typing import Dict, Optional, Any, Union, Tuple
|
|
21
|
+
|
|
22
|
+
# Import validation base classes
|
|
23
|
+
from pydasa.core.basic import Foundation
|
|
24
|
+
|
|
25
|
+
# Import related classes
|
|
26
|
+
from pydasa.elements.parameter import Variable
|
|
27
|
+
from pydasa.dimensional.buckingham import Coefficient
|
|
28
|
+
from pydasa.dimensional.model import Matrix
|
|
29
|
+
from pydasa.dimensional.vaschy import Schema
|
|
30
|
+
|
|
31
|
+
# Import utils
|
|
32
|
+
from pydasa.validations.error import inspect_var
|
|
33
|
+
from pydasa.serialization.parser import latex_to_python
|
|
34
|
+
|
|
35
|
+
# Import validation decorators
|
|
36
|
+
from pydasa.validations.decorators import validate_type
|
|
37
|
+
# from pydasa.validations.decorators import validate_custom
|
|
38
|
+
|
|
39
|
+
# Import global configuration
|
|
40
|
+
from pydasa.core.setup import Frameworks # , PYDASA_CFG
|
|
41
|
+
|
|
42
|
+
# custom type hinting
|
|
43
|
+
Variables = Union[Dict[str, Variable], Dict[str, Any]]
|
|
44
|
+
Coefficients = Union[Dict[str, Coefficient], Dict[str, Any]]
|
|
45
|
+
FDUs = Union[str, Dict[str, Any], Schema]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass
|
|
49
|
+
class AnalysisEngine(Foundation):
|
|
50
|
+
"""**AnalysisEngine** class for orchestrating dimensional analysis workflows in *PyDASA*.
|
|
51
|
+
|
|
52
|
+
Main entry point that coordinates dimensional matrix solving and coefficient generation.
|
|
53
|
+
Also known as DimProblem.
|
|
54
|
+
|
|
55
|
+
Attributes:
|
|
56
|
+
# Identification and Classification
|
|
57
|
+
name (str): User-friendly name of the problem.
|
|
58
|
+
description (str): Brief summary of the problem.
|
|
59
|
+
_idx (int): Index/precedence of the problem.
|
|
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
|
+
|
|
64
|
+
# Problem Components
|
|
65
|
+
_variables (Dict[str, Variable]): All dimensional variables in the problem.
|
|
66
|
+
_schema (Optional[Schema]): Dimensional framework schema for the problem.
|
|
67
|
+
_model (Optional[Matrix]): Dimensional matrix for analysis.
|
|
68
|
+
|
|
69
|
+
# Generated Results
|
|
70
|
+
_coefficients (Dict[str, Coefficient]): Generated dimensionless coefficients.
|
|
71
|
+
|
|
72
|
+
# Workflow State
|
|
73
|
+
_is_solved (bool): Whether the dimensional matrix has been solved.
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
# Problem components
|
|
77
|
+
# :attr: _variables
|
|
78
|
+
_variables: Dict[str, Variable] = field(default_factory=dict)
|
|
79
|
+
"""Dictionary of all dimensional variables in the problem."""
|
|
80
|
+
|
|
81
|
+
# :attr: _schema
|
|
82
|
+
_schema: Optional[FDUs] = None
|
|
83
|
+
"""Dimensional framework schema (manages FDUs). Always a Schema object after initialization."""
|
|
84
|
+
|
|
85
|
+
# :attr: _model
|
|
86
|
+
_model: Optional[Matrix] = None
|
|
87
|
+
"""Dimensional matrix for Buckingham Pi analysis."""
|
|
88
|
+
|
|
89
|
+
# Generated results
|
|
90
|
+
# :attr: _coefficients
|
|
91
|
+
_coefficients: Dict[str, Coefficient] = field(default_factory=dict)
|
|
92
|
+
"""Dictionary of generated dimensionless coefficients."""
|
|
93
|
+
|
|
94
|
+
# Workflow state
|
|
95
|
+
# :attr: _is_solved
|
|
96
|
+
_is_solved: bool = False
|
|
97
|
+
"""Flag indicating if dimensional matrix has been solved."""
|
|
98
|
+
|
|
99
|
+
def __post_init__(self) -> None:
|
|
100
|
+
"""*__post_init__()* Initializes the solver.
|
|
101
|
+
|
|
102
|
+
Validates basic properties and sets up default values.
|
|
103
|
+
"""
|
|
104
|
+
# Initialize from base class
|
|
105
|
+
super().__post_init__()
|
|
106
|
+
|
|
107
|
+
# Set default symbol if not specified
|
|
108
|
+
if not self._sym:
|
|
109
|
+
self._sym = f"Solver_{{{self._idx}}}" if self._idx >= 0 else "Solver_{-1}"
|
|
110
|
+
|
|
111
|
+
if not self._alias:
|
|
112
|
+
self._alias = latex_to_python(self._sym)
|
|
113
|
+
|
|
114
|
+
# Set name and description if not already set
|
|
115
|
+
if not self.name:
|
|
116
|
+
self.name = f"Dimensional Analysis Engine {self._idx}"
|
|
117
|
+
|
|
118
|
+
if not self.description:
|
|
119
|
+
self.description = "Solves dimensional analysis using the Buckingham Pi-Theorem."
|
|
120
|
+
|
|
121
|
+
# Initialize schema with default PHYSICAL framework
|
|
122
|
+
# The setter will handle conversion if _schema was set to a string/dict in __init__
|
|
123
|
+
# Ensure _schema is a Schema object
|
|
124
|
+
# Handle the case where _schema might be set to a string, dict, or None during initialization
|
|
125
|
+
if not isinstance(self._schema, Schema):
|
|
126
|
+
if self._schema in (None, Frameworks.PHYSICAL.value):
|
|
127
|
+
# Default to PHYSICAL framework
|
|
128
|
+
self._schema = Schema(_fwk=Frameworks.PHYSICAL.value)
|
|
129
|
+
elif isinstance(self._schema, str):
|
|
130
|
+
# Convert string to Schema
|
|
131
|
+
self._schema = Schema(_fwk=self._schema.upper())
|
|
132
|
+
elif isinstance(self._schema, dict):
|
|
133
|
+
# Convert dict to Schema
|
|
134
|
+
self._schema = Schema.from_dict(self._schema)
|
|
135
|
+
# else:
|
|
136
|
+
# # Fallback to default
|
|
137
|
+
# self._schema = Schema(_fwk=Frameworks.PHYSICAL.value)
|
|
138
|
+
|
|
139
|
+
# # Initialize schema with default PHYSICAL framework
|
|
140
|
+
# self._schema = Schema(_fwk=Frameworks.PHYSICAL.value)
|
|
141
|
+
|
|
142
|
+
# # Convert default schema string to Schema object
|
|
143
|
+
# # if isinstance(self._schema, str):
|
|
144
|
+
# # self._schema = Schema(_fwk=self._schema)
|
|
145
|
+
# if self._schema is not Frameworks.PHYSICAL.value:
|
|
146
|
+
# if isinstance(self._schema, str):
|
|
147
|
+
# self.schema = Schema(self._schema)
|
|
148
|
+
|
|
149
|
+
# # Initialize with default PHYSICAL framework if not already set
|
|
150
|
+
# if not hasattr(self, "_schema") or self._schema is None:
|
|
151
|
+
# self._schema = Schema(_fwk=Frameworks.PHYSICAL.value)
|
|
152
|
+
|
|
153
|
+
# ========================================================================
|
|
154
|
+
# Validation Methods
|
|
155
|
+
# ========================================================================
|
|
156
|
+
|
|
157
|
+
def _validate_dict(self,
|
|
158
|
+
dt: Dict[str, Any],
|
|
159
|
+
exp_type: Union[type, Tuple[type, ...]]) -> bool:
|
|
160
|
+
"""*_validate_dict()* Validates a dictionary with expected value types.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
dt (Dict[str, Any]): Dictionary to validate.
|
|
164
|
+
exp_type (Union[type, Tuple[type, ...]]): Expected type(s) for dictionary values.
|
|
165
|
+
|
|
166
|
+
Raises:
|
|
167
|
+
ValueError: If the object is not a dictionary.
|
|
168
|
+
ValueError: If the dictionary is empty.
|
|
169
|
+
ValueError: If the dictionary contains values of unexpected types.
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
bool: True if the dictionary is valid.
|
|
173
|
+
"""
|
|
174
|
+
# variable inspection
|
|
175
|
+
var_name = inspect_var(dt)
|
|
176
|
+
|
|
177
|
+
# Validate is dictionary
|
|
178
|
+
if not isinstance(dt, dict):
|
|
179
|
+
_msg = f"{var_name} must be a dictionary. "
|
|
180
|
+
_msg += f"Provided: {type(dt).__name__}"
|
|
181
|
+
raise ValueError(_msg)
|
|
182
|
+
|
|
183
|
+
# Validate not empty
|
|
184
|
+
if len(dt) == 0:
|
|
185
|
+
_msg = f"{var_name} cannot be empty. "
|
|
186
|
+
_msg += f"Provided: {dt}"
|
|
187
|
+
raise ValueError(_msg)
|
|
188
|
+
|
|
189
|
+
# Convert list to tuple for isinstance()
|
|
190
|
+
type_check = exp_type if isinstance(exp_type, tuple) else (exp_type,) if not isinstance(exp_type, tuple) else exp_type
|
|
191
|
+
|
|
192
|
+
# Validate value types
|
|
193
|
+
if not all(isinstance(v, type_check) for v in dt.values()):
|
|
194
|
+
# Format expected types for error message
|
|
195
|
+
if isinstance(exp_type, tuple):
|
|
196
|
+
type_names = " or ".join(t.__name__ for t in exp_type)
|
|
197
|
+
else:
|
|
198
|
+
type_names = exp_type.__name__
|
|
199
|
+
|
|
200
|
+
actual_types = [type(v).__name__ for v in dt.values()]
|
|
201
|
+
_msg = f"{var_name} must contain {type_names} values. "
|
|
202
|
+
_msg += f"Provided: {actual_types}"
|
|
203
|
+
raise ValueError(_msg)
|
|
204
|
+
|
|
205
|
+
return True
|
|
206
|
+
|
|
207
|
+
# ========================================================================
|
|
208
|
+
# Property Getters and Setters
|
|
209
|
+
# ========================================================================
|
|
210
|
+
|
|
211
|
+
@property
|
|
212
|
+
def variables(self) -> Dict[str, Variable]:
|
|
213
|
+
"""*variables* Get the dictionary of variables.
|
|
214
|
+
|
|
215
|
+
Returns:
|
|
216
|
+
Dict[str, Variable]: Dictionary of variables.
|
|
217
|
+
"""
|
|
218
|
+
return self._variables.copy()
|
|
219
|
+
|
|
220
|
+
@variables.setter
|
|
221
|
+
@validate_type(dict, Variable, allow_none=False)
|
|
222
|
+
def variables(self, val: Variables) -> None:
|
|
223
|
+
"""*variables* Set the dictionary of variables.
|
|
224
|
+
|
|
225
|
+
Args:
|
|
226
|
+
val (Variables): Dictionary of variables (Variable objects or dicts).
|
|
227
|
+
|
|
228
|
+
Raises:
|
|
229
|
+
ValueError: If dictionary is invalid or contains invalid values.
|
|
230
|
+
"""
|
|
231
|
+
# Convert dict values to Variable objects if needed
|
|
232
|
+
converted = {}
|
|
233
|
+
for key, value in val.items():
|
|
234
|
+
# if value is already a Variable, keep it
|
|
235
|
+
if isinstance(value, Variable):
|
|
236
|
+
converted[key] = value
|
|
237
|
+
# if value is a dict, convert to Variable
|
|
238
|
+
elif isinstance(value, dict):
|
|
239
|
+
# Convert dict to Variable
|
|
240
|
+
converted[key] = Variable.from_dict(value)
|
|
241
|
+
else:
|
|
242
|
+
_msg = f"Input '{key}' must be type 'Variable' or 'dict'. "
|
|
243
|
+
_msg += f"Provided: {type(value).__name__}"
|
|
244
|
+
raise ValueError(_msg)
|
|
245
|
+
|
|
246
|
+
self._variables = converted
|
|
247
|
+
self._is_solved = False # Reset solve state
|
|
248
|
+
|
|
249
|
+
@property
|
|
250
|
+
def schema(self) -> Schema:
|
|
251
|
+
"""*schema* Get the dimensional framework schema.
|
|
252
|
+
|
|
253
|
+
Returns:
|
|
254
|
+
Optional[Schema]: Dimensional framework schema.
|
|
255
|
+
"""
|
|
256
|
+
return self._schema
|
|
257
|
+
|
|
258
|
+
@schema.setter
|
|
259
|
+
@validate_type(str, dict, Schema, allow_none=False)
|
|
260
|
+
def schema(self, val: Union[str, dict, Schema]) -> None:
|
|
261
|
+
"""*schema* Set the dimensional framework schema.
|
|
262
|
+
|
|
263
|
+
Args:
|
|
264
|
+
val (Union[str, dict, Schema]): Dimensional framework schema.
|
|
265
|
+
|
|
266
|
+
Raises:
|
|
267
|
+
ValueError: If string is not a valid framework name or dict is invalid.
|
|
268
|
+
TypeError: If val is not a valid type.
|
|
269
|
+
"""
|
|
270
|
+
# if schema is a string, convert to Schema
|
|
271
|
+
if isinstance(val, str):
|
|
272
|
+
self._schema = Schema(_fwk=val.upper())
|
|
273
|
+
# if schema is a dict, convert to Schema
|
|
274
|
+
elif isinstance(val, dict):
|
|
275
|
+
self._schema = Schema.from_dict(val)
|
|
276
|
+
# if schema is already a Schema, keep it
|
|
277
|
+
elif isinstance(val, Schema):
|
|
278
|
+
self._schema = val
|
|
279
|
+
else:
|
|
280
|
+
_msg = "Input must be type 'str', 'dict', or 'Schema'. "
|
|
281
|
+
_msg += f"Provided: {type(val).__name__}"
|
|
282
|
+
raise TypeError(_msg)
|
|
283
|
+
|
|
284
|
+
@property
|
|
285
|
+
def matrix(self) -> Optional[Matrix]:
|
|
286
|
+
"""*matrix* Get the dimensional matrix.
|
|
287
|
+
|
|
288
|
+
Returns:
|
|
289
|
+
Optional[Matrix]: Dimensional matrix.
|
|
290
|
+
"""
|
|
291
|
+
return self._model
|
|
292
|
+
|
|
293
|
+
@matrix.setter
|
|
294
|
+
@validate_type(Matrix, allow_none=True)
|
|
295
|
+
def matrix(self, val: Optional[Matrix]) -> None:
|
|
296
|
+
"""*matrix* Set the dimensional matrix.
|
|
297
|
+
|
|
298
|
+
Args:
|
|
299
|
+
val (Optional[Matrix]): Dimensional matrix.
|
|
300
|
+
"""
|
|
301
|
+
self._model = val
|
|
302
|
+
if val is not None:
|
|
303
|
+
self._is_solved = False # Reset solve state
|
|
304
|
+
|
|
305
|
+
@property
|
|
306
|
+
def coefficients(self) -> Dict[str, Coefficient]:
|
|
307
|
+
"""*coefficients* Get the generated coefficients.
|
|
308
|
+
|
|
309
|
+
Returns:
|
|
310
|
+
Dict[str, Coefficient]: Dictionary of coefficients.
|
|
311
|
+
"""
|
|
312
|
+
return self._coefficients.copy()
|
|
313
|
+
|
|
314
|
+
@coefficients.setter
|
|
315
|
+
@validate_type(dict, Coefficient, allow_none=False)
|
|
316
|
+
def coefficients(self, val: Coefficients) -> None:
|
|
317
|
+
"""*coefficients* Set the generated coefficients.
|
|
318
|
+
|
|
319
|
+
Args:
|
|
320
|
+
val (Coefficients): Dictionary of coefficients (Coefficient objects or dicts).
|
|
321
|
+
|
|
322
|
+
Raises:
|
|
323
|
+
ValueError: If dictionary is invalid or contains invalid values.
|
|
324
|
+
"""
|
|
325
|
+
# Convert dict values to Coefficient objects if needed
|
|
326
|
+
converted = {}
|
|
327
|
+
for key, value in val.items():
|
|
328
|
+
# if value is already a Coefficient, keep it
|
|
329
|
+
if isinstance(value, Coefficient):
|
|
330
|
+
converted[key] = value
|
|
331
|
+
# if value is a dict, convert to Coefficient
|
|
332
|
+
elif isinstance(value, dict):
|
|
333
|
+
# Convert dict to Coefficient
|
|
334
|
+
converted[key] = Coefficient.from_dict(value)
|
|
335
|
+
else:
|
|
336
|
+
_msg = f"Input '{key}' must be type 'Coefficient' or 'dict'. "
|
|
337
|
+
_msg += f"Provided: {type(value).__name__}"
|
|
338
|
+
raise ValueError(_msg)
|
|
339
|
+
|
|
340
|
+
self._coefficients = converted
|
|
341
|
+
self._is_solved = False # Reset solve state
|
|
342
|
+
|
|
343
|
+
@property
|
|
344
|
+
def is_solved(self) -> bool:
|
|
345
|
+
"""*is_solved* Check if dimensional matrix has been solved.
|
|
346
|
+
|
|
347
|
+
Returns:
|
|
348
|
+
bool: True if solved, False otherwise.
|
|
349
|
+
"""
|
|
350
|
+
return self._is_solved
|
|
351
|
+
|
|
352
|
+
# ========================================================================
|
|
353
|
+
# Workflow Methods
|
|
354
|
+
# ========================================================================
|
|
355
|
+
|
|
356
|
+
def create_matrix(self, **kwargs) -> None:
|
|
357
|
+
"""*create_matrix()* Create and configure dimensional matrix.
|
|
358
|
+
|
|
359
|
+
Creates a Matrix object from the current variables and optional parameters.
|
|
360
|
+
|
|
361
|
+
Args:
|
|
362
|
+
**kwargs: Optional keyword arguments to pass to Matrix constructor.
|
|
363
|
+
|
|
364
|
+
Returns:
|
|
365
|
+
Matrix: Configured dimensional matrix.
|
|
366
|
+
|
|
367
|
+
Raises:
|
|
368
|
+
ValueError: If variables are not set.
|
|
369
|
+
"""
|
|
370
|
+
if not self._variables:
|
|
371
|
+
raise ValueError("Variables must be set before creating matrix.")
|
|
372
|
+
|
|
373
|
+
# Create matrix with variables
|
|
374
|
+
self._model = Matrix(
|
|
375
|
+
_idx=self.idx,
|
|
376
|
+
_fwk=self._fwk,
|
|
377
|
+
_schema=self._schema,
|
|
378
|
+
_variables=self._variables,
|
|
379
|
+
# **kwargs
|
|
380
|
+
)
|
|
381
|
+
|
|
382
|
+
self._is_solved = False # Reset solve state
|
|
383
|
+
# return self._model
|
|
384
|
+
|
|
385
|
+
def solve(self) -> Dict[str, Coefficient]:
|
|
386
|
+
"""*solve()* Solve the dimensional matrix and generate coefficients.
|
|
387
|
+
|
|
388
|
+
Performs dimensional analysis using the Buckingham Pi theorem to generate
|
|
389
|
+
dimensionless coefficients.
|
|
390
|
+
|
|
391
|
+
Returns:
|
|
392
|
+
Dict[str, Coefficient]: Dictionary of generated coefficients.
|
|
393
|
+
|
|
394
|
+
Raises:
|
|
395
|
+
ValueError: If matrix is not created.
|
|
396
|
+
RuntimeError: If solving fails.
|
|
397
|
+
"""
|
|
398
|
+
if self._model is None:
|
|
399
|
+
raise ValueError("Matrix must be created before solving. Call create_matrix() first.")
|
|
400
|
+
|
|
401
|
+
try:
|
|
402
|
+
# Solve the matrix (generate coefficients)
|
|
403
|
+
self._model.create_matrix()
|
|
404
|
+
self._model.solve_matrix()
|
|
405
|
+
# self._model.solve()
|
|
406
|
+
|
|
407
|
+
# Extract generated coefficients from matrix
|
|
408
|
+
self._coefficients = self._model.coefficients
|
|
409
|
+
self._is_solved = True
|
|
410
|
+
return self._coefficients.copy()
|
|
411
|
+
|
|
412
|
+
except Exception as e:
|
|
413
|
+
_msg = f"Failed to solve dimensional matrix: {str(e)}"
|
|
414
|
+
raise RuntimeError(_msg) from e
|
|
415
|
+
|
|
416
|
+
def run_analysis(self) -> Dict[str, Any]:
|
|
417
|
+
"""*run_analysis()* Execute complete dimensional analysis workflow. Convenience method that runs the entire workflow: create matrix and solve.
|
|
418
|
+
|
|
419
|
+
Returns:
|
|
420
|
+
Dict[str, Any]: Dictionary of generated dimensionless coefficient in native python format
|
|
421
|
+
|
|
422
|
+
Raises:
|
|
423
|
+
ValueError: If variables are not set.
|
|
424
|
+
"""
|
|
425
|
+
# Step 1: Create matrix if not already created
|
|
426
|
+
if self._model is None:
|
|
427
|
+
self.create_matrix()
|
|
428
|
+
|
|
429
|
+
# Step 2: Solve and return coefficients
|
|
430
|
+
# return self.solve()
|
|
431
|
+
# Create + Solve matrix
|
|
432
|
+
coefficients = self.solve()
|
|
433
|
+
results = {k: v.to_dict() for k, v in coefficients.items()}
|
|
434
|
+
return results
|
|
435
|
+
|
|
436
|
+
# ========================================================================
|
|
437
|
+
# Utility Methods
|
|
438
|
+
# ========================================================================
|
|
439
|
+
|
|
440
|
+
def reset(self) -> None:
|
|
441
|
+
"""*reset()* Reset the solver state.
|
|
442
|
+
|
|
443
|
+
Clears all generated results, keeping only the input variables.
|
|
444
|
+
"""
|
|
445
|
+
self._model = None
|
|
446
|
+
self._coefficients.clear()
|
|
447
|
+
self._is_solved = False
|
|
448
|
+
|
|
449
|
+
def clear(self) -> None:
|
|
450
|
+
"""*clear()* Reset all attributes to default values.
|
|
451
|
+
|
|
452
|
+
Resets all solver properties to their initial state, including variables.
|
|
453
|
+
"""
|
|
454
|
+
self._variables.clear()
|
|
455
|
+
self._schema = Schema(_fwk=Frameworks.PHYSICAL.value)
|
|
456
|
+
self.reset()
|
|
457
|
+
|
|
458
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
459
|
+
"""*to_dict()* Convert solver state to dictionary.
|
|
460
|
+
|
|
461
|
+
Returns:
|
|
462
|
+
Dict[str, Any]: Dictionary representation of solver state.
|
|
463
|
+
"""
|
|
464
|
+
return {
|
|
465
|
+
"name": self.name,
|
|
466
|
+
"description": self.description,
|
|
467
|
+
"idx": self._idx,
|
|
468
|
+
"sym": self._sym,
|
|
469
|
+
"alias": self._alias,
|
|
470
|
+
"fwk": self._fwk,
|
|
471
|
+
"variables": {
|
|
472
|
+
k: v.to_dict() for k, v in self._variables.items()
|
|
473
|
+
},
|
|
474
|
+
"coefficients": {
|
|
475
|
+
k: v.to_dict() for k, v in self._coefficients.items()
|
|
476
|
+
},
|
|
477
|
+
"is_solved": self._is_solved,
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
@classmethod
|
|
481
|
+
def from_dict(cls, data: Dict[str, Any]) -> AnalysisEngine:
|
|
482
|
+
"""*from_dict()* Create a AnalysisEngine instance from a dictionary.
|
|
483
|
+
|
|
484
|
+
Args:
|
|
485
|
+
data (Dict[str, Any]): Dictionary containing the solver"s state.
|
|
486
|
+
|
|
487
|
+
Returns:
|
|
488
|
+
AnalysisEngine: New instance of AnalysisEngine.
|
|
489
|
+
"""
|
|
490
|
+
# Create instance with basic attributes
|
|
491
|
+
instance = cls(
|
|
492
|
+
_name=data.get("name", ""),
|
|
493
|
+
description=data.get("description", ""),
|
|
494
|
+
_idx=data.get("idx", -1),
|
|
495
|
+
_sym=data.get("sym", ""),
|
|
496
|
+
_alias=data.get("alias", ""),
|
|
497
|
+
_fwk=data.get("fwk", ""),
|
|
498
|
+
)
|
|
499
|
+
|
|
500
|
+
# Set variables
|
|
501
|
+
vars_data = data.get("variables", {})
|
|
502
|
+
if vars_data:
|
|
503
|
+
vars_dict = {k: Variable.from_dict(v) for k, v in vars_data.items()}
|
|
504
|
+
instance.variables = vars_dict
|
|
505
|
+
|
|
506
|
+
# Set coefficients
|
|
507
|
+
coefs_data = data.get("coefficients", {})
|
|
508
|
+
if coefs_data:
|
|
509
|
+
coefs_dict = {k: Coefficient.from_dict(v) for k, v in coefs_data.items()}
|
|
510
|
+
instance._coefficients = coefs_dict
|
|
511
|
+
|
|
512
|
+
# Set state flags
|
|
513
|
+
instance._is_solved = data.get("is_solved", False)
|
|
514
|
+
|
|
515
|
+
return instance
|
|
516
|
+
|
|
517
|
+
def __repr__(self) -> str:
|
|
518
|
+
"""*__repr__()* String representation of solver.
|
|
519
|
+
|
|
520
|
+
Returns:
|
|
521
|
+
str: String representation.
|
|
522
|
+
"""
|
|
523
|
+
status = "solved" if self._is_solved else "not solved"
|
|
524
|
+
coef_count = len(self._coefficients)
|
|
525
|
+
|
|
526
|
+
return (f"AnalysisEngine(name={self.name!r}, "
|
|
527
|
+
f"variables={len(self._variables)}, "
|
|
528
|
+
f"coefficients={coef_count}, "
|
|
529
|
+
f"status={status})")
|