sgio 0.3.0__py3-none-any.whl → 0.3.2__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.
- sgio/_version.py +1 -1
- sgio/core/builder.py +2 -1
- sgio/iofunc/gmsh/_gmsh41_refactored.py +1115 -0
- sgio/model/failure.py +8 -10
- sgio/model/solid.py +405 -440
- {sgio-0.3.0.dist-info → sgio-0.3.2.dist-info}/METADATA +1 -1
- {sgio-0.3.0.dist-info → sgio-0.3.2.dist-info}/RECORD +10 -9
- {sgio-0.3.0.dist-info → sgio-0.3.2.dist-info}/WHEEL +0 -0
- {sgio-0.3.0.dist-info → sgio-0.3.2.dist-info}/entry_points.txt +0 -0
- {sgio-0.3.0.dist-info → sgio-0.3.2.dist-info}/licenses/LICENSE +0 -0
sgio/model/solid.py
CHANGED
|
@@ -1,82 +1,216 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
3
|
+
from typing import Iterable, Optional, List, Literal, Sequence, Union, cast
|
|
4
|
+
|
|
5
|
+
FloatSequence = Sequence[float]
|
|
6
|
+
MatrixSequence = Sequence[Sequence[float]]
|
|
7
|
+
ElasticInput = Union[FloatSequence, MatrixSequence]
|
|
8
|
+
from pydantic import BaseModel, Field, field_validator, ConfigDict
|
|
9
|
+
|
|
10
|
+
class CauchyContinuumModel(BaseModel):
|
|
11
|
+
"""Cauchy continuum model with Pydantic validation.
|
|
12
|
+
|
|
13
|
+
This implementation follows the beam model pattern and provides
|
|
14
|
+
built-in validation for all material properties.
|
|
15
|
+
|
|
16
|
+
Attributes
|
|
17
|
+
----------
|
|
18
|
+
dim : int
|
|
19
|
+
Dimension of the model (always 3 for 3D continuum)
|
|
20
|
+
label : str
|
|
21
|
+
Model label identifier
|
|
22
|
+
model_name : str
|
|
23
|
+
Human-readable model name
|
|
24
|
+
strain_name : List[str]
|
|
25
|
+
Names of strain components
|
|
26
|
+
stress_name : List[str]
|
|
27
|
+
Names of stress components
|
|
28
|
+
name : str
|
|
29
|
+
Material name
|
|
30
|
+
id : Optional[int]
|
|
31
|
+
Material ID
|
|
32
|
+
isotropy : Literal[0, 1, 2]
|
|
33
|
+
Material isotropy type: 0=Isotropic, 1=Orthotropic, 2=Anisotropic
|
|
34
|
+
density : float
|
|
35
|
+
Material density (must be >= 0)
|
|
36
|
+
temperature : float
|
|
37
|
+
Temperature
|
|
38
|
+
e1, e2, e3 : Optional[float]
|
|
39
|
+
Young's moduli in material directions 1, 2, 3 (must be > 0 if set)
|
|
40
|
+
g12, g13, g23 : Optional[float]
|
|
41
|
+
Shear moduli in material planes 12, 13, 23 (must be > 0 if set)
|
|
42
|
+
nu12, nu13, nu23 : Optional[float]
|
|
43
|
+
Poisson's ratios in material planes 12, 13, 23 (must be in range (-1, 0.5))
|
|
44
|
+
stff : Optional[List[List[float]]]
|
|
45
|
+
6x6 stiffness matrix
|
|
46
|
+
cmpl : Optional[List[List[float]]]
|
|
47
|
+
6x6 compliance matrix
|
|
48
|
+
x1t, x2t, x3t : Optional[float]
|
|
49
|
+
Tensile strengths in directions 1, 2, 3
|
|
50
|
+
x1c, x2c, x3c : Optional[float]
|
|
51
|
+
Compressive strengths in directions 1, 2, 3
|
|
52
|
+
x23, x13, x12 : Optional[float]
|
|
53
|
+
Shear strengths in planes 23, 13, 12
|
|
54
|
+
strength_measure : int
|
|
55
|
+
Strength measure type: 0=stress, 1=strain
|
|
56
|
+
strength_constants : Optional[List[float]]
|
|
57
|
+
Array of strength constants
|
|
58
|
+
char_len : float
|
|
59
|
+
Characteristic length
|
|
60
|
+
cte : Optional[List[float]]
|
|
61
|
+
Thermal expansion coefficients (6 components)
|
|
62
|
+
specific_heat : float
|
|
63
|
+
Specific heat capacity
|
|
64
|
+
failure_criterion : int
|
|
65
|
+
Failure criterion identifier
|
|
66
|
+
d_thetatheta : float
|
|
67
|
+
Thermal property
|
|
68
|
+
f_eff : float
|
|
69
|
+
Effective property
|
|
29
70
|
"""
|
|
30
71
|
|
|
31
|
-
#
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
72
|
+
# Class attributes (model metadata)
|
|
73
|
+
dim: int = 3
|
|
74
|
+
label: str = 'sd1'
|
|
75
|
+
model_name: str = 'Cauchy continuum model'
|
|
76
|
+
strain_name: List[str] = ['e11', 'e22', 'e33', 'e23', 'e13', 'e12']
|
|
77
|
+
stress_name: List[str] = ['s11', 's22', 's33', 's23', 's13', 's12']
|
|
78
|
+
|
|
79
|
+
# Basic properties
|
|
80
|
+
name: str = Field(default='', description="Material name")
|
|
81
|
+
id: Optional[int] = Field(default=None, description="Material ID")
|
|
82
|
+
|
|
83
|
+
# Material type
|
|
84
|
+
isotropy: Literal[0, 1, 2] = Field(
|
|
85
|
+
default=0,
|
|
86
|
+
description="Isotropy type: 0=Isotropic, 1=Orthotropic, 2=Anisotropic"
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
# Inertial properties
|
|
90
|
+
density: float = Field(default=0, ge=0, description="Material density")
|
|
91
|
+
temperature: float = Field(default=0, description="Temperature")
|
|
92
|
+
|
|
93
|
+
# Elastic constants (orthotropic)
|
|
94
|
+
e1: Optional[float] = Field(default=None, gt=0, description="Young's modulus E1")
|
|
95
|
+
e2: Optional[float] = Field(default=None, gt=0, description="Young's modulus E2")
|
|
96
|
+
e3: Optional[float] = Field(default=None, gt=0, description="Young's modulus E3")
|
|
97
|
+
g12: Optional[float] = Field(default=None, gt=0, description="Shear modulus G12")
|
|
98
|
+
g13: Optional[float] = Field(default=None, gt=0, description="Shear modulus G13")
|
|
99
|
+
g23: Optional[float] = Field(default=None, gt=0, description="Shear modulus G23")
|
|
100
|
+
nu12: Optional[float] = Field(default=None, description="Poisson's ratio nu12")
|
|
101
|
+
nu13: Optional[float] = Field(default=None, description="Poisson's ratio nu13")
|
|
102
|
+
nu23: Optional[float] = Field(default=None, description="Poisson's ratio nu23")
|
|
103
|
+
|
|
104
|
+
# Stiffness/compliance matrices
|
|
105
|
+
stff: Optional[List[List[float]]] = Field(
|
|
106
|
+
default=None,
|
|
107
|
+
description="6x6 stiffness matrix"
|
|
108
|
+
)
|
|
109
|
+
cmpl: Optional[List[List[float]]] = Field(
|
|
110
|
+
default=None,
|
|
111
|
+
description="6x6 compliance matrix"
|
|
112
|
+
)
|
|
47
113
|
|
|
48
114
|
# Strength properties
|
|
49
|
-
|
|
50
|
-
|
|
115
|
+
x1t: Optional[float] = Field(default=None, ge=0, description="Tensile strength X1t")
|
|
116
|
+
x2t: Optional[float] = Field(default=None, ge=0, description="Tensile strength X2t")
|
|
117
|
+
x3t: Optional[float] = Field(default=None, ge=0, description="Tensile strength X3t")
|
|
118
|
+
x1c: Optional[float] = Field(default=None, ge=0, description="Compressive strength X1c")
|
|
119
|
+
x2c: Optional[float] = Field(default=None, ge=0, description="Compressive strength X2c")
|
|
120
|
+
x3c: Optional[float] = Field(default=None, ge=0, description="Compressive strength X3c")
|
|
121
|
+
x23: Optional[float] = Field(default=None, ge=0, description="Shear strength X23")
|
|
122
|
+
x13: Optional[float] = Field(default=None, ge=0, description="Shear strength X13")
|
|
123
|
+
x12: Optional[float] = Field(default=None, ge=0, description="Shear strength X12")
|
|
124
|
+
|
|
125
|
+
strength_measure: int = Field(
|
|
126
|
+
default=0,
|
|
127
|
+
description="Strength measure: 0=stress, 1=strain"
|
|
128
|
+
)
|
|
129
|
+
strength_constants: Optional[List[float]] = Field(
|
|
130
|
+
default=None,
|
|
131
|
+
description="Strength constants array"
|
|
132
|
+
)
|
|
133
|
+
char_len: float = Field(default=0, ge=0, description="Characteristic length")
|
|
134
|
+
|
|
135
|
+
# Thermal properties
|
|
136
|
+
cte: Optional[List[float]] = Field(
|
|
137
|
+
default=None,
|
|
138
|
+
description="Thermal expansion coefficients (6 components)"
|
|
139
|
+
)
|
|
140
|
+
specific_heat: float = Field(default=0, ge=0, description="Specific heat capacity")
|
|
141
|
+
|
|
142
|
+
# Failure properties
|
|
143
|
+
failure_criterion: int = Field(default=0, description="Failure criterion ID")
|
|
144
|
+
d_thetatheta: float = Field(default=0, description="Thermal property")
|
|
145
|
+
f_eff: float = Field(default=0, description="Effective property")
|
|
146
|
+
|
|
147
|
+
# Pydantic configuration
|
|
148
|
+
model_config = ConfigDict(
|
|
149
|
+
arbitrary_types_allowed=True,
|
|
150
|
+
validate_assignment=True,
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
def __init__(self, name: str = '', **data):
|
|
154
|
+
"""Initialize model with optional positional name argument for backward compatibility.
|
|
51
155
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
156
|
+
Parameters
|
|
157
|
+
----------
|
|
158
|
+
name : str, optional
|
|
159
|
+
Material name (can be positional for backward compatibility)
|
|
160
|
+
**data
|
|
161
|
+
Additional keyword arguments for other fields
|
|
162
|
+
"""
|
|
163
|
+
if name:
|
|
164
|
+
data['name'] = name
|
|
165
|
+
super().__init__(**data)
|
|
166
|
+
|
|
167
|
+
# Field validators
|
|
168
|
+
@field_validator('stff', 'cmpl')
|
|
169
|
+
@classmethod
|
|
170
|
+
def validate_6x6_matrix(cls, v):
|
|
171
|
+
"""Validate that stiffness/compliance matrices are 6x6."""
|
|
172
|
+
if v is not None:
|
|
173
|
+
if not isinstance(v, list) or len(v) != 6:
|
|
174
|
+
raise ValueError('Matrix must be 6x6 (6 rows)')
|
|
175
|
+
for i, row in enumerate(v):
|
|
176
|
+
if not isinstance(row, list) or len(row) != 6:
|
|
177
|
+
raise ValueError(f'Row {i} must have 6 columns')
|
|
178
|
+
return v
|
|
65
179
|
|
|
66
|
-
|
|
180
|
+
@field_validator('nu12', 'nu13', 'nu23')
|
|
181
|
+
@classmethod
|
|
182
|
+
def validate_poisson_ratio(cls, v):
|
|
183
|
+
"""Validate that Poisson's ratios are in valid range."""
|
|
184
|
+
if v is not None and not (-1 < v < 0.5):
|
|
185
|
+
raise ValueError('Poisson ratio must be in range (-1, 0.5)')
|
|
186
|
+
return v
|
|
67
187
|
|
|
68
|
-
cte
|
|
69
|
-
|
|
188
|
+
@field_validator('cte')
|
|
189
|
+
@classmethod
|
|
190
|
+
def validate_cte(cls, v):
|
|
191
|
+
"""Validate that CTE has 6 components if provided."""
|
|
192
|
+
if v is not None:
|
|
193
|
+
if not isinstance(v, list) or len(v) != 6:
|
|
194
|
+
raise ValueError('CTE must have 6 components')
|
|
195
|
+
return v
|
|
70
196
|
|
|
71
197
|
def __repr__(self) -> str:
|
|
198
|
+
"""String representation of the model."""
|
|
199
|
+
constant_name = ['e1', 'e2', 'e3', 'g12', 'g13', 'g23', 'nu12', 'nu13', 'nu23']
|
|
200
|
+
constant_label = ['E1', 'E2', 'E3', 'G12', 'G13', 'G23', 'nu12', 'nu13', 'nu23']
|
|
201
|
+
strength_name = ['x1t', 'x2t', 'x3t', 'x1c', 'x2c', 'x3c', 'x23', 'x13', 'x12']
|
|
202
|
+
strength_label = ['X1t', 'X2t', 'X3t', 'X1c', 'X2c', 'X3c', 'X23', 'X13', 'X12']
|
|
203
|
+
|
|
72
204
|
s = [
|
|
205
|
+
self.model_name,
|
|
206
|
+
'-' * len(self.model_name),
|
|
73
207
|
f'density = {self.density}',
|
|
74
208
|
f'isotropy = {self.isotropy}'
|
|
75
209
|
]
|
|
76
210
|
|
|
77
|
-
s.append('-'*20)
|
|
211
|
+
s.append('-' * 20)
|
|
78
212
|
s.append('stiffness matrix')
|
|
79
|
-
if
|
|
213
|
+
if self.stff is not None:
|
|
80
214
|
for i in range(6):
|
|
81
215
|
_row = []
|
|
82
216
|
for j in range(6):
|
|
@@ -86,7 +220,7 @@ class CauchyContinuumProperty:
|
|
|
86
220
|
s.append('NONE')
|
|
87
221
|
|
|
88
222
|
s.append('compliance matrix')
|
|
89
|
-
if
|
|
223
|
+
if self.cmpl is not None:
|
|
90
224
|
for i in range(6):
|
|
91
225
|
_row = []
|
|
92
226
|
for j in range(6):
|
|
@@ -95,449 +229,280 @@ class CauchyContinuumProperty:
|
|
|
95
229
|
else:
|
|
96
230
|
s.append('NONE')
|
|
97
231
|
|
|
98
|
-
s.append('-'*20)
|
|
232
|
+
s.append('-' * 20)
|
|
99
233
|
s.append('engineering constants')
|
|
100
|
-
for _label, _name in zip(
|
|
101
|
-
_value =
|
|
234
|
+
for _label, _name in zip(constant_label, constant_name):
|
|
235
|
+
_value = getattr(self, _name)
|
|
102
236
|
s.append(f'{_label} = {_value}')
|
|
103
237
|
|
|
104
|
-
s.append('-'*20)
|
|
238
|
+
s.append('-' * 20)
|
|
105
239
|
s.append('strength')
|
|
106
|
-
for _label, _name in zip(
|
|
107
|
-
_value =
|
|
240
|
+
for _label, _name in zip(strength_label, strength_name):
|
|
241
|
+
_value = getattr(self, _name)
|
|
108
242
|
s.append(f'{_label} = {_value}')
|
|
109
243
|
|
|
110
|
-
s.append('-'*20)
|
|
244
|
+
s.append('-' * 20)
|
|
111
245
|
s.append('cte')
|
|
112
|
-
if
|
|
246
|
+
if self.cte is not None:
|
|
113
247
|
s.append(' '.join(list(map(str, self.cte))))
|
|
114
248
|
else:
|
|
115
249
|
s.append('NONE')
|
|
116
250
|
|
|
117
|
-
return '\n'.join(s)
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
class CauchyContinuumModel:
|
|
123
|
-
"""Cauchy continuum model
|
|
124
|
-
"""
|
|
125
|
-
|
|
126
|
-
dim = 3
|
|
127
|
-
model_name = 'Cauchy continuum model'
|
|
128
|
-
strain_name = ['e11', 'e22', 'e33', 'e23', 'e13', 'e12']
|
|
129
|
-
stress_name = ['s11', 's22', 's33', 's23', 's13', 's12']
|
|
130
|
-
|
|
131
|
-
def __init__(self, name:str=''):
|
|
132
|
-
|
|
133
|
-
self.name = name
|
|
134
|
-
self.id = None
|
|
135
|
-
|
|
136
|
-
self.property = CauchyContinuumProperty()
|
|
137
|
-
|
|
138
|
-
# Inertial
|
|
139
|
-
# --------
|
|
140
|
-
|
|
141
|
-
# self.density : float = None
|
|
142
|
-
self.temperature : float = 0
|
|
143
|
-
|
|
144
|
-
# self.strength_constants : Iterable = None
|
|
145
|
-
self.failure_criterion = 0
|
|
146
|
-
|
|
147
|
-
# Thermal
|
|
148
|
-
# -------
|
|
149
|
-
|
|
150
|
-
# self.cte : Iterable[float] = None
|
|
151
|
-
# self.specific_heat : float = 0
|
|
152
|
-
|
|
153
|
-
self.d_thetatheta : float = 0
|
|
154
|
-
self.f_eff : float = 0
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
def __repr__(self) -> str:
|
|
158
|
-
s = [
|
|
159
|
-
self.model_name,
|
|
160
|
-
'-'*len(self.model_name),
|
|
161
|
-
# f'density = {self.density}',
|
|
162
|
-
# f'isotropy = {self.isotropy}'
|
|
163
|
-
]
|
|
164
|
-
|
|
165
|
-
s.append(str(self.property))
|
|
166
|
-
|
|
167
251
|
s.append(f'failure criterion = {self.failure_criterion}')
|
|
168
252
|
|
|
169
253
|
return '\n'.join(s)
|
|
170
254
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
255
|
+
def __eq__(self, other):
|
|
256
|
+
"""Check equality based on all material properties."""
|
|
257
|
+
if not isinstance(other, CauchyContinuumModel):
|
|
258
|
+
return False
|
|
259
|
+
return self.model_dump() == other.model_dump()
|
|
260
|
+
|
|
261
|
+
def __call__(self, x):
|
|
262
|
+
"""Placeholder for model evaluation."""
|
|
263
|
+
return
|
|
264
|
+
|
|
265
|
+
# Property aliases -----------------------------------------------------
|
|
266
|
+
|
|
267
|
+
@property
|
|
268
|
+
def e(self) -> Optional[float]:
|
|
269
|
+
"""Legacy alias for the isotropic Young's modulus."""
|
|
270
|
+
|
|
271
|
+
return self.e1
|
|
272
|
+
|
|
273
|
+
@e.setter
|
|
274
|
+
def e(self, value: float) -> None:
|
|
275
|
+
self.e1 = float(value)
|
|
276
|
+
|
|
277
|
+
@property
|
|
278
|
+
def nu(self) -> Optional[float]:
|
|
279
|
+
"""Legacy alias for the isotropic Poisson ratio."""
|
|
280
|
+
|
|
281
|
+
return self.nu12
|
|
282
|
+
|
|
283
|
+
@nu.setter
|
|
284
|
+
def nu(self, value: float) -> None:
|
|
285
|
+
self.nu12 = float(value)
|
|
286
|
+
|
|
287
|
+
# Backward compatibility methods
|
|
288
|
+
def get(self, name: str):
|
|
289
|
+
"""Get material properties (backward compatibility with old API).
|
|
182
290
|
|
|
183
291
|
Parameters
|
|
184
|
-
|
|
292
|
+
----------
|
|
185
293
|
name : str
|
|
186
|
-
Name of the property
|
|
294
|
+
Name of the property to retrieve
|
|
187
295
|
|
|
188
296
|
Returns
|
|
189
|
-
---------
|
|
190
|
-
int or float or :obj:`Iterable`:
|
|
191
|
-
Value of the specified beam property.
|
|
192
|
-
|
|
193
|
-
Notes
|
|
194
297
|
-------
|
|
298
|
+
int or float or List
|
|
299
|
+
Value of the specified property
|
|
195
300
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
* - nu
|
|
210
|
-
- Poisson's ratio of isotropic materials
|
|
211
|
-
* - e1 | e2 | e3
|
|
212
|
-
- Modulus of elasticity in material direction 1, 2, or 3
|
|
213
|
-
* - nu12 | nu13 | nu23
|
|
214
|
-
- Poisson's ratio in material plane 1-2, 1-3, or 2-3
|
|
215
|
-
* - g12 | g13 | g23
|
|
216
|
-
- Shear modulus in material plane 1-2, 1-3, or 2-3
|
|
217
|
-
* - c
|
|
218
|
-
- 6x6 stiffness matrix
|
|
219
|
-
* - cij (i, j = 1 to 6)
|
|
220
|
-
- Component (i, j) of the stiffness matrix
|
|
221
|
-
* - s
|
|
222
|
-
- 6x6 compliance matrix
|
|
223
|
-
* - sij (i, j = 1 to 6)
|
|
224
|
-
- Component (i, j) of the compliance matrix
|
|
225
|
-
* - strength
|
|
226
|
-
-
|
|
227
|
-
* - alpha
|
|
228
|
-
- CTE of isotropic materials
|
|
229
|
-
* - alpha11 | alpha22 | alpha33 | alpha12 | alpha13 | alpha23
|
|
230
|
-
- The 11, 22, 33, 12, 13, or 23 component of CTE
|
|
231
|
-
* - specific_heat
|
|
232
|
-
-
|
|
301
|
+
Notes
|
|
302
|
+
-----
|
|
303
|
+
Supported property names:
|
|
304
|
+
- density, temperature, isotropy
|
|
305
|
+
- e, nu (isotropic shortcuts for e1, nu12)
|
|
306
|
+
- e1, e2, e3, g12, g13, g23, nu12, nu13, nu23
|
|
307
|
+
- c, s (stiffness/compliance matrices)
|
|
308
|
+
- cij, sij (matrix components, i,j=1..6)
|
|
309
|
+
- x (shortcut for x1t)
|
|
310
|
+
- x1t, x2t, x3t, x1c, x2c, x3c, x23, x13, x12
|
|
311
|
+
- strength, char_len, failure_criterion
|
|
312
|
+
- cte, alpha, alphaij (thermal expansion)
|
|
313
|
+
- specific_heat
|
|
233
314
|
"""
|
|
234
|
-
|
|
235
315
|
v = None
|
|
236
316
|
|
|
237
317
|
if name == 'density':
|
|
238
|
-
v = self.
|
|
318
|
+
v = self.density
|
|
239
319
|
elif name == 'temperature':
|
|
240
320
|
v = self.temperature
|
|
241
321
|
elif name == 'isotropy':
|
|
242
|
-
v = self.
|
|
322
|
+
v = self.isotropy
|
|
243
323
|
|
|
244
324
|
# Stiffness
|
|
245
325
|
elif name == 'e':
|
|
246
|
-
v = self.
|
|
326
|
+
v = self.e1
|
|
247
327
|
elif name == 'nu':
|
|
248
|
-
v = self.
|
|
328
|
+
v = self.nu12
|
|
249
329
|
elif name in ['e1', 'e2', 'e3', 'g12', 'g13', 'g23', 'nu12', 'nu13', 'nu23']:
|
|
250
|
-
v =
|
|
330
|
+
v = getattr(self, name)
|
|
251
331
|
|
|
252
332
|
elif name == 'c':
|
|
253
|
-
v = self.
|
|
333
|
+
v = self.stff
|
|
254
334
|
elif name == 's':
|
|
255
|
-
v = self.
|
|
335
|
+
v = self.cmpl
|
|
256
336
|
|
|
257
337
|
# Strength
|
|
258
338
|
elif name == 'x':
|
|
259
|
-
v = self.
|
|
339
|
+
v = self.x1t
|
|
260
340
|
elif name in ['x1t', 'x2t', 'x3t', 'x1c', 'x2c', 'x3c', 'x23', 'x13', 'x12']:
|
|
261
|
-
v =
|
|
341
|
+
v = getattr(self, name)
|
|
262
342
|
elif name == 'strength':
|
|
263
|
-
v = self.
|
|
343
|
+
v = self.strength_constants
|
|
264
344
|
elif name == 'char_len':
|
|
265
|
-
v = self.
|
|
345
|
+
v = self.char_len
|
|
266
346
|
elif name == 'failure_criterion':
|
|
267
347
|
v = self.failure_criterion
|
|
268
348
|
|
|
269
349
|
# Thermal
|
|
270
350
|
elif name == 'cte':
|
|
271
|
-
v = self.
|
|
351
|
+
v = self.cte
|
|
272
352
|
elif name == 'specific_heat':
|
|
273
|
-
v = self.
|
|
353
|
+
v = self.specific_heat
|
|
274
354
|
|
|
275
355
|
elif name.startswith('alpha'):
|
|
276
356
|
if name == 'alpha':
|
|
277
|
-
v = self.
|
|
357
|
+
v = self.cte[0] if self.cte is not None else None
|
|
278
358
|
else:
|
|
279
359
|
_ij = name[-2:]
|
|
280
360
|
for _k, __ij in enumerate(['11', '22', '33', '23', '13', '12']):
|
|
281
361
|
if _ij == __ij:
|
|
282
|
-
v = self.
|
|
362
|
+
v = self.cte[_k] if self.cte is not None else None
|
|
283
363
|
break
|
|
284
|
-
elif name.startswith('c'):
|
|
364
|
+
elif name.startswith('c') and len(name) == 3:
|
|
285
365
|
_i = int(name[1]) - 1
|
|
286
366
|
_j = int(name[2]) - 1
|
|
287
|
-
v = self.
|
|
288
|
-
elif name.startswith('s'):
|
|
367
|
+
v = self.stff[_i][_j] if self.stff is not None else None
|
|
368
|
+
elif name.startswith('s') and len(name) == 3:
|
|
289
369
|
_i = int(name[1]) - 1
|
|
290
370
|
_j = int(name[2]) - 1
|
|
291
|
-
v = self.
|
|
371
|
+
v = self.cmpl[_i][_j] if self.cmpl is not None else None
|
|
292
372
|
|
|
293
373
|
return v
|
|
294
374
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
"""Set material properties.
|
|
375
|
+
def set(self, name: str, value, **kwargs):
|
|
376
|
+
"""Set material properties (backward compatibility with old API).
|
|
298
377
|
|
|
299
378
|
Parameters
|
|
300
|
-
|
|
379
|
+
----------
|
|
301
380
|
name : str
|
|
302
|
-
Name of the property
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
381
|
+
Name of the property to set
|
|
382
|
+
value : str or int or float or List
|
|
383
|
+
Value to set
|
|
384
|
+
**kwargs
|
|
385
|
+
Additional keyword arguments (e.g., input_type for elastic)
|
|
307
386
|
"""
|
|
308
|
-
if name == 'isotropy':
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
elif
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
if
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
self.
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
#
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
#
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
#
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
#
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
#
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
# if self.isotropy == 0:
|
|
431
|
-
# s.append('isotropic')
|
|
432
|
-
# s.append(f'E = {self.e1}, v = {self.nu12}')
|
|
433
|
-
# elif self.isotropy == 1:
|
|
434
|
-
# s.append('orthotropic')
|
|
435
|
-
# s.append(f'E1 = {self.e1}, E2 = {self.e2}, E3 = {self.e3}')
|
|
436
|
-
# s.append(f'G12 = {self.g12}, G13 = {self.g13}, G23 = {self.g23}')
|
|
437
|
-
# s.append(f'v12 = {self.nu12}, v13 = {self.nu13}, v23 = {self.nu23}')
|
|
438
|
-
# elif self.isotropy == 2:
|
|
439
|
-
# s.append('anisotropic')
|
|
440
|
-
# for i in range(6):
|
|
441
|
-
# _row = []
|
|
442
|
-
# for j in range(i, 6):
|
|
443
|
-
# _row.append(f'C{i+1}{j+1} = {self.stff[i][j]}')
|
|
444
|
-
# s.append(', '.join(_row))
|
|
445
|
-
|
|
446
|
-
# return '\n'.join(s)
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
# def summary(self):
|
|
450
|
-
# stype = 'isotropic'
|
|
451
|
-
# sprop = [['e = {0}'.format(self.e1),], ['nu = {0}'.format(self.nu12),]]
|
|
452
|
-
# if self.isotropy == 1:
|
|
453
|
-
# stype = 'orthotropic'
|
|
454
|
-
# sprop = [
|
|
455
|
-
# ['e1 = {0}'.format(self.e1), 'e2 = {0}'.format(self.e2), 'e3 = {0}'.format(self.e3)],
|
|
456
|
-
# ['g12 = {0}'.format(self.g12), 'g13 = {0}'.format(self.g13), 'g23 = {0}'.format(self.g23)],
|
|
457
|
-
# ['nu12 = {0}'.format(self.nu12), 'nu13 = {0}'.format(self.nu13), 'nu23 = {0}'.format(self.nu23)]
|
|
458
|
-
# ]
|
|
459
|
-
# elif self.isotropy == 2:
|
|
460
|
-
# stype = 'anisotropic'
|
|
461
|
-
# print('type:', stype)
|
|
462
|
-
# print('density =', self.density)
|
|
463
|
-
# print('elastic properties:')
|
|
464
|
-
# for p in sprop:
|
|
465
|
-
# print(', '.join(p))
|
|
466
|
-
# return
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
# def get(self, name):
|
|
470
|
-
# r"""
|
|
471
|
-
# """
|
|
472
|
-
# v = None
|
|
473
|
-
|
|
474
|
-
# if name == 'density':
|
|
475
|
-
# v = self.density
|
|
476
|
-
|
|
477
|
-
# elif name in ['e', 'e1', 'e2', 'e3', 'g12', 'g13', 'g23', 'nu', 'nu12', 'nu13', 'nu23']:
|
|
478
|
-
# v = self.constants[name]
|
|
479
|
-
|
|
480
|
-
# elif name in ['xt', 'yt', 'zt', 'xc', 'yc', 'zc', 'r', 't', 's']:
|
|
481
|
-
# v = self.strength_constants[name]
|
|
482
|
-
# # v = eval('self.{}'.format(name))
|
|
483
|
-
|
|
484
|
-
# return v
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
# def assignConstants(self, consts):
|
|
488
|
-
# if len(consts) == 2:
|
|
489
|
-
# self.isotropy = 0
|
|
490
|
-
# self.e1 = float(consts[0])
|
|
491
|
-
# self.nu12 = float(consts[1])
|
|
492
|
-
# elif len(consts) == 9:
|
|
493
|
-
# self.isotropy = 1
|
|
494
|
-
# self.e1, self.e2, self.e3 = list(map(float, consts[:3]))
|
|
495
|
-
# self.g12, self.g13, self.g23 = list(map(float, consts[3:6]))
|
|
496
|
-
# self.nu12, self.nu13, self.nu23 = list(map(float, consts[6:]))
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
# def setElasticProperty(self, consts, ctype):
|
|
500
|
-
# if ctype == 'isotropic' or ctype == 0:
|
|
501
|
-
# self.isotropy = 0
|
|
502
|
-
# self.e1 = float(consts[0])
|
|
503
|
-
# self.nu12 = float(consts[1])
|
|
504
|
-
# elif ctype == 'lamina':
|
|
505
|
-
# self.isotropy = 1
|
|
506
|
-
# self.e1 = float(consts[0])
|
|
507
|
-
# self.e2 = float(consts[1])
|
|
508
|
-
# self.g12 = float(consts[2])
|
|
509
|
-
# self.nu12 = float(consts[3])
|
|
510
|
-
# self.e3 = self.e2
|
|
511
|
-
# self.g13 = self.g12
|
|
512
|
-
# self.nu13 = self.nu12
|
|
513
|
-
# self.nu23 = 0.3
|
|
514
|
-
# self.g23 = self.e3 / (2.0 * (1 + self.nu23))
|
|
515
|
-
# elif ctype == 'engineering' or ctype == 1:
|
|
516
|
-
# self.isotropy = 1
|
|
517
|
-
# self.e1, self.e2, self.e3 = list(map(float, consts[:3]))
|
|
518
|
-
# self.g12, self.g13, self.g23 = list(map(float, consts[3:6]))
|
|
519
|
-
# self.nu12, self.nu13, self.nu23 = list(map(float, consts[6:]))
|
|
520
|
-
# elif ctype == 'orthotropic':
|
|
521
|
-
# self.isotropy = 1
|
|
522
|
-
# elif ctype == 'anisotropic' or ctype == 2:
|
|
523
|
-
# self.isotropy = 2
|
|
524
|
-
|
|
525
|
-
# return
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
# def setStrengthProperty(self, strength):
|
|
529
|
-
# self.strength['constants'] = list(map(float, strength))
|
|
530
|
-
# return
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
# def setFailureCriterion(self, criterion):
|
|
534
|
-
# if isinstance(criterion, str):
|
|
535
|
-
# self.strength['criterion'] = self.FAILURE_CRITERION_NAME_TO_ID[criterion]
|
|
536
|
-
# return
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
# def setCharacteristicLength(self, char_len=0):
|
|
540
|
-
# self.strength['chara_len'] = char_len
|
|
541
|
-
# return
|
|
542
|
-
|
|
387
|
+
if name == 'isotropy':
|
|
388
|
+
iso_value: Optional[int] = None
|
|
389
|
+
if isinstance(value, str):
|
|
390
|
+
value_lower = value.lower()
|
|
391
|
+
if value_lower.startswith('iso'):
|
|
392
|
+
iso_value = 0
|
|
393
|
+
elif value_lower.startswith(('ortho', 'eng', 'lam')):
|
|
394
|
+
iso_value = 1
|
|
395
|
+
elif value_lower.startswith('aniso'):
|
|
396
|
+
iso_value = 2
|
|
397
|
+
else:
|
|
398
|
+
iso_value = int(value)
|
|
399
|
+
elif isinstance(value, int):
|
|
400
|
+
iso_value = value
|
|
401
|
+
else:
|
|
402
|
+
raise TypeError('Isotropy must be provided as string or integer')
|
|
403
|
+
|
|
404
|
+
if iso_value not in (0, 1, 2):
|
|
405
|
+
raise ValueError('Isotropy value must be 0, 1, or 2')
|
|
406
|
+
|
|
407
|
+
self.isotropy = cast(Literal[0, 1, 2], iso_value)
|
|
408
|
+
|
|
409
|
+
elif name == 'elastic':
|
|
410
|
+
self.setElastic(value, **kwargs)
|
|
411
|
+
|
|
412
|
+
elif name == 'strength_constants':
|
|
413
|
+
values = list(value)
|
|
414
|
+
if len(values) == 9:
|
|
415
|
+
self.x1t = values[0]
|
|
416
|
+
self.x2t = values[1]
|
|
417
|
+
self.x3t = values[2]
|
|
418
|
+
self.x1c = values[3]
|
|
419
|
+
self.x2c = values[4]
|
|
420
|
+
self.x3c = values[5]
|
|
421
|
+
self.x23 = values[6]
|
|
422
|
+
self.x13 = values[7]
|
|
423
|
+
self.x12 = values[8]
|
|
424
|
+
|
|
425
|
+
self.strength_constants = values
|
|
426
|
+
|
|
427
|
+
else:
|
|
428
|
+
# Direct attribute setting
|
|
429
|
+
setattr(self, name, value)
|
|
430
|
+
|
|
431
|
+
return
|
|
432
|
+
|
|
433
|
+
def setElastic(
|
|
434
|
+
self,
|
|
435
|
+
consts: ElasticInput,
|
|
436
|
+
input_type: str = '',
|
|
437
|
+
**kwargs,
|
|
438
|
+
):
|
|
439
|
+
"""Set elastic properties based on isotropy type.
|
|
440
|
+
|
|
441
|
+
Parameters
|
|
442
|
+
----------
|
|
443
|
+
consts : Iterable
|
|
444
|
+
Elastic constants (format depends on isotropy and input_type)
|
|
445
|
+
input_type : str, optional
|
|
446
|
+
Type of input: 'lamina', 'engineering', 'orthotropic', 'stiffness', 'compliance'
|
|
447
|
+
**kwargs
|
|
448
|
+
Additional keyword arguments
|
|
449
|
+
"""
|
|
450
|
+
if self.isotropy == 0:
|
|
451
|
+
# Isotropic: [E, nu]
|
|
452
|
+
seq = list(cast(FloatSequence, consts))
|
|
453
|
+
if len(seq) < 2:
|
|
454
|
+
raise ValueError('Isotropic elastic input requires [E, nu]')
|
|
455
|
+
self.e1 = float(seq[0])
|
|
456
|
+
self.nu12 = float(seq[1])
|
|
457
|
+
|
|
458
|
+
elif self.isotropy == 1:
|
|
459
|
+
# Orthotropic
|
|
460
|
+
seq = list(cast(FloatSequence, consts))
|
|
461
|
+
if input_type == 'lamina':
|
|
462
|
+
# [E1, E2, G12, nu12]
|
|
463
|
+
if len(seq) < 4:
|
|
464
|
+
raise ValueError('Lamina input requires 4 values [E1, E2, G12, nu12]')
|
|
465
|
+
self.e1 = float(seq[0])
|
|
466
|
+
self.e2 = float(seq[1])
|
|
467
|
+
self.g12 = float(seq[2])
|
|
468
|
+
self.nu12 = float(seq[3])
|
|
469
|
+
self.e3 = self.e2
|
|
470
|
+
self.g13 = self.g12
|
|
471
|
+
self.nu13 = self.nu12
|
|
472
|
+
self.nu23 = 0.3
|
|
473
|
+
self.g23 = self.e3 / (2.0 * (1 + self.nu23))
|
|
474
|
+
elif input_type == 'engineering':
|
|
475
|
+
# [E1, E2, E3, G12, G13, G23, nu12, nu13, nu23]
|
|
476
|
+
if len(seq) < 9:
|
|
477
|
+
raise ValueError('Engineering input requires 9 values')
|
|
478
|
+
self.e1, self.e2, self.e3 = list(map(float, seq[:3]))
|
|
479
|
+
self.g12, self.g13, self.g23 = list(map(float, seq[3:6]))
|
|
480
|
+
self.nu12, self.nu13, self.nu23 = list(map(float, seq[6:9]))
|
|
481
|
+
elif input_type == 'orthotropic':
|
|
482
|
+
# TODO: implement orthotropic input
|
|
483
|
+
pass
|
|
484
|
+
else:
|
|
485
|
+
# Default: same as engineering
|
|
486
|
+
if len(seq) < 9:
|
|
487
|
+
raise ValueError('Orthotropic input requires 9 values')
|
|
488
|
+
self.e1, self.e2, self.e3 = list(map(float, seq[:3]))
|
|
489
|
+
self.g12, self.g13, self.g23 = list(map(float, seq[3:6]))
|
|
490
|
+
self.nu12, self.nu13, self.nu23 = list(map(float, seq[6:9]))
|
|
491
|
+
|
|
492
|
+
elif self.isotropy == 2:
|
|
493
|
+
# Anisotropic: provide full matrices
|
|
494
|
+
matrix_input = cast(MatrixSequence, consts)
|
|
495
|
+
rows: List[List[float]] = []
|
|
496
|
+
for row in matrix_input:
|
|
497
|
+
rows.append([float(value) for value in row])
|
|
498
|
+
if input_type == 'stiffness':
|
|
499
|
+
self.stff = rows
|
|
500
|
+
elif input_type == 'compliance':
|
|
501
|
+
self.cmpl = rows
|
|
502
|
+
|
|
503
|
+
return
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
# Backward-compatible alias retained for downstream imports
|
|
507
|
+
CauchyContinuumModelNew = CauchyContinuumModel
|
|
543
508
|
|