Name | fenicsx-beat JSON |
Version |
0.2.2
JSON |
| download |
home_page | None |
Summary | Library to run cardiac EP simulations |
upload_time | 2025-07-21 14:44:31 |
maintainer | None |
docs_url | None |
author | None |
requires_python | >=3.8 |
license | MIT |
keywords |
cardiac
electrophysiology
|
VCS |
 |
bugtrack_url |
|
requirements |
No requirements were recorded.
|
Travis-CI |
No Travis.
|
coveralls test coverage |
No coveralls.
|

# fenicsx-beat
Cardiac electrophysiology simulator in FEniCSx
- Source code: https://github.com/finsberg/fenicsx-beat
- Documentation: https://finsberg.github.io/fenicsx-beat
## Install
You can install the library with `pip`
```
python3 -m pip install fenicsx-beat
```
or with `conda`
```
conda install -c conda-forge fenicsx-beat
```
Note that installing with `pip` requires [FEniCSx already installed](https://fenicsproject.org/download/)
## Getting started
The following minimal example demonstrates simulating the Monodomain model on a unit square domain using a modified FitzHugh-Nagumo model
```python
import shutil
import matplotlib.pyplot as plt
import numpy as np
from mpi4py import MPI
import dolfinx
import ufl
import beat
# MPI communicator
comm = MPI.COMM_WORLD
# Create mesh
mesh = dolfinx.mesh.create_unit_square(comm, 32, 32, dolfinx.cpp.mesh.CellType.triangle)
# Create a variable for time
time = dolfinx.fem.Constant(mesh, dolfinx.default_scalar_type(0.0))
# Define forward euler scheme for solving the ODEs
# This just needs to be a function that takes the current time, states, parameters and dt
# and returns the new states
def fitzhughnagumo_forward_euler(t, states, parameters, dt):
s, v = states
(
c_1,
c_2,
c_3,
a,
b,
v_amp,
v_rest,
v_peak,
stim_amplitude,
stim_duration,
stim_start,
) = parameters
i_app = np.where(
np.logical_and(t > stim_start, t < stim_start + stim_duration),
stim_amplitude,
0,
)
values = np.zeros_like(states)
ds_dt = b * (-c_3 * s + (v - v_rest))
values[0] = ds_dt * dt + s
v_th = v_amp * a + v_rest
I = -s * (c_2 / v_amp) * (v - v_rest) + (
((c_1 / v_amp**2) * (v - v_rest)) * (v - v_th)
) * (-v + v_peak)
dV_dt = I + i_app
values[1] = v + dV_dt * dt
return values
# Define space for the ODEs
ode_space = dolfinx.fem.functionspace(mesh, ("P", 1))
# Define parameters for the ODEs
a = 0.13
b = 0.013
c1 = 0.26
c2 = 0.1
c3 = 1.0
v_peak = 40.0
v_rest = -85.0
stim_amplitude = 100.0
stim_duration = 1
stim_start = 0.0
# Collect the parameter in a numpy array
parameters = np.array(
[
c1,
c2,
c3,
a,
b,
v_peak - v_rest,
v_rest,
v_peak,
stim_amplitude,
stim_duration,
stim_start,
],
dtype=np.float64,
)
# Define the initial states
init_states = np.array([0.0, -85], dtype=np.float64)
# Specify the index of state for the membrane potential
# which will also inform the PDE solver later
v_index = 1
# We can also check the solution of the ODE
# by solving the ODE for a single cell
times = np.arange(0.0, 1000.0, 0.1)
values = np.zeros((len(times), 2))
values[0, :] = np.array([0.0, -85.0])
for i, t in enumerate(times[1:]):
values[i + 1, :] = fitzhughnagumo_forward_euler(t, values[i, :], parameters, dt=0.1)
fig, ax = plt.subplots()
ax.plot(times, values[:, v_index])
ax.set_xlabel("Time")
ax.set_ylabel("States")
ax.legend()
fig.savefig("ode_solution.png")
# Now we set external stimulus to zero for ODE
parameters[-3] = 0.0
# and create stimulus for PDE
stim_expr = ufl.conditional(ufl.And(ufl.ge(time, 0.0), ufl.le(time, 0.5)), 600.0, 0.0)
stim_marker = 1
cells = dolfinx.mesh.locate_entities(
mesh, mesh.topology.dim, lambda x: np.logical_and(x[0] <= 0.5, x[1] <= 0.5)
)
stim_tags = dolfinx.mesh.meshtags(
mesh,
mesh.topology.dim,
cells,
np.full(len(cells), stim_marker, dtype=np.int32),
)
dx = ufl.Measure("dx", domain=mesh, subdomain_data=stim_tags)
I_s = beat.Stimulus(expr=stim_expr, dZ=dx, marker=stim_marker)
# Create PDE model
pde = beat.MonodomainModel(time=time, mesh=mesh, M=0.001, I_s=I_s, dx=dx)
# Next we create the PDE solver where we make sure to
# pass the variable for the membrane potential from the PDE
ode = beat.odesolver.DolfinODESolver(
v_ode=dolfinx.fem.Function(ode_space),
v_pde=pde.state,
fun=fitzhughnagumo_forward_euler,
init_states=init_states,
parameters=parameters,
num_states=len(init_states),
v_index=1,
)
# Combine PDE and ODE solver
solver = beat.MonodomainSplittingSolver(pde=pde, ode=ode)
# Now we setup file for saving results
# First remove any existing files
shutil.rmtree("voltage.bp", ignore_errors=True)
vtx = dolfinx.io.VTXWriter(mesh.comm, "voltage.bp", [pde.state], engine="BP5")
vtx.write(0.0)
# Finally we run the simulation for 400 ms using a time step of 0.01 ms
T = 400.0
t = 0.0
dt = 0.01
i = 0
while t < T:
v = solver.pde.state.x.array
solver.step((t, t + dt))
t += dt
if i % 500 == 0:
vtx.write(t)
i += 1
vtx.close()
```


See more examples in the [documentation](https://finsberg.github.io/fenicsx-beat)
## License
MIT
## Need help or having issues
Please submit an [issue](https://github.com/finsberg/fenicsx-beat/issues)
Raw data
{
"_id": null,
"home_page": null,
"name": "fenicsx-beat",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": null,
"keywords": "cardiac, electrophysiology",
"author": null,
"author_email": "Henrik Finsberg <henriknf@simula.no>",
"download_url": "https://files.pythonhosted.org/packages/66/24/39f13b92b8389163c887e90f6c0879f6a00f8b93c6e4bc19de22ebb4a82f/fenicsx_beat-0.2.2.tar.gz",
"platform": null,
"description": "\n\n# fenicsx-beat\nCardiac electrophysiology simulator in FEniCSx\n\n- Source code: https://github.com/finsberg/fenicsx-beat\n- Documentation: https://finsberg.github.io/fenicsx-beat\n\n\n## Install\nYou can install the library with `pip`\n```\npython3 -m pip install fenicsx-beat\n```\nor with `conda`\n```\nconda install -c conda-forge fenicsx-beat\n```\nNote that installing with `pip` requires [FEniCSx already installed](https://fenicsproject.org/download/)\n\n## Getting started\n\nThe following minimal example demonstrates simulating the Monodomain model on a unit square domain using a modified FitzHugh-Nagumo model\n\n```python\nimport shutil\n\nimport matplotlib.pyplot as plt\nimport numpy as np\n\nfrom mpi4py import MPI\nimport dolfinx\nimport ufl\n\nimport beat\n\n# MPI communicator\ncomm = MPI.COMM_WORLD\n# Create mesh\nmesh = dolfinx.mesh.create_unit_square(comm, 32, 32, dolfinx.cpp.mesh.CellType.triangle)\n# Create a variable for time\ntime = dolfinx.fem.Constant(mesh, dolfinx.default_scalar_type(0.0))\n\n\n# Define forward euler scheme for solving the ODEs\n# This just needs to be a function that takes the current time, states, parameters and dt\n# and returns the new states\ndef fitzhughnagumo_forward_euler(t, states, parameters, dt):\n s, v = states\n (\n c_1,\n c_2,\n c_3,\n a,\n b,\n v_amp,\n v_rest,\n v_peak,\n stim_amplitude,\n stim_duration,\n stim_start,\n ) = parameters\n i_app = np.where(\n np.logical_and(t > stim_start, t < stim_start + stim_duration),\n stim_amplitude,\n 0,\n )\n values = np.zeros_like(states)\n\n ds_dt = b * (-c_3 * s + (v - v_rest))\n values[0] = ds_dt * dt + s\n\n v_th = v_amp * a + v_rest\n I = -s * (c_2 / v_amp) * (v - v_rest) + (\n ((c_1 / v_amp**2) * (v - v_rest)) * (v - v_th)\n ) * (-v + v_peak)\n dV_dt = I + i_app\n values[1] = v + dV_dt * dt\n return values\n\n\n# Define space for the ODEs\node_space = dolfinx.fem.functionspace(mesh, (\"P\", 1))\n\n# Define parameters for the ODEs\na = 0.13\nb = 0.013\nc1 = 0.26\nc2 = 0.1\nc3 = 1.0\nv_peak = 40.0\nv_rest = -85.0\nstim_amplitude = 100.0\nstim_duration = 1\nstim_start = 0.0\n\n# Collect the parameter in a numpy array\nparameters = np.array(\n [\n c1,\n c2,\n c3,\n a,\n b,\n v_peak - v_rest,\n v_rest,\n v_peak,\n stim_amplitude,\n stim_duration,\n stim_start,\n ],\n dtype=np.float64,\n)\n\n# Define the initial states\ninit_states = np.array([0.0, -85], dtype=np.float64)\n# Specify the index of state for the membrane potential\n# which will also inform the PDE solver later\nv_index = 1\n\n# We can also check the solution of the ODE\n# by solving the ODE for a single cell\ntimes = np.arange(0.0, 1000.0, 0.1)\nvalues = np.zeros((len(times), 2))\nvalues[0, :] = np.array([0.0, -85.0])\nfor i, t in enumerate(times[1:]):\n values[i + 1, :] = fitzhughnagumo_forward_euler(t, values[i, :], parameters, dt=0.1)\n\n\nfig, ax = plt.subplots()\nax.plot(times, values[:, v_index])\nax.set_xlabel(\"Time\")\nax.set_ylabel(\"States\")\nax.legend()\nfig.savefig(\"ode_solution.png\")\n\n\n# Now we set external stimulus to zero for ODE\nparameters[-3] = 0.0\n\n# and create stimulus for PDE\nstim_expr = ufl.conditional(ufl.And(ufl.ge(time, 0.0), ufl.le(time, 0.5)), 600.0, 0.0)\nstim_marker = 1\ncells = dolfinx.mesh.locate_entities(\n mesh, mesh.topology.dim, lambda x: np.logical_and(x[0] <= 0.5, x[1] <= 0.5)\n)\nstim_tags = dolfinx.mesh.meshtags(\n mesh,\n mesh.topology.dim,\n cells,\n np.full(len(cells), stim_marker, dtype=np.int32),\n)\ndx = ufl.Measure(\"dx\", domain=mesh, subdomain_data=stim_tags)\nI_s = beat.Stimulus(expr=stim_expr, dZ=dx, marker=stim_marker)\n\n# Create PDE model\npde = beat.MonodomainModel(time=time, mesh=mesh, M=0.001, I_s=I_s, dx=dx)\n\n# Next we create the PDE solver where we make sure to\n# pass the variable for the membrane potential from the PDE\node = beat.odesolver.DolfinODESolver(\n v_ode=dolfinx.fem.Function(ode_space),\n v_pde=pde.state,\n fun=fitzhughnagumo_forward_euler,\n init_states=init_states,\n parameters=parameters,\n num_states=len(init_states),\n v_index=1,\n)\n\n# Combine PDE and ODE solver\nsolver = beat.MonodomainSplittingSolver(pde=pde, ode=ode)\n\n# Now we setup file for saving results\n# First remove any existing files\nshutil.rmtree(\"voltage.bp\", ignore_errors=True)\n\nvtx = dolfinx.io.VTXWriter(mesh.comm, \"voltage.bp\", [pde.state], engine=\"BP5\")\nvtx.write(0.0)\n\n# Finally we run the simulation for 400 ms using a time step of 0.01 ms\nT = 400.0\nt = 0.0\ndt = 0.01\ni = 0\nwhile t < T:\n v = solver.pde.state.x.array\n solver.step((t, t + dt))\n t += dt\n if i % 500 == 0:\n vtx.write(t)\n i += 1\n\nvtx.close()\n\n```\n\n\n\nSee more examples in the [documentation](https://finsberg.github.io/fenicsx-beat)\n\n## License\nMIT\n\n## Need help or having issues\nPlease submit an [issue](https://github.com/finsberg/fenicsx-beat/issues)\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Library to run cardiac EP simulations",
"version": "0.2.2",
"project_urls": {
"Documentation": "https://finsberg.github.io/fenicsx-beat",
"Homepage": "https://finsberg.github.io/fenicsx-beat",
"Source": "https://github.com/finsberg/fenicsx-beat",
"Tracker": "https://github.com/finsberg/fenicsx-beat/issues"
},
"split_keywords": [
"cardiac",
" electrophysiology"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "e7b34938e4cc09caee2bb32e5edaff3962c32adde69735294dd0c21248c3bc1f",
"md5": "f7f650847f0719a0100e468a9ef8449b",
"sha256": "80fb116581f46a893b89aa1a03bbeeda44a2b3466bd1b4352366f1168a94e642"
},
"downloads": -1,
"filename": "fenicsx_beat-0.2.2-py3-none-any.whl",
"has_sig": false,
"md5_digest": "f7f650847f0719a0100e468a9ef8449b",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 27985,
"upload_time": "2025-07-21T14:44:29",
"upload_time_iso_8601": "2025-07-21T14:44:29.863005Z",
"url": "https://files.pythonhosted.org/packages/e7/b3/4938e4cc09caee2bb32e5edaff3962c32adde69735294dd0c21248c3bc1f/fenicsx_beat-0.2.2-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "662439f13b92b8389163c887e90f6c0879f6a00f8b93c6e4bc19de22ebb4a82f",
"md5": "86fc0b9af739063956af4cdc27ce37c4",
"sha256": "d4a164d92980e1a1d326d887815d6e5831c4bc84064e84b55daeb15ec47ac045"
},
"downloads": -1,
"filename": "fenicsx_beat-0.2.2.tar.gz",
"has_sig": false,
"md5_digest": "86fc0b9af739063956af4cdc27ce37c4",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 33123,
"upload_time": "2025-07-21T14:44:31",
"upload_time_iso_8601": "2025-07-21T14:44:31.347521Z",
"url": "https://files.pythonhosted.org/packages/66/24/39f13b92b8389163c887e90f6c0879f6a00f8b93c6e4bc19de22ebb4a82f/fenicsx_beat-0.2.2.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-07-21 14:44:31",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "finsberg",
"github_project": "fenicsx-beat",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "fenicsx-beat"
}