Proton Analytical Models
The pyamtrack.proton_models module provides analytical models for calculating
depth-dependent proton dose and linear energy transfer (LET). These models are
useful for inspecting proton-beam depth profiles and for simplified analytical
studies in proton therapy.
Common Input Conventions
Both functions accept scalar values, Python lists, and NumPy arrays for their numeric arguments.
depth_cmis the depth in the material, measured in centimetres.energy_MeVis the initial proton kinetic energy in MeV.energy_spread_fractionis the relative standard deviation of the beam energy. For example,0.01represents a 1% energy spread.materialcan be a material ID or aMaterialobject. The default material is liquid water (material=1).
By default, vector inputs are evaluated elementwise. Scalar arguments are broadcast to the length of the first list or array argument, while all non-scalar arguments must have compatible lengths. A vectorized calculation returns a NumPy array.
Set cartesian_product=True to evaluate every combination of iterable
arguments. The returned array has dimensions corresponding to the iterable
arguments.
Bortfeld Dose
dose_bortfeld
The dose_bortfeld function calculates absorbed dose at a specified depth
using the analytical Bortfeld approximation of the proton Bragg curve.
import pyamtrack
pyamtrack.proton_models.dose_bortfeld(
depth_cm,
fluence_cm2,
energy_MeV,
energy_spread_fraction=0.01,
material=1,
eps=0.03,
cartesian_product=False,
)
Parameters
depth_cm(float,list, or NumPy array): Depth in the material[cm]. Values must be non-negative.fluence_cm2(float,list, or NumPy array): Proton fluence[1/cm²].energy_MeV(float,list, or NumPy array): Initial proton kinetic energy[MeV]. Values must be in the inclusive range[0.1, 10000.0].energy_spread_fraction(float,list, or NumPy array, optional): Relative kinetic energy spread (one standard deviation). Values must be in(0, 1). The wrapper converts this fraction internally to an energy standard deviation in MeV.material(int,Material, list, or NumPy array, optional): Material ID or material object. The default is1, liquid water.eps(float,list, or NumPy array, optional): Fraction of primary fluence assigned to the nuclear-interaction tail. Values must be in[0, 1). The default is0.03.cartesian_product(bool, optional): IfTrue, evaluate all combinations of iterable arguments. The default isFalse, which uses elementwise evaluation.
Return Value
The function returns dose in Gy:
- a Python
floatwhen all arguments are scalars; - a NumPy array when at least one argument is a list or NumPy array.
Example
The following example calculates a dose profile for a 150 MeV proton beam in liquid water:
import numpy as np
import pyamtrack
depth_cm = np.linspace(0.0, 17.0, 500)
dose_Gy = pyamtrack.proton_models.dose_bortfeld(
depth_cm=depth_cm,
fluence_cm2=1e8,
energy_MeV=150.0,
energy_spread_fraction=0.01,
material=pyamtrack.materials.water_liquid,
eps=0.03,
)
print(dose_Gy.shape)
print(f"Maximum dose: {dose_Gy.max():.3g} Gy")
Wilkens LET
let_wilkens
The let_wilkens function calculates depth-dependent proton LET using the
analytical model of Wilkens and Oelfke. It supports both dose-averaged and
track-averaged LET.
import pyamtrack
pyamtrack.proton_models.let_wilkens(
depth_cm,
energy_MeV,
energy_spread_fraction=0.01,
material=1,
averaging="dose",
cartesian_product=False,
)
Parameters
-
depth_cm(float,list, or NumPy array): Depth in the material[cm]. Values must be non-negative. -
energy_MeV(float,list, or NumPy array): Initial proton energy[MeV]. Values must be in the inclusive range[0.1, 10000.0]. -
energy_spread_fraction(float,list, or NumPy array, optional): Relative energy spread. Values must be in(0, 1). The default is0.01. -
material(int,Material, list, or NumPy array, optional): Material ID or material object. The default is1, liquid water. -
averaging(strorAveraging, optional): LET averaging convention:"dose"orpyamtrack.proton_models.Averaging.DOSEcalculates dose-averaged LET (LET_d)."track"orpyamtrack.proton_models.Averaging.TRACKcalculates track-averaged LET (LET_t).
String values are case-insensitive. The default is
"dose". -
cartesian_product(bool, optional): IfTrue, evaluate all combinations of iterable arguments. The default isFalse, which uses elementwise evaluation.
Dose-Averaged and Track-Averaged LET
Dose-averaged LET (LET_d) gives greater weight to tracks contributing more
dose and is commonly used in radiobiological analyses. Track-averaged LET
(LET_t) is the fluence-weighted mean of the stopping power.
For the same beam parameters, LET_d is generally greater than or equal to
LET_t, with the difference becoming more noticeable near the Bragg peak.
Return Value
The function returns LET in keV/µm:
- a Python
floatwhen all arguments are scalars; - a NumPy array when at least one argument is a list or NumPy array.
Example
The two averaging conventions can be compared for the same proton beam:
import numpy as np
import pyamtrack
depth_cm = np.linspace(0.0, 17.0, 500)
material = pyamtrack.materials.water_liquid
let_d_keV_um = pyamtrack.proton_models.let_wilkens(
depth_cm=depth_cm,
energy_MeV=150.0,
energy_spread_fraction=0.01,
material=material,
averaging="dose",
)
let_t_keV_um = pyamtrack.proton_models.let_wilkens(
depth_cm=depth_cm,
energy_MeV=150.0,
energy_spread_fraction=0.01,
material=material,
averaging=pyamtrack.proton_models.Averaging.TRACK,
)
index_10cm = np.abs(depth_cm - 10.0).argmin()
print(f"LET_d at 10 cm: {let_d_keV_um[index_10cm]:.3g} keV/µm")
print(f"LET_t at 10 cm: {let_t_keV_um[index_10cm]:.3g} keV/µm")
Cartesian-Product Evaluation
Elementwise evaluation is useful when corresponding values are already paired. For example, the first depth is evaluated with the first energy:
import pyamtrack
depths_cm = [2.0, 8.0]
energies_MeV = [100.0, 150.0]
let_elementwise = pyamtrack.proton_models.let_wilkens(
depths_cm,
energies_MeV,
averaging="track",
)
To evaluate every depth at every energy, use cartesian_product=True:
import pyamtrack
let_all_combinations = pyamtrack.proton_models.let_wilkens(
depths_cm,
energies_MeV,
averaging="track",
cartesian_product=True,
)
print(let_all_combinations.shape) # (2, 2)
The same option is available for dose_bortfeld, including its fluence,
energy-spread, material, and eps arguments.
Validation and Errors
The functions raise an error when:
- a depth is negative;
energy_MeVis outside[0.1, 10000.0];energy_spread_fractionis outside(0, 1);epsis outside[0, 1)fordose_bortfeld;- a material ID is invalid;
- vector inputs have incompatible lengths in elementwise mode;
- an invalid value is supplied for
averaging.
Material names such as "water_liquid" are not accepted directly as function
arguments. Use the corresponding material ID or a Material object instead:
import pyamtrack
from pyamtrack import materials
water = materials.water_liquid
dose = pyamtrack.proton_models.dose_bortfeld(
depth_cm=10.0,
fluence_cm2=1e8,
energy_MeV=150.0,
material=water,
)
References
- T. Bortfeld, “An analytical approximation of the Bragg curve for therapeutic proton beams,” Medical Physics, 24(12), 2024–2033 (1997).
- J. J. Wilkens and U. Oelfke, “Analytical linear energy transfer calculations for proton therapy,” Medical Physics, 30(5), 806–815 (2003).
See also: