simcardemsx 0.1.0__tar.gz
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.
- simcardemsx-0.1.0/LICENSE +7 -0
- simcardemsx-0.1.0/PKG-INFO +213 -0
- simcardemsx-0.1.0/README.md +148 -0
- simcardemsx-0.1.0/pyproject.toml +236 -0
- simcardemsx-0.1.0/setup.cfg +4 -0
- simcardemsx-0.1.0/src/simcardemsx/__init__.py +18 -0
- simcardemsx-0.1.0/src/simcardemsx/averaging.py +112 -0
- simcardemsx-0.1.0/src/simcardemsx/backends/__init__.py +37 -0
- simcardemsx-0.1.0/src/simcardemsx/backends/base.py +120 -0
- simcardemsx-0.1.0/src/simcardemsx/backends/generated.py +660 -0
- simcardemsx-0.1.0/src/simcardemsx/backends/segregated.py +565 -0
- simcardemsx-0.1.0/src/simcardemsx/backends/zeta_split.py +415 -0
- simcardemsx-0.1.0/src/simcardemsx/checkpoint.py +727 -0
- simcardemsx-0.1.0/src/simcardemsx/config.py +43 -0
- simcardemsx-0.1.0/src/simcardemsx/controller.py +323 -0
- simcardemsx-0.1.0/src/simcardemsx/datacollector.py +503 -0
- simcardemsx-0.1.0/src/simcardemsx/interpolation.py +41 -0
- simcardemsx-0.1.0/src/simcardemsx/mechanics.py +219 -0
- simcardemsx-0.1.0/src/simcardemsx/ode_model.py +137 -0
- simcardemsx-0.1.0/src/simcardemsx/provenance.py +68 -0
- simcardemsx-0.1.0/src/simcardemsx/py.typed +0 -0
- simcardemsx-0.1.0/src/simcardemsx/results.py +324 -0
- simcardemsx-0.1.0/src/simcardemsx/transfer_plan.py +362 -0
- simcardemsx-0.1.0/src/simcardemsx/units.py +101 -0
- simcardemsx-0.1.0/src/simcardemsx/utils.py +12 -0
- simcardemsx-0.1.0/src/simcardemsx/zero_d.py +294 -0
- simcardemsx-0.1.0/src/simcardemsx.egg-info/PKG-INFO +213 -0
- simcardemsx-0.1.0/src/simcardemsx.egg-info/SOURCES.txt +63 -0
- simcardemsx-0.1.0/src/simcardemsx.egg-info/dependency_links.txt +1 -0
- simcardemsx-0.1.0/src/simcardemsx.egg-info/requires.txt +51 -0
- simcardemsx-0.1.0/src/simcardemsx.egg-info/top_level.txt +1 -0
- simcardemsx-0.1.0/tests/test_averaging.py +151 -0
- simcardemsx-0.1.0/tests/test_backends.py +363 -0
- simcardemsx-0.1.0/tests/test_biv_example.py +187 -0
- simcardemsx-0.1.0/tests/test_checkpoint.py +577 -0
- simcardemsx-0.1.0/tests/test_checkpoint_backends.py +268 -0
- simcardemsx-0.1.0/tests/test_checkpoint_circulation.py +200 -0
- simcardemsx-0.1.0/tests/test_circulation.py +290 -0
- simcardemsx-0.1.0/tests/test_coupled_backend.py +223 -0
- simcardemsx-0.1.0/tests/test_coupled_system.py +107 -0
- simcardemsx-0.1.0/tests/test_crossbridge_coupling.py +345 -0
- simcardemsx-0.1.0/tests/test_cycle_coupling.py +248 -0
- simcardemsx-0.1.0/tests/test_datacollector.py +111 -0
- simcardemsx-0.1.0/tests/test_demo_io.py +171 -0
- simcardemsx-0.1.0/tests/test_dynamic_coupling.py +127 -0
- simcardemsx-0.1.0/tests/test_generated_activation.py +836 -0
- simcardemsx-0.1.0/tests/test_interpolation.py +69 -0
- simcardemsx-0.1.0/tests/test_isolated_ep.py +77 -0
- simcardemsx-0.1.0/tests/test_isolated_mechanics.py +198 -0
- simcardemsx-0.1.0/tests/test_mechanics_drivers.py +112 -0
- simcardemsx-0.1.0/tests/test_monolithic_coupling.py +140 -0
- simcardemsx-0.1.0/tests/test_ode_model.py +216 -0
- simcardemsx-0.1.0/tests/test_provenance.py +65 -0
- simcardemsx-0.1.0/tests/test_results.py +225 -0
- simcardemsx-0.1.0/tests/test_rodero_case.py +161 -0
- simcardemsx-0.1.0/tests/test_rodero_example.py +419 -0
- simcardemsx-0.1.0/tests/test_rollback.py +492 -0
- simcardemsx-0.1.0/tests/test_round_trip.py +106 -0
- simcardemsx-0.1.0/tests/test_scheme_comparison.py +589 -0
- simcardemsx-0.1.0/tests/test_scheme_report.py +557 -0
- simcardemsx-0.1.0/tests/test_segregated_backend.py +816 -0
- simcardemsx-0.1.0/tests/test_slab_example.py +478 -0
- simcardemsx-0.1.0/tests/test_stability.py +148 -0
- simcardemsx-0.1.0/tests/test_transfer_plan.py +155 -0
- simcardemsx-0.1.0/tests/test_zero_d_coupling.py +180 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright 2025 Simula Research Laboratory and Henrik Finsberg
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: simcardemsx
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Next generation cardiac electro-mechanics solver based on FEniCSx
|
|
5
|
+
Author-email: Henrik Finsberg <henriknf@simula.no>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://computationalphysiology.github.io/simcardemsx
|
|
8
|
+
Project-URL: Documentation, https://computationalphysiology.github.io/simcardemsx
|
|
9
|
+
Project-URL: Source, https://github.com/ComputationalPhysiology/simcardemsx
|
|
10
|
+
Project-URL: Issues, https://github.com/ComputationalPhysiology/simcardemsx/issues
|
|
11
|
+
Keywords: cardiac,electromechanics,finite element,fenicsx,electrophysiology
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
|
|
16
|
+
Requires-Python: >=3.12.0
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: fenics-dolfinx
|
|
20
|
+
Requires-Dist: fenicsx-pulse>=0.11.1
|
|
21
|
+
Requires-Dist: fenicsx-beat>=0.9.0
|
|
22
|
+
Requires-Dist: crossbridge>=0.3.4
|
|
23
|
+
Requires-Dist: io4dolfinx
|
|
24
|
+
Requires-Dist: scipy
|
|
25
|
+
Requires-Dist: gotranx>=2.2.0
|
|
26
|
+
Requires-Dist: toml
|
|
27
|
+
Requires-Dist: pydantic
|
|
28
|
+
Requires-Dist: pydantic_pint
|
|
29
|
+
Provides-Extra: test
|
|
30
|
+
Requires-Dist: pytest; extra == "test"
|
|
31
|
+
Requires-Dist: pytest-cov; extra == "test"
|
|
32
|
+
Requires-Dist: cardiac-geometriesx; extra == "test"
|
|
33
|
+
Requires-Dist: circulation>=0.4.0; extra == "test"
|
|
34
|
+
Provides-Extra: dev
|
|
35
|
+
Requires-Dist: pdbpp; extra == "dev"
|
|
36
|
+
Requires-Dist: ipython; extra == "dev"
|
|
37
|
+
Requires-Dist: bump2version; extra == "dev"
|
|
38
|
+
Requires-Dist: mypy; extra == "dev"
|
|
39
|
+
Requires-Dist: types-toml; extra == "dev"
|
|
40
|
+
Requires-Dist: pre-commit; extra == "dev"
|
|
41
|
+
Provides-Extra: demo
|
|
42
|
+
Requires-Dist: matplotlib; extra == "demo"
|
|
43
|
+
Requires-Dist: cardiac-geometriesx; extra == "demo"
|
|
44
|
+
Requires-Dist: fenicsx-ldrb; extra == "demo"
|
|
45
|
+
Requires-Dist: ukb-atlas; extra == "demo"
|
|
46
|
+
Requires-Dist: circulation>=0.4.0; extra == "demo"
|
|
47
|
+
Requires-Dist: gotranx; extra == "demo"
|
|
48
|
+
Requires-Dist: io4dolfinx; extra == "demo"
|
|
49
|
+
Provides-Extra: docs
|
|
50
|
+
Requires-Dist: mystmd; extra == "docs"
|
|
51
|
+
Requires-Dist: jupytext; extra == "docs"
|
|
52
|
+
Requires-Dist: ipykernel; extra == "docs"
|
|
53
|
+
Requires-Dist: jupyter-server; extra == "docs"
|
|
54
|
+
Requires-Dist: griffe>=2; extra == "docs"
|
|
55
|
+
Requires-Dist: simcardemsx[demo]; extra == "docs"
|
|
56
|
+
Provides-Extra: benchmark
|
|
57
|
+
Requires-Dist: pytest-codspeed; extra == "benchmark"
|
|
58
|
+
Requires-Dist: simcardemsx[test]; extra == "benchmark"
|
|
59
|
+
Provides-Extra: all
|
|
60
|
+
Requires-Dist: simcardemsx[test]; extra == "all"
|
|
61
|
+
Requires-Dist: simcardemsx[docs]; extra == "all"
|
|
62
|
+
Requires-Dist: simcardemsx[dev]; extra == "all"
|
|
63
|
+
Requires-Dist: simcardemsx[demo]; extra == "all"
|
|
64
|
+
Dynamic: license-file
|
|
65
|
+
|
|
66
|
+
# simcardemsx
|
|
67
|
+
|
|
68
|
+
Cardiac electro-mechanics in [FEniCSx](https://fenicsproject.org).
|
|
69
|
+
|
|
70
|
+
simcardemsx couples a model of cardiac electrophysiology (EP) to a model of tissue
|
|
71
|
+
mechanics, with the contraction of each cell in between. It does not solve either
|
|
72
|
+
physics itself. It connects two solvers that do:
|
|
73
|
+
|
|
74
|
+
- [fenicsx-beat](https://github.com/finsberg/fenicsx-beat) for EP (the monodomain
|
|
75
|
+
model);
|
|
76
|
+
- [fenicsx-pulse](https://github.com/finsberg/fenicsx-pulse) for the mechanics, from a
|
|
77
|
+
single element to a beating heart in a circulation model.
|
|
78
|
+
|
|
79
|
+
The cell model, EP and contraction alike, is a single
|
|
80
|
+
[gotranx](https://github.com/finsberg/gotranx) `.ode` file. simcardemsx splits it in two
|
|
81
|
+
and generates code for each half. The EP half is NumPy code for beat. The contraction
|
|
82
|
+
half is UFL, which the mechanics solver can differentiate through, so the contraction
|
|
83
|
+
model can be stepped *inside* the mechanics Newton iteration. That removes the
|
|
84
|
+
instability the usual staggered coupling suffers from when the tissue is soft
|
|
85
|
+
(Regazzoni & Quarteroni, 2021). Models that cannot be written in UFL, such as the NumPy
|
|
86
|
+
models of [crossbridge](https://github.com/ComputationalPhysiology/crossbridge), are
|
|
87
|
+
coupled with the stabilized scheme of the same paper instead.
|
|
88
|
+
|
|
89
|
+
📖 **Documentation**: <https://computationalphysiology.github.io/simcardemsx>
|
|
90
|
+
|
|
91
|
+
## Features
|
|
92
|
+
|
|
93
|
+
- One `.ode` file for the whole cell, split anywhere between EP and contraction.
|
|
94
|
+
Which variables cross between the two sides is read off the generated code, not
|
|
95
|
+
configured by hand.
|
|
96
|
+
- Separate meshes and time steps for EP and mechanics.
|
|
97
|
+
- Three coupling schemes: monolithic (the default), stabilized segregated, and the
|
|
98
|
+
naive segregated scheme for comparison.
|
|
99
|
+
- Static and dynamic mechanics, closed-loop circulation (Regazzoni's 0D circuit) and
|
|
100
|
+
open-loop cardiac cycles, through pulse.
|
|
101
|
+
- Rollback of a failed time step, and checkpoints that a run continues from in a new
|
|
102
|
+
process (bit for bit under pulse's default direct solver).
|
|
103
|
+
|
|
104
|
+
## Installation
|
|
105
|
+
|
|
106
|
+
simcardemsx needs [DOLFINx](https://github.com/FEniCS/dolfinx), which is not
|
|
107
|
+
installable from PyPI on its own. The simplest way to get it is the DOLFINx container:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
docker run -ti -v $(pwd):/home/shared -w /home/shared ghcr.io/fenics/dolfinx/dolfinx:stable
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Inside it, install simcardemsx with pip:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
python3 -m pip install simcardemsx
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
or from source, with the dependencies of the examples:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
git clone https://github.com/ComputationalPhysiology/simcardemsx.git
|
|
123
|
+
cd simcardemsx
|
|
124
|
+
python3 -m pip install -e ".[demo]"
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
The extras are `demo` (the examples), `test`, `docs` and `dev`.
|
|
128
|
+
|
|
129
|
+
## A first look
|
|
130
|
+
|
|
131
|
+
Generate the two halves of a cell model and see what crosses between them:
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
from pathlib import Path
|
|
135
|
+
|
|
136
|
+
from simcardemsx.ode_model import load_ode_modules
|
|
137
|
+
|
|
138
|
+
odefile = Path("examples/odefiles/ToRORd_dynCl_endo_zetasplit.ode")
|
|
139
|
+
modules = load_ode_modules(odefile, Path("generated_odes"))
|
|
140
|
+
|
|
141
|
+
print(modules.mechanics.missing) # what contraction needs from EP: {'XS': 0, 'XW': 1}
|
|
142
|
+
print(modules.ep.missing) # what EP needs back: {'Zetas': 0, 'Zetaw': 1}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
The contraction half becomes the active stress of a pulse model:
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
from simcardemsx.backends import GeneratedActivation
|
|
149
|
+
|
|
150
|
+
backend = GeneratedActivation(modules.mechanics, mesh, f0, quadrature_degree=4)
|
|
151
|
+
model = pulse.CardiacModel(material=material, active=backend, compressibility=compressibility)
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
and `SimulationController` steps EP and mechanics together:
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
from simcardemsx.controller import SimulationController
|
|
158
|
+
|
|
159
|
+
controller = SimulationController(
|
|
160
|
+
mechanics=problem, # a pulse.StaticProblem or DynamicProblem using `model`
|
|
161
|
+
ep_solver=ep_solver, # a beat.MonodomainSplittingSolver
|
|
162
|
+
backend=backend,
|
|
163
|
+
ode_modules=modules,
|
|
164
|
+
dt_mech=1.0, # ms
|
|
165
|
+
dt_ep=0.05, # ms
|
|
166
|
+
)
|
|
167
|
+
while controller.t < 200.0:
|
|
168
|
+
controller.step()
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
The [tutorials](https://github.com/ComputationalPhysiology/simcardemsx/tree/main/examples) build this up step by step, from one cell to a
|
|
172
|
+
coupled slab.
|
|
173
|
+
|
|
174
|
+
## Examples
|
|
175
|
+
|
|
176
|
+
[`examples/`](https://github.com/ComputationalPhysiology/simcardemsx/tree/main/examples) holds four short tutorials and four larger
|
|
177
|
+
applications:
|
|
178
|
+
|
|
179
|
+
- an electro-mechanical slab, for any split, scheme or contraction model;
|
|
180
|
+
- a biventricular heart beating in a closed-loop circulation;
|
|
181
|
+
- a biventricular heart from the Rodero cohort, driven through the cardiac cycle;
|
|
182
|
+
- a convergence study of the coupling schemes.
|
|
183
|
+
|
|
184
|
+
## Development
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
python3 -m pip install -e ".[dev,test]"
|
|
188
|
+
python3 -m pytest # the test suite
|
|
189
|
+
python3 -m pytest -m "not slow" # the fast part of it
|
|
190
|
+
pre-commit run --all # ruff, mypy, cspell, …
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
The documentation is built with [MyST](https://mystmd.org). See
|
|
194
|
+
[`docs/README.md`](https://github.com/ComputationalPhysiology/simcardemsx/blob/main/docs/README.md).
|
|
195
|
+
|
|
196
|
+
## References
|
|
197
|
+
|
|
198
|
+
- F. Regazzoni and A. Quarteroni. An oscillation-free fully staggered algorithm for
|
|
199
|
+
velocity-dependent active models of cardiac mechanics. *Computer Methods in Applied
|
|
200
|
+
Mechanics and Engineering* 373, 113506 (2021).
|
|
201
|
+
- J. Tomek et al. Development, calibration, and validation of a novel human ventricular
|
|
202
|
+
myocyte model in health, disease, and drug block. *eLife* 8, e48890 (2019).
|
|
203
|
+
- S. Land et al. A model of cardiac contraction based on novel measurements of tension
|
|
204
|
+
development in human cardiomyocytes. *Journal of Molecular and Cellular Cardiology*
|
|
205
|
+
106, 68–83 (2017).
|
|
206
|
+
|
|
207
|
+
## Authors
|
|
208
|
+
|
|
209
|
+
Henrik Finsberg (<henriknf@simula.no>)
|
|
210
|
+
|
|
211
|
+
## License
|
|
212
|
+
|
|
213
|
+
MIT. See [LICENSE](https://github.com/ComputationalPhysiology/simcardemsx/blob/main/LICENSE).
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# simcardemsx
|
|
2
|
+
|
|
3
|
+
Cardiac electro-mechanics in [FEniCSx](https://fenicsproject.org).
|
|
4
|
+
|
|
5
|
+
simcardemsx couples a model of cardiac electrophysiology (EP) to a model of tissue
|
|
6
|
+
mechanics, with the contraction of each cell in between. It does not solve either
|
|
7
|
+
physics itself. It connects two solvers that do:
|
|
8
|
+
|
|
9
|
+
- [fenicsx-beat](https://github.com/finsberg/fenicsx-beat) for EP (the monodomain
|
|
10
|
+
model);
|
|
11
|
+
- [fenicsx-pulse](https://github.com/finsberg/fenicsx-pulse) for the mechanics, from a
|
|
12
|
+
single element to a beating heart in a circulation model.
|
|
13
|
+
|
|
14
|
+
The cell model, EP and contraction alike, is a single
|
|
15
|
+
[gotranx](https://github.com/finsberg/gotranx) `.ode` file. simcardemsx splits it in two
|
|
16
|
+
and generates code for each half. The EP half is NumPy code for beat. The contraction
|
|
17
|
+
half is UFL, which the mechanics solver can differentiate through, so the contraction
|
|
18
|
+
model can be stepped *inside* the mechanics Newton iteration. That removes the
|
|
19
|
+
instability the usual staggered coupling suffers from when the tissue is soft
|
|
20
|
+
(Regazzoni & Quarteroni, 2021). Models that cannot be written in UFL, such as the NumPy
|
|
21
|
+
models of [crossbridge](https://github.com/ComputationalPhysiology/crossbridge), are
|
|
22
|
+
coupled with the stabilized scheme of the same paper instead.
|
|
23
|
+
|
|
24
|
+
📖 **Documentation**: <https://computationalphysiology.github.io/simcardemsx>
|
|
25
|
+
|
|
26
|
+
## Features
|
|
27
|
+
|
|
28
|
+
- One `.ode` file for the whole cell, split anywhere between EP and contraction.
|
|
29
|
+
Which variables cross between the two sides is read off the generated code, not
|
|
30
|
+
configured by hand.
|
|
31
|
+
- Separate meshes and time steps for EP and mechanics.
|
|
32
|
+
- Three coupling schemes: monolithic (the default), stabilized segregated, and the
|
|
33
|
+
naive segregated scheme for comparison.
|
|
34
|
+
- Static and dynamic mechanics, closed-loop circulation (Regazzoni's 0D circuit) and
|
|
35
|
+
open-loop cardiac cycles, through pulse.
|
|
36
|
+
- Rollback of a failed time step, and checkpoints that a run continues from in a new
|
|
37
|
+
process (bit for bit under pulse's default direct solver).
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
simcardemsx needs [DOLFINx](https://github.com/FEniCS/dolfinx), which is not
|
|
42
|
+
installable from PyPI on its own. The simplest way to get it is the DOLFINx container:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
docker run -ti -v $(pwd):/home/shared -w /home/shared ghcr.io/fenics/dolfinx/dolfinx:stable
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Inside it, install simcardemsx with pip:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
python3 -m pip install simcardemsx
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
or from source, with the dependencies of the examples:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
git clone https://github.com/ComputationalPhysiology/simcardemsx.git
|
|
58
|
+
cd simcardemsx
|
|
59
|
+
python3 -m pip install -e ".[demo]"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The extras are `demo` (the examples), `test`, `docs` and `dev`.
|
|
63
|
+
|
|
64
|
+
## A first look
|
|
65
|
+
|
|
66
|
+
Generate the two halves of a cell model and see what crosses between them:
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
from pathlib import Path
|
|
70
|
+
|
|
71
|
+
from simcardemsx.ode_model import load_ode_modules
|
|
72
|
+
|
|
73
|
+
odefile = Path("examples/odefiles/ToRORd_dynCl_endo_zetasplit.ode")
|
|
74
|
+
modules = load_ode_modules(odefile, Path("generated_odes"))
|
|
75
|
+
|
|
76
|
+
print(modules.mechanics.missing) # what contraction needs from EP: {'XS': 0, 'XW': 1}
|
|
77
|
+
print(modules.ep.missing) # what EP needs back: {'Zetas': 0, 'Zetaw': 1}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The contraction half becomes the active stress of a pulse model:
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
from simcardemsx.backends import GeneratedActivation
|
|
84
|
+
|
|
85
|
+
backend = GeneratedActivation(modules.mechanics, mesh, f0, quadrature_degree=4)
|
|
86
|
+
model = pulse.CardiacModel(material=material, active=backend, compressibility=compressibility)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
and `SimulationController` steps EP and mechanics together:
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
from simcardemsx.controller import SimulationController
|
|
93
|
+
|
|
94
|
+
controller = SimulationController(
|
|
95
|
+
mechanics=problem, # a pulse.StaticProblem or DynamicProblem using `model`
|
|
96
|
+
ep_solver=ep_solver, # a beat.MonodomainSplittingSolver
|
|
97
|
+
backend=backend,
|
|
98
|
+
ode_modules=modules,
|
|
99
|
+
dt_mech=1.0, # ms
|
|
100
|
+
dt_ep=0.05, # ms
|
|
101
|
+
)
|
|
102
|
+
while controller.t < 200.0:
|
|
103
|
+
controller.step()
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
The [tutorials](https://github.com/ComputationalPhysiology/simcardemsx/tree/main/examples) build this up step by step, from one cell to a
|
|
107
|
+
coupled slab.
|
|
108
|
+
|
|
109
|
+
## Examples
|
|
110
|
+
|
|
111
|
+
[`examples/`](https://github.com/ComputationalPhysiology/simcardemsx/tree/main/examples) holds four short tutorials and four larger
|
|
112
|
+
applications:
|
|
113
|
+
|
|
114
|
+
- an electro-mechanical slab, for any split, scheme or contraction model;
|
|
115
|
+
- a biventricular heart beating in a closed-loop circulation;
|
|
116
|
+
- a biventricular heart from the Rodero cohort, driven through the cardiac cycle;
|
|
117
|
+
- a convergence study of the coupling schemes.
|
|
118
|
+
|
|
119
|
+
## Development
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
python3 -m pip install -e ".[dev,test]"
|
|
123
|
+
python3 -m pytest # the test suite
|
|
124
|
+
python3 -m pytest -m "not slow" # the fast part of it
|
|
125
|
+
pre-commit run --all # ruff, mypy, cspell, …
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
The documentation is built with [MyST](https://mystmd.org). See
|
|
129
|
+
[`docs/README.md`](https://github.com/ComputationalPhysiology/simcardemsx/blob/main/docs/README.md).
|
|
130
|
+
|
|
131
|
+
## References
|
|
132
|
+
|
|
133
|
+
- F. Regazzoni and A. Quarteroni. An oscillation-free fully staggered algorithm for
|
|
134
|
+
velocity-dependent active models of cardiac mechanics. *Computer Methods in Applied
|
|
135
|
+
Mechanics and Engineering* 373, 113506 (2021).
|
|
136
|
+
- J. Tomek et al. Development, calibration, and validation of a novel human ventricular
|
|
137
|
+
myocyte model in health, disease, and drug block. *eLife* 8, e48890 (2019).
|
|
138
|
+
- S. Land et al. A model of cardiac contraction based on novel measurements of tension
|
|
139
|
+
development in human cardiomyocytes. *Journal of Molecular and Cellular Cardiology*
|
|
140
|
+
106, 68–83 (2017).
|
|
141
|
+
|
|
142
|
+
## Authors
|
|
143
|
+
|
|
144
|
+
Henrik Finsberg (<henriknf@simula.no>)
|
|
145
|
+
|
|
146
|
+
## License
|
|
147
|
+
|
|
148
|
+
MIT. See [LICENSE](https://github.com/ComputationalPhysiology/simcardemsx/blob/main/LICENSE).
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
[build-system] # Require setuptool version due to https://github.com/pypa/setuptools/issues/2938
|
|
2
|
+
requires = [
|
|
3
|
+
"scikit-build-core[pyproject]>=0.11.0", # Ensuring license field is correctly parsed]
|
|
4
|
+
"nanobind>=1.3.2",
|
|
5
|
+
"setuptools>=77.0.3", # Ensuring license field is correctly parsed]
|
|
6
|
+
"packaging>=24.2", # Ensuring license field is correctly parsed]
|
|
7
|
+
"pkgconfig",
|
|
8
|
+
]
|
|
9
|
+
|
|
10
|
+
[project]
|
|
11
|
+
name = "simcardemsx"
|
|
12
|
+
version = "0.1.0"
|
|
13
|
+
description = "Next generation cardiac electro-mechanics solver based on FEniCSx"
|
|
14
|
+
authors = [{name = "Henrik Finsberg", email = "henriknf@simula.no"}]
|
|
15
|
+
license = "MIT"
|
|
16
|
+
license-files = ["LICENSE"]
|
|
17
|
+
readme = "README.md"
|
|
18
|
+
keywords = ["cardiac", "electromechanics", "finite element", "fenicsx", "electrophysiology"]
|
|
19
|
+
classifiers = [
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Intended Audience :: Science/Research",
|
|
22
|
+
"Topic :: Scientific/Engineering :: Physics",
|
|
23
|
+
"Topic :: Scientific/Engineering :: Medical Science Apps.",
|
|
24
|
+
]
|
|
25
|
+
requires-python = ">=3.12.0"
|
|
26
|
+
dependencies = [
|
|
27
|
+
"fenics-dolfinx",
|
|
28
|
+
# pulse 0.11.1 and beat 0.7.2 are the first releases with the restart API that
|
|
29
|
+
# simcardemsx.checkpoint calls (restart_functions/restart_metadata, and pulse's
|
|
30
|
+
# CycleController.state_dict); SimulationController.step() snapshots through it.
|
|
31
|
+
# beat 0.9.0 replaced beat.ecg.Leads12 with twelve_lead(), which rodero_05's post.py uses.
|
|
32
|
+
"fenicsx-pulse>=0.11.1",
|
|
33
|
+
"fenicsx-beat>=0.9.0",
|
|
34
|
+
# crossbridge backs the Ca_i-split activation backend and is imported at
|
|
35
|
+
# package import time via simcardemsx.backends. 0.3.3 is the first release whose
|
|
36
|
+
# RDQ20MF can take a step longer than its 25 us RU step (gate X2), and 0.3.4 the
|
|
37
|
+
# first with get_state/set_state, which the backend's checkpoint saves.
|
|
38
|
+
"crossbridge>=0.3.4",
|
|
39
|
+
# io4dolfinx writes and reads restart.bp and results.bp (simcardemsx.checkpoint,
|
|
40
|
+
# simcardemsx.results), and is imported at package import time.
|
|
41
|
+
"io4dolfinx",
|
|
42
|
+
# scipy is used by simcardemsx.zero_d for the monolithic reference solve
|
|
43
|
+
"scipy",
|
|
44
|
+
# gotranx 2.2.0 orders the generated states independently of PYTHONHASHSEED, and its
|
|
45
|
+
# GRL step linearizes every state, also through intermediates (1.8 advanced v, cai
|
|
46
|
+
# and the concentrations by forward Euler).
|
|
47
|
+
"gotranx>=2.2.0",
|
|
48
|
+
"toml",
|
|
49
|
+
"pydantic",
|
|
50
|
+
"pydantic_pint",
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
[project.urls]
|
|
54
|
+
Homepage = "https://computationalphysiology.github.io/simcardemsx"
|
|
55
|
+
Documentation = "https://computationalphysiology.github.io/simcardemsx"
|
|
56
|
+
Source = "https://github.com/ComputationalPhysiology/simcardemsx"
|
|
57
|
+
Issues = "https://github.com/ComputationalPhysiology/simcardemsx/issues"
|
|
58
|
+
|
|
59
|
+
[project.optional-dependencies]
|
|
60
|
+
test = [
|
|
61
|
+
"pytest",
|
|
62
|
+
"pytest-cov",
|
|
63
|
+
"cardiac-geometriesx",
|
|
64
|
+
# Regazzoni's closed-loop circuit (its .ode file), for the circulation gates
|
|
65
|
+
"circulation>=0.4.0",
|
|
66
|
+
]
|
|
67
|
+
dev = [
|
|
68
|
+
"pdbpp",
|
|
69
|
+
"ipython",
|
|
70
|
+
"bump2version",
|
|
71
|
+
"mypy",
|
|
72
|
+
# Stubs for mypy, which pre-commit runs from this environment (language: system).
|
|
73
|
+
# Not scipy-stubs: see the scipy mypy override below.
|
|
74
|
+
"types-toml",
|
|
75
|
+
"pre-commit",
|
|
76
|
+
]
|
|
77
|
+
demo = [
|
|
78
|
+
"matplotlib",
|
|
79
|
+
"cardiac-geometriesx",
|
|
80
|
+
"fenicsx-ldrb",
|
|
81
|
+
"ukb-atlas",
|
|
82
|
+
"circulation>=0.4.0",
|
|
83
|
+
"gotranx",
|
|
84
|
+
"io4dolfinx",
|
|
85
|
+
]
|
|
86
|
+
docs = [
|
|
87
|
+
# MyST builds the site (docs/myst.yml); jupytext turns the tutorials into the notebooks
|
|
88
|
+
# it executes, in the kernel ipykernel registers; griffe reads the docstrings for the
|
|
89
|
+
# API reference (docs/generate_api_pages.py) without importing the package.
|
|
90
|
+
"mystmd",
|
|
91
|
+
"jupytext",
|
|
92
|
+
"ipykernel",
|
|
93
|
+
"jupyter-server",
|
|
94
|
+
"griffe>=2",
|
|
95
|
+
"simcardemsx[demo]",
|
|
96
|
+
]
|
|
97
|
+
benchmark = [
|
|
98
|
+
"pytest-codspeed",
|
|
99
|
+
"simcardemsx[test]",
|
|
100
|
+
]
|
|
101
|
+
|
|
102
|
+
all = [
|
|
103
|
+
"simcardemsx[test]",
|
|
104
|
+
"simcardemsx[docs]",
|
|
105
|
+
"simcardemsx[dev]",
|
|
106
|
+
"simcardemsx[demo]",
|
|
107
|
+
]
|
|
108
|
+
|
|
109
|
+
[dependency-groups]
|
|
110
|
+
# Keep in sync with [build-system].requires above. Needed as a group, not just
|
|
111
|
+
# as build-system metadata, because scifem is installed with
|
|
112
|
+
# `--no-build-isolation`, so the build backend has to already be present.
|
|
113
|
+
build = [
|
|
114
|
+
"scikit-build-core[pyproject]>=0.11.0",
|
|
115
|
+
"nanobind>=1.3.2",
|
|
116
|
+
"setuptools>=77.0.3",
|
|
117
|
+
"packaging>=24.2",
|
|
118
|
+
"pkgconfig",
|
|
119
|
+
]
|
|
120
|
+
|
|
121
|
+
[tool.pytest.ini_options]
|
|
122
|
+
addopts = [
|
|
123
|
+
"--cov=simcardemsx",
|
|
124
|
+
"--cov-report=html",
|
|
125
|
+
"--cov-report=xml",
|
|
126
|
+
"--cov-report=term-missing",
|
|
127
|
+
"-v",
|
|
128
|
+
]
|
|
129
|
+
testpaths = [
|
|
130
|
+
"tests"
|
|
131
|
+
]
|
|
132
|
+
markers = ["slow: takes more than a few seconds"]
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
[[tool.mypy.overrides]]
|
|
136
|
+
# scipy ships no types of its own. scipy-stubs would supply them, but it installs optype, and
|
|
137
|
+
# pint >= 0.26 imports optype when it is present, which sends mypy <= 2.3.1 into infinite
|
|
138
|
+
# recursion (python/mypy#21985; fixed on mypy master by python/mypy#22036, not yet released).
|
|
139
|
+
module = ["scipy", "scipy.*"]
|
|
140
|
+
ignore_missing_imports = true
|
|
141
|
+
|
|
142
|
+
[[tool.mypy.overrides]]
|
|
143
|
+
# circulation (Regazzoni's circuit, for the circulation gates) ships no py.typed marker.
|
|
144
|
+
module = ["circulation", "circulation.*"]
|
|
145
|
+
ignore_missing_imports = true
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
[tool.ruff]
|
|
149
|
+
# Exclude a variety of commonly ignored directories.
|
|
150
|
+
exclude = [
|
|
151
|
+
".bzr",
|
|
152
|
+
".direnv",
|
|
153
|
+
".eggs",
|
|
154
|
+
".git",
|
|
155
|
+
".hg",
|
|
156
|
+
".mypy_cache",
|
|
157
|
+
".nox",
|
|
158
|
+
".pants.d",
|
|
159
|
+
".pytype",
|
|
160
|
+
".ruff_cache",
|
|
161
|
+
".svn",
|
|
162
|
+
".tox",
|
|
163
|
+
".venv",
|
|
164
|
+
"__pypackages__",
|
|
165
|
+
"_build",
|
|
166
|
+
"buck-out",
|
|
167
|
+
"build",
|
|
168
|
+
"dist",
|
|
169
|
+
"node_modules",
|
|
170
|
+
"venv",
|
|
171
|
+
"third-party",
|
|
172
|
+
]
|
|
173
|
+
|
|
174
|
+
# Same as Black.
|
|
175
|
+
line-length = 100
|
|
176
|
+
|
|
177
|
+
# Allow unused variables when underscore-prefixed.
|
|
178
|
+
|
|
179
|
+
# Assume Python 3.10.
|
|
180
|
+
target-version = "py312"
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
[tool.ruff.lint]
|
|
184
|
+
# Enable pycodestyle (`E`) and Pyflakes (`F`) codes by default.
|
|
185
|
+
select = ["E", "F", "I"]
|
|
186
|
+
ignore = ["E402", "E741", "E743", "E731"]
|
|
187
|
+
|
|
188
|
+
# Allow autofix for all enabled rules (when `--fix`) is provided.
|
|
189
|
+
fixable = ["A", "B", "C", "D", "E", "F", "I"]
|
|
190
|
+
unfixable = []
|
|
191
|
+
|
|
192
|
+
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
|
193
|
+
|
|
194
|
+
[tool.ruff.lint.mccabe]
|
|
195
|
+
# Unlike Flake8, default to a complexity level of 10.
|
|
196
|
+
max-complexity = 10
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
[tool.ruff.lint.isort]
|
|
200
|
+
known-first-party = ["cardiac_geometries"]
|
|
201
|
+
known-third-party = [
|
|
202
|
+
"basix",
|
|
203
|
+
"dolfinx",
|
|
204
|
+
"ffcx",
|
|
205
|
+
"ufl",
|
|
206
|
+
"gmsh",
|
|
207
|
+
"numpy",
|
|
208
|
+
"pytest",
|
|
209
|
+
]
|
|
210
|
+
section-order = [
|
|
211
|
+
"future",
|
|
212
|
+
"standard-library",
|
|
213
|
+
"mpi",
|
|
214
|
+
"third-party",
|
|
215
|
+
"first-party",
|
|
216
|
+
"local-folder",
|
|
217
|
+
]
|
|
218
|
+
[tool.ruff.lint.isort.sections]
|
|
219
|
+
"mpi" = ["mpi4py", "petsc4py"]
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
[tool.bumpversion]
|
|
223
|
+
allow_dirty = false
|
|
224
|
+
commit = true
|
|
225
|
+
message = "Bump version: {current_version} → {new_version}"
|
|
226
|
+
tag = true
|
|
227
|
+
sign_tags = false
|
|
228
|
+
tag_name = "v{new_version}"
|
|
229
|
+
tag_message = "Bump version: {current_version} → {new_version}"
|
|
230
|
+
current_version = "0.1.0"
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
[[tool.bumpversion.files]]
|
|
234
|
+
filename = "pyproject.toml"
|
|
235
|
+
search = 'version = "{current_version}"'
|
|
236
|
+
replace = 'version = "{new_version}"'
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from importlib.metadata import metadata
|
|
2
|
+
|
|
3
|
+
meta = metadata("simcardemsx")
|
|
4
|
+
__version__ = meta.get("Version")
|
|
5
|
+
__author__ = meta.get("Author-email")
|
|
6
|
+
__license__ = meta.get("license-expression")
|
|
7
|
+
__email__ = meta.get("Author-email")
|
|
8
|
+
__program_name__ = meta.get("Name")
|
|
9
|
+
|
|
10
|
+
from . import backends, controller, interpolation, ode_model, utils
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"backends",
|
|
14
|
+
"controller",
|
|
15
|
+
"ode_model",
|
|
16
|
+
"utils",
|
|
17
|
+
"interpolation",
|
|
18
|
+
]
|