A computational model of cardiac tissue electrophysiology using the FitzHugh-Nagumo (FHN) reaction-diffusion system. This project simulates wave propagation in 2D myocardial tissue and investigates the relationship between electrical stimulus parameters and the initiation of self-sustaining excitation waves.
Cardiac tissue is an electrically active medium where action potentials—rapid depolarization followed by repolarization—drive rhythmic contraction. Understanding how electrical stimuli initiate self-sustaining waves is crucial for studying defibrillation, arrhythmias, and pacemaker design.
This project models cardiac tissue as a 2D continuous medium governed by a reaction-diffusion partial differential equation (PDE) using the FitzHugh-Nagumo model—a simplified two-variable system that captures essential excitable dynamics:
- Existence of an excitation threshold
- "All-or-nothing" behavior
- Wave propagation through diffusive coupling
What is the relationship between stimulus intensity (I_amp) and duration (t_dur) required to trigger a self-sustaining propagating wave in 2D cardiac tissue?
The FHN model describes each point in the tissue with two variables:
- V: Membrane potential (fast, excitatory variable)
- w: Recovery variable (slow, inhibitory variable)
Parameters:
$\epsilon$ : Recovery rate (typically 0.01 - 0.1)$\beta$ : Polarization parameter$\gamma$ : Recovery parameter
Using the monodomain approximation, the complete system is:
where:
$D$ : Diffusion coefficient (electrical conductivity)$I_{stim}$ : Applied stimulus current (non-zero only during the pulse)
Spatial Discretization: 5-point finite difference Laplacian with Neumann (zero-flux) boundary conditions using reflected ghost nodes.
Temporal Integration: Operator splitting with two approaches:
| Method | Update | Stability | Performance |
|---|---|---|---|
| Explicit (Euler Forward) | Conditional (CFL: | Fast per step, many steps needed | |
| Implicit (Crank-Nicolson) | Unconditional | Slower per step, fewer steps |
The strength-duration relationship follows the Lapicque/Weiss equation:
Physiological Parameters Derived:
- Reobase (R): ~1.133 μA/cm² (minimum current for infinite duration)
- Chronaxia (t_c): ~2.250 ms (time needed at 2× reobase)
| Method | Precision | Avg. CPU Time (s) | Stability |
|---|---|---|---|
| Explicit | Reference | ~7.3 | Conditional (requires |
| Implicit (Crank-Nicolson) | Nearly identical | ~16.3 | Unconditional |
The implicit method is ~2.2× slower for this grid size but enables larger time steps for finer meshes.
When the explicit method violates the CFL condition (
Python 3.8+- Clone the repository:
git clone https://github.com/yourusername/CardioPy.git
cd CardioPy- Install dependencies:
pip install -r requirements.txt- Launch the Jupyter notebook:
jupyter notebook notebooks/Complete_Notebook.ipynb- NumPy
- SciPy (sparse matrices, linear solvers)
- Matplotlib
- Jupyter Notebook
cardiac-modeling/
├── README.md
├── requirements.txt
├── LICENSE
├── notebooks/
│ └── Complete_Notebook.ipynb
├── src/
│ ├── __init__.py
│ ├── fhn_model.py # FHN derivatives and cellular dynamics
│ ├── diffusion.py # Laplacian operator and diffusion solvers
│ ├── stimulus.py # Stimulus application
│ └── simulation.py # Main simulation functions
├── tests/
│ ├── test_fhn.py
│ ├── test_diffusion.py
│ └── test_simulation.py
└── images/
├── strength_duration_curve.png
├── propagation_animation.gif
└── stability_test.png
# FHN cellular dynamicsdefFHN_derivatives(V, w, I_stim, epsilon=0.01, beta=0.7, gamma=0.8):
"""Calculate FHN derivatives"""dVdt=V- (V**3)/3-w+I_stimdwdt=epsilon* (V+beta-gamma*w)
returndVdt, dwdt# Diffusion operator (Neumann boundary conditions)defapply_laplacian(V, dx):
"""2D Laplacian with zero-flux boundary"""# Reflected ghost node implementation
...
# Implicit solver with LU factorization cachingdefimplicit_diffusion_solver(V, dx, dt, D, theta=0.5):
"""Crank-Nicolson diffusion solver with cached LU decomposition"""
...
# Parameter sweepdeffind_threshold(t_dur, metodo='explicito', tol=0.1):
"""Find threshold current using binary search"""
...The implicit solver uses LU factorization caching to avoid rebuilding and refactoring the sparse matrix at each time step—since the matrix depends only on grid parameters (
_cache_implicito= {}
defimplicit_diffusion_solver(V, dx, dt, D, theta=0.5):
key= (Nx, Ny, dx, dt, D, theta)
ifkeynotin_implicit_cache:
L=_build_2d_laplacian_operator(Nx, Ny, dx)
I=identity(N, format='csc')
A= (I-theta*dt*D*L).tocsc()
M= (I+ (1-theta) *dt*D*L).tocsc()
lu=splu(A)
_implicit_cache[key] = (M, lu)
M, lu=_implicit_cache[key]
# Solve using cached factorizationMemory/Performance Trade-off:
- Explicit: Scales as
$O(N^2)$ in time steps due to CFL restriction - implicit: Scales as
$O(N^{1.5})$ due to LU fill-in but maintains constant time steps
- FitzHugh, R. (1961). Impulses and physiological states in theoretical models of nerve membrane. Biophysical Journal, 1(6), 445-466.
- Nagumo, J., Arimoto, S., & Yoshizawa, S. (1962). An active pulse transmission line simulating nerve axon. Proceedings of the IRE, 50(10), 2061-2070.
- Aliev, R. R., & Panfilov, A. V. (1996). A simple two-variable model of cardiac excitation. Chaos, Solitons & Fractals, 7(3), 293-301.
- Clayton, R. H., Bernus, O., Cherry, E. M., et al. (2011). Models of cardiac tissue electrophysiology: progress, challenges and open questions. Progress in Biophysics and Molecular Biology, 104(1-3), 22-48.
This project is licensed under the Apache License, Version 2.0 - see the LICENSE file for details.
- This project was developed as part of a Dynamical Systems Modeling course
- Special thanks to Ronny Calixto for guidance


