finitewave-model-barkley 0.2.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.
- finitewave_model_barkley-0.2.0/.gitignore +207 -0
- finitewave_model_barkley-0.2.0/LICENSE +21 -0
- finitewave_model_barkley-0.2.0/PKG-INFO +68 -0
- finitewave_model_barkley-0.2.0/README.md +53 -0
- finitewave_model_barkley-0.2.0/barkley/__init__.py +1 -0
- finitewave_model_barkley-0.2.0/barkley/ops.py +77 -0
- finitewave_model_barkley-0.2.0/examples/barkley_example.py +27 -0
- finitewave_model_barkley-0.2.0/implementation/__init__.py +0 -0
- finitewave_model_barkley-0.2.0/implementation/barkley_0d.py +106 -0
- finitewave_model_barkley-0.2.0/pyproject.toml +30 -0
- finitewave_model_barkley-0.2.0/tests/barkley_test.py +102 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py.cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
.pybuilder/
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
+
# .python-version
|
|
89
|
+
|
|
90
|
+
# pipenv
|
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
+
# install all needed dependencies.
|
|
95
|
+
#Pipfile.lock
|
|
96
|
+
|
|
97
|
+
# UV
|
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
+
# commonly ignored for libraries.
|
|
101
|
+
#uv.lock
|
|
102
|
+
|
|
103
|
+
# poetry
|
|
104
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
105
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
106
|
+
# commonly ignored for libraries.
|
|
107
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
108
|
+
#poetry.lock
|
|
109
|
+
#poetry.toml
|
|
110
|
+
|
|
111
|
+
# pdm
|
|
112
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
113
|
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
114
|
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
115
|
+
#pdm.lock
|
|
116
|
+
#pdm.toml
|
|
117
|
+
.pdm-python
|
|
118
|
+
.pdm-build/
|
|
119
|
+
|
|
120
|
+
# pixi
|
|
121
|
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
122
|
+
#pixi.lock
|
|
123
|
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
124
|
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
125
|
+
.pixi
|
|
126
|
+
|
|
127
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
128
|
+
__pypackages__/
|
|
129
|
+
|
|
130
|
+
# Celery stuff
|
|
131
|
+
celerybeat-schedule
|
|
132
|
+
celerybeat.pid
|
|
133
|
+
|
|
134
|
+
# SageMath parsed files
|
|
135
|
+
*.sage.py
|
|
136
|
+
|
|
137
|
+
# Environments
|
|
138
|
+
.env
|
|
139
|
+
.envrc
|
|
140
|
+
.venv
|
|
141
|
+
env/
|
|
142
|
+
venv/
|
|
143
|
+
ENV/
|
|
144
|
+
env.bak/
|
|
145
|
+
venv.bak/
|
|
146
|
+
|
|
147
|
+
# Spyder project settings
|
|
148
|
+
.spyderproject
|
|
149
|
+
.spyproject
|
|
150
|
+
|
|
151
|
+
# Rope project settings
|
|
152
|
+
.ropeproject
|
|
153
|
+
|
|
154
|
+
# mkdocs documentation
|
|
155
|
+
/site
|
|
156
|
+
|
|
157
|
+
# mypy
|
|
158
|
+
.mypy_cache/
|
|
159
|
+
.dmypy.json
|
|
160
|
+
dmypy.json
|
|
161
|
+
|
|
162
|
+
# Pyre type checker
|
|
163
|
+
.pyre/
|
|
164
|
+
|
|
165
|
+
# pytype static type analyzer
|
|
166
|
+
.pytype/
|
|
167
|
+
|
|
168
|
+
# Cython debug symbols
|
|
169
|
+
cython_debug/
|
|
170
|
+
|
|
171
|
+
# PyCharm
|
|
172
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
173
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
174
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
175
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
176
|
+
#.idea/
|
|
177
|
+
|
|
178
|
+
# Abstra
|
|
179
|
+
# Abstra is an AI-powered process automation framework.
|
|
180
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
181
|
+
# Learn more at https://abstra.io/docs
|
|
182
|
+
.abstra/
|
|
183
|
+
|
|
184
|
+
# Visual Studio Code
|
|
185
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
|
186
|
+
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
187
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
|
188
|
+
# you could uncomment the following to ignore the entire vscode folder
|
|
189
|
+
# .vscode/
|
|
190
|
+
|
|
191
|
+
# Ruff stuff:
|
|
192
|
+
.ruff_cache/
|
|
193
|
+
|
|
194
|
+
# PyPI configuration file
|
|
195
|
+
.pypirc
|
|
196
|
+
|
|
197
|
+
# Cursor
|
|
198
|
+
# Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
|
|
199
|
+
# exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
|
|
200
|
+
# refer to https://docs.cursor.com/context/ignore-files
|
|
201
|
+
.cursorignore
|
|
202
|
+
.cursorindexingignore
|
|
203
|
+
|
|
204
|
+
# Marimo
|
|
205
|
+
marimo/_static/
|
|
206
|
+
marimo/_lsp/
|
|
207
|
+
__marimo__/
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Finitewave
|
|
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.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: finitewave-model-barkley
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Barkley model.
|
|
5
|
+
License: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Keywords: cardiac,electrophysiology,finitewave,model
|
|
8
|
+
Classifier: Intended Audience :: Science/Research
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
|
|
13
|
+
Requires-Python: >=3.10
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
## Barkley Finitewave model
|
|
17
|
+
|
|
18
|
+
The Barkley model is a simplified two-variable reaction–diffusion system
|
|
19
|
+
originally developed to study wave propagation in excitable media. While it is
|
|
20
|
+
not biophysically detailed, it captures essential qualitative features of
|
|
21
|
+
cardiac-like excitation dynamics such as spiral waves, wave break, and reentry.
|
|
22
|
+
|
|
23
|
+
This model implementation can be used separately from the Finitewave, allowing for standalone simulations and testing of the model dynamics without the need for the entire framework.
|
|
24
|
+
|
|
25
|
+
### Reference
|
|
26
|
+
Barkley, D. (1991).
|
|
27
|
+
A model for fast computer simulation of waves in excitable media.
|
|
28
|
+
Physica D: Nonlinear Phenomena, 61-70.
|
|
29
|
+
|
|
30
|
+
DOI: https://doi.org/10.1016/0167-2789(86)90198-1.
|
|
31
|
+
|
|
32
|
+
### How to use (quickstart)
|
|
33
|
+
```bash
|
|
34
|
+
python -m examples.barkley_example
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### How to test
|
|
38
|
+
```bash
|
|
39
|
+
python -m pytest -q
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Repository structure
|
|
43
|
+
```text
|
|
44
|
+
.
|
|
45
|
+
├── barkley/ # equations package (ops.py)
|
|
46
|
+
│ ├── __init__.py
|
|
47
|
+
│ └── ops.py # model equations (pure functions)
|
|
48
|
+
├── implementation/ # 0D model implementation
|
|
49
|
+
│ ├── __init__.py
|
|
50
|
+
│ └── barkley_0d.py
|
|
51
|
+
├── example/
|
|
52
|
+
│ └── barkley_example.py # minimal script to run a short trace
|
|
53
|
+
├── tests/
|
|
54
|
+
│ └── test.py # smoke test; reproducibility checks
|
|
55
|
+
├── .gitignore
|
|
56
|
+
├── LICENSE # MIT
|
|
57
|
+
├── pyproject.toml # configuration file
|
|
58
|
+
└── README.md # this file
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Variables
|
|
62
|
+
- `u` — Transmembrane potential (dimensionless).
|
|
63
|
+
- `v` — Recovery variable (dimensionless).
|
|
64
|
+
|
|
65
|
+
### Parameters
|
|
66
|
+
- `a = 0.75` - Threshold-like parameter controlling excitability.
|
|
67
|
+
- `b = 0.02` - Recovery time scale.
|
|
68
|
+
- `eps = 0.02` - Controls sharpness of the activation term (nonlinear gain).
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
## Barkley Finitewave model
|
|
2
|
+
|
|
3
|
+
The Barkley model is a simplified two-variable reaction–diffusion system
|
|
4
|
+
originally developed to study wave propagation in excitable media. While it is
|
|
5
|
+
not biophysically detailed, it captures essential qualitative features of
|
|
6
|
+
cardiac-like excitation dynamics such as spiral waves, wave break, and reentry.
|
|
7
|
+
|
|
8
|
+
This model implementation can be used separately from the Finitewave, allowing for standalone simulations and testing of the model dynamics without the need for the entire framework.
|
|
9
|
+
|
|
10
|
+
### Reference
|
|
11
|
+
Barkley, D. (1991).
|
|
12
|
+
A model for fast computer simulation of waves in excitable media.
|
|
13
|
+
Physica D: Nonlinear Phenomena, 61-70.
|
|
14
|
+
|
|
15
|
+
DOI: https://doi.org/10.1016/0167-2789(86)90198-1.
|
|
16
|
+
|
|
17
|
+
### How to use (quickstart)
|
|
18
|
+
```bash
|
|
19
|
+
python -m examples.barkley_example
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### How to test
|
|
23
|
+
```bash
|
|
24
|
+
python -m pytest -q
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Repository structure
|
|
28
|
+
```text
|
|
29
|
+
.
|
|
30
|
+
├── barkley/ # equations package (ops.py)
|
|
31
|
+
│ ├── __init__.py
|
|
32
|
+
│ └── ops.py # model equations (pure functions)
|
|
33
|
+
├── implementation/ # 0D model implementation
|
|
34
|
+
│ ├── __init__.py
|
|
35
|
+
│ └── barkley_0d.py
|
|
36
|
+
├── example/
|
|
37
|
+
│ └── barkley_example.py # minimal script to run a short trace
|
|
38
|
+
├── tests/
|
|
39
|
+
│ └── test.py # smoke test; reproducibility checks
|
|
40
|
+
├── .gitignore
|
|
41
|
+
├── LICENSE # MIT
|
|
42
|
+
├── pyproject.toml # configuration file
|
|
43
|
+
└── README.md # this file
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Variables
|
|
47
|
+
- `u` — Transmembrane potential (dimensionless).
|
|
48
|
+
- `v` — Recovery variable (dimensionless).
|
|
49
|
+
|
|
50
|
+
### Parameters
|
|
51
|
+
- `a = 0.75` - Threshold-like parameter controlling excitability.
|
|
52
|
+
- `b = 0.02` - Recovery time scale.
|
|
53
|
+
- `eps = 0.02` - Controls sharpness of the activation term (nonlinear gain).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from . import ops
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ops.py — mathematical core of the model.
|
|
3
|
+
|
|
4
|
+
This module provides functions to compute the model equations,
|
|
5
|
+
as well as functions to retrieve default parameters and initial
|
|
6
|
+
values for the state variables.
|
|
7
|
+
|
|
8
|
+
References:
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
__all__ = (
|
|
12
|
+
"get_variables",
|
|
13
|
+
"get_parameters",
|
|
14
|
+
"calc_rhs",
|
|
15
|
+
"calc_dv",
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def get_variables() -> dict[str, float]:
|
|
20
|
+
"""
|
|
21
|
+
Returns default initial values for state variables.
|
|
22
|
+
"""
|
|
23
|
+
return {"u": 0.0, "v": 0.0}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def get_parameters() -> dict[str, float]:
|
|
27
|
+
"""
|
|
28
|
+
Returns default parameter values for the model.
|
|
29
|
+
"""
|
|
30
|
+
return {"a": 0.75, "b": 0.02, "eps": 0.02}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def calc_rhs(u, v, a, b, eps) -> float:
|
|
34
|
+
"""
|
|
35
|
+
Computes the right-hand side of the model.
|
|
36
|
+
|
|
37
|
+
Parameters
|
|
38
|
+
----------
|
|
39
|
+
u : float
|
|
40
|
+
Current value of the excitation variable.
|
|
41
|
+
v : float
|
|
42
|
+
Current value of the recovery variable.
|
|
43
|
+
a : float
|
|
44
|
+
Parameter controlling the excitation threshold.
|
|
45
|
+
b : float
|
|
46
|
+
Parameter influencing the recovery dynamics.
|
|
47
|
+
eps : float
|
|
48
|
+
Parameter scaling the time dynamics.
|
|
49
|
+
|
|
50
|
+
Returns
|
|
51
|
+
-------
|
|
52
|
+
float
|
|
53
|
+
Right-hand side of the model.
|
|
54
|
+
"""
|
|
55
|
+
return (u*(1 - u)*(u - (v + b)/a))/eps
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def calc_dv(v, u):
|
|
59
|
+
"""
|
|
60
|
+
Calculates the recovery variable v for the Barkley model.
|
|
61
|
+
|
|
62
|
+
The recovery variable follows a simple linear relaxation toward the
|
|
63
|
+
excitation variable `u`, simulating return to the resting state after excitation.
|
|
64
|
+
|
|
65
|
+
Parameters
|
|
66
|
+
----------
|
|
67
|
+
v : float
|
|
68
|
+
Current value of the recovery variable.
|
|
69
|
+
u : float
|
|
70
|
+
Current value of the excitation variable.
|
|
71
|
+
|
|
72
|
+
Returns
|
|
73
|
+
-------
|
|
74
|
+
float
|
|
75
|
+
Updated value of the recovery variable.
|
|
76
|
+
"""
|
|
77
|
+
return (u-v)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Example script to run a 0D model simulation and plot the results.
|
|
3
|
+
|
|
4
|
+
This script sets up a simple stimulation protocol, runs the simulation,
|
|
5
|
+
and plots the membrane potential over time.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
import matplotlib.pyplot as plt
|
|
10
|
+
|
|
11
|
+
from implementation.barkley_0d import Barkley0D, Stimulation
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
stimulations = [Stimulation(t_start=0.1, duration=0.2, amplitude=1.0)]
|
|
15
|
+
t_max = 100.0
|
|
16
|
+
|
|
17
|
+
model = Barkley0D(dt=0.01, stimulations=stimulations)
|
|
18
|
+
model.run(t_max=t_max)
|
|
19
|
+
|
|
20
|
+
time = np.arange(0, t_max, model.dt)
|
|
21
|
+
plt.plot(time, model.history['u'])
|
|
22
|
+
plt.xlabel('Time (s)')
|
|
23
|
+
plt.ylabel('Membrane Potential (u)')
|
|
24
|
+
plt.title('0D Model Simulation')
|
|
25
|
+
plt.grid()
|
|
26
|
+
plt.show()
|
|
27
|
+
|
|
File without changes
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module provides a simple interface to run the model in a 0D setting,
|
|
3
|
+
i.e., without spatial dimensions. It includes class for defining stimulation protocols
|
|
4
|
+
and a class for the 0D model itself.
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from barkley import ops
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Stimulation:
|
|
12
|
+
"""
|
|
13
|
+
Stimulus protocol for the 0D model.
|
|
14
|
+
|
|
15
|
+
Parameters
|
|
16
|
+
----------
|
|
17
|
+
t_start : float
|
|
18
|
+
Start time (ms) of the first stimulus window.
|
|
19
|
+
duration : float
|
|
20
|
+
Duration (ms) of a single pulse.
|
|
21
|
+
amplitude : float
|
|
22
|
+
Pulse amplitude in the same units as du/dt contribution (typically "units/ms").
|
|
23
|
+
|
|
24
|
+
Method
|
|
25
|
+
------
|
|
26
|
+
stim(t: float) -> float
|
|
27
|
+
Returns the instantaneous stimulus value at time t.
|
|
28
|
+
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def __init__(self, t_start: float, duration: float, amplitude: float):
|
|
32
|
+
self.t_start = t_start
|
|
33
|
+
self.duration = duration
|
|
34
|
+
self.amplitude = amplitude
|
|
35
|
+
|
|
36
|
+
def stim(self, t: float) -> float:
|
|
37
|
+
return self.amplitude if self.t_start <= t < self.t_start + self.duration else 0.0
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class Barkley0D:
|
|
41
|
+
"""
|
|
42
|
+
Model OD implementation.
|
|
43
|
+
|
|
44
|
+
Parameters
|
|
45
|
+
----------
|
|
46
|
+
|
|
47
|
+
dt : float
|
|
48
|
+
Time step size (ms).
|
|
49
|
+
stimulations : list[Stimulation]
|
|
50
|
+
List of stimulation protocols to apply during the simulation.
|
|
51
|
+
|
|
52
|
+
Attributes
|
|
53
|
+
----------
|
|
54
|
+
variables : dict[str, float]
|
|
55
|
+
Current state variables of the model.
|
|
56
|
+
parameters : dict[str, float]
|
|
57
|
+
Model parameters.
|
|
58
|
+
history : dict[str, list[float]]
|
|
59
|
+
Time history of state variables for post-processing.
|
|
60
|
+
|
|
61
|
+
Methods
|
|
62
|
+
-------
|
|
63
|
+
step(i: int)
|
|
64
|
+
Perform a single time step update.
|
|
65
|
+
run(t_max: float)
|
|
66
|
+
Run the simulation up to time t_max.
|
|
67
|
+
"""
|
|
68
|
+
def __init__(self, dt: float, stimulations: list[Stimulation]):
|
|
69
|
+
self.dt = dt
|
|
70
|
+
self.stimulations = stimulations
|
|
71
|
+
self.variables = ops.get_variables()
|
|
72
|
+
self.parameters = ops.get_parameters()
|
|
73
|
+
self.history = {s: [] for s in self.variables}
|
|
74
|
+
|
|
75
|
+
def step(self, i: int):
|
|
76
|
+
"""
|
|
77
|
+
Perform a single time step update.
|
|
78
|
+
|
|
79
|
+
Parameters
|
|
80
|
+
----------
|
|
81
|
+
i : int
|
|
82
|
+
Current time step index.
|
|
83
|
+
"""
|
|
84
|
+
self.variables["v"] += self.dt*ops.calc_dv(self.variables["v"], self.variables["u"])
|
|
85
|
+
|
|
86
|
+
self.variables["u"] += self.dt*(ops.calc_rhs(self.variables["u"],
|
|
87
|
+
self.variables["v"],
|
|
88
|
+
self.parameters["a"],
|
|
89
|
+
self.parameters["b"],
|
|
90
|
+
self.parameters["eps"])
|
|
91
|
+
+ sum(stim.stim(t=self.dt*i) for stim in self.stimulations))
|
|
92
|
+
|
|
93
|
+
def run(self, t_max: float):
|
|
94
|
+
"""
|
|
95
|
+
Run the simulation up to time t_max.
|
|
96
|
+
|
|
97
|
+
Parameters
|
|
98
|
+
----------
|
|
99
|
+
t_max : float
|
|
100
|
+
Maximum simulation time.
|
|
101
|
+
"""
|
|
102
|
+
n_steps = int(round(t_max/self.dt))
|
|
103
|
+
for i in range(n_steps):
|
|
104
|
+
self.step(i)
|
|
105
|
+
for s in self.variables:
|
|
106
|
+
self.history[s].append(self.variables[s])
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.21.0"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "finitewave-model-barkley"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "Barkley model."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
keywords = ["cardiac", "electrophysiology", "finitewave", "model"]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Programming Language :: Python :: 3",
|
|
15
|
+
"License :: OSI Approved :: MIT License",
|
|
16
|
+
"Operating System :: OS Independent",
|
|
17
|
+
"Intended Audience :: Science/Research",
|
|
18
|
+
"Topic :: Scientific/Engineering :: Medical Science Apps."
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
dependencies = []
|
|
22
|
+
|
|
23
|
+
[project.entry-points."finitewave.models"]
|
|
24
|
+
barkley = "barkley.ops"
|
|
25
|
+
|
|
26
|
+
[tool.hatch.build.targets.wheel]
|
|
27
|
+
packages = ["barkley"]
|
|
28
|
+
|
|
29
|
+
[tool.hatch.metadata]
|
|
30
|
+
allow-direct-references = true
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pytest
|
|
3
|
+
|
|
4
|
+
from implementation.barkley_0d import Barkley0D, Stimulation
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def prepare_model(model_class, dt, curr_dur, curr_value, t_prebeats):
|
|
8
|
+
"""
|
|
9
|
+
Prepares a 2D cardiac model with a stimulation protocol.
|
|
10
|
+
|
|
11
|
+
Parameters
|
|
12
|
+
----------
|
|
13
|
+
model_class : Callable
|
|
14
|
+
The cardiac model class to be instantiated.
|
|
15
|
+
dt : float
|
|
16
|
+
Time step for the simulation (ms or model units).
|
|
17
|
+
curr_value : float
|
|
18
|
+
Amplitude of the stimulus current (μA/cm² or model units).
|
|
19
|
+
curr_dur : float
|
|
20
|
+
Duration of each stimulus pulse (ms or model units).
|
|
21
|
+
t_prebeats : float
|
|
22
|
+
Interval between preconditioning stimuli (ms or model units).
|
|
23
|
+
|
|
24
|
+
Returns
|
|
25
|
+
-------
|
|
26
|
+
model : Model
|
|
27
|
+
Configured and initialized model ready for simulation.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
stimulations = [Stimulation(t_start=0.0, duration=curr_dur, amplitude=curr_value),
|
|
31
|
+
Stimulation(t_start=t_prebeats, duration=curr_dur, amplitude=curr_value),
|
|
32
|
+
Stimulation(t_start=2*t_prebeats, duration=curr_dur, amplitude=curr_value),
|
|
33
|
+
Stimulation(t_start=3*t_prebeats, duration=curr_dur, amplitude=curr_value)]
|
|
34
|
+
|
|
35
|
+
model = model_class(dt=dt, stimulations=stimulations)
|
|
36
|
+
|
|
37
|
+
return model
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def calculate_apd(u, dt, threshold, beat_index=3):
|
|
41
|
+
"""
|
|
42
|
+
Calculates the action potential duration (APD) for a single beat (third by default).
|
|
43
|
+
|
|
44
|
+
Parameters
|
|
45
|
+
----------
|
|
46
|
+
u : np.ndarray
|
|
47
|
+
Membrane potential time series.
|
|
48
|
+
dt : float
|
|
49
|
+
Time step of the simulation (ms).
|
|
50
|
+
threshold : float
|
|
51
|
+
Voltage threshold to define APD90 (e.g., -70 mV or 0.1 for normalized models).
|
|
52
|
+
beat_index : int, optional
|
|
53
|
+
Index of the beat to analyze (default is 3).
|
|
54
|
+
|
|
55
|
+
Returns
|
|
56
|
+
-------
|
|
57
|
+
apd : float or None
|
|
58
|
+
Duration of the action potential (ms or model units), or None if no complete AP was found.
|
|
59
|
+
"""
|
|
60
|
+
up_idx = np.where((u[:-1] < threshold) & (u[1:] >= threshold))[0]
|
|
61
|
+
down_idx = np.where((u[:-1] > threshold) & (u[1:] <= threshold))[0]
|
|
62
|
+
|
|
63
|
+
if len(up_idx) <= beat_index or len(down_idx) == 0:
|
|
64
|
+
return None
|
|
65
|
+
|
|
66
|
+
ap_start = up_idx[beat_index]
|
|
67
|
+
ap_end_candidates = down_idx[down_idx > ap_start]
|
|
68
|
+
if len(ap_end_candidates) == 0:
|
|
69
|
+
return None
|
|
70
|
+
|
|
71
|
+
ap_end = ap_end_candidates[0]
|
|
72
|
+
return (ap_end - ap_start) * dt
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def test_model_attributes():
|
|
76
|
+
"""
|
|
77
|
+
Test that the model has the expected attributes.
|
|
78
|
+
Checks for the presence of key variables and parameters in the 0D Model.
|
|
79
|
+
"""
|
|
80
|
+
model = Barkley0D(dt=0.01, stimulations=[])
|
|
81
|
+
|
|
82
|
+
assert 'u' in model.variables, "Model should have variable 'u'"
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def test_model_run():
|
|
86
|
+
"""
|
|
87
|
+
Test the model run for a short simulation.
|
|
88
|
+
Runs the 0D Model with a predefined stimulation protocol and checks
|
|
89
|
+
that the membrane potential 'u' stays within expected physiological ranges.
|
|
90
|
+
"""
|
|
91
|
+
t_prebeats = 60.0 # interval between preconditioning stimuli (ms or model units).
|
|
92
|
+
t_calc = 80.0 # time after the last preconditioning beat to continue recording (ms or model units).
|
|
93
|
+
t_max = 3*t_prebeats + t_calc
|
|
94
|
+
model = prepare_model(Barkley0D, dt=0.01, curr_dur=0.1, curr_value=5.0, t_prebeats=t_prebeats)
|
|
95
|
+
model.run(t_max=t_max)
|
|
96
|
+
u = np.array(model.history['u'])
|
|
97
|
+
|
|
98
|
+
assert np.max(u) == pytest.approx(1.0, abs=0.1)
|
|
99
|
+
assert np.min(u) == pytest.approx(0.0, abs=0.01)
|
|
100
|
+
|
|
101
|
+
apd = calculate_apd(u, model.dt, threshold=0.1)
|
|
102
|
+
assert 1 <= apd <= 4, f"Model is out of expected range {apd}"
|