GPU-accelerated cardiac electrophysiology simulation in PyTorch
TorchCor is a high-performance simulator for cardiac electrophysiology (CEP) using the finite element method (FEM) on general-purpose GPUs. Built on top of PyTorch, TorchCor delivers substantial computational acceleration for large-scale CEP simulations, with seamless integration into modern deep learning workflows and efficient handling of complex mesh geometries.
TorchCor offers:
- 🚀 Fast, scalable CEP simulations on large and complex heart meshes
- 🔗 Seamless integration with PyTorch and scientific machine learning workflows
- ⚙️ Support for a wide range of ionic models and conductivity heterogeneity
- 📈 Generation of precise local activation and repolarization time maps with monodomain or reaction-eikonal
- 🩺 Simulation of clinically relevant 12-lead ECG signals via the lead-field method
🫀 Simulation Previews
Below are simulation results showcasing the electrical activation patterns over time in the left atrium and bi-ventricle.
|
3D left atrium surface mesh |
3D bi-ventricle volume mesh |
📦 Installation
pip install torchcor
Note: Requires PyTorch with CUDA support for GPU acceleration.
🚀 Quickstart Example
Here’s a concise example to run a simulation using the TenTusscher-Panfilov ionic model on a bi-ventricle mesh.
🧩 Mesh file:
You can download the bi-ventricle mesh from the following link:
Google Drive – Bi-ventricle Mesh
The inputs are:
.ptsfile: coordinates of the vertices.elemfile: connectivities of the mesh.lonfile: fibre orientations- One or more
.vtxfiles: pacing locations
Running the following code will produce:
- a list of membrane potentials, each saved after every 1 ms of the simulation
- a local activation time map and a repolarisation time map
all saved in .pt file format readable by torch.load. This execution takes 47.6 seconds on RTX 5090.
import torchcor as tc
from torchcor.simulator import Monodomain
from torchcor.ionic import TenTusscherPanfilov
from pathlib import Path
# Specify the GPU device for running the simulation
tc.set_device("cuda:0")
dtype = tc.float32
# The total simulation duration (ms)
simulation_time = 500
dt = 0.01
home_dir = Path.home()
mesh_dir = home_dir / "Data/ventricle/Case_1"
# Load in the ionic model. Here we use TenTusscherPanfilov for the simulation on bi-ventricle
im = TenTusscherPanfilov(cell_type="ENDO", dt=dt, dtype=dtype)
# 1. Initialise the Monodomain model
simulator = Monodomain(ionic_models=[im], T=simulation_time, dt=dt, dtype=dtype)
# 2. Load in the mesh files (.pts .elem .lon)
simulator.load_mesh(path=mesh_dir)
# 3. Specify the conductivity for each region
simulator.add_conductivity([34, 35], il=0.5272, it=0.2076, el=1.0732, et=0.4227)
simulator.add_conductivity([44, 45, 46], il=0.9074, it=0.3332, el=0.9074, et=0.3332)
# 4. Specify the locations where stimulation is applied
simulator.add_stimulus(mesh_dir / "LV_sf.vtx", start=0.0, duration=1.0, intensity=100)
simulator.add_stimulus(mesh_dir / "LV_pf.vtx", start=0.0, duration=1.0, intensity=100)
simulator.add_stimulus(mesh_dir / "LV_af.vtx", start=0.0, duration=1.0, intensity=100)
simulator.add_stimulus(mesh_dir / "RV_sf.vtx", start=5.0, duration=1.0, intensity=100)
simulator.add_stimulus(mesh_dir / "RV_mod.vtx", start=5.0, duration=1.0, intensity=100)
# 5. Start the simulation
snapshot_interval = 1
Vm = simulator.solve(a_tol=1e-5, # absolute tolerance
r_tol=1e-5, # relative tolerance
max_iter=100, # maximum number of iterations for each CG calculation
snapshot_interval=snapshot_interval, # save the solution after every 1 ms
verbose=True,
result_path="./biventricle") # the folder in which the results are saved
# POSTPROCESSING:
ATs = simulator.compute_activation_map(Vm=Vm,
snapshot_interval=snapshot_interval,
threshold=0)
print("ATs: ", ATs.min().item(), ATs.cpu().max().item(), flush=True)
RTs = simulator.compute_repolarization_map(Vm=Vm,
snapshot_interval=snapshot_interval,
threshold=-70)
print("RTs: ", RTs.min().item(), RTs.cpu().max().item(), flush=True)
simulator.vm_to_vtk(Vm=Vm, step=10)
🩺 12-Lead ECG via the Lead-Field Method
TorchCor reconstructs the clinical 12-lead ECG directly from a cardiac simulation using the lead-field (reciprocity) method on a coupled heart–torso mesh. Each electrode's lead field is precomputed with a single elliptic solve; the entire ECG time series then follows from one matrix–vector product with the transmembrane potential Vm, so evaluating the ECG over a whole beat is effectively free.
Simulated 12-lead ECG from a full heart–torso model, reconstructed with the lead-field reciprocity method.
import torch
from torchcor.ecg import LeadField
# Coupled heart–torso mesh; precompute one lead field per electrode
lf = LeadField(torso_mesh_dir, heart_mesh_dir,
device=torch.device("cuda:0"), dtype=torch.float64)
# Passive torso/organ conductivities (S/m) + anisotropic bidomain heart conductivities
lf.add_torso_conductivity([10, 11, 12], g=0.6667)
lf.add_heart_conductivity([24, 25], il=0.5272, it=0.2076, el=1.0732, et=0.4227)
lf.build()
# Electrodes (V1–V6, RA, LA, RL, LL); one elliptic solve per lead
lf.load_electrodes("electrodes/lf_src.vtx")
lf.precompute_all()
# Vm: (T, N_heart) from the EP simulation -> dict of the 12 standard leads
ecg = lf.compute_12lead(Vm)
A complete heart → torso → 12-lead pipeline is provided in torchcor/demo/ecg_12lead.py.
🐳 Docker Support
For a GPU-enabled Docker setup to run TorchCor without installing dependencies on your host system, please see the docker/ folder.
📖 Citation
If you use TorchCor in academic work, please consider citing:
@article{zhou2026torchcor,
title={TorchCor: High-performance cardiac electrophysiology simulations with the finite element method on GPUs},
author={Zhou, Bei and Balmus, Maximilian and Corrado, Cesare and Cicci, Ludovica and Qian, Shuang and Niederer, Steven A},
journal={SoftwareX},
volume={33},
pages={102521},
year={2026},
publisher={Elsevier}
}
👩💻 Contributors
TorchCor is developed and maintained by Bei Zhou, Maximilian Balmus, Cesare Corrado, Shuang Qian, and Steven A. Niederer in the Cardiac Electro-Mechanics Research Group (CEMRG) at Imperial College London.
We welcome contributions from the community! Feel free to open issues or submit pull requests.
Metadata
Release files for torchcor 1.0.6
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| torchcor-1.0.6.tar.gz | 199.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| torchcor-1.0.6-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 420.5 kB
Release files / torchcor-1.0.6.tar.gz
| Download URL | torchcor-1.0.6.tar.gz |
|---|---|
| Size | 199.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
be78dd466cef0fba73147b1828338dc0e4b495f963c79d2629bdb8c30262f3c6
|
|
BLAKE2b-256 checksum How to use checksums |
faad8cce9104d445d2b24abeff65de18cb0e5377071a9692653b5f96437d6ea3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.6
|
Release files / torchcor-1.0.6-py3-none-any.whl
| Download URL | torchcor-1.0.6-py3-none-any.whl |
|---|---|
| Size | 221.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
aff74f4af07f64c22ee5ec1ae5df4da1b902415cb6e0c9afe64c89438502e240
|
|
BLAKE2b-256 checksum How to use checksums |
1af90eb254a57acc86c3c0ff5e43c0cdaedff17756bee1b15da7d13cd4559f96
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.6
|