Skip to content

Commit 58e3932

Browse files
feat: use pydantic for materials (#1233)
Co-authored-by: pyansys-ci-bot <[email protected]>
1 parent d976db4 commit 58e3932

File tree

13 files changed

+2043
-53
lines changed

13 files changed

+2043
-53
lines changed

doc/source/changelog/1233.added.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Use pydantic for materials

examples/preprocessor/demo-material_pr.py

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -35,19 +35,20 @@
3535

3636
import matplotlib.pyplot as plt
3737

38-
from ansys.health.heart.settings.material.curve import (
39-
ActiveCurve,
40-
constant_ca2,
41-
kumaraswamy_active,
42-
)
43-
from ansys.health.heart.settings.material.ep_material import CellModel, EPMaterial
44-
from ansys.health.heart.settings.material.material import (
38+
from ansys.health.heart.settings.material.cell_models import Tentusscher
39+
from ansys.health.heart.settings.material.curve import kumaraswamy_active
40+
from ansys.health.heart.settings.material.ep_material import EPMaterial
41+
from ansys.health.heart.settings.material.material_pyd import (
4542
ACTIVE,
4643
ANISO,
4744
ISO,
48-
ActiveModel,
45+
ActiveCurve,
46+
ActiveModel1,
47+
ActiveModel3,
48+
HGOFiber,
4949
Mat295,
5050
NeoHookean,
51+
constant_ca2,
5152
)
5253

5354
###############################################################################
@@ -74,18 +75,18 @@
7475
iso = ISO(k1=1, k2=1, kappa=100)
7576

7677
# step 2: create an anisotropic module
77-
fiber = ANISO.HGOFiber(k1=1, k2=1)
78+
fiber = HGOFiber(k1=1, k2=1)
7879
aniso1 = ANISO(fibers=[fiber])
7980

8081
# Create fiber with sheet, and their interactions
81-
sheet = ANISO.HGOFiber(k1=1, k2=1)
82+
sheet = HGOFiber(k1=1, k2=1)
8283
aniso2 = ANISO(fibers=[fiber, sheet], k1fs=1, k2fs=1)
8384

8485
# step3: create the active module
8586

8687
# example 1:
8788
# create active model 1
88-
ac_model1 = ActiveModel.Model1()
89+
ac_model1 = ActiveModel1()
8990
# create Ca2+ curve
9091
ac_curve1 = ActiveCurve(constant_ca2(tb=800, ca2ionm=ac_model1.ca2ionm), type="ca2", threshold=0.5)
9192
# build active module
@@ -103,7 +104,7 @@
103104

104105
# example 2
105106
# create active model 3
106-
ac_model3 = ActiveModel.Model3()
107+
ac_model3 = ActiveModel3()
107108
# create a stress curve and show
108109
ac_curve3 = ActiveCurve(kumaraswamy_active(t_end=800), type="stress")
109110
fig = ac_curve3.plot_time_vs_stress()
@@ -133,7 +134,7 @@
133134
# Create EP materials
134135
# ~~~~~~~~~~~~~~~~~~~
135136
ep_mat_active = EPMaterial.Active(
136-
sigma_fiber=1, sigma_sheet=0.5, beta=140, cm=0.01, cell_model=CellModel.Tentusscher()
137+
sigma_fiber=1, sigma_sheet=0.5, beta=140, cm=0.01, cell_model=Tentusscher()
137138
)
138139
epinsulator = EPMaterial.Insulator()
139140

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ dependencies = [
4040
"scipy<=1.15.3",
4141
"validators>=0.34.0",
4242
"vtk>=9.1.0,<9.6,!=9.4.*",
43+
"pydantic>=2.0.0"
4344
]
4445

4546
# Optional dependencies:
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
# Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates.
2+
# SPDX-License-Identifier: MIT
3+
#
4+
#
5+
# Permission is hereby granted, free of charge, to any person obtaining a copy
6+
# of this software and associated documentation files (the "Software"), to deal
7+
# in the Software without restriction, including without limitation the rights
8+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
# copies of the Software, and to permit persons to whom the Software is
10+
# furnished to do so, subject to the following conditions:
11+
#
12+
# The above copyright notice and this permission notice shall be included in all
13+
# copies or substantial portions of the Software.
14+
#
15+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
# SOFTWARE.
22+
23+
"""Module to collect ionic cell models."""
24+
25+
from pydantic import BaseModel
26+
27+
28+
class Tentusscher(BaseModel):
29+
"""Data for Tentusscher cell model."""
30+
31+
gas_constant: float = 8314.472
32+
t: float = 310
33+
faraday_constant: float = 96485.3415
34+
cm: float = 0.185
35+
vc: float = 0.016404
36+
vsr: float = 0.001094
37+
vss: float = 0.00005468
38+
pkna: float = 0.03
39+
ko: float = 5.4
40+
nao: float = 140.0
41+
cao: float = 2.0
42+
gk1: float = 5.405
43+
gkr: float = 0.153
44+
gna: float = 14.838
45+
gbna: float = 0.0002
46+
gcal: float = 0.0000398
47+
gbca: float = 0.000592
48+
gpca: float = 0.1238
49+
gpk: float = 0.0146
50+
pnak: float = 2.724
51+
km: float = 1.0
52+
kmna: float = 40.0
53+
knaca: float = 1000.0
54+
ksat: float = 0.1
55+
alpha: float = 2.5
56+
gamma: float = 0.35
57+
kmca: float = 1.38
58+
kmnai: float = 87.5
59+
kpca: float = 0.0005
60+
k1: float = 0.15
61+
k2: float = 0.045
62+
k3: float = 0.06
63+
k4: float = 0.005
64+
ec: float = 1.5
65+
maxsr: float = 2.5
66+
minsr: float = 1.0
67+
vrel: float = 0.102
68+
vleak: float = 0.00036
69+
vxfer: float = 0.0038
70+
vmaxup: float = 0.006375
71+
kup: float = 0.00025
72+
bufc: float = 0.2
73+
kbufc: float = 0.001
74+
bufsr: float = 10.0
75+
kbufsf: float = 0.3
76+
bufss: float = 0.4
77+
kbufss: float = 0.00025
78+
# gas_constant=8314.472,
79+
# faraday_constant=96485.3415,
80+
gks: float = 0.392
81+
gto: float = 0.294
82+
v: float = -85.23
83+
ki: float = 136.89
84+
nai: float = 8.604
85+
cai: float = 0.000126
86+
cass: float = 0.00036
87+
casr: float = 3.64
88+
rpri: float = 0.9073
89+
xr1: float = 0.00621
90+
xr2: float = 0.4712
91+
xs: float = 0.0095
92+
m: float = 0.00172
93+
h: float = 0.7444
94+
j: float = 0.7045
95+
d: float = 3.373e-5
96+
f: float = 0.7888
97+
f2: float = 0.9755
98+
fcass: float = 0.9953
99+
s: float = 0.999998
100+
r: float = 2.42e-8
101+
pass
102+
103+
104+
class TentusscherEndo(Tentusscher):
105+
"""Data for Tentusscher cell model in its endocardium version."""
106+
107+
gks: float = 0.392
108+
gto: float = 0.073
109+
v: float = -86.709
110+
ki: float = 138.4
111+
nai: float = 10.355
112+
cai: float = 0.00013
113+
cass: float = 0.00036
114+
casr: float = 3.715
115+
rpri: float = 0.9068
116+
xr1: float = 0.00448
117+
xr2: float = 0.476
118+
xs: float = 0.0087
119+
m: float = 0.00155
120+
h: float = 0.7573
121+
j: float = 0.7225
122+
d: float = 3.164e-5
123+
f: float = 0.8009
124+
f2: float = 0.9778
125+
fcass: float = 0.9953
126+
s: float = 0.3212
127+
r: float = 2.235e-8
128+
129+
130+
class TentusscherEpi(Tentusscher):
131+
"""Data for Tentusscher cell model in its epicardium version."""
132+
133+
gks: float = 0.392
134+
gto: float = 0.294
135+
v: float = -85.23
136+
ki: float = 136.89
137+
nai: float = 8.604
138+
cai: float = 0.000126
139+
cass: float = 0.00036
140+
casr: float = 3.64
141+
rpri: float = 0.9073
142+
xr1: float = 0.00621
143+
xr2: float = 0.4712
144+
xs: float = 0.0095
145+
m: float = 0.00172
146+
h: float = 0.7444
147+
j: float = 0.7045
148+
d: float = 3.373e-5
149+
f: float = 0.7888
150+
f2: float = 0.9755
151+
fcass: float = 0.9953
152+
s: float = 0.999998
153+
r: float = 2.42e-8
154+
155+
156+
class TentusscherMid(Tentusscher):
157+
"""Data for Tentusscher cell model in its mid-myocardium version."""
158+
159+
gks: float = 0.098
160+
gto: float = 0.294
161+
v: float = -85.423
162+
ki: float = 138.52
163+
nai: float = 10.132
164+
cai: float = 0.000153
165+
cass: float = 0.00042
166+
casr: float = 4.272
167+
rpri: float = 0.8978
168+
xr1: float = 0.0165
169+
xr2: float = 0.473
170+
xs: float = 0.0174
171+
m: float = 0.00165
172+
h: float = 0.749
173+
j: float = 0.6788
174+
d: float = 3.288e-5
175+
f: float = 0.7026
176+
f2: float = 0.9526
177+
fcass: float = 0.9942
178+
s: float = 0.999998
179+
r: float = 2.347e-8

src/ansys/health/heart/settings/material/curve.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ def constant_ca2(tb: float = 800, ca2ionm: float = 4.35) -> tuple[np.ndarray, np
126126
return (t, v)
127127

128128

129+
# TODO: Use pydantic to easily (de)serialize the curve.
129130
class ActiveCurve:
130131
"""Active stress or Ca2+ curve."""
131132

src/ansys/health/heart/settings/material/ep_material.py

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,13 @@
2525
from dataclasses import dataclass, field
2626
from typing import Optional
2727

28+
from deprecated import deprecated
29+
2830
from ansys.health.heart.settings.defaults import electrophysiology as ep_defaults
31+
import ansys.health.heart.settings.material.cell_models as cell_models
2932

3033

34+
@deprecated("Use the cell models in src.ansys.health.heart.settings.material.cell_models instead.")
3135
@dataclass
3236
class CellModel:
3337
"""Abstract class for different cell models."""
@@ -211,6 +215,10 @@ def __post_init__(self):
211215
self.sigma_sheet = self.sigma_sheet_normal
212216

213217

218+
@deprecated(
219+
reason="""Use the Pydantic-based EPMaterial instead.
220+
import ansys.health.heart.settings.material.ep_material_pyd as ep_material"""
221+
)
214222
@dataclass
215223
class EPMaterial(EPMaterialModel):
216224
"""EP material module."""
@@ -247,13 +255,15 @@ class Active(EPMaterialModel):
247255
sigma_fiber: float = sig1
248256
sigma_sheet: Optional[float] = sig2
249257
sigma_sheet_normal: Optional[float] = sig3
250-
cell_model: CellModel = field(default_factory=lambda: CellModel.Tentusscher())
258+
cell_model: cell_models.Tentusscher = field(
259+
default_factory=lambda: cell_models.Tentusscher()
260+
)
251261

252262
class ActiveBeam(Active):
253263
"""Hold data for beam active EP material."""
254264

255265
sigma_fiber = ep_defaults.material["beam"]["sigma"].m
256-
cell_model = field(default_factory=lambda: CellModel.TentusscherEndo())
266+
cell_model = field(default_factory=lambda: cell_models.TentusscherEndo())
257267
pmjres: float = ep_defaults.material["beam"]["pmjres"].m
258268

259269
class Passive(EPMaterialModel):
@@ -272,16 +282,3 @@ class DummyMaterial:
272282
def __repr__(self):
273283
"""Print a message."""
274284
return "Material is empty."
275-
276-
277-
if __name__ == "__main__":
278-
m1 = EPMaterial.DummyMaterial()
279-
280-
tentus1 = CellModel.Tentusscher()
281-
tentus2 = CellModel.TentusscherEndo()
282-
283-
mat1 = EPMaterial.Insulator()
284-
mat2 = EPMaterial.Passive(sigma_fiber=0, sigma_sheet=2)
285-
mat3 = EPMaterial.Active(sigma_fiber=1, sigma_sheet=2, sigma_sheet_normal=3)
286-
mat4 = EPMaterial.Active(sigma_fiber=1, sigma_sheet=2, sigma_sheet_normal=3)
287-
print("done")

0 commit comments

Comments
 (0)