physioblocks 1.0.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.
Files changed (93) hide show
  1. physioblocks/__init__.py +37 -0
  2. physioblocks/base/__init__.py +27 -0
  3. physioblocks/base/operators.py +176 -0
  4. physioblocks/base/registers.py +108 -0
  5. physioblocks/computing/__init__.py +47 -0
  6. physioblocks/computing/assembling.py +291 -0
  7. physioblocks/computing/models.py +811 -0
  8. physioblocks/computing/quantities.py +354 -0
  9. physioblocks/configuration/__init__.py +38 -0
  10. physioblocks/configuration/aliases.py +203 -0
  11. physioblocks/configuration/base.py +123 -0
  12. physioblocks/configuration/computing/__init__.py +27 -0
  13. physioblocks/configuration/computing/quantities.py +56 -0
  14. physioblocks/configuration/constants.py +121 -0
  15. physioblocks/configuration/description/__init__.py +33 -0
  16. physioblocks/configuration/description/blocks.py +239 -0
  17. physioblocks/configuration/description/nets.py +155 -0
  18. physioblocks/configuration/functions.py +695 -0
  19. physioblocks/configuration/simulation/__init__.py +32 -0
  20. physioblocks/configuration/simulation/simulations.py +280 -0
  21. physioblocks/description/__init__.py +34 -0
  22. physioblocks/description/blocks.py +418 -0
  23. physioblocks/description/flux.py +157 -0
  24. physioblocks/description/nets.py +746 -0
  25. physioblocks/io/__init__.py +29 -0
  26. physioblocks/io/aliases.py +73 -0
  27. physioblocks/io/configuration.py +125 -0
  28. physioblocks/launcher/__main__.py +285 -0
  29. physioblocks/launcher/configuration.py +231 -0
  30. physioblocks/launcher/configure/__main__.py +99 -0
  31. physioblocks/launcher/constants.py +105 -0
  32. physioblocks/launcher/files.py +150 -0
  33. physioblocks/launcher/series.py +165 -0
  34. physioblocks/library/__init__.py +27 -0
  35. physioblocks/library/aliases/blocks/c_block.json +5 -0
  36. physioblocks/library/aliases/blocks/rc_block.json +5 -0
  37. physioblocks/library/aliases/blocks/rcr_block.json +5 -0
  38. physioblocks/library/aliases/blocks/spherical_cavity_block.json +5 -0
  39. physioblocks/library/aliases/blocks/valve_rl_block.json +5 -0
  40. physioblocks/library/aliases/flux/heart_flux_dof_couples.jsonc +4 -0
  41. physioblocks/library/aliases/model_components/active_law_macro_huxley_two_moments.json +5 -0
  42. physioblocks/library/aliases/model_components/rheology_fiber_additive.json +5 -0
  43. physioblocks/library/aliases/model_components/spherical_dynamics.json +5 -0
  44. physioblocks/library/aliases/model_components/velocity_law_hht.json +5 -0
  45. physioblocks/library/aliases/nets/circulation_alone_net.json +31 -0
  46. physioblocks/library/aliases/nets/spherical_heart_net.json +93 -0
  47. physioblocks/library/aliases/simulations/circulation_alone_forward_simulation.jsonc +55 -0
  48. physioblocks/library/aliases/simulations/default_forward_simulation.jsonc +7 -0
  49. physioblocks/library/aliases/simulations/default_time.jsonc +8 -0
  50. physioblocks/library/aliases/simulations/newton_method_solver.json +5 -0
  51. physioblocks/library/aliases/simulations/spherical_heart_forward_simulation.jsonc +157 -0
  52. physioblocks/library/aliases/simulations/spherical_heart_with_respiration_forward_simulation.jsonc +45 -0
  53. physioblocks/library/blocks/__init__.py +27 -0
  54. physioblocks/library/blocks/capacitances.py +516 -0
  55. physioblocks/library/blocks/cavity.py +192 -0
  56. physioblocks/library/blocks/valves.py +281 -0
  57. physioblocks/library/functions/__init__.py +27 -0
  58. physioblocks/library/functions/base_operations.py +129 -0
  59. physioblocks/library/functions/first_order.py +113 -0
  60. physioblocks/library/functions/piecewise.py +271 -0
  61. physioblocks/library/functions/trigonometric.py +78 -0
  62. physioblocks/library/functions/watchers.py +113 -0
  63. physioblocks/library/model_components/__init__.py +27 -0
  64. physioblocks/library/model_components/active_law.py +345 -0
  65. physioblocks/library/model_components/dynamics.py +986 -0
  66. physioblocks/library/model_components/rheology.py +160 -0
  67. physioblocks/library/model_components/velocity_law.py +169 -0
  68. physioblocks/references/circulation_alone_sim.jsonc +24 -0
  69. physioblocks/references/spherical_heart_respiration_sim.jsonc +33 -0
  70. physioblocks/references/spherical_heart_sim.jsonc +29 -0
  71. physioblocks/registers/__init__.py +32 -0
  72. physioblocks/registers/load_function_register.py +93 -0
  73. physioblocks/registers/save_function_register.py +106 -0
  74. physioblocks/registers/type_register.py +97 -0
  75. physioblocks/simulation/__init__.py +48 -0
  76. physioblocks/simulation/constants.py +30 -0
  77. physioblocks/simulation/functions.py +71 -0
  78. physioblocks/simulation/runtime.py +484 -0
  79. physioblocks/simulation/saved_quantities.py +129 -0
  80. physioblocks/simulation/setup.py +576 -0
  81. physioblocks/simulation/solvers.py +235 -0
  82. physioblocks/simulation/state.py +340 -0
  83. physioblocks/simulation/time_manager.py +354 -0
  84. physioblocks/utils/__init__.py +27 -0
  85. physioblocks/utils/dynamic_import_utils.py +150 -0
  86. physioblocks/utils/exceptions_utils.py +115 -0
  87. physioblocks/utils/gradient_test_utils.py +337 -0
  88. physioblocks/utils/math_utils.py +109 -0
  89. physioblocks-1.0.0.dist-info/METADATA +127 -0
  90. physioblocks-1.0.0.dist-info/RECORD +93 -0
  91. physioblocks-1.0.0.dist-info/WHEEL +4 -0
  92. physioblocks-1.0.0.dist-info/licenses/licenses/GPL-3.0-only.txt +674 -0
  93. physioblocks-1.0.0.dist-info/licenses/licenses/LGPL-3.0-only.txt +165 -0
@@ -0,0 +1,986 @@
1
+ # SPDX-FileCopyrightText: Copyright INRIA
2
+ #
3
+ # SPDX-License-Identifier: LGPL-3.0-only
4
+ #
5
+ # Copyright INRIA
6
+ #
7
+ # This file is part of PhysioBlocks, a library mostly developed by the
8
+ # [Ananke project-team](https://team.inria.fr/ananke) at INRIA.
9
+ #
10
+ # Authors:
11
+ # - Colin Drieu
12
+ # - Dominique Chapelle
13
+ # - François Kimmig
14
+ # - Philippe Moireau
15
+ #
16
+ # PhysioBlocks is free software: you can redistribute it and/or modify it under the
17
+ # terms of the GNU Lesser General Public License as published by the Free Software
18
+ # Foundation, version 3 of the License.
19
+ #
20
+ # PhysioBlocks is distributed in the hope that it will be useful, but WITHOUT ANY
21
+ # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
22
+ # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
23
+ #
24
+ # You should have received a copy of the GNU Lesser General Public License along with
25
+ # PhysioBlocks. If not, see <https://www.gnu.org/licenses/>.
26
+
27
+ """
28
+ Describes dynamics models.
29
+ """
30
+
31
+ from dataclasses import dataclass
32
+ from typing import Any
33
+
34
+ import numpy as np
35
+ from numpy.typing import NDArray
36
+
37
+ import physioblocks.utils.math_utils as math_utils
38
+ from physioblocks.computing import Expression, ModelComponent, Quantity, diff, mid_point
39
+ from physioblocks.computing.assembling import EqSystem
40
+ from physioblocks.registers import register_type
41
+ from physioblocks.simulation import State, Time
42
+ from physioblocks.simulation.solvers import NewtonSolver
43
+
44
+ # Constant for the spherical dynamics type id
45
+ SPHERICAL_DYNAMICS_TYPE_ID = "spherical_dynamics"
46
+
47
+ SPHERICAL_DYNAMICS_STATIC_DISP_LOCAL_ID = "disp"
48
+
49
+ _DISP_EPSILON = 1.0e-6
50
+ _STATIC_PROBLEM_TOL = 1e-6
51
+ _STATIC_PROBLEM_MAX_IT = 10
52
+ _STATIC_PROBLEM_DISP_MAG = 0.010
53
+ _STATIC_PROBLEM_MIN_PRESSURE_STEP = 1.0
54
+
55
+
56
+ @dataclass
57
+ class _SphericalDynamicsStaticModelComponent(ModelComponent):
58
+ """
59
+ ModelComponent containing the quantities and the expressions to solve the
60
+ static problem in the Spherical Dynamics Model in order to initialize the
61
+ displacement and fiber deformation.
62
+ """
63
+
64
+ disp: Quantity[np.float64]
65
+ """Displacement"""
66
+
67
+ pressure: Quantity[np.float64]
68
+ """pressure"""
69
+
70
+ thickness: Quantity[np.float64]
71
+ """thickness"""
72
+
73
+ hyperelastic_cst: Quantity[NDArray[np.float64]]
74
+ """hyperelastic constant"""
75
+
76
+ inv_radius: Quantity[np.float64]
77
+ """sphere radius"""
78
+
79
+ pressure_external: Quantity[np.float64]
80
+ """external pressure"""
81
+
82
+ def initialize(self) -> None:
83
+ """
84
+ Initialize block attributes from current quantity values
85
+ """
86
+ self.hyperelastic_cst_01 = (
87
+ self.hyperelastic_cst.current[0] * self.hyperelastic_cst.current[1]
88
+ )
89
+ self.hyperelastic_cst_23 = (
90
+ self.hyperelastic_cst.current[2] * self.hyperelastic_cst.current[3]
91
+ )
92
+ self.thickness_radius_ratio = self.thickness.current * self.inv_radius.current
93
+ self.half_thickness_radius_ratio = 0.5 * self.thickness_radius_ratio
94
+
95
+ def dynamics_static_residual(self) -> Any:
96
+ """
97
+ Compute the residual for the dynamics static problem.
98
+
99
+ :return: the static problem residual
100
+ :rtype: np.float64
101
+ """
102
+ pressure_external = mid_point(self.pressure_external)
103
+
104
+ disp_new_ratio = 1.0 + self.disp.new * self.inv_radius.current
105
+ fluid_volume_diff = np.pow(
106
+ disp_new_ratio
107
+ - np.pow(disp_new_ratio, -2) * self.half_thickness_radius_ratio,
108
+ 2,
109
+ ) * (1.0 + np.pow(disp_new_ratio, -3) * self.thickness_radius_ratio)
110
+
111
+ j4_new = np.pow(disp_new_ratio, 2)
112
+ j1_new = 2.0 * j4_new + np.pow(disp_new_ratio, -4)
113
+
114
+ j4_diff_new = 2.0 * self.inv_radius.current * (disp_new_ratio)
115
+ j1_diff_new = 2.0 * (
116
+ j4_diff_new - 2.0 * self.inv_radius.current * np.pow(disp_new_ratio, -5)
117
+ )
118
+
119
+ hyperelastic_potential_diff = 2.0 * (
120
+ (
121
+ self.hyperelastic_cst_01
122
+ * j1_diff_new
123
+ * (j1_new - 3.0)
124
+ * np.exp(self.hyperelastic_cst.current[1] * np.pow(j1_new - 3.0, 2))
125
+ )
126
+ + (
127
+ self.hyperelastic_cst_23
128
+ * j4_diff_new
129
+ * (j4_new - 1.0)
130
+ * np.exp(self.hyperelastic_cst.current[3] * np.pow(j4_new - 1.0, 2))
131
+ )
132
+ )
133
+
134
+ passive_stress = self.thickness.current * hyperelastic_potential_diff
135
+ external_stress = (self.pressure.new - pressure_external) * fluid_volume_diff
136
+
137
+ return passive_stress - external_stress
138
+
139
+ def dynamics_static_residual_ddisp(self) -> Any:
140
+ """
141
+ Compute the partial derivative for disp for the the static problem residual.
142
+
143
+ :return: the residual partial derivative for disp
144
+ :rtype: np.float64
145
+ """
146
+ disp_new_ratio = 1.0 + self.disp.new * self.inv_radius.current
147
+ pressure_external = mid_point(self.pressure_external)
148
+
149
+ j4_new = np.pow(disp_new_ratio, 2)
150
+ j1_new = 2.0 * j4_new + np.pow(disp_new_ratio, -4)
151
+
152
+ j4_diff_new = 2.0 * self.inv_radius.current * (disp_new_ratio)
153
+ j1_diff_new = 2.0 * (
154
+ j4_diff_new - 2.0 * self.inv_radius.current * np.pow(disp_new_ratio, -5)
155
+ )
156
+
157
+ j1_diff_diff_new = 4.0 * np.pow(self.inv_radius.current, 2) + 20.0 * np.pow(
158
+ self.inv_radius.current, 2
159
+ ) * np.pow(disp_new_ratio, -6)
160
+ j4_diff_diff_new = 2.0 * np.pow(self.inv_radius.current, 2)
161
+
162
+ hyperelastic_potential_diff_diff = 2.0 * (
163
+ self.hyperelastic_cst.current[0]
164
+ * self.hyperelastic_cst.current[1]
165
+ * (
166
+ j1_diff_diff_new * (j1_new - 3.0)
167
+ + np.pow(j1_diff_new, 2)
168
+ + (
169
+ 2.0
170
+ * self.hyperelastic_cst.current[1]
171
+ * np.pow(j1_diff_new * (j1_new - 3.0), 2)
172
+ )
173
+ )
174
+ * np.exp(self.hyperelastic_cst.current[1] * np.pow(j1_new - 3.0, 2))
175
+ + self.hyperelastic_cst.current[2]
176
+ * self.hyperelastic_cst.current[3]
177
+ * (
178
+ j4_diff_diff_new * (j4_new - 1.0)
179
+ + np.pow(j4_diff_new, 2)
180
+ + (
181
+ 2.0
182
+ * self.hyperelastic_cst.current[3]
183
+ * np.pow(j4_diff_new * (j4_new - 1.0), 2)
184
+ )
185
+ )
186
+ * np.exp(self.hyperelastic_cst.current[3] * np.pow(j4_new - 1.0, 2))
187
+ )
188
+
189
+ passive_stress_diff = self.thickness.current * hyperelastic_potential_diff_diff
190
+
191
+ fluid_volume_diff_diff = self.inv_radius.current * (
192
+ 2.0
193
+ * (
194
+ 1.0
195
+ + self.disp.new * self.inv_radius.current
196
+ - np.pow(disp_new_ratio, -2) * self.half_thickness_radius_ratio
197
+ )
198
+ * np.pow(1.0 + np.pow(disp_new_ratio, -3) * self.thickness_radius_ratio, 2)
199
+ - 3.0
200
+ * self.thickness_radius_ratio
201
+ * np.pow(disp_new_ratio, -4)
202
+ * np.pow(
203
+ 1.0
204
+ + self.disp.new * self.inv_radius.current
205
+ - np.pow(disp_new_ratio, -2) * self.half_thickness_radius_ratio,
206
+ 2,
207
+ )
208
+ )
209
+
210
+ ext_stress_diff = (
211
+ self.pressure.new - pressure_external
212
+ ) * fluid_volume_diff_diff
213
+
214
+ return passive_stress_diff - ext_stress_diff
215
+
216
+
217
+ _spherical_dynamics_static_residual_expr = Expression(
218
+ 1,
219
+ _SphericalDynamicsStaticModelComponent.dynamics_static_residual,
220
+ {
221
+ SPHERICAL_DYNAMICS_STATIC_DISP_LOCAL_ID: _SphericalDynamicsStaticModelComponent.dynamics_static_residual_ddisp # noqa: E501
222
+ },
223
+ )
224
+
225
+ _SphericalDynamicsStaticModelComponent.declares_internal_expression(
226
+ SPHERICAL_DYNAMICS_STATIC_DISP_LOCAL_ID, _spherical_dynamics_static_residual_expr
227
+ )
228
+
229
+
230
+ @dataclass
231
+ @register_type(SPHERICAL_DYNAMICS_TYPE_ID)
232
+ class SphericalDynamicsModelComponent(ModelComponent):
233
+ r"""
234
+ Implementation of the spherical dynamics model.
235
+
236
+ It provides a dynamics on :math:`y` the displacement of a sphere radius.
237
+
238
+ **Internal Equation:**
239
+
240
+ .. math::
241
+
242
+ \frac{P - P_{ext}}{|\Omega_0|} \frac{\partial V(y)}{\partial y}
243
+ - \rho_0 \ddot{y}
244
+ - \frac{\partial W_e(y)}{\partial y}
245
+ - \frac{1}{R_0} \Sigma_v(y,\dot{y})
246
+ - \frac{k_s}{R_0} \Bigl( \frac{y}{R_0}-e_c \Bigr) = 0
247
+
248
+ With:
249
+
250
+ * :math:`W_e(y)` represents an elastic energy per unit volume given by
251
+
252
+ .. math::
253
+
254
+ W_e(y) =
255
+ C_0 \exp \Bigl[ C_1 \bigl(2C(y)+C(y)^{-2}-3\bigr)^2 \Bigr]
256
+ + C_2 \exp \Bigl[ C_3 \bigl(C(y)-1\bigr)^2 \Bigr]
257
+
258
+ where :math:`(C_0,C_1,C_2,C_3)` denote material parameters of the passive
259
+ constitutive law, and :math:`C(y)= (1+y/R_0)^2` represents the component
260
+ of the right Cauchy-Green deformation tensor along the fiber direction
261
+
262
+ * :math:`\Sigma_v(y,\dot{y})` denotes a viscous stress defined by
263
+
264
+ .. math::
265
+
266
+ \Sigma_v(y,\dot{y}) =
267
+ 2 \eta \, \bigl( C(y) + 2 C(y)^{-5} \bigr) \frac{\dot{y}}{R_0}
268
+
269
+ with :math:`\eta` a solid viscosity parameter
270
+
271
+ * :math:`k_s` denotes a passive elastic modulus
272
+
273
+ * :math:`|\Omega_0|` is the total volume of sphere tissue in the reference
274
+ configuration
275
+
276
+ .. math::
277
+
278
+ |\Omega_0| =
279
+ \frac{4}{3}\pi \Biggl[ \Bigl( R_0+\frac{d_0}{2} \Bigr)^3
280
+ - \Bigl( R_0-\frac{d_0}{2} \Bigr)^3 \Biggr]
281
+
282
+ with :math:`d_0` the wall thickness in the reference configuration, and
283
+ factorizing :math:`R_0` from the bracket a Taylor expansion readily gives
284
+ the following approximation that we use in the implementation up to the
285
+ third order in :math:`d_0/2R_0`:
286
+
287
+ .. math::
288
+
289
+ |\Omega_0| = 4\pi R_0^2 d_0
290
+
291
+ * :math:`V(y)` denotes the volume of the sphere, i.e.
292
+
293
+ .. math::
294
+
295
+ V(y) = \frac{4}{3}\pi \Bigl( R(y)-\frac{d(y)}{2} \Bigr)^3
296
+ = \frac{4}{3}\pi \Bigl( R_0+y-\frac{d_0}{2}C(y)^{-1} \Bigr)^3,
297
+
298
+ with :math:`d(y)` the wall thickness in the deformed configuration.
299
+
300
+ **Discretised:**
301
+
302
+ .. math::
303
+
304
+ \frac{P^{n + \frac{1}{2}}
305
+ - P_{ext}^{n + \frac{1}{2}}}{|\Omega_0|} DV^{{n + \frac{1}{2}}\sharp}
306
+ - \rho_0 \Bigl[ \frac{\dot{y}^{n+1}-\dot{y}^n}{\Delta t^n} \Bigr]
307
+ - DW^{{n + \frac{1}{2}}\sharp}
308
+ - \frac{1}{R_0} \Sigma_v(y^{n + \frac{1}{2}},\dot{y}^{n + \frac{1}{2}})
309
+ - \frac{k_s}{R_0} \Bigl(
310
+ \frac{y^{n + \frac{1}{2}}}{R_0}-e_c^{n + \frac{1}{2}} \Bigr) = 0
311
+ """
312
+
313
+ disp: Quantity[np.float64]
314
+ """:math:`y` the displacement along the radius."""
315
+
316
+ fib_deform: Quantity[np.float64]
317
+ """:math:`e_c` the sphere fiber deformation"""
318
+
319
+ pressure: Quantity[np.float64]
320
+ """:math:`P` the pressure in the sphere"""
321
+
322
+ pressure_external: Quantity[np.float64]
323
+ """:math:`P_{ext}` the pressure outside the sphere"""
324
+
325
+ vel: Quantity[np.float64]
326
+ """:math:`\\dot{y}` the displacement velocity"""
327
+
328
+ radius: Quantity[np.float64]
329
+ """:math:`R_0` the initial sphere radius"""
330
+
331
+ vol_mass: Quantity[np.float64]
332
+ """:math:`\\rho_0` the volumic mass of the sphere tissu"""
333
+
334
+ thickness: Quantity[np.float64]
335
+ """:math:`d_0` the initial thickness of the sphere"""
336
+
337
+ damping_coef: Quantity[np.float64]
338
+ """Damping coeff for the viscous coeff :math:`\\eta`"""
339
+
340
+ series_stiffness: Quantity[np.float64]
341
+ """:math:`k_s` the series stiffness"""
342
+
343
+ hyperelastic_cst: Quantity[NDArray[np.float64]]
344
+ """:math:`(C_0,C_1,C_2,C_3)` the hyperelastic constants"""
345
+
346
+ time: Time
347
+ """Simulation time"""
348
+
349
+ def initialize(self) -> None:
350
+ """
351
+ Compute initial block quantities (sphere radius, surface, volume, ...) and solve
352
+ the associated static problem to get initial displacement and fiber deformation.
353
+ """
354
+ self.inv_radius = 1.0 / self.radius.current
355
+ self.sphere_surface = 4.0 * np.pi * np.power(self.radius.current, 2)
356
+ self.sphere_volume = 4.0 / 3.0 * np.pi * np.power(self.radius.current, 3)
357
+ self.thickness_radius_ratio = self.thickness.current * self.inv_radius
358
+ self.half_thickness_radius_ratio = 0.5 * self.thickness_radius_ratio
359
+ self.viscous_coef = (
360
+ 2.0
361
+ * self.damping_coef.current
362
+ * self.thickness.current
363
+ * np.pow(self.inv_radius, 2)
364
+ )
365
+ self.hyperelastic_cst_01 = (
366
+ self.hyperelastic_cst.current[0] * self.hyperelastic_cst.current[1]
367
+ )
368
+ self.hyperelastic_cst_23 = (
369
+ self.hyperelastic_cst.current[2] * self.hyperelastic_cst.current[3]
370
+ )
371
+
372
+ disp_init, fib_deform_init = self._solve_static_problem()
373
+
374
+ self.disp.initialize(disp_init)
375
+ self.fib_deform.initialize(fib_deform_init)
376
+
377
+ def dynamics_residual(self) -> Any:
378
+ """
379
+ Compute the residual giving a dynamics on `disp`
380
+
381
+ :return: the residual
382
+ :rtype: np.float64
383
+ """
384
+
385
+ disp_mid = mid_point(self.disp)
386
+ fib_deform_mid = mid_point(self.fib_deform)
387
+ p_mid_point = mid_point(self.pressure)
388
+ pressure_external = mid_point(self.pressure_external)
389
+
390
+ disp_diff = diff(self.disp)
391
+
392
+ disp_mid_ratio = 1.0 + disp_mid * self.inv_radius
393
+ disp_new_ratio = 1.0 + self.disp.new * self.inv_radius
394
+ disp_cur_ratio = 1.0 + self.disp.current * self.inv_radius
395
+
396
+ j4_mid = np.pow(disp_mid_ratio, 2)
397
+ j4_diff_mid = 2.0 * self.inv_radius * disp_mid_ratio
398
+
399
+ j1_mid = 2.0 * j4_mid + np.pow(disp_mid_ratio, -4)
400
+
401
+ # inertia stress
402
+ inertia_stress = (
403
+ self.vol_mass.current
404
+ * self.thickness.current
405
+ * diff(self.vel)
406
+ * self.time.inv_dt
407
+ )
408
+
409
+ # viscous stress
410
+ viscous_diff_vel = (
411
+ self.viscous_coef * j4_mid * (1.0 + 2.0 * np.pow(disp_mid_ratio, -12))
412
+ )
413
+ viscous_stress = viscous_diff_vel * disp_diff * self.time.inv_dt
414
+
415
+ # active stress
416
+ active_stress = (
417
+ self.thickness_radius_ratio
418
+ * self.series_stiffness.current
419
+ * (disp_mid * self.inv_radius - fib_deform_mid)
420
+ )
421
+ if np.abs(disp_diff / self.disp.current) > _DISP_EPSILON:
422
+ disp_diff_ratio = disp_diff * self.inv_radius
423
+
424
+ # external stress
425
+ fluid_volume_cur = (
426
+ disp_cur_ratio
427
+ - np.pow(disp_cur_ratio, -2) * self.half_thickness_radius_ratio
428
+ )
429
+ fluid_volume_new = (
430
+ disp_new_ratio
431
+ - np.pow(disp_new_ratio, -2) * self.half_thickness_radius_ratio
432
+ )
433
+ fluid_volume_diff3 = (
434
+ disp_diff_ratio
435
+ - math_utils.power_diff(
436
+ disp_new_ratio, disp_cur_ratio, disp_diff_ratio, -2
437
+ )
438
+ * self.half_thickness_radius_ratio
439
+ )
440
+ fluid_volume_diff = self.sphere_volume * math_utils.power_diff(
441
+ fluid_volume_new, fluid_volume_cur, fluid_volume_diff3, 3
442
+ )
443
+
444
+ external_stress = (
445
+ (p_mid_point - pressure_external)
446
+ * fluid_volume_diff
447
+ / (self.sphere_surface * disp_diff)
448
+ )
449
+
450
+ # passive stress
451
+ j4_new = np.pow(disp_new_ratio, 2)
452
+ j4_cur = np.pow(disp_cur_ratio, 2)
453
+
454
+ j4_diff = math_utils.power_diff(
455
+ disp_new_ratio, disp_cur_ratio, disp_diff_ratio, 2
456
+ )
457
+
458
+ j1_new = 2.0 * j4_new + np.pow(disp_new_ratio, -4)
459
+ j1_cur = 2.0 * j4_cur + np.pow(disp_cur_ratio, -4)
460
+ j1_diff = 2.0 * j4_diff + math_utils.power_diff(
461
+ disp_new_ratio, disp_cur_ratio, disp_diff_ratio, -4
462
+ )
463
+
464
+ arg_exp_j1_new = self.hyperelastic_cst.current[1] * np.pow(j1_new - 3.0, 2)
465
+ arg_exp_j1_cur = self.hyperelastic_cst.current[1] * np.pow(j1_cur - 3.0, 2)
466
+ arg_exp_j1_diff = self.hyperelastic_cst.current[1] * math_utils.power_diff(
467
+ j1_new - 3.0, j1_cur - 3.0, j1_diff, 2
468
+ )
469
+
470
+ arg_exp_j4_new = self.hyperelastic_cst.current[3] * np.pow(j4_new - 1.0, 2)
471
+ arg_exp_j4_cur = self.hyperelastic_cst.current[3] * np.pow(j4_cur - 1.0, 2)
472
+ arg_exp_j4_diff = self.hyperelastic_cst.current[3] * math_utils.power_diff(
473
+ j4_new - 1.0, j4_cur - 1.0, j4_diff, 2
474
+ )
475
+
476
+ exp_j1_diff = math_utils.exp_diff(
477
+ arg_exp_j1_new, arg_exp_j1_cur, arg_exp_j1_diff
478
+ )
479
+ exp_j4_diff = math_utils.exp_diff(
480
+ arg_exp_j4_new, arg_exp_j4_cur, arg_exp_j4_diff
481
+ )
482
+
483
+ hyperelastic_potential_diff = (
484
+ self.hyperelastic_cst.current[0] * exp_j1_diff
485
+ + self.hyperelastic_cst.current[2] * exp_j4_diff
486
+ )
487
+
488
+ passive_stress = (
489
+ self.thickness.current * hyperelastic_potential_diff / disp_diff
490
+ )
491
+
492
+ else:
493
+ # external stress
494
+ disp_mid_ratio_adj = np.pow(
495
+ disp_mid_ratio
496
+ - np.pow(disp_mid_ratio, -2) * self.half_thickness_radius_ratio,
497
+ 2,
498
+ )
499
+ external_stress = (
500
+ (p_mid_point - pressure_external)
501
+ * disp_mid_ratio_adj
502
+ * (1.0 + np.pow(disp_mid_ratio, -3) * self.thickness_radius_ratio)
503
+ )
504
+
505
+ # passive stress
506
+ exp_j1_mid = np.exp(
507
+ self.hyperelastic_cst.current[1] * np.pow(j1_mid - 3.0, 2)
508
+ )
509
+ exp_j4_mid = np.exp(
510
+ self.hyperelastic_cst.current[3] * np.pow(j4_mid - 1.0, 2)
511
+ )
512
+
513
+ j1_diff_mid = 2.0 * j4_diff_mid - 4.0 * self.inv_radius * np.pow(
514
+ disp_mid_ratio, -5
515
+ )
516
+
517
+ hyperelastic_potential_diff_mid = (
518
+ 2.0
519
+ * self.hyperelastic_cst_01
520
+ * j1_diff_mid
521
+ * (j1_mid - 3.0)
522
+ * exp_j1_mid
523
+ ) + (
524
+ 2.0
525
+ * self.hyperelastic_cst_23
526
+ * j4_diff_mid
527
+ * (j4_mid - 1.0)
528
+ * exp_j4_mid
529
+ )
530
+
531
+ passive_stress = self.thickness.current * hyperelastic_potential_diff_mid
532
+
533
+ return (
534
+ inertia_stress
535
+ + passive_stress
536
+ + viscous_stress
537
+ + active_stress
538
+ - external_stress
539
+ )
540
+
541
+ def dynamics_residual_ddisp(self) -> Any:
542
+ """
543
+ Compute the partial derivative of the residual for `disp`.
544
+
545
+ :return: the residual partial derivative for `disp`
546
+ :rtype: np.float64
547
+ """
548
+
549
+ disp_mid = mid_point(self.disp)
550
+ disp_diff = diff(self.disp)
551
+ disp_mid_ratio = 1.0 + disp_mid * self.inv_radius
552
+ disp_cur_ratio = 1.0 + self.disp.current * self.inv_radius
553
+ disp_new_ratio = 1.0 + self.disp.new * self.inv_radius
554
+ pressure_external = mid_point(self.pressure_external)
555
+
556
+ p_mid_point = mid_point(self.pressure)
557
+
558
+ j4_mid = np.pow(disp_mid_ratio, 2)
559
+ j4_diff_mid = 2.0 * self.inv_radius * (disp_mid_ratio)
560
+ j1_mid = 2.0 * j4_mid + np.pow(disp_mid_ratio, -4)
561
+
562
+ # viscous
563
+ viscous_diff_vel = (
564
+ self.viscous_coef * j4_mid * (1.0 + 2.0 * np.pow(disp_mid_ratio, -12))
565
+ )
566
+ viscous_potential_diff_disp_mid = (
567
+ self.viscous_coef
568
+ * disp_diff
569
+ * self.time.inv_dt
570
+ * (
571
+ j4_diff_mid * (1.0 + 2.0 * np.pow(disp_mid_ratio, -12))
572
+ - 24.0 * self.inv_radius * j4_mid * np.pow(disp_mid_ratio, -13)
573
+ )
574
+ )
575
+ viscous_ddisp = (
576
+ 0.5 * viscous_potential_diff_disp_mid + self.time.inv_dt * viscous_diff_vel
577
+ )
578
+
579
+ # active
580
+ active_ddisp = (
581
+ self.half_thickness_radius_ratio
582
+ * self.series_stiffness.current
583
+ * self.inv_radius
584
+ )
585
+
586
+ if np.fabs(disp_diff / self.disp.current) > _DISP_EPSILON:
587
+ # external
588
+ disp_diff_ratio = disp_diff * self.inv_radius
589
+ fluid_volume_term_cur = (
590
+ disp_cur_ratio
591
+ - np.pow(disp_cur_ratio, -2) * self.half_thickness_radius_ratio
592
+ )
593
+ fluid_volume_term_new = (
594
+ disp_new_ratio
595
+ - np.pow(disp_new_ratio, -2) * self.half_thickness_radius_ratio
596
+ )
597
+ fluid_volume_term_diff3 = (
598
+ disp_diff_ratio
599
+ - math_utils.power_diff(
600
+ disp_new_ratio, disp_cur_ratio, disp_diff_ratio, -2
601
+ )
602
+ * self.half_thickness_radius_ratio
603
+ )
604
+ fluid_volume_diff = self.sphere_volume * math_utils.power_diff(
605
+ fluid_volume_term_new, fluid_volume_term_cur, fluid_volume_term_diff3, 3
606
+ )
607
+
608
+ fluid_volume_diff_new = (
609
+ self.sphere_surface
610
+ * np.pow(
611
+ disp_new_ratio
612
+ - np.pow(disp_new_ratio, -2) * self.half_thickness_radius_ratio,
613
+ 2,
614
+ )
615
+ * (1.0 + np.pow(disp_new_ratio, -3) * self.thickness_radius_ratio)
616
+ )
617
+
618
+ fluid_volume_diff_scheme_diff = (
619
+ (1.0 / self.sphere_surface)
620
+ * (fluid_volume_diff_new * disp_diff - fluid_volume_diff)
621
+ * np.pow(disp_diff, -2)
622
+ )
623
+
624
+ external_ddisp = (
625
+ p_mid_point - pressure_external
626
+ ) * fluid_volume_diff_scheme_diff
627
+
628
+ # passive
629
+ j4_cur = np.pow(disp_cur_ratio, 2)
630
+ j4_new = np.pow(disp_new_ratio, 2)
631
+ j4_diff_new = 2.0 * self.inv_radius * disp_new_ratio
632
+ j4_diff = math_utils.power_diff(
633
+ disp_new_ratio, disp_cur_ratio, disp_diff_ratio, 2
634
+ )
635
+
636
+ j1_new = 2.0 * j4_new + np.pow(disp_new_ratio, -4)
637
+ j1_cur = 2.0 * j4_cur + np.pow(disp_cur_ratio, -4)
638
+ j1_diff_new = 2.0 * j4_diff_new - 4.0 * self.inv_radius * np.pow(
639
+ disp_new_ratio, -5
640
+ )
641
+ j1_diff = 2.0 * j4_diff + math_utils.power_diff(
642
+ disp_new_ratio, disp_cur_ratio, disp_diff_ratio, -4
643
+ )
644
+
645
+ arg_exp_j1_cur = self.hyperelastic_cst.current[1] * np.pow(j1_cur - 3.0, 2)
646
+ arg_exp_j1_new = self.hyperelastic_cst.current[1] * np.pow(j1_new - 3.0, 2)
647
+ arg_exp_j1_diff = self.hyperelastic_cst.current[1] * math_utils.power_diff(
648
+ j1_new - 3.0, j1_cur - 3.0, j1_diff, 2
649
+ )
650
+ exp_j1_new = np.exp(arg_exp_j1_new)
651
+
652
+ arg_exp_j4_cur = self.hyperelastic_cst.current[3] * np.pow(j4_cur - 1.0, 2)
653
+ arg_exp_j4_new = self.hyperelastic_cst.current[3] * np.pow(j4_new - 1.0, 2)
654
+ arg_exp_j4_diff = self.hyperelastic_cst.current[3] * math_utils.power_diff(
655
+ j4_new - 1.0, j4_cur - 1.0, j4_diff, 2
656
+ )
657
+ exp_j4_new = np.exp(arg_exp_j4_new)
658
+
659
+ exp_j4_diff = math_utils.exp_diff(
660
+ arg_exp_j4_new, arg_exp_j4_cur, arg_exp_j4_diff
661
+ )
662
+ exp_j1_diff = math_utils.exp_diff(
663
+ arg_exp_j1_new, arg_exp_j1_cur, arg_exp_j1_diff
664
+ )
665
+
666
+ hyperelastic_potential_diff = (
667
+ self.hyperelastic_cst.current[0] * exp_j1_diff
668
+ + self.hyperelastic_cst.current[2] * exp_j4_diff
669
+ )
670
+ hyperelastic_potential_diff_new = (
671
+ 2.0
672
+ * self.hyperelastic_cst_01
673
+ * j1_diff_new
674
+ * (j1_new - 3.0)
675
+ * exp_j1_new
676
+ ) + (
677
+ 2.0
678
+ * self.hyperelastic_cst_23
679
+ * j4_diff_new
680
+ * (j4_new - 1.0)
681
+ * exp_j4_new
682
+ )
683
+ passive_ddisp = (
684
+ self.thickness.current
685
+ * (
686
+ hyperelastic_potential_diff_new * disp_diff
687
+ - hyperelastic_potential_diff
688
+ )
689
+ / np.pow(disp_diff, 2)
690
+ )
691
+
692
+ else:
693
+ # external
694
+ disp_mid_ratio_adj = np.pow(
695
+ disp_mid_ratio
696
+ - np.pow(disp_mid_ratio, -2) * self.half_thickness_radius_ratio,
697
+ 2,
698
+ )
699
+ fluid_volume_diff_diff_mid = (
700
+ 4.0
701
+ * np.pi
702
+ * self.radius.current
703
+ * (
704
+ 2.0
705
+ * (
706
+ disp_mid_ratio
707
+ - np.pow(disp_mid_ratio, -2) * self.half_thickness_radius_ratio
708
+ )
709
+ * np.pow(
710
+ 1.0 + np.pow(disp_mid_ratio, -3) * self.thickness_radius_ratio,
711
+ 2,
712
+ )
713
+ - (
714
+ 3.0
715
+ * self.thickness_radius_ratio
716
+ * np.pow(disp_mid_ratio, -4)
717
+ * disp_mid_ratio_adj
718
+ )
719
+ )
720
+ )
721
+ fluid_volume_diff_scheme_diff = (
722
+ 1.0 / self.sphere_surface * 0.5 * fluid_volume_diff_diff_mid
723
+ )
724
+ external_ddisp = (
725
+ p_mid_point - pressure_external
726
+ ) * fluid_volume_diff_scheme_diff
727
+
728
+ # passive
729
+ exp_j1_mid = np.exp(
730
+ self.hyperelastic_cst.current[1] * np.pow(j1_mid - 3.0, 2)
731
+ )
732
+ exp_j4_mid = np.exp(
733
+ self.hyperelastic_cst.current[3] * np.pow(j4_mid - 1.0, 2)
734
+ )
735
+ j1_diff_mid = 2.0 * j4_diff_mid - 4.0 * self.inv_radius * np.pow(
736
+ disp_mid_ratio, -5
737
+ )
738
+ j4_diff_diff = 2.0 * np.pow(self.inv_radius, 2)
739
+ j1_diff_diff_mid = 4.0 * np.pow(self.inv_radius, 2) + 20.0 * np.pow(
740
+ self.inv_radius, 2
741
+ ) * np.pow(disp_mid_ratio, -6)
742
+ hyperelastic_potential_diff_diff_mid = (
743
+ 2.0
744
+ * self.hyperelastic_cst_01
745
+ * (
746
+ j1_diff_diff_mid * (j1_mid - 3.0)
747
+ + np.pow(j1_diff_mid, 2)
748
+ + 2.0
749
+ * self.hyperelastic_cst.current[1]
750
+ * np.pow(j1_diff_mid * (j1_mid - 3.0), 2)
751
+ )
752
+ * exp_j1_mid
753
+ + 2.0
754
+ * self.hyperelastic_cst_23
755
+ * (
756
+ j4_diff_diff * (j4_mid - 1.0)
757
+ + np.pow(j4_diff_mid, 2)
758
+ + 2.0
759
+ * self.hyperelastic_cst.current[3]
760
+ * np.pow(j4_diff_mid * (j4_mid - 1.0), 2)
761
+ )
762
+ * exp_j4_mid
763
+ )
764
+ passive_ddisp = (
765
+ self.thickness.current * 0.5 * hyperelastic_potential_diff_diff_mid
766
+ )
767
+
768
+ return viscous_ddisp + active_ddisp - external_ddisp + passive_ddisp
769
+
770
+ def dynamics_residual_dfib_deform(self) -> Any:
771
+ """
772
+
773
+ Compute the partial derivative of the residual for `fib_deform`.
774
+
775
+ :return: the residual partial derivative for `fib_deform`
776
+ :rtype: np.float64
777
+ """
778
+ # activ stress
779
+ return -self.half_thickness_radius_ratio * self.series_stiffness.current
780
+
781
+ def dynamics_residual_dvel(self) -> Any:
782
+ """
783
+ Compute the partial derivative of the residual for `vel`.
784
+
785
+ :return: the residual partial derivative for `vel`
786
+ :rtype: np.float64
787
+ """
788
+ # inertia
789
+ return self.vol_mass.current * self.thickness.current * self.time.inv_dt
790
+
791
+ def dynamics_residual_dpressure(self) -> Any:
792
+ """
793
+ Compute the partial derivative of the residual for the `pressure`.
794
+
795
+ :return: the residual partial derivative for `pressure`
796
+ :rtype: np.float64
797
+ """
798
+
799
+ # external
800
+ disp_diff = diff(self.disp)
801
+ disp_mid = mid_point(self.disp)
802
+ disp_mid_ratio = 1.0 + disp_mid * self.inv_radius
803
+ disp_new_ratio = 1.0 + self.disp.new * self.inv_radius
804
+ disp_cur_ratio = 1.0 + self.disp.current * self.inv_radius
805
+
806
+ if np.fabs(disp_diff / self.disp.current) > _DISP_EPSILON:
807
+ disp_diff_ratio = disp_diff * self.inv_radius
808
+ fluid_volume_term_cur = (
809
+ disp_cur_ratio
810
+ - np.pow(disp_cur_ratio, -2) * self.half_thickness_radius_ratio
811
+ )
812
+ fluid_volume_term_new = (
813
+ disp_new_ratio
814
+ - np.pow(disp_new_ratio, -2) * self.half_thickness_radius_ratio
815
+ )
816
+
817
+ fluid_volume_term_diff3 = (
818
+ disp_diff_ratio
819
+ - math_utils.power_diff(
820
+ disp_new_ratio, disp_cur_ratio, disp_diff_ratio, -2
821
+ )
822
+ * self.half_thickness_radius_ratio
823
+ )
824
+
825
+ fluid_volume_diff = self.sphere_volume * math_utils.power_diff(
826
+ fluid_volume_term_new, fluid_volume_term_cur, fluid_volume_term_diff3, 3
827
+ )
828
+ external_dpressure = -(
829
+ (0.5 * fluid_volume_diff) / (self.sphere_surface * disp_diff)
830
+ )
831
+ else:
832
+ disp_mid_ratio_adj = np.pow(
833
+ disp_mid_ratio
834
+ - np.pow(disp_mid_ratio, -2) * self.half_thickness_radius_ratio,
835
+ 2,
836
+ )
837
+ external_dpressure = (
838
+ -0.5
839
+ * disp_mid_ratio_adj
840
+ * (1.0 + np.pow(disp_mid_ratio, -3) * self.thickness_radius_ratio)
841
+ )
842
+
843
+ return external_dpressure
844
+
845
+ def dynamics_residual_dpressure_external(self) -> Any:
846
+ """
847
+ Compute the partial derivative of the residual `pressure_external`.
848
+
849
+ :return: the residual partial derivative `pressure_external`.
850
+ :rtype: np.float64
851
+ """
852
+
853
+ disp_diff = diff(self.disp)
854
+
855
+ if np.abs(disp_diff / self.disp.current) > _DISP_EPSILON:
856
+ disp_diff_ratio = disp_diff * self.inv_radius
857
+ disp_new_ratio = 1.0 + self.disp.new * self.inv_radius
858
+ disp_cur_ratio = 1.0 + self.disp.current * self.inv_radius
859
+
860
+ fluid_volume_cur = (
861
+ disp_cur_ratio
862
+ - np.pow(disp_cur_ratio, -2) * self.half_thickness_radius_ratio
863
+ )
864
+ fluid_volume_new = (
865
+ disp_new_ratio
866
+ - np.pow(disp_new_ratio, -2) * self.half_thickness_radius_ratio
867
+ )
868
+ fluid_volume_diff3 = (
869
+ disp_diff_ratio
870
+ - math_utils.power_diff(
871
+ disp_new_ratio, disp_cur_ratio, disp_diff_ratio, -2
872
+ )
873
+ * self.half_thickness_radius_ratio
874
+ )
875
+ fluid_volume_diff = self.sphere_volume * math_utils.power_diff(
876
+ fluid_volume_new, fluid_volume_cur, fluid_volume_diff3, 3
877
+ )
878
+
879
+ dresidual_dpressure_external = (
880
+ 0.5 * fluid_volume_diff / (self.sphere_surface * disp_diff)
881
+ )
882
+
883
+ else:
884
+ disp_mid = mid_point(self.disp)
885
+ disp_mid_ratio = 1.0 + disp_mid * self.inv_radius
886
+
887
+ disp_mid_ratio_adj = np.pow(
888
+ disp_mid_ratio
889
+ - np.pow(disp_mid_ratio, -2) * self.half_thickness_radius_ratio,
890
+ 2,
891
+ )
892
+
893
+ dresidual_dpressure_external = (
894
+ 0.5
895
+ * disp_mid_ratio_adj
896
+ * (1.0 + np.pow(disp_mid_ratio, -3) * self.thickness_radius_ratio)
897
+ )
898
+
899
+ return dresidual_dpressure_external
900
+
901
+ def _solve_static_problem(self) -> tuple[np.float64, np.float64]:
902
+ eq_system = EqSystem(1)
903
+
904
+ state = State()
905
+ state.add_variable(SPHERICAL_DYNAMICS_STATIC_DISP_LOCAL_ID, 1)
906
+ state[SPHERICAL_DYNAMICS_STATIC_DISP_LOCAL_ID].initialize(self.disp.current)
907
+
908
+ static_block = _SphericalDynamicsStaticModelComponent(
909
+ disp=state[SPHERICAL_DYNAMICS_STATIC_DISP_LOCAL_ID],
910
+ pressure=Quantity(self.pressure.current),
911
+ thickness=Quantity(self.thickness.current),
912
+ hyperelastic_cst=Quantity(self.hyperelastic_cst.current),
913
+ inv_radius=Quantity(self.inv_radius),
914
+ pressure_external=Quantity(self.pressure_external.current),
915
+ )
916
+ static_block.initialize()
917
+ static_expression = (
918
+ _SphericalDynamicsStaticModelComponent.get_internal_variable_expression(
919
+ SPHERICAL_DYNAMICS_STATIC_DISP_LOCAL_ID
920
+ )[0]
921
+ )
922
+ eq_system.add_system_part(
923
+ 0,
924
+ static_expression.size,
925
+ static_expression.expr_func,
926
+ {
927
+ 0: static_expression.expr_gradients[
928
+ SPHERICAL_DYNAMICS_STATIC_DISP_LOCAL_ID
929
+ ]
930
+ },
931
+ static_block,
932
+ )
933
+
934
+ solver = NewtonSolver(
935
+ _STATIC_PROBLEM_TOL,
936
+ _STATIC_PROBLEM_MAX_IT,
937
+ )
938
+ p_test = static_block.pressure.current
939
+ p_step = p_test
940
+
941
+ while (
942
+ p_test <= static_block.pressure.current
943
+ and p_test > _STATIC_PROBLEM_MIN_PRESSURE_STEP
944
+ ):
945
+ sol = solver.solve(
946
+ state,
947
+ eq_system,
948
+ {SPHERICAL_DYNAMICS_STATIC_DISP_LOCAL_ID: _STATIC_PROBLEM_DISP_MAG},
949
+ )
950
+
951
+ if sol.converged is True:
952
+ state[SPHERICAL_DYNAMICS_STATIC_DISP_LOCAL_ID].initialize(sol.x[0])
953
+
954
+ if np.abs(p_test - static_block.pressure.current) < _STATIC_PROBLEM_TOL:
955
+ break # disp and fib deform initialized
956
+
957
+ p_test = np.min([p_test + p_step, static_block.pressure.current])
958
+ static_block.pressure.update(p_test)
959
+
960
+ else:
961
+ # solver did not converged, update pressure
962
+ state.reset_state_vector()
963
+ p_step = p_step * 0.5
964
+ p_test = p_test - p_step
965
+ static_block.pressure.update(p_test)
966
+
967
+ disp_init = state[SPHERICAL_DYNAMICS_STATIC_DISP_LOCAL_ID].current
968
+ return disp_init, disp_init * self.inv_radius
969
+
970
+
971
+ # Define the dynamics residual expression and its partial derivatives.
972
+ _spherical_dynamics_residual_expr = Expression(
973
+ 1,
974
+ SphericalDynamicsModelComponent.dynamics_residual,
975
+ {
976
+ "fib_deform": SphericalDynamicsModelComponent.dynamics_residual_dfib_deform,
977
+ "disp": SphericalDynamicsModelComponent.dynamics_residual_ddisp,
978
+ "vel": SphericalDynamicsModelComponent.dynamics_residual_dvel,
979
+ "pressure": SphericalDynamicsModelComponent.dynamics_residual_dpressure,
980
+ "pressure_external": SphericalDynamicsModelComponent.dynamics_residual_dpressure_external, # noqa: E501
981
+ },
982
+ )
983
+
984
+ SphericalDynamicsModelComponent.declares_internal_expression(
985
+ "disp", _spherical_dynamics_residual_expr
986
+ )