pyfebio 0.1.1__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.
pyfebio/model.py ADDED
@@ -0,0 +1,145 @@
1
+ import math
2
+ import subprocess
3
+ from pathlib import Path
4
+
5
+ from pydantic_xml import BaseXmlModel, attr, element
6
+
7
+ from . import (
8
+ boundary,
9
+ constraints,
10
+ contact,
11
+ control,
12
+ discrete,
13
+ globals,
14
+ include,
15
+ initial,
16
+ loaddata,
17
+ loads,
18
+ material,
19
+ mesh,
20
+ meshadaptor,
21
+ meshdata,
22
+ meshdomains,
23
+ module,
24
+ output,
25
+ rigid,
26
+ step,
27
+ )
28
+
29
+ SectionTypes = (
30
+ module.Module
31
+ | globals.Globals
32
+ | control.Control
33
+ | material.Material
34
+ | material.RigidBody
35
+ | meshdomains.MeshDomains
36
+ | mesh.Mesh
37
+ | meshdata.MeshData
38
+ | discrete.Discrete
39
+ | loaddata.LoadData
40
+ | loads.Loads
41
+ | rigid.Rigid
42
+ | initial.Initial
43
+ | boundary.Boundary
44
+ | contact.Contact
45
+ | constraints.Constraints
46
+ | step.Step
47
+ | output.Output
48
+ | include.Include
49
+ )
50
+
51
+
52
+ class FEBioRoot(BaseXmlModel, tag="febio_spec", validate_assignment=True):
53
+ version: str = attr(default="4.0")
54
+ sections: list[SectionTypes] = element(default=[])
55
+
56
+ def add_section(self, section: SectionTypes):
57
+ self.sections.append(section)
58
+
59
+ def save(self, filename: str):
60
+ xml = self.to_xml(
61
+ pretty_print=True,
62
+ encoding="ISO-8859-1",
63
+ xml_declaration=True,
64
+ skip_empty=True,
65
+ )
66
+ with open(filename, "wb") as fid:
67
+ fid.write(xml) # type: ignore
68
+
69
+
70
+ class Model(BaseXmlModel, tag="febio_spec", validate_assignment=True, extra="forbid"):
71
+ version: str = attr(default="4.0")
72
+ module_: module.Module | None = element(default=module.Module(), tag="Module")
73
+ globals_: globals.Globals = element(default=globals.Globals(), tag="Globals")
74
+ control_: control.Control | None = element(default=control.Control(), tag="Control")
75
+ material_: material.Material = element(default=material.Material(), tag="Material")
76
+ mesh_: mesh.Mesh = element(default=mesh.Mesh(), tag="Mesh")
77
+ meshdomains_: meshdomains.MeshDomains = element(default=meshdomains.MeshDomains(), tag="MeshDomains")
78
+ meshdata_: meshdata.MeshData = element(default=meshdata.MeshData(), tag="MeshData")
79
+ meshadaptor_: meshadaptor.MeshAdaptor = element(default=meshadaptor.MeshAdaptor())
80
+ discrete_: discrete.Discrete = element(default=discrete.Discrete(), tag="Discrete")
81
+ loaddata_: loaddata.LoadData = element(default=loaddata.LoadData(), tag="LoadData")
82
+ loads_: loads.Loads = element(default=loads.Loads(), tag="Loads")
83
+ rigid_: rigid.Rigid = element(default=rigid.Rigid(), tag="Rigid")
84
+ initial_: initial.Initial = element(default=initial.Initial(), tag="Initial")
85
+ boundary_: boundary.Boundary = element(default=boundary.Boundary(), tag="Boundary")
86
+ contact_: contact.Contact = element(default=contact.Contact(), tag="Contact")
87
+ constraints_: constraints.Constraints = element(default=constraints.Constraints(), tag="Constraints")
88
+ step_: step.Step = element(default=step.Step(), tag="Step")
89
+ output_: output.Output = element(default=output.Output(), tag="Output")
90
+
91
+ def save(self, filename: str):
92
+ xml = self.to_xml(
93
+ pretty_print=True,
94
+ encoding="ISO-8859-1",
95
+ xml_declaration=True,
96
+ skip_empty=True,
97
+ )
98
+ assert isinstance(xml, bytes)
99
+ with open(filename, "wb") as fid:
100
+ fid.write(xml)
101
+
102
+ def add_simple_rigid_body(self, origin: tuple[float, float, float], name: str):
103
+ element_id = self.mesh_.elements[-1].all_elements[-1].id + 1
104
+ node_id_start = self.mesh_.nodes[-1].all_nodes[-1].id + 1
105
+ connectivity = [node_id for node_id in range(node_id_start, node_id_start + 4)]
106
+ sqrt3over2 = math.sqrt(3.0) / 2.0
107
+ ideal_tet = [
108
+ [-1.0, -sqrt3over2, -sqrt3over2],
109
+ [1.0, -sqrt3over2, -sqrt3over2],
110
+ [0.0, sqrt3over2, -sqrt3over2],
111
+ [0.0, 0.0, sqrt3over2],
112
+ ]
113
+ new_tet = [[tet[i] + origin[i] for i in range(3)] for tet in ideal_tet]
114
+ nodes = [mesh.Node(id=node_id_start + i, text=",".join(map(str, new_tet[i]))) for i in range(4)]
115
+ node_domain = mesh.Nodes(name=name, all_nodes=nodes)
116
+ element = mesh.Tet4Element(id=element_id, text=",".join(map(str, connectivity)))
117
+ element_domain = mesh.Elements(name=name, all_elements=[element], type="tet4")
118
+ self.mesh_.add_node_domain(node_domain)
119
+ self.mesh_.add_element_domain(element_domain)
120
+
121
+ material_id = len(self.material_.all_materials) + 1
122
+
123
+ mat = material.RigidBody(name=name, id=material_id, center_of_mass=",".join(map(str, origin)))
124
+ self.material_.add_material(mat)
125
+ self.meshdomains_.add_solid_domain(meshdomains.SolidDomain(name=name, mat=name))
126
+
127
+
128
+ class BiphasicModel(Model):
129
+ module_: module.Module | None = element(default=module.Module(type="biphasic"))
130
+ control_: control.Control | None = element(
131
+ default=control.Control(
132
+ analysis="TRANSIENT",
133
+ solver=control.Solver(type="biphasic", ptol=0.01),
134
+ time_steps=100,
135
+ step_size=0.1,
136
+ time_stepper=control.TimeStepper(dtmax=control.TimeStepValue(text=0.1)),
137
+ )
138
+ )
139
+
140
+
141
+ def run_model(filepath: str | Path, silent: bool = False) -> int:
142
+ if silent:
143
+ return subprocess.run(f"febio4 -i {filepath} -silent", shell=True).returncode
144
+ else:
145
+ return subprocess.run(f"febio4 -i {filepath}", shell=True).returncode
pyfebio/module.py ADDED
@@ -0,0 +1,14 @@
1
+ from typing import Literal
2
+
3
+ from pydantic_xml import BaseXmlModel, attr
4
+
5
+
6
+ class Module(BaseXmlModel, tag="Module", validate_assignment=True):
7
+ """
8
+ FEBio module -- currently, only supporting "solid" and "biphasic".
9
+
10
+ Unsupported modules: "solute", "multiphasic", "heat", "fluid", "fluid-FSI"
11
+
12
+ """
13
+
14
+ type: Literal["solid", "biphasic"] = attr(default="solid")
pyfebio/output.py ADDED
@@ -0,0 +1,298 @@
1
+ from enum import Enum
2
+ from typing import Literal
3
+
4
+ from pydantic_xml import BaseXmlModel, attr, element
5
+
6
+
7
+ class NodeDataEnum(str, Enum):
8
+ x_coordinate = "x"
9
+ y_coordinate = "y"
10
+ z_coordinate = "z"
11
+ x_displacement = "ux"
12
+ y_displacement = "uy"
13
+ z_displacement = "uz"
14
+ x_velocity = "vx"
15
+ y_velocity = "vy"
16
+ z_velocity = "vz"
17
+ x_acceleration = "ax"
18
+ y_acceleration = "ay"
19
+ z_acceleration = "az"
20
+ x_reaction_force = "Rx"
21
+ y_reaction_force = "Ry"
22
+ z_reaction_force = "Rz"
23
+ fluid_pressure = "p"
24
+
25
+
26
+ class ElementDataEnum(str, Enum):
27
+ x_coordinate = "x"
28
+ y_coordinate = "y"
29
+ z_coordinate = "z"
30
+ x_stress = "sx"
31
+ y_stress = "sy"
32
+ z_stress = "sz"
33
+ xy_stress = "sxy"
34
+ yz_stress = "syz"
35
+ xz_stress = "sxz"
36
+ p1_stress = "s1"
37
+ p2_stress = "s2"
38
+ p3_stress = "s3"
39
+ x_strain = "Ex"
40
+ y_strain = "Ey"
41
+ z_strain = "Ez"
42
+ xy_strain = "Exy"
43
+ yz_strain = "Eyz"
44
+ xz_strain = "Exz"
45
+ p1_strain = "E1"
46
+ p2_strain = "E2"
47
+ p3_strain = "E3"
48
+ strain_energy_density = "sed"
49
+ devaiatoric_strain_energy_density = "devsed"
50
+ fluid_pressure = "p"
51
+ x_flux = "wx"
52
+ y_flux = "wy"
53
+ z_flux = "wz"
54
+
55
+
56
+ class RigidBodyDataEnum(str, Enum):
57
+ x_coordinate = "x"
58
+ y_coordinate = "y"
59
+ z_coordinate = "z"
60
+ x_velocity = "vx"
61
+ y_velocity = "vy"
62
+ z_velocity = "vz"
63
+ x_acceleration = "ax"
64
+ y_acceleration = "ay"
65
+ z_acceleration = "az"
66
+ x_rotation = "thx"
67
+ y_rotation = "thy"
68
+ z_rotation = "thz"
69
+ x_angular_velocity = "omx"
70
+ y_angular_velocity = "omy"
71
+ z_angular_velocity = "omz"
72
+ x_angular_acceleration = "alx"
73
+ y_angular_acceleration = "aly"
74
+ z_angular_acceleration = "alz"
75
+ x_force = "Fx"
76
+ y_force = "Fy"
77
+ z_force = "Fz"
78
+ x_moment = "Mx"
79
+ y_moment = "My"
80
+ z_moment = "Mz"
81
+ x_euler = "XEuler"
82
+ y_euler = "YEuler"
83
+ z_euler = "ZEuler"
84
+
85
+
86
+ class RigidConnectorDataEnum(str, Enum):
87
+ x_force = "RCFx"
88
+ y_force = "RCFy"
89
+ z_force = "RCFz"
90
+ x_moment = "RCMx"
91
+ y_moment = "RCMy"
92
+ z_moment = "RCMz"
93
+ x_translation = "RCx"
94
+ y_translation = "RCy"
95
+ z_translation = "RCz"
96
+ x_rotation = "RCthx"
97
+ y_rotation = "RCthy"
98
+ z_rotation = "RCthz"
99
+
100
+
101
+ def assemble_data_string(
102
+ requests: list[RigidBodyDataEnum] | list[NodeDataEnum] | list[ElementDataEnum] | list[RigidConnectorDataEnum],
103
+ ) -> str:
104
+ return ";".join(requests)
105
+
106
+
107
+ class DataEntry(BaseXmlModel, validate_assignment=True):
108
+ data: str = attr(default="")
109
+ file: str | None = attr(default=None)
110
+ delim: str = attr(default=" ")
111
+ format: str | None = attr(default=None)
112
+ text: str | None = None
113
+
114
+
115
+ class OutputLogfile(BaseXmlModel, tag="logfile", validate_assignment=True):
116
+ file: str | None = attr(default=None)
117
+ node_data: list[DataEntry] = element(default=[], tag="node_data")
118
+ element_data: list[DataEntry] = element(default=[], tag="element_data")
119
+ face_data: list[DataEntry] = element(default=[], tag="face_data")
120
+ rigid_body_data: list[DataEntry] = element(default=[], tag="rigid_body_data")
121
+ rigid_connector_data: list[DataEntry] = element(default=[], tag="rigid_connector_data")
122
+
123
+ def add_node_data(self, new_output: DataEntry):
124
+ self.node_data.append(new_output)
125
+
126
+ def add_element_data(self, new_output: DataEntry):
127
+ self.element_data.append(new_output)
128
+
129
+ def add_face_data(self, new_output: DataEntry):
130
+ self.face_data.append(new_output)
131
+
132
+ def add_rigid_body_data(self, new_output: DataEntry):
133
+ self.rigid_body_data.append(new_output)
134
+
135
+ def add_rigid_connector_data(self, new_output: DataEntry):
136
+ self.rigid_connector_data.append(new_output)
137
+
138
+
139
+ PlotDataVariables = Literal[
140
+ "Acceleration",
141
+ "contact area",
142
+ "contact force",
143
+ "contact gap",
144
+ "contact penalty",
145
+ "contact pressure",
146
+ "contact traction",
147
+ "current density",
148
+ "current element angular momentum",
149
+ "current element center of mass",
150
+ "current element kinetic energy",
151
+ "current element linear momentum",
152
+ "current element strain energy",
153
+ "damage",
154
+ "density",
155
+ "deviatoric strain energy density",
156
+ "displacement",
157
+ "effective elasticity",
158
+ "effective fluid pressure",
159
+ "effective friction coefficient",
160
+ "effective solute concentration",
161
+ "elasticity",
162
+ "electric potential",
163
+ "element angular momentum",
164
+ "element center of mass",
165
+ "element kinetic energy",
166
+ "element linear momentum",
167
+ "element strain energy",
168
+ "element stress power",
169
+ "enclosed volume",
170
+ "enclosed volume change",
171
+ "Euler angle",
172
+ "fiber stretch",
173
+ "fiber vector",
174
+ "field",
175
+ "fixed charge density",
176
+ "fluid acceleration",
177
+ "fluid density",
178
+ "fluid dilatation",
179
+ "fluid element angular momentum",
180
+ "fluid element center of mass",
181
+ "fluid element kinetic energy",
182
+ "fluid element linear momentum",
183
+ "fluid element strain energy",
184
+ "fluid energy density",
185
+ "fluid flow rate",
186
+ "fluid flux",
187
+ "fluid force",
188
+ "fluid force2",
189
+ "fluid heat supply density",
190
+ "fluid kinetic energy density",
191
+ "fluid load support",
192
+ "fluid mass flow rate",
193
+ "fluid pressure",
194
+ "fluid rate of deformation",
195
+ "fluid shear viscosity",
196
+ "fluid strain energy density",
197
+ "fluid stress",
198
+ "fluid stress power density",
199
+ "fluid surface energy flux",
200
+ "fluid surface force",
201
+ "fluid surface pressure",
202
+ "fluid surface traction power",
203
+ "fluid velocity",
204
+ "fluid volume ratio",
205
+ "fluid vorticity",
206
+ "heat flux",
207
+ "kinetic energy density",
208
+ "Lagrange strain",
209
+ "left Hencky",
210
+ "left stretch",
211
+ "local fluid load support",
212
+ "nested damage",
213
+ "nodal acceleration",
214
+ "nodal contact gap",
215
+ "nodal contact pressure",
216
+ "nodal contact traction",
217
+ "nodal fluid flux",
218
+ "nodal fluid velocity",
219
+ "nodal stress",
220
+ "nodal surface traction",
221
+ "nodal vector gap",
222
+ "nodal velocity",
223
+ "osmolarity",
224
+ "parameter",
225
+ "pressure gap",
226
+ "reaction forces",
227
+ "receptor-ligand concentration",
228
+ "referential fixed charge density",
229
+ "referential solid volume fraction",
230
+ "relative fluid velocity",
231
+ "relative volume",
232
+ "right Hencky",
233
+ "right stretch",
234
+ "rigid acceleration",
235
+ "rigid angular acceleration",
236
+ "rigid angular momentum",
237
+ "rigid angular position",
238
+ "rigid angular velocity",
239
+ "rigid force",
240
+ "rigid kinetic energy",
241
+ "rigid linear momentum",
242
+ "rigid position",
243
+ "rigid rotation vector",
244
+ "rigid torque",
245
+ "rigid velocity",
246
+ "RVE generations",
247
+ "RVE reforming bonds",
248
+ "RVE strain",
249
+ "sbm concentration",
250
+ "sbm referential apparent density",
251
+ "shell director",
252
+ "shell relative volume",
253
+ "shell strain",
254
+ "shell thickness",
255
+ "solute concentration",
256
+ "solute flux",
257
+ "specific strain energy",
258
+ "SPR principal stress",
259
+ "SPR stress",
260
+ "SPR-P1 stress",
261
+ "strain energy density",
262
+ "stress",
263
+ "stress error",
264
+ "surface traction",
265
+ "uncoupled pressure",
266
+ "ut4 nodal stress",
267
+ "vector gap",
268
+ "velocity",
269
+ "volume fraction",
270
+ "in-situ target stretch",
271
+ "prestrain stretch",
272
+ "prestrain correction",
273
+ "SPR prestrain correction",
274
+ ]
275
+
276
+
277
+ class Var(BaseXmlModel, validate_assignment=True):
278
+ type: PlotDataVariables = attr()
279
+
280
+
281
+ class OutputPlotfile(BaseXmlModel, tag="plotfile", validate_assignment=True):
282
+ type: Literal["febio", "vtk"] = attr(default="febio")
283
+ file: str | None = attr(default=None)
284
+ all_vars: list[Var] = element(default=[], tag="var")
285
+
286
+ def add_var(self, new_var: Var):
287
+ self.all_vars.append(new_var)
288
+
289
+
290
+ class Output(BaseXmlModel, validate_assignment=True):
291
+ logfile: list[OutputLogfile] = element(default=[])
292
+ plotfile: list[OutputPlotfile] = element(default=[])
293
+
294
+ def add_plotfile(self, new_plotfile: OutputPlotfile):
295
+ self.plotfile.append(new_plotfile)
296
+
297
+ def add_logfile(self, new_logfile: OutputLogfile):
298
+ self.logfile.append(new_logfile)
pyfebio/py.typed ADDED
File without changes