Python package for generating arterial pressure and flow waveforms with a 55-segment systemic arterial transmission-line model from an aortic inflow waveform.
The package solves the arterial tree in the frequency domain, reconstructs time-domain waveforms, and returns pressure and flow at the outlet of all 55 arterial segments together with midpoint pressure for selected segments.
If you use this code, please cite the following references:
-
Sina Masoumi Shahrbabak, A. Mousavi, R. Mukkamala, and J.-O. Hahn, "In Silico Investigation of a Mathematical Model Relating the Ballistocardiogram to Aortic Blood Pressure," IEEE Transactions on Biomedical Engineering, vol. 73, no. 1, pp. 462-471, Jan. 2026, doi: 10.1109/TBME.2025.3584979.
-
W. He, H. Xiao, and X. Liu, "Numerical Simulation of Human Systemic Arterial Hemodynamics Based on a Transmission Line Model and Recursive Algorithm," Journal of Mechanics in Medicine and Biology, vol. 12, no. 1, 2012.
Use of this code in published work should acknowledge these references.
Given:
- an aortic inflow waveform
- a heart rate
- a stroke volume
- relative arterial stiffness
- relative peripheral resistance
- optional peripheral artery disease settings
the package computes:
- pressure waveforms at the outlet of all 55 arterial segments
- flow waveforms at the outlet of all 55 arterial segments
- pressure waveforms at the midpoint of selected segments
- pressure and flow at the root of the arterial tree
- the effective segment properties used in the solve
Install the required packages with:
pip install -r requirements.txtTypical dependencies are:
- numpy
- scipy
- pandas
-
tl55/api.py
User-facing entry point -
tl55/data.py
Nominal arterial geometry, topology, and terminal loads -
tl55/constants.py
Physical constants and nominal operating-point values -
tl55/input_flow.py
Aortic inflow loading, heart-rate warping, stroke-volume scaling, repetition, and FFT preparation -
tl55/impedance.py
Segment impedance formulas, recursive tree solve, and optional stenosis handling -
tl55/solver.py
Frequency-domain solve, waveform reconstruction, and beat trimming
from tl55 import generate_waveforms
result = generate_waveforms(
sv_rel=1.0,
hr_rel=1.0,
tpr_rel=1.0,
e_rel=1.0,
q_input_path="data/Q_inputwave2.mat",
)This returns a TL55Result object containing the simulated waveforms and metadata.
Path to a .mat file containing the nominal aortic inflow waveform.
The file must contain a variable named f with two columns:
- column 1: time in seconds
- column 2: flow in mL/s
Relative stroke volume scaling.
Nominal stroke volume is 60 mL.
Examples:
sv_rel=1.0gives 60 mLsv_rel=0.5gives 30 mLsv_rel=1.5gives 90 mL
Relative heart rate scaling.
Nominal heart rate is 75 bpm.
Examples:
hr_rel=1.0gives 75 bpmhr_rel=0.8gives 60 bpmhr_rel=1.2gives 90 bpm
Relative scaling applied to terminal resistances of the arterial tree.
This changes the downstream vascular load.
Relative scaling applied to Young's modulus of the arterial segments.
This changes arterial stiffness globally.
Stenosis severity fraction.
If a segment is diseased, its lesion radius is set to:
r_lesion = (1 - sl) * r_nominalExamples:
sl=0.0means no stenosissl=0.5means 50% radius reduction in the diseased region
Tuple of segment indices where stenosis should be applied.
Example:
pad_nodes=(33,)Fraction of the segment length occupied by the stenosis.
Examples:
l_pad=1.0means the whole segment is diseasedl_pad=0.5means half the segment length is diseased
from tl55 import generate_waveforms
result = generate_waveforms(
sv_rel=1.0,
hr_rel=1.0,
tpr_rel=1.0,
e_rel=1.0,
q_input_path="data/Q_inputwave2.mat",
)
print(result.pressure_outlet_mmHg.shape)
print(result.flow_outlet_mL_s.shape)
print(result.time_s[0], result.time_s[-1])Typical interpretation:
pressure_outlet_mmHg[i, :]is the outlet pressure waveform of segmenti+1flow_outlet_mL_s[i, :]is the outlet flow waveform of segmenti+1
from tl55 import generate_waveforms
result = generate_waveforms(
sv_rel=0.8,
hr_rel=1.2,
tpr_rel=1.1,
e_rel=0.9,
q_input_path="data/Q_inputwave2.mat",
)This example uses:
- lower stroke volume
- higher heart rate
- higher peripheral resistance
- lower arterial stiffness
from tl55 import generate_waveforms
result = generate_waveforms(
sv_rel=1.0,
hr_rel=1.0,
tpr_rel=1.0,
e_rel=1.0,
sl=0.5,
pad_nodes=(33,),
l_pad=1.0,
q_input_path="data/Q_inputwave2.mat",
)This applies a 50% radius reduction to segment 33 over the full segment length.
Pressure waveforms at the outlet of all 55 arterial segments, in mmHg.
Shape: (55, n_samples)
Pressure waveforms at the midpoint of selected segments, in mmHg.
Shape: (n_mid_segments, n_samples)
Flow waveforms at the outlet of all 55 arterial segments, in mL/s.
Shape: (55, n_samples)
Pressure waveform at the root of the arterial tree, in mmHg.
Shape: (n_samples,)
Flow waveform at the root of the arterial tree, in mL/s.
Shape: (n_samples,)
Time vector associated with the returned trimmed beat.
This axis preserves the sample times of the beat as extracted from the longer reconstructed periodic signal, so it does not usually start at 0 s. It is mainly useful for traceability and internal consistency with the trimming step. For display, it is often clearer to shift it to a beat-local axis with:
t_plot = result.time_s - result.time_s[0]Time vector for the full reconstructed periodic waveform before the final single-beat trimming step.
Frequency grid used in the frequency-domain solve.
Input impedance at the root of the arterial tree as a function of frequency.
Pressure transmission ratio from the root to the outlet of each segment.
Pressure transmission ratio from the root to the midpoint of each selected segment.
pandas.DataFrame listing the segment properties actually used in the solve.
This includes:
- segment index
- segment name
- length
- radius
- wall thickness
- Young's modulus
- terminal Windkessel parameters where applicable
- angle
- child segments
Dictionary summarizing the simulation settings used for that run.
import matplotlib.pyplot as plt
from tl55 import generate_waveforms
result = generate_waveforms(
sv_rel=1.0,
hr_rel=1.0,
tpr_rel=1.0,
e_rel=1.0,
q_input_path="data/Q_inputwave2.mat",
)
segment_idx = 0 # segment 1
t_plot = result.time_s - result.time_s[0]
plt.figure()
plt.plot(t_plot, result.pressure_outlet_mmHg[segment_idx, :])
plt.xlabel("Time within beat [s]")
plt.ylabel("Pressure [mmHg]")
plt.title("Outlet pressure, segment 1")
plt.show()print(result.effective_segments.head())This is useful for confirming:
- which segment properties were used
- which segments are terminal
- where stenosis was applied
- the effective geometry after stenosis
- The aortic inflow waveform is first warped to the requested heart rate.
- Stroke volume is imposed by scaling the area under the inflow waveform.
- Terminal branches use 3-element Windkessel loads.
- The arterial tree is solved recursively in the frequency domain from the terminal branches back to the root.
- Pressure and flow waveforms are reconstructed in the time domain by inverse FFT.
- A single representative beat is returned for convenience, while the full reconstructed signal is also available through
time_all_s.
- Choose a nominal aortic inflow waveform file
- Set
sv_rel,hr_rel,tpr_rel, ande_rel - Optionally add stenosis with
sl,pad_nodes, andl_pad - Run
generate_waveforms(...) - Extract and plot the segment waveforms of interest
- Inspect
effective_segmentsto confirm the simulated arterial properties
from tl55 import generate_waveforms
import matplotlib.pyplot as plt
result = generate_waveforms(
sv_rel=0.9,
hr_rel=1.1,
tpr_rel=1.0,
e_rel=1.0,
q_input_path="data/Q_inputwave2.mat",
)
plt.figure()
plt.plot(result.time_s, result.root_pressure_mmHg)
plt.xlabel("Time [s]")
plt.ylabel("Root pressure [mmHg]")
plt.title("Root pressure waveform")
plt.show()