Monodomain simulation¶
The monodomain model is the full reaction-diffusion reference: it solves
on the heart mesh, with an implicit (conjugate-gradient) diffusion solve and an explicit ionic update each time step.
1. Ionic model¶
Pick a cell model and (optionally) tune its parameters:
import torchcor as tc
from torchcor.simulator import Monodomain
from torchcor.ionic import TenTusscherPanfilov
tc.set_device("cuda:0")
dtype = tc.float64
im = TenTusscherPanfilov(cell_type="ENDO", dt=0.01, dtype=dtype)
For multiple tissue types, pass a list of models; assign each to regions with
its region_ids attribute (unset models cover the remaining regions).
2. Build the simulator and load the mesh¶
sim = Monodomain(ionic_models=[im], T=500, dt=0.01, dtype=dtype,
mass_lumping=True)
sim.load_mesh(path="/path/to/heart", unit_conversion=1000)
Tis the total duration (ms),dtthe time step (ms).load_meshreads CARP.pts/.elem/.lonfiles;unit_conversiondivides the node coordinates (CARP stores micrometres, so1000gives mm).mass_lumping=Truelumps the FEM mass matrix — this matches openCARP’s default and gives the correct conduction velocity (the consistent mass matrix conducts ~35% too fast).
3. Conductivities¶
Anisotropic bidomain conductivities (S/m) per region. il/it are
intracellular longitudinal/transverse, el/et extracellular; the
monodomain effective conductivity is their harmonic mean.
sim.add_conductivity([24, 25], il=0.5272, it=0.2076, el=1.0732, et=0.4227)
sim.add_conductivity([34, 35, 36], il=0.9074, it=0.3332, el=0.9074, et=0.3332)
(Passing only il/it uses them directly as the monodomain conductivity.)
4. Stimuli¶
Each stimulus depolarises the nodes listed in a .vtx file:
for name, start in [("LV_sf", 0.0), ("LV_pf", 0.0), ("LV_af", 0.0),
("RV_sf", 5.0), ("RV_mod", 5.0)]:
sim.add_stimulus(f"/path/to/heart/pacing/{name}.vtx",
start=start, duration=1.0, intensity=100)
For pacing, pass period and count to repeat a stimulus.
5. Solve¶
Vm = sim.solve(a_tol=1e-5, r_tol=1e-5, max_iter=100,
snapshot_interval=1, verbose=True, result_path="./out")
Vm is a (T, N_nodes) tensor sampled every snapshot_interval ms.
a_tol / r_tol / max_iter control the per-step CG solve.
6. Post-processing¶
ATs = sim.compute_activation_map(Vm, snapshot_interval=1, threshold=0)
RTs = sim.compute_repolarization_map(Vm, search_after=ATs,
snapshot_interval=1, threshold=-40)
sim.save_vm(Vm) # -> result_path/Vm.pt (e.g. for the ECG solver)
sim.vm_to_vtk(Vm=Vm, step=10) # VTK frames + ATs/RTs for ParaView
compute_activation_mapreturns the local activation time (first up-crossing ofthreshold) per node,NaNwhere the node never activated.compute_repolarization_mapfinds the down-crossing aftersearch_after(the activation map), so it returns proper repolarisation, not the upstroke.
How the solve works¶
Internally, solve() does the following.
1. Assembly (once).
The mesh is turned into finite-element mass M and stiffness K
matrices, where K carries the conductivity tensor sigma_m. The system
matrix
is assembled and a Jacobi preconditioner is built for the conjugate-gradient
(CG) solver. With mass_lumping=True the mass matrix is diagonalised, which
speeds up the solve and corrects the conduction velocity.
2. Time stepping (operator splitting).
Each dt is split into a reaction half-step and a diffusion half-step:
Reaction (explicit). Every node advances its ionic model one step (
differentiate) — an embarrassingly parallel per-node ODE that fills the right-hand sidebtogether with any stimulus current.Diffusion (implicit).
bis multiplied byM, the explicit part of the diffusion is subtracted, andA u = bis solved with preconditioned CG. Because the diffusion is implicit it is unconditionally stable, sodtis limited only by the stiffness of the ionic model.
3. Snapshots.
Vm is recorded every snapshot_interval ms and returned as a
(T, N_nodes) tensor.
The per-step cost is dominated by the CG diffusion solve — which is exactly what the Reaction-Eikonal model model avoids when you don’t need the full physics.
Tip
If you only need activation timing, the Reaction-Eikonal model model gives it far faster. Use the monodomain when you need the full diffusion physics (e.g. as the most accurate source for ECGs).