pyfebio 0.1.0__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.
Potentially problematic release.
This version of pyfebio might be problematic. Click here for more details.
- pyfebio/__init__.py +45 -0
- pyfebio/_types.py +95 -0
- pyfebio/boundary.py +119 -0
- pyfebio/constraints.py +41 -0
- pyfebio/contact.py +172 -0
- pyfebio/control.py +73 -0
- pyfebio/discrete.py +40 -0
- pyfebio/globals.py +12 -0
- pyfebio/include.py +5 -0
- pyfebio/initial.py +27 -0
- pyfebio/loaddata.py +51 -0
- pyfebio/loads.py +55 -0
- pyfebio/material.py +1191 -0
- pyfebio/mesh.py +354 -0
- pyfebio/meshadaptor.py +159 -0
- pyfebio/meshdata.py +52 -0
- pyfebio/meshdomains.py +65 -0
- pyfebio/model.py +144 -0
- pyfebio/module.py +14 -0
- pyfebio/output.py +298 -0
- pyfebio/py.typed +0 -0
- pyfebio/rigid.py +275 -0
- pyfebio/step.py +28 -0
- pyfebio-0.1.0.dist-info/METADATA +336 -0
- pyfebio-0.1.0.dist-info/RECORD +26 -0
- pyfebio-0.1.0.dist-info/WHEEL +4 -0
pyfebio/mesh.py
ADDED
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
3
|
+
import meshio
|
|
4
|
+
import numpy as np
|
|
5
|
+
from pydantic import Field
|
|
6
|
+
from pydantic_xml import BaseXmlModel, attr, element
|
|
7
|
+
|
|
8
|
+
from ._types import (
|
|
9
|
+
StringFloatVec3,
|
|
10
|
+
StringUIntVec,
|
|
11
|
+
StringUIntVec2,
|
|
12
|
+
StringUIntVec3,
|
|
13
|
+
StringUIntVec4,
|
|
14
|
+
StringUIntVec6,
|
|
15
|
+
StringUIntVec8,
|
|
16
|
+
StringUIntVec9,
|
|
17
|
+
StringUIntVec10,
|
|
18
|
+
StringUIntVec15,
|
|
19
|
+
StringUIntVec20,
|
|
20
|
+
StringUIntVec27,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
SolidFEBioElementType = Literal["tet4", "tet10", "tet15", "hex8", "hex20", "hex27", "penta6"]
|
|
24
|
+
ShellFEBioElementType = Literal["tri3", "tri6", "quad4", "quad8", "quad9", "q4ans", "q4eas"]
|
|
25
|
+
BeamFEBioElementType = Literal["line2", "line3"]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class Node(BaseXmlModel, tag="node", validate_assignment=True):
|
|
29
|
+
text: StringFloatVec3 = Field(default="0.0, 0.0, 0.0")
|
|
30
|
+
id: int = attr()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class Nodes(BaseXmlModel, validate_assignment=True):
|
|
34
|
+
name: str = attr(default="")
|
|
35
|
+
all_nodes: list[Node] = element(tag="node", default=[])
|
|
36
|
+
|
|
37
|
+
def add_node(self, new_node: Node):
|
|
38
|
+
self.all_nodes.append(new_node)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class Tet4Element(BaseXmlModel, tag="elem", validate_assignment=True):
|
|
42
|
+
text: StringUIntVec4 = Field(default="1,2,3,4")
|
|
43
|
+
id: int = attr()
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class Tet10Element(BaseXmlModel, tag="elem", validate_assignment=True):
|
|
47
|
+
text: StringUIntVec10 = Field(default="1,2,3,4,5,6,7,8,9,10")
|
|
48
|
+
id: int = attr()
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class Tet15Element(BaseXmlModel, tag="elem", validate_assignment=True):
|
|
52
|
+
text: StringUIntVec15 = Field(default="1,2,3,4,5,6,7,8,9,10,11,12,13,14,15")
|
|
53
|
+
id: int = attr()
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class Hex8Element(BaseXmlModel, tag="elem", validate_assignment=True):
|
|
57
|
+
text: StringUIntVec8 = Field(default="1,2,3,4,5,6,7,8")
|
|
58
|
+
id: int = attr()
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class Hex20Element(BaseXmlModel, tag="elem", validate_assignment=True):
|
|
62
|
+
text: StringUIntVec20 = Field(default="1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20")
|
|
63
|
+
id: int = attr()
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class Hex27Element(BaseXmlModel, tag="elem", validate_assignment=True):
|
|
67
|
+
text: StringUIntVec27 = Field(default="1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27")
|
|
68
|
+
id: int = attr()
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class Penta6Element(BaseXmlModel, tag="elem", validate_assignment=True):
|
|
72
|
+
text: StringUIntVec6 = Field(default="1,2,3,4,5,6")
|
|
73
|
+
id: int = attr()
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class Tri3Element(BaseXmlModel, tag="elem", validate_assignment=True):
|
|
77
|
+
text: StringUIntVec3 = Field(default="1,2,3")
|
|
78
|
+
id: int = attr()
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class Tri6Element(BaseXmlModel, tag="elem", validate_assignment=True):
|
|
82
|
+
text: StringUIntVec6 = Field(default="1,2,3,4,5,6")
|
|
83
|
+
id: int = attr()
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class Quad4Element(BaseXmlModel, tag="elem", validate_assignment=True):
|
|
87
|
+
text: StringUIntVec4 = Field(default="1,2,3,4")
|
|
88
|
+
id: int = attr()
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class Quad8Element(BaseXmlModel, tag="elem", validate_assignment=True):
|
|
92
|
+
text: StringUIntVec8 = Field(default="1,2,3,4,5,6,7,8")
|
|
93
|
+
id: int = attr()
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class Quad9Element(BaseXmlModel, tag="elem", validate_assignment=True):
|
|
97
|
+
text: StringUIntVec9 = Field(default="1,2,3,4,5,6,7,8,9")
|
|
98
|
+
id: int = attr()
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class Line2Element(BaseXmlModel, tag="elem", validate_assignment=True):
|
|
102
|
+
text: StringUIntVec2 = Field(default="1,2")
|
|
103
|
+
id: int = attr()
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class Line3Element(BaseXmlModel, tag="elem", validate_assignment=True):
|
|
107
|
+
text: StringUIntVec3 = Field(default="1,2,3")
|
|
108
|
+
id: int = attr()
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
ElementType = (
|
|
112
|
+
Tet4Element
|
|
113
|
+
| Tet10Element
|
|
114
|
+
| Tet15Element
|
|
115
|
+
| Hex8Element
|
|
116
|
+
| Hex20Element
|
|
117
|
+
| Hex27Element
|
|
118
|
+
| Penta6Element
|
|
119
|
+
| Tri3Element
|
|
120
|
+
| Tri6Element
|
|
121
|
+
| Quad4Element
|
|
122
|
+
| Quad8Element
|
|
123
|
+
| Quad9Element
|
|
124
|
+
| Line2Element
|
|
125
|
+
| Line3Element
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class Elements(BaseXmlModel, tag="elements", validate_assignment=True):
|
|
130
|
+
name: str = attr(default="Part")
|
|
131
|
+
type: SolidFEBioElementType | ShellFEBioElementType | BeamFEBioElementType = attr(default="hex8")
|
|
132
|
+
all_elements: list[ElementType] = element(default=[], tag="elem")
|
|
133
|
+
|
|
134
|
+
def add_element(self, new_element: ElementType):
|
|
135
|
+
self.all_elements.append(new_element)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class ElementSet(BaseXmlModel, tag="ElementSet", validate_assignment=True):
|
|
139
|
+
name: str = attr(default="")
|
|
140
|
+
text: StringUIntVec
|
|
141
|
+
|
|
142
|
+
def add_element(self, new_element_id: int):
|
|
143
|
+
",".join([self.text, str(new_element_id)])
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
class NodeSet(BaseXmlModel, tag="NodeSet", validate_assignment=True):
|
|
147
|
+
name: str = attr(default="")
|
|
148
|
+
text: StringUIntVec
|
|
149
|
+
|
|
150
|
+
def add_node(self, new_node_id: int):
|
|
151
|
+
",".join([self.text, str(new_node_id)])
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
class Surface(BaseXmlModel, tag="Surface", validate_assignment=True):
|
|
155
|
+
name: str = attr(default="")
|
|
156
|
+
all_tri3: list[Tri3Element] = element(default=[], tag="tri3")
|
|
157
|
+
all_tri6: list[Tri6Element] = element(default=[], tag="tri6")
|
|
158
|
+
all_quad4: list[Quad4Element] = element(default=[], tag="quad4")
|
|
159
|
+
all_quad8: list[Quad8Element] = element(default=[], tag="quad8")
|
|
160
|
+
all_quad9: list[Quad9Element] = element(default=[], tag="quad9")
|
|
161
|
+
|
|
162
|
+
def add_tri3(self, new_tri: Tri3Element):
|
|
163
|
+
self.all_tri3.append(new_tri)
|
|
164
|
+
|
|
165
|
+
def add_tri6(self, new_tri: Tri6Element):
|
|
166
|
+
self.all_tri6.append(new_tri)
|
|
167
|
+
|
|
168
|
+
def add_quad4(self, new_quad: Quad4Element):
|
|
169
|
+
self.all_quad4.append(new_quad)
|
|
170
|
+
|
|
171
|
+
def add_quad8(self, new_quad: Quad8Element):
|
|
172
|
+
self.all_quad8.append(new_quad)
|
|
173
|
+
|
|
174
|
+
def add_quad9(self, new_quad: Quad9Element):
|
|
175
|
+
self.all_quad9.append(new_quad)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
class SurfacePair(BaseXmlModel, tag="SurfacePair", validate_assignment=True):
|
|
179
|
+
name: str = attr(default="")
|
|
180
|
+
primary: str = element()
|
|
181
|
+
secondary: str = element()
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
class DiscreteElement(BaseXmlModel, tag="delem", validate_assignment=True):
|
|
185
|
+
text: StringUIntVec2
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
class DiscreteSet(BaseXmlModel, tag="DiscreteSet", validate_assignment=True):
|
|
189
|
+
name: str = attr(default="")
|
|
190
|
+
elements: list[DiscreteElement] = element(default=[])
|
|
191
|
+
|
|
192
|
+
def add_element(self, new_element: DiscreteElement):
|
|
193
|
+
self.elements.append(new_element)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
class Mesh(BaseXmlModel, validate_assignment=True):
|
|
197
|
+
nodes: list[Nodes] = element(default=[], tag="Nodes")
|
|
198
|
+
elements: list[Elements] = element(default=[], tag="Elements")
|
|
199
|
+
surfaces: list[Surface] = element(default=[], tag="Surface")
|
|
200
|
+
element_sets: list[ElementSet] = element(default=[], tag="ElementSet")
|
|
201
|
+
node_sets: list[NodeSet] = element(default=[], tag="NodeSet")
|
|
202
|
+
discrete_sets: list[DiscreteSet] = element(default=[], tag="DiscreteSet")
|
|
203
|
+
surface_pairs: list[SurfacePair] = element(default=[], tag="SurfacePair")
|
|
204
|
+
|
|
205
|
+
def add_node_domain(self, new_node_domain: Nodes):
|
|
206
|
+
if not new_node_domain.name:
|
|
207
|
+
new_node_domain.name = f"Part{len(self.nodes) + 1}"
|
|
208
|
+
self.nodes.append(new_node_domain)
|
|
209
|
+
|
|
210
|
+
def add_element_domain(self, new_element_domain: Elements):
|
|
211
|
+
if new_element_domain.name == "Part":
|
|
212
|
+
new_element_domain.name = f"Part{len(self.elements) + 1}"
|
|
213
|
+
self.elements.append(new_element_domain)
|
|
214
|
+
|
|
215
|
+
def add_surface(self, new_surface: Surface):
|
|
216
|
+
if not new_surface.name:
|
|
217
|
+
new_surface.name = f"Surface{len(self.surfaces) + 1}"
|
|
218
|
+
self.surfaces.append(new_surface)
|
|
219
|
+
|
|
220
|
+
def add_element_set(self, new_element_set: ElementSet):
|
|
221
|
+
if not new_element_set.name:
|
|
222
|
+
new_element_set.name = f"ElementSet{len(self.element_sets) + 1}"
|
|
223
|
+
self.element_sets.append(new_element_set)
|
|
224
|
+
|
|
225
|
+
def add_node_set(self, new_node_set: NodeSet):
|
|
226
|
+
if not new_node_set.name:
|
|
227
|
+
new_node_set.name = f"NodeSet{len(self.node_sets) + 1}"
|
|
228
|
+
self.node_sets.append(new_node_set)
|
|
229
|
+
|
|
230
|
+
def add_discrete_set(self, new_discrete_set: DiscreteSet):
|
|
231
|
+
if not new_discrete_set.name:
|
|
232
|
+
new_discrete_set.name = f"DiscreteSet{len(self.discrete_sets) + 1}"
|
|
233
|
+
self.discrete_sets.append(new_discrete_set)
|
|
234
|
+
|
|
235
|
+
def add_surface_pair(self, new_surface_pair: SurfacePair):
|
|
236
|
+
if not new_surface_pair.name:
|
|
237
|
+
new_surface_pair.name = f"SurfacePair{len(self.surface_pairs) + 1}"
|
|
238
|
+
self.surface_pairs.append(new_surface_pair)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
ELEMENT_MAP: dict[str, SolidFEBioElementType | ShellFEBioElementType | BeamFEBioElementType] = {
|
|
242
|
+
"tetra": "tet4",
|
|
243
|
+
"tetra10": "tet10",
|
|
244
|
+
"hexahedron": "hex8",
|
|
245
|
+
"hexahedron20": "hex20",
|
|
246
|
+
"hexahedron27": "hex27",
|
|
247
|
+
"triangle": "tri3",
|
|
248
|
+
"triangle6": "tri6",
|
|
249
|
+
"quad": "quad4",
|
|
250
|
+
"quad8": "quad8",
|
|
251
|
+
"quad9": "quad9",
|
|
252
|
+
"line": "line2",
|
|
253
|
+
"line3": "line3",
|
|
254
|
+
}
|
|
255
|
+
ELEMENT_CLASS_MAP: dict[str, type[ElementType]] = {
|
|
256
|
+
"tet4": Tet4Element,
|
|
257
|
+
"tet10": Tet10Element,
|
|
258
|
+
"hex8": Hex8Element,
|
|
259
|
+
"hex20": Hex20Element,
|
|
260
|
+
"hex27": Hex27Element,
|
|
261
|
+
"tri3": Tri3Element,
|
|
262
|
+
"tri6": Tri6Element,
|
|
263
|
+
"quad4": Quad4Element,
|
|
264
|
+
"quad8": Quad8Element,
|
|
265
|
+
"quad9": Quad9Element,
|
|
266
|
+
"line2": Line2Element,
|
|
267
|
+
"line3": Line3Element,
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
EXCLUDE_SET_STR = ("gmsh:bounding_entities",)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def translate_meshio(
|
|
274
|
+
meshobj: meshio.Mesh,
|
|
275
|
+
nodeoffset: int = 0,
|
|
276
|
+
elementoffset: int = 0,
|
|
277
|
+
surfaceoffset: int = 0,
|
|
278
|
+
shell_sets: list[str] | None = None,
|
|
279
|
+
) -> Mesh:
|
|
280
|
+
if shell_sets is None:
|
|
281
|
+
shell_sets = []
|
|
282
|
+
solid_nodes = []
|
|
283
|
+
for key, value in meshobj.cells_dict.items():
|
|
284
|
+
if meshio._mesh.topological_dimension[key] == 3:
|
|
285
|
+
solid_nodes.extend(np.unique(value.ravel()).tolist())
|
|
286
|
+
solid_nodes = set(solid_nodes)
|
|
287
|
+
|
|
288
|
+
make_element = {}
|
|
289
|
+
for key, values in meshobj.cells_dict.items():
|
|
290
|
+
make_element[key] = []
|
|
291
|
+
if meshio._mesh.topological_dimension[key] == 2:
|
|
292
|
+
for element in values:
|
|
293
|
+
make_element[key].append(bool(set(np.unique(element.ravel())).difference(solid_nodes)))
|
|
294
|
+
else:
|
|
295
|
+
make_element[key].extend([True] * len(values))
|
|
296
|
+
|
|
297
|
+
febio_mesh = Mesh()
|
|
298
|
+
nodes_object = Nodes()
|
|
299
|
+
for i, node in enumerate(meshobj.points):
|
|
300
|
+
nodes_object.add_node(Node(id=i + 1 + nodeoffset, text=",".join(map(str, node))))
|
|
301
|
+
num_elements = 0
|
|
302
|
+
num_surface_elements = 0
|
|
303
|
+
hex27_reorder = [2, 6, 7, 3, 1, 5, 4, 0, 18, 14, 19, 10, 17, 12, 16, 8, 9, 13, 15, 11]
|
|
304
|
+
hex27_reorder.extend([21, 25, 20, 24, 23, 22, 26])
|
|
305
|
+
for name, members in meshobj.cell_sets_dict.items():
|
|
306
|
+
if any([exclude in name.lower() for exclude in EXCLUDE_SET_STR]):
|
|
307
|
+
continue
|
|
308
|
+
shell_set = name in shell_sets
|
|
309
|
+
for member, offsets in members.items():
|
|
310
|
+
if len(members.keys()) > 1:
|
|
311
|
+
set_name = f"{name}_{ELEMENT_MAP[member]}"
|
|
312
|
+
else:
|
|
313
|
+
set_name = name
|
|
314
|
+
etype = ELEMENT_MAP[member]
|
|
315
|
+
if shell_set or np.array(make_element[member])[offsets].all():
|
|
316
|
+
elements_object = Elements(name=set_name, type=etype)
|
|
317
|
+
for offset in offsets:
|
|
318
|
+
element = meshobj.cells_dict[member][offset]
|
|
319
|
+
if etype == "hex27":
|
|
320
|
+
element = element[hex27_reorder]
|
|
321
|
+
|
|
322
|
+
num_elements += 1
|
|
323
|
+
elements_object.add_element(
|
|
324
|
+
ELEMENT_CLASS_MAP[etype](
|
|
325
|
+
id=num_elements + elementoffset,
|
|
326
|
+
text=",".join(map(str, element + 1)),
|
|
327
|
+
)
|
|
328
|
+
)
|
|
329
|
+
febio_mesh.elements.append(elements_object)
|
|
330
|
+
else:
|
|
331
|
+
surface_object = Surface(name=set_name)
|
|
332
|
+
fn_map = {
|
|
333
|
+
"tri3": surface_object.add_tri3,
|
|
334
|
+
"tri6": surface_object.add_tri6,
|
|
335
|
+
"quad4": surface_object.add_quad4,
|
|
336
|
+
"quad8": surface_object.add_quad8,
|
|
337
|
+
"quad9": surface_object.add_quad9,
|
|
338
|
+
}
|
|
339
|
+
node_set = []
|
|
340
|
+
for offset in offsets:
|
|
341
|
+
num_surface_elements += 1
|
|
342
|
+
element = meshobj.cells_dict[member][offset]
|
|
343
|
+
fn_map[ELEMENT_MAP[member]](
|
|
344
|
+
ELEMENT_CLASS_MAP[etype](
|
|
345
|
+
id=num_surface_elements + surfaceoffset,
|
|
346
|
+
text=",".join(map(str, element + 1)),
|
|
347
|
+
)
|
|
348
|
+
)
|
|
349
|
+
node_set.extend((element + 1).tolist())
|
|
350
|
+
node_sets = sorted(set(node_set))
|
|
351
|
+
febio_mesh.node_sets.append(NodeSet(name=set_name, text=",".join(map(str, node_sets))))
|
|
352
|
+
febio_mesh.surfaces.append(surface_object)
|
|
353
|
+
febio_mesh.nodes.append(nodes_object)
|
|
354
|
+
return febio_mesh
|
pyfebio/meshadaptor.py
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
3
|
+
from pydantic_xml import BaseXmlModel, attr, element
|
|
4
|
+
|
|
5
|
+
from ._types import StringFloatVec2, StringUIntVec
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class MaxVariableCriterion(BaseXmlModel, tag="criterion"):
|
|
9
|
+
type: Literal["max_variable"] = attr(default="max_variable", frozen=True)
|
|
10
|
+
dof: int = element(default=-1)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ElementSelectionCriterion(BaseXmlModel, tag="criterion"):
|
|
14
|
+
type: Literal["element_selection"] = attr(default="element_selection", frozen=True)
|
|
15
|
+
element_list: StringUIntVec = element()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ContactGapCriterion(BaseXmlModel, tag="criterion"):
|
|
19
|
+
type: Literal["contact gap"] = attr(default="contact gap", frozen=True)
|
|
20
|
+
gap: float = element(default=0.0)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class StressCriterion(BaseXmlModel, tag="criterion"):
|
|
24
|
+
type: Literal["stress"] = attr(default="stress", frozen=True)
|
|
25
|
+
metric: Literal[0, 1] = element(default=0, description="0=effective stress, 1=max principal stress")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class MathCriterion(BaseXmlModel, tag="criterion"):
|
|
29
|
+
type: Literal["math"] = attr(default="math", frozen=True)
|
|
30
|
+
math: str = element(default="1")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class DamageCriterion(BaseXmlModel, tag="criterion"):
|
|
34
|
+
type: Literal["damage"] = attr(default="damage", frozen=True)
|
|
35
|
+
damage: float = element(default=0.0)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class MinMaxFilterCriterion(BaseXmlModel, tag="criterion"):
|
|
39
|
+
type: Literal["min-max filter"] = attr(default="min-max filter", frozen=True)
|
|
40
|
+
min: float = element(default=-1e37)
|
|
41
|
+
max: float = element(default=1e37)
|
|
42
|
+
clamp: Literal[0, 1] = element(default=0)
|
|
43
|
+
data: ContactGapCriterion | StressCriterion | DamageCriterion | MathCriterion = element(default=StressCriterion(), tag="data")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class RelativeErrorCriterion(BaseXmlModel, tag="criterion"):
|
|
47
|
+
type: Literal["relative error"] = attr(default="relative error", frozen=True)
|
|
48
|
+
error: Literal[0] | float = element(default=0)
|
|
49
|
+
data: ContactGapCriterion | StressCriterion | DamageCriterion | MathCriterion = element(default=StressCriterion(), tag="data")
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
CriterionType = (
|
|
53
|
+
MaxVariableCriterion
|
|
54
|
+
| ElementSelectionCriterion
|
|
55
|
+
| ContactGapCriterion
|
|
56
|
+
| StressCriterion
|
|
57
|
+
| MathCriterion
|
|
58
|
+
| DamageCriterion
|
|
59
|
+
| MinMaxFilterCriterion
|
|
60
|
+
| RelativeErrorCriterion
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class MMGStepSizeFunction(BaseXmlModel, tag="size_function"):
|
|
65
|
+
type: Literal["step"] = attr(default="step", frozen=True)
|
|
66
|
+
x0: float = element(default=0.0)
|
|
67
|
+
left_val: float = element(default=0.0)
|
|
68
|
+
right_val: float = element(default=1.0)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class MMGConstantSizeFunction(BaseXmlModel, tag="size_function"):
|
|
72
|
+
type: Literal["const"] = attr(default="const", frozen=True)
|
|
73
|
+
value: float = element(default=0.0)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class MMGLinearRampSizeFunction(BaseXmlModel, tag="size_function"):
|
|
77
|
+
type: Literal["linear ramp"] = attr(default="linear ramp", frozen=True)
|
|
78
|
+
slope: float = element(default=1.0)
|
|
79
|
+
intercept: float = element(default=0.0)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class MMGMathSizeFunction(BaseXmlModel, tag="size_function"):
|
|
83
|
+
type: Literal["math"] = attr(default="math", frozen=True)
|
|
84
|
+
math: str = element(default="1")
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class CurvePoints(BaseXmlModel, tag="points"):
|
|
88
|
+
type: Literal["curve"] = attr(default="curve", frozen=True)
|
|
89
|
+
pt: list[StringFloatVec2] = element(default=["0,0.25", "1,1"])
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class MMGPointSizeFunction(BaseXmlModel, tag="size_function"):
|
|
93
|
+
type: Literal["point"] = attr(default="point", frozen=True)
|
|
94
|
+
interpolate: Literal["linear", "smooth", "step"] = element(default="linear")
|
|
95
|
+
extend: Literal["constant", "extrapolate", "repeat", "repeat offset"] = element(default="constant")
|
|
96
|
+
points: CurvePoints = element(default=CurvePoints())
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
MMGSizeFunctionType = MMGStepSizeFunction | MMGConstantSizeFunction | MMGLinearRampSizeFunction | MMGMathSizeFunction | MMGPointSizeFunction
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class ErosionAdaptor(BaseXmlModel, tag="mesh_adaptor"):
|
|
103
|
+
type: Literal["erosion"] = attr(default="erosion", frozen=True)
|
|
104
|
+
elem_set: str | None = attr(default=None)
|
|
105
|
+
max_iters: int = element(default=1)
|
|
106
|
+
max_elements: int = element(default=3)
|
|
107
|
+
remove_islands: Literal[0, 1] = element(default=0)
|
|
108
|
+
sort: Literal[0, 1] = element(default=1)
|
|
109
|
+
erode_surfaces: Literal["no", "yes", "grow", "reconstruct"] = element(default="no")
|
|
110
|
+
criterion: CriterionType = element(default=MinMaxFilterCriterion(data=StressCriterion()))
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class MMGRemeshAdaptor(BaseXmlModel, tag="mesh_adaptor"):
|
|
114
|
+
type: Literal["mmg_remesh"] = attr(default="mmg_remesh", frozen=True)
|
|
115
|
+
elem_set: str | None = attr(default=None)
|
|
116
|
+
max_iters: int = element(default=1)
|
|
117
|
+
max_elements: int = element(default=-1)
|
|
118
|
+
min_element_size: float = element(default=0.1)
|
|
119
|
+
hausdorff: float = element(default=0.01)
|
|
120
|
+
gradation: float = element(default=1.3, gt=1.0)
|
|
121
|
+
mesh_coarsen: Literal[0, 1] = element(default=0)
|
|
122
|
+
normalize_data: Literal[0, 1] = element(default=0)
|
|
123
|
+
relative_size: Literal[0, 1] = element(default=1)
|
|
124
|
+
criterion: CriterionType = element(default=MinMaxFilterCriterion(data=StressCriterion()))
|
|
125
|
+
size_function: MMGSizeFunctionType | None = element(default=None)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
class HexRefine2dAdaptor(BaseXmlModel, tag="mesh_adaptor"):
|
|
129
|
+
type: Literal["hex_refine2d"] = attr(default="hex_refine2d", frozen=True)
|
|
130
|
+
elem_set: str | None = attr(default=None)
|
|
131
|
+
max_iters: int = element(default=1)
|
|
132
|
+
max_elements: int = element(default=-1)
|
|
133
|
+
max_elem_refine: int = element(default=0)
|
|
134
|
+
max_value: float = element(default=0.01)
|
|
135
|
+
nnc: int = element(default=8)
|
|
136
|
+
nsdim: int = element(default=3)
|
|
137
|
+
criterion: CriterionType = element(default=RelativeErrorCriterion(data=StressCriterion()))
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class HexRefineAdaptor(BaseXmlModel, tag="mesh_adaptor"):
|
|
141
|
+
type: Literal["hex_refine"] = attr(default="hex_refine", frozen=True)
|
|
142
|
+
elem_set: str | None = attr(default=None)
|
|
143
|
+
max_iters: int = element(default=1)
|
|
144
|
+
max_elements: int = element(default=-1)
|
|
145
|
+
max_elem_refine: int = element(default=0)
|
|
146
|
+
max_value: float = element(default=0.01)
|
|
147
|
+
nnc: int = element(default=8)
|
|
148
|
+
nsdim: int = element(default=3)
|
|
149
|
+
criterion: CriterionType = element(default=RelativeErrorCriterion(data=StressCriterion()))
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
AdaptorType = ErosionAdaptor | MMGRemeshAdaptor | HexRefine2dAdaptor | HexRefineAdaptor
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
class MeshAdaptor(BaseXmlModel, tag="MeshAdaptor"):
|
|
156
|
+
all_adaptors: list[AdaptorType] = element(default=[])
|
|
157
|
+
|
|
158
|
+
def add_adaptor(self, adaptor: AdaptorType):
|
|
159
|
+
self.all_adaptors.append(adaptor)
|
pyfebio/meshdata.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
3
|
+
from pydantic_xml import BaseXmlModel, attr, element
|
|
4
|
+
|
|
5
|
+
from ._types import (
|
|
6
|
+
StringFloatVec3,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class NodeDataNode(BaseXmlModel, tag="node", validate_assignment=True):
|
|
11
|
+
lid: int = attr(ge=1)
|
|
12
|
+
text: float | StringFloatVec3
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class NodeData(BaseXmlModel, validate_assignment=True):
|
|
16
|
+
name: str = attr()
|
|
17
|
+
node_set: str = attr()
|
|
18
|
+
data_type: Literal["scalar", "vec3"] = attr()
|
|
19
|
+
all_nodes: list[NodeDataNode] = element(default=[], tag="node")
|
|
20
|
+
|
|
21
|
+
def add_node(self, new_node: NodeDataNode):
|
|
22
|
+
self.all_nodes.append(new_node)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ElementDataElement(BaseXmlModel, tag="elem", validate_assignment=True):
|
|
26
|
+
lid: int = attr(ge=1)
|
|
27
|
+
text: float | StringFloatVec3
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class ElementData(BaseXmlModel, validate_assignment=True):
|
|
31
|
+
name: str = attr()
|
|
32
|
+
elem_set: str = attr()
|
|
33
|
+
data_type: Literal["scalar", "vec3"] = attr()
|
|
34
|
+
all_elements: list[ElementDataElement] = element(default=[], tag="elem")
|
|
35
|
+
|
|
36
|
+
def add_element(self, new_element: ElementDataElement):
|
|
37
|
+
self.all_elements.append(new_element)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class SurfaceData(BaseXmlModel, validate_assignment=True):
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class MeshData(BaseXmlModel, validate_assignment=True):
|
|
45
|
+
element_data: list[ElementData] = element(default=[], tag="ElementData")
|
|
46
|
+
node_data: list[NodeData] = element(default=[], tag="NodeData")
|
|
47
|
+
|
|
48
|
+
def add_element_data(self, new_element_data: ElementData):
|
|
49
|
+
self.element_data.append(new_element_data)
|
|
50
|
+
|
|
51
|
+
def add_node_data(self, new_node_data: NodeData):
|
|
52
|
+
self.node_data.append(new_node_data)
|
pyfebio/meshdomains.py
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
3
|
+
from pydantic_xml import BaseXmlModel, attr, element
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class SolidDomain(BaseXmlModel, validate_assignment=True):
|
|
7
|
+
name: str = attr(default="SolidPart")
|
|
8
|
+
type: Literal["elastic-solid", "three-field-solid", "rigid-solid", "udg-hex", "sri-solid", "remodelling-solid", "ut4-solid"] | None = (
|
|
9
|
+
attr(default=None)
|
|
10
|
+
)
|
|
11
|
+
elem_type: (
|
|
12
|
+
Literal[
|
|
13
|
+
"HEX8G6",
|
|
14
|
+
"HEX8G8",
|
|
15
|
+
"HEX20G8",
|
|
16
|
+
"TET4G1",
|
|
17
|
+
"TET4G4",
|
|
18
|
+
"TET10G4",
|
|
19
|
+
"TET10G8",
|
|
20
|
+
"TET10GL11",
|
|
21
|
+
"TET15G8",
|
|
22
|
+
"TET15G11",
|
|
23
|
+
"TET15G15",
|
|
24
|
+
"PENTA15G8",
|
|
25
|
+
]
|
|
26
|
+
| None
|
|
27
|
+
) = attr(default=None)
|
|
28
|
+
mat: str = attr(default="material")
|
|
29
|
+
alpha: float | None = element(default=None)
|
|
30
|
+
iso_stab: Literal[0, 1] | None = element(default=None)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class ShellDomain(BaseXmlModel, validate_assignment=True):
|
|
34
|
+
name: str = attr(default="ShellPart")
|
|
35
|
+
type: Literal[
|
|
36
|
+
"elastic-shell",
|
|
37
|
+
"three-field-shell",
|
|
38
|
+
"rigid-shell",
|
|
39
|
+
"elastic-shell-old",
|
|
40
|
+
"elastic-shell-eas",
|
|
41
|
+
"elastic-shell-ans",
|
|
42
|
+
] = attr(default="elastic-shell")
|
|
43
|
+
mat: str = attr(default="material")
|
|
44
|
+
shell_thickness: float = element(default=0.01)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class BeamDomain(BaseXmlModel, validate_assignment=True):
|
|
48
|
+
name: str = attr(default="BeamPart")
|
|
49
|
+
type: Literal["linear-truss", "elastic-truss", "linear-beam"]
|
|
50
|
+
mat: str = attr(default="material")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class MeshDomains(BaseXmlModel, validate_assignment=True):
|
|
54
|
+
solid_domains: list[SolidDomain] = element(default=[], tag="SolidDomain")
|
|
55
|
+
shell_domains: list[ShellDomain] = element(default=[], tag="ShellDomain")
|
|
56
|
+
beam_domains: list[BeamDomain] = element(default=[], tag="BeamDomain")
|
|
57
|
+
|
|
58
|
+
def add_solid_domain(self, new_solid_domain: SolidDomain):
|
|
59
|
+
self.solid_domains.append(new_solid_domain)
|
|
60
|
+
|
|
61
|
+
def add_shell_domain(self, new_shell_domain: ShellDomain):
|
|
62
|
+
self.shell_domains.append(new_shell_domain)
|
|
63
|
+
|
|
64
|
+
def add_beam_domain(self, new_beam_domain: BeamDomain):
|
|
65
|
+
self.beam_domains.append(new_beam_domain)
|