cerr.imrtp.dosecalc package

Dose-calculation back-ends used by IMRTP: ray tracing, influence-matrix assembly, Monte-Carlo, QIB data handling and RTPLAN recalculation.

Submodules

cerr.imrtp.dosecalc.influence module

influence

QIB influence (beamlet) generation and dose assembly.

Python port of Matlab CERR:

IMRTP/generateQIBInfluence.m (per-structure, per-beam, per-PB dose) IMRTP/getIMDose.m (influence x beamlet weights -> dose3D) IMRTP/createIMBeamlet.m (sparse beamlet records)

Beamlets are stored on each beam as beam.beamlets – a flat list of Beamlet, one per (structure, pencil beam), holding the non-zero dose contributions (float32, uncompressed) and the voxel indices into the scan grid.

This file is part of pyCERR and is distributed under the terms of the Lesser GNU Public License (same terms as CERR).

cerr.imrtp.dosecalc.influence.generateQIBInfluence(im, planC, statusCallback=None)[source]

Generate QIB beamlet influence for every goal structure and beam.

Port of generateQIBInfluence.m. Fills beam.beamlets on every beam of im and returns im.

cerr.imrtp.dosecalc.influence.getIMDose(im, weightsV: Sequence[float] | None, structNumsV, planC) ndarray[source]

Assemble a 3-D dose distribution from beamlets and PB weights.

Port of getIMDose.m.

Parameters:
  • im – IMRTProblem with computed beamlets.

  • weightsV – one weight per pencil beam (flat, ordered beam-by-beam in PB order), or None for unit weights (open fields).

  • structNumsV – structure indices over which dose is assembled.

  • planC – plan container.

Returns:

np.ndarray of dose on the scan grid (rows x cols x slices).

Note

Where Matlab CERR fills sub-sampled structures with 3-D linear interpolation, this port uses nearest-sampled-point filling inside the structure mask.

cerr.imrtp.dosecalc.montecarlo module

montecarlo

Placeholders for the Monte-Carlo dose engines of CERR’s IMRTP/recompDose toolbox (DPM and VMC++).

The Matlab originals (https://github.com/cerr/CERR/tree/master/IMRTP/recompDose) drive compiled MC executables and a measurement-based photon source model:

beam2MCdose.m / beam2MCdose_with_QIB.m

Build the fluence (leaf sequences, MLC transmission leak, wedges, tongue-and-groove, horn/softening corrections, source model) for one beam of an RTPLAN and run the selected solver.

calcDoseByBeamMeterset.m

Read per-beam MC dose from disk and combine using the plan metersets (DPM path of call_doseRecal.m).

Porting these requires the DPM / VMC++ binaries, their phase-space / spectrum inputs (e.g. 6MV10x10MDA.spectrum) and the accelerator-head source model, none of which ship with pyCERR. The functions below define the intended Python interfaces and raise NotImplementedError with pointers to the Matlab references; the parameter set mirrors call_doseRecal.m so existing workflows translate directly.

This file is part of pyCERR and is distributed under the terms of the Lesser GNU Public License (same terms as CERR).

cerr.imrtp.dosecalc.montecarlo.MC_SOLVER_DPM = 1

solver codes used by call_doseRecal.m

class cerr.imrtp.dosecalc.montecarlo.MCRecompParams(leak: float = 0.032, spectrum_File: str = '6MV10x10MDA.spectrum', nhist: float = 100000.0, batch: int = 101, OutputError: int = 0, PBMaxWidth: float = 10.0, gradsense: float = 25.0, MCsolver: int = 1, saveIM: int = 0, sourceModel: int = 0, doseToWater: int = 0, fillWater: int = 0, useWedge: int = 0, inputPB: int = 0, inputIM: int = 1, Softening: int = 1, UseFlatFilter: int = 1, MLC: int = 0, TongueGroove: int = 0, interactiveMode: int = 0, LS_flag: int = 0, K: int = 1, scatterThreshold: float = 11.2)[source]

Bases: object

Inputs of beam2MCdose.m (defaults from call_doseRecal.m).

leak: float = 0.032
spectrum_File: str = '6MV10x10MDA.spectrum'
nhist: float = 100000.0
batch: int = 101
OutputError: int = 0
PBMaxWidth: float = 10.0
gradsense: float = 25.0
MCsolver: int = 1
saveIM: int = 0
sourceModel: int = 0
doseToWater: int = 0
fillWater: int = 0
useWedge: int = 0
inputPB: int = 0
inputIM: int = 1
Softening: int = 1
UseFlatFilter: int = 1
MLC: int = 0
TongueGroove: int = 0
interactiveMode: int = 0
LS_flag: int = 0
K: int = 1
scatterThreshold: float = 11.2
cerr.imrtp.dosecalc.montecarlo.beam2MCdose(im, planC, whichBeam: int, params: MCRecompParams = None, statusCallback=None)[source]

PLACEHOLDER – Monte-Carlo dose for one beam (DPM or VMC++).

Intended port of beam2MCdose.m / beam2MCdose_with_QIB.m from the CERR recompDose toolbox: build the beam fluence map (including MLC leaf sequences, transmission, wedge and source model) and run the selected Monte-Carlo solver, returning the per-beam IM dosimetry and beamlet weights.

cerr.imrtp.dosecalc.montecarlo.calcDoseByBeamMeterset(planC, nhist: float, batch: int)[source]

PLACEHOLDER – combine per-beam DPM doses using plan metersets.

Intended port of calcDoseByBeamMeterset.m (DPM path of call_doseRecal.m): reads the per-beam MC dose files written by the DPM solver and sums them weighted by the beam metersets.

cerr.imrtp.dosecalc.montecarlo.dpmEngine(im, planC, statusCallback=None)[source]

PLACEHOLDER engine entry (‘DPM’) for the cerr.imrtp registry.

cerr.imrtp.dosecalc.montecarlo.vmcEngine(im, planC, statusCallback=None)[source]

PLACEHOLDER engine entry (‘VMC++’) for the cerr.imrtp registry.

cerr.imrtp.dosecalc.qib module

qib

QIB (Quadrant Infinite Beam) photon pencil-beam dose calculation.

Python port of Matlab CERR:

IMRTP/getQIBDose.m (the kernel evaluation, all DoseTerm modes) IMRTP/GetPBConsts.m (‘nearest’ branch) IMRTP/applyIMRTCompression.m (scatter compression of the influence) IMRTP/RTOGVectors2Gantry.m IMRTP/gantry2RTOGVectors.m

Algorithm (Ahnesjo et al., Med. Phys. 19, 263-273 (1992)): dose around a pencil beam is A(z)exp(-a(z)r)/r + B(z)exp(-b(z)r)/r; the dose from a finite-width beamlet is obtained by differencing precomputed quadrant infinite integrals of the kernel. An optional Gaussian smear of the incident fluence models the finite source size (sigma_100 = projected source sigma at 100 cm).

Outputs are in relative energy deposited per (g/cm^3) from a point-monodirectional beam (same units as Matlab CERR).

This file is part of pyCERR and is distributed under the terms of the Lesser GNU Public License (same terms as CERR).

cerr.imrtp.dosecalc.qib.getPBConsts(depthsV: ndarray, energy: float, qib: QIBDataS)[source]

Nearest-neighbour lookup of the Ahnesjo A, a, B, b constants vs depth.

Port of GetPBConsts.m with flag 'nearest' (the branch used by generateQIBInfluence.m). Depths in g/cm^2 (radiological), clipped at the 50 cm dosimetry limit.

cerr.imrtp.dosecalc.qib.getQIBDose(posU: ndarray, radDepthV: ndarray, widthUX: ndarray, widthUY: ndarray, qib: QIBDataS, A_zV, a_zV, B_zV, b_zV, energy: float, how2Compute: str, gaussSigma_100: float, distV: ndarray) ndarray[source]

Fast pencil-beam dose calculation for photon beams.

Port of getQIBDose.m.

Parameters:
  • posU – (N, 2) beamlet-frame coordinates (Xb, Yb) of the dose points, cm.

  • radDepthV – radiological depths (g/cm^2) – unused by the lookup itself but kept for signature parity.

  • widthUX/Y – beamlet widths at each point’s source distance, cm.

  • qib – kernel data from loadPBData().

  • A_zV..b_zV – depth constants from getPBConsts().

  • energy – nominal beam energy (6 / 18 MV).

  • how2Compute – one of 'primary', 'scatter', 'nogauss+scatter' (a.k.a. 'primary+scatter'), 'GaussPrimary', 'GaussPrimary+scatter'.

  • gaussSigma_100 – projected source sigma at 100 cm (cm).

  • distV – distance from source to each dose point, cm (needed for the Gaussian smear).

Returns:

Dose vector (relative energy deposited per (g/cm^3)).

cerr.imrtp.dosecalc.qib.applyIMRTCompression(params, doseV: ndarray) ndarray[source]

Selectively eliminate low-dose scatter components.

Port of applyIMRTCompression.m; params is an IMParams (uses ScatterMethod, Scatter.Threshold and Scatter.RandomStep).

cerr.imrtp.dosecalc.qib.rtogVectors2Gantry(rtogVectorsM: ndarray, gantryAngle: float) ndarray[source]

Rotate RTOG-frame vectors into the gantry frame.

Port of RTOGVectors2Gantry.m. Gantry frame: Xb in-plane, Yb out-of-plane (along -z), Zb along the beam axis.

cerr.imrtp.dosecalc.qib.gantry2RTOGVectors(gantryVectorsM: ndarray, gantryAngle: float, couchAngle: float = 0.0) ndarray[source]

Rotate gantry-frame vectors back into the RTOG frame.

Port of gantry2RTOGVectors.m (including couch rotation).

cerr.imrtp.dosecalc.qib_data module

qib_data

Loader for the QIB (Quadrant Infinite Beam) pencil-beam kernel data. Python port of Matlab CERR IMRTP/loadPBData.m.

The QIB algorithm is based on an analytical fit of Monte-Carlo generated pencil-beam data due to Ahnesjo et al. [Med. Phys. 19, 263-273 (1992)]:

D(r, z) = A(z) exp(-a(z) r)/r + B(z) exp(-b(z) r)/r

with fast table look-up of quadrant infinite integrals (integrals of the exponential / Gaussian kernel over an infinite 2-D quadrant).

Data files (in dosecalc/QIBData/):

aahn_6b.dat / aahn_18b.dat depth tables of A, a, B, b for 6 / 18 MV qib_tables.npz QIB and QIBGauss quadrant-integral

lookup tables (1593 x 1593, originally QIB_lin_0pt125.mat / QIBGauss_lin_0pt125.mat)

If qib_tables.npz is missing, the original .mat files are downloaded from the CERR GitHub repository and converted (requires scipy).

This file is part of pyCERR and is distributed under the terms of the Lesser GNU Public License (same terms as CERR).

class cerr.imrtp.dosecalc.qib_data.QIBDataS(aahn6b: ndarray = None, aahn18b: ndarray = None, QIB: ndarray = None, QIBGauss: ndarray = None, deltaQBM: float = 0.0125, QBMidIndexX: int = 792, QBMidIndexY: int = 792, k1: float = 1.1284, k2: float = 0.476, k3: float = 0.0354, k4: float = 0.715)[source]

Bases: object

Container mirroring the Matlab QIBDataS structure.

aahn6b: ndarray = None
aahn18b: ndarray = None
QIB: ndarray = None
QIBGauss: ndarray = None
deltaQBM: float = 0.0125
QBMidIndexX: int = 792
QBMidIndexY: int = 792
k1: float = 1.1284
k2: float = 0.476
k3: float = 0.0354
k4: float = 0.715
cerr.imrtp.dosecalc.qib_data.loadPBData() QIBDataS[source]

Load (and cache) the QIB pencil-beam kernel data.

Port of loadPBData.m.

cerr.imrtp.dosecalc.raytrace module

raytrace

Pencil-beam geometry and CT ray tracing for IMRTP dose calculation.

Python port of Matlab CERR:

IMRTP/IMRTP.m (getTargetSurfacePoints / getPBList helpers) IMRTP/getPBRays.m (which PBs are needed to cover the target) IMRTP/getPBRayData.m (radiological-depth trace through the CT)

Coordinates are pyCERR/CERR “virtual” coordinates as returned by planC.scan[n].getScanXYZVals() (x ascending, y descending, z ascending), all in cm. The scan array is indexed [row, col, slice] with row <-> y and col <-> x.

This file is part of pyCERR and is distributed under the terms of the Lesser GNU Public License (same terms as CERR).

class cerr.imrtp.dosecalc.raytrace.ScanGrid(planC, scanNum: int)[source]

Bases: object

Cached geometry of a pyCERR scan, plus trilinear density sampling.

fracIndex(x, y, z)[source]

Fractional [row, col, slice] indices of (x, y, z) points.

sampleCTNums(x, y, z)[source]

Trilinear interpolation of CT numbers at (x, y, z), 0 outside.

cerr.imrtp.dosecalc.raytrace.getTargetSurfacePoints(im, planC)[source]

Surface points of all target structures, expanded by PBMargin + 0.5 cm.

Returns (xS, yS, zS) coordinate vectors (cm) of the surface voxels of the (margin-expanded) union of the target structures, mirroring getSurface(structTargetV, PBMarginV + 0.5, ...) in IMRTP.m.

cerr.imrtp.dosecalc.raytrace.getPBRays(xS, yS, zS, beam)[source]

Determine the pencil beams required to cover the target.

Port of getPBRays.m: target surface points are projected (through the source) onto the gantry frame at the isocenter distance, binned on the beamlet grid, row gaps are filled, and unit RTOG-frame direction vectors are generated for each pencil beam.

Returns:

(RTOGPBVectorsM, RTOGPBVectorsM_MC, PBMaskM, rowPBV, colPBV,

xPBPosV, yPBPosV)

class cerr.imrtp.dosecalc.raytrace.CTTrace(distSamplePts: ndarray = None, densityRay: ndarray = None, cumDensityRay: ndarray = None)[source]

Bases: object

Cumulative radiological depth along one pencil-beam ray.

distSamplePts: ndarray = None
densityRay: ndarray = None
cumDensityRay: ndarray = None
cerr.imrtp.dosecalc.raytrace.getPBRayData(xS, yS, zS, beam, numSamplePts: int, grid: ScanGrid)[source]

Pencil-beam geometry plus cumulative-density CT traces for one beam.

Port of getPBRayData.m. Returns (ctTraceList, RTOGPBVectorsM, RTOGPBVectorsM_MC, pbMask, rowPBV, colPBV, xPBPosV, yPBPosV).

cerr.imrtp.dosecalc.raytrace.setBeamRayData(im, planC, statusCallback=None)[source]

Populate ray-trace fields on every beam of im.

Port of the getPBList helper in IMRTP.m: computes the PB direction matrix and CT traces, and sets the absolute source position (xRel/yRel/zRel + isocenter) on each beam.

cerr.imrtp.dosecalc.recalc module

recalc

Dose recomputation driver – Python port of CERR’s IMRTP/recompDose/call_doseRecal.m.

Workflow (as in the Matlab script):

  1. (optional CT downsample – not yet ported, see note below)

  2. choose the solver: QIB (implemented) or DPM / VMC++ (placeholders, see cerr.imrtp.dosecalc.montecarlo)

  3. compute the beamlet influence for every beam over the requested structures / sample rates

  4. store the IM dosimetry on planC.im and assemble the 3-D dose

  5. optionally scale the result to a clinical dose using a DVH metric of a target structure (mean dose or D98, as in call_doseRecal.m)

  6. import the dose into planC.dose

Example:

from cerr.imrtp.dosecalc import recalcDose
doseNum, dose3D, im = recalcDose(
    planC, im,
    structNumsV=[5, 6, 7], sampleRateV=[4, 2, 2],
    imrtpName='QIB reCalc',
    ptvStructNum=13, clinicDoseNum=0, scaleMetric='meanDose')

This file is part of pyCERR and is distributed under the terms of the Lesser GNU Public License (same terms as CERR).

cerr.imrtp.dosecalc.recalc.structDoseMetric(planC, structNum: int, doseNum: int, metric: str = 'meanDose', x: float = 98.0, binWidth: float = 0.01) float[source]

DVH metric of a structure for a given dose (Dx / meanDose helpers used by call_doseRecal.m for clinical-dose scaling).

cerr.imrtp.dosecalc.recalc.recalcDose(planC, im: IMRTProblem, structNumsV: Sequence[int] | None = None, sampleRateV: Sequence[int] | None = None, algorithm: str = 'QIB', imrtpName: str = 'QIB reCalc', beamletWeightsV: Sequence[float] | None = None, scatterThreshold: float | None = None, ptvStructNum: int | None = None, clinicDoseNum: int | None = None, scaleMetric: str = 'meanDose', dxPercent: float = 98.0, saveIM: bool = True, statusCallback=None)[source]

Recompute dose for an IMRT problem (port of call_doseRecal.m).

Parameters:
  • planC – pyCERR plan container.

  • im – IMRTProblem describing beams, goals and params (build one with the IMRTP GUI or cerr.imrtp.initIMRTProblem).

  • structNumsV – structure indices where dose is computed. If None, the structures of im.goals are used.

  • sampleRateV – per-structure in-plane sample rates (powers of 2; e.g. skin 8, targets/critical structures 2). Same length as structNumsV.

  • algorithm – ‘QIB’ (implemented), ‘DPM’ or ‘VMC++’ (placeholders; see dosecalc.montecarlo).

  • imrtpName – name of the IM dosimetry stored on planC.im.

  • beamletWeightsV – one weight per pencil beam (flat, beam-by-beam) or None for open (unit-weight) fields.

  • scatterThreshold – optional override of im.params.Scatter.Threshold (QIB default in call_doseRecal.m: 0.1).

  • ptvStructNum – target structure used to scale to the clinical dose (skipped when clinicDoseNum is None).

  • clinicDoseNum – index of the clinical dose in planC.dose to scale against.

  • scaleMetric – ‘meanDose’ (call_doseRecal.m default) or ‘Dx’.

  • dxPercent – x for the Dx metric (default 98).

  • saveIM – store the IM dosimetry on planC.im.

  • statusCallback – optional fn(msg, frac).

Returns:

index of the new dose in planC.dose, the dose array, and the updated IMRTProblem.

Return type:

(doseNum, dose3D, im)

Note

The Matlab script optionally downsamples the CT by a factor of 2 before computing (getplanCDownSample); CT downsampling is not yet available in pyCERR, so compute time is controlled through sampleRateV and the beamlet size instead.

cerr.imrtp.dosecalc.rtplan module

rtplan

Build an IMRT problem from the beams of a DICOM RTPLAN stored on planC.beams – the role of the beam-geometry section of CERR’s recompDose/beam2MCdose.m (gantry / couch / collimator angles, isocenter, SAD and nominal energy per beam).

Typical use (the call_doseRecal.m workflow on a DICOM dataset):

from cerr import plan_container as pc
from cerr.imrtp.dosecalc import imFromRTPlan, recalcDose

planC = pc.loadDcmDir(r'C:/data/myPlan')      # CT + RTSTRUCT + RTPLAN
im = imFromRTPlan(planC, targetStructNum=ptvNum)
doseNum, dose3D, im = recalcDose(planC, im, structNumsV=[...],
                                 sampleRateV=[...])
What is taken from the RTPLAN (as in beam2MCdose.m):
  • gantry angle, couch (PatientSupportAngle) and collimator angles of the first control point of each TREATMENT beam,

  • isocenter position (converted from DICOM mm to pyCERR virtual cm via scan.cerrToDcmTransM),

  • source-axis distance (mm -> cm),

  • nominal beam energy (mapped to the nearest QIB kernel energy, 6 or 18 MV, with a warning when not exact).

What is NOT taken: MLC leaf sequences / fluence maps, wedges and per-beam metersets. The QIB recomputation covers the target (+ PBMargin) with open beamlets; use beamletWeightsV for IMRT weights and the clinical-dose scaling of recalcDose() to set absolute dose.

This file is part of pyCERR and is distributed under the terms of the Lesser GNU Public License (same terms as CERR).

cerr.imrtp.dosecalc.rtplan.QIB_ENERGIES = (6.0, 18.0)

energies for which QIB kernel tables exist

cerr.imrtp.dosecalc.rtplan.dcmIsocenterToCerr(isoDcm_mm, planC, scanNum: int)[source]

DICOM patient coordinates (mm) -> pyCERR virtual coordinates (cm).

Uses the scan’s cerrToDcmTransM (pyCERR xyz cm -> DICOM mm); falls back to the simple HFS relation used by beam2MCdose.m (x/10, -y/10, -z/10) if the matrix is unavailable.

cerr.imrtp.dosecalc.rtplan.beamsFromRTPlan(planC, beamsNum: int = 0, scanNum: int | None = None, energyMap: bool = True) List[IMBeam][source]

Convert the TREATMENT beams of planC.beams[beamsNum] to IMBeams.

Mirrors the geometry import of beam2MCdose.m: per beam the first control point supplies gantry / couch / collimator angles, nominal energy and isocenter; SAD comes from the beam sequence. Source positions are derived as xRel = SAD sin(g), yRel = SAD cos(g), zRel = 0, rotated by the couch angle.

Parameters:
  • planC – plan container with an RTPLAN loaded.

  • beamsNum – index into planC.beams.

  • scanNum – scan used for the DICOM->CERR isocenter transform (default 0).

  • energyMap – map nominal energies to the nearest QIB energy (6 / 18 MV). Set False to keep the RTPLAN value.

Returns:

list of IMBeam.

cerr.imrtp.dosecalc.rtplan.imFromRTPlan(planC, beamsNum: int = 0, targetStructNum: int | None = None, structNumsV: Sequence[int] | None = None, sampleRateV: Sequence[int] | None = None, scanNum: int = 0, PBMargin: float = 0.5, beamletDelta: float = 1.0, name: str | None = None) IMRTProblem[source]

Build an IMRTProblem from the RTPLAN of planC.

Parameters:
  • planC – plan container (CT + RTSTRUCT + RTPLAN loaded).

  • beamsNum – index into planC.beams.

  • targetStructNumplanC.structure index of the target (PTV) that the beamlets must cover.

  • structNumsV – additional structures where dose is computed (default: target only); planC.structure indices.

  • sampleRateV – per-structure sample rates for structNumsV (default 2 each).

  • scanNum – associated scan.

  • PBMargin – beamlet margin around the target, cm (beam2MCdose.m uses 0.5).

  • beamletDelta – beamlet width at the isocenter, cm.

  • name – IM name (default: the RTPLAN label).

Returns:

IMRTProblem ready for recalcDose() / runIMRTP().

Module contents

cerr.imrtp.dosecalc

Dose-calculation engines for pyCERR’s IMRTP module.

Implemented:
QIB Quadrant Infinite Beam photon pencil-beam algorithm (Ahnesjo

analytical kernel with quadrant-integral lookup tables), ported from Matlab CERR IMRTP/getQIBDose.m and friends.

Placeholders (interfaces defined, ports pending – see https://github.com/cerr/CERR/tree/master/IMRTP/recompDose):

DPM Dose Planning Method Monte Carlo VMC++ Voxel Monte Carlo

Importing this package registers ‘QIB’ (and the ‘DPM’ / ‘VMC++’ placeholders) with the cerr.imrtp engine registry, so they appear in the IMRTP GUI’s algorithm drop-down and work with cerr.imrtp.runIMRTP.

High-level entry point (port of recompDose/call_doseRecal.m):

from cerr.imrtp.dosecalc import recalcDose
doseNum, dose3D, im = recalcDose(planC, im, structNumsV=[1, 2],
                                 sampleRateV=[2, 2])

This file is part of pyCERR and is distributed under the terms of the Lesser GNU Public License (same terms as CERR).

class cerr.imrtp.dosecalc.QIBDataS(aahn6b: ndarray = None, aahn18b: ndarray = None, QIB: ndarray = None, QIBGauss: ndarray = None, deltaQBM: float = 0.0125, QBMidIndexX: int = 792, QBMidIndexY: int = 792, k1: float = 1.1284, k2: float = 0.476, k3: float = 0.0354, k4: float = 0.715)[source]

Bases: object

Container mirroring the Matlab QIBDataS structure.

aahn6b: ndarray = None
aahn18b: ndarray = None
QIB: ndarray = None
QIBGauss: ndarray = None
deltaQBM: float = 0.0125
QBMidIndexX: int = 792
QBMidIndexY: int = 792
k1: float = 1.1284
k2: float = 0.476
k3: float = 0.0354
k4: float = 0.715
cerr.imrtp.dosecalc.loadPBData() QIBDataS[source]

Load (and cache) the QIB pencil-beam kernel data.

Port of loadPBData.m.

cerr.imrtp.dosecalc.getQIBDose(posU: ndarray, radDepthV: ndarray, widthUX: ndarray, widthUY: ndarray, qib: QIBDataS, A_zV, a_zV, B_zV, b_zV, energy: float, how2Compute: str, gaussSigma_100: float, distV: ndarray) ndarray[source]

Fast pencil-beam dose calculation for photon beams.

Port of getQIBDose.m.

Parameters:
  • posU – (N, 2) beamlet-frame coordinates (Xb, Yb) of the dose points, cm.

  • radDepthV – radiological depths (g/cm^2) – unused by the lookup itself but kept for signature parity.

  • widthUX/Y – beamlet widths at each point’s source distance, cm.

  • qib – kernel data from loadPBData().

  • A_zV..b_zV – depth constants from getPBConsts().

  • energy – nominal beam energy (6 / 18 MV).

  • how2Compute – one of 'primary', 'scatter', 'nogauss+scatter' (a.k.a. 'primary+scatter'), 'GaussPrimary', 'GaussPrimary+scatter'.

  • gaussSigma_100 – projected source sigma at 100 cm (cm).

  • distV – distance from source to each dose point, cm (needed for the Gaussian smear).

Returns:

Dose vector (relative energy deposited per (g/cm^3)).

cerr.imrtp.dosecalc.getPBConsts(depthsV: ndarray, energy: float, qib: QIBDataS)[source]

Nearest-neighbour lookup of the Ahnesjo A, a, B, b constants vs depth.

Port of GetPBConsts.m with flag 'nearest' (the branch used by generateQIBInfluence.m). Depths in g/cm^2 (radiological), clipped at the 50 cm dosimetry limit.

cerr.imrtp.dosecalc.applyIMRTCompression(params, doseV: ndarray) ndarray[source]

Selectively eliminate low-dose scatter components.

Port of applyIMRTCompression.m; params is an IMParams (uses ScatterMethod, Scatter.Threshold and Scatter.RandomStep).

cerr.imrtp.dosecalc.rtogVectors2Gantry(rtogVectorsM: ndarray, gantryAngle: float) ndarray[source]

Rotate RTOG-frame vectors into the gantry frame.

Port of RTOGVectors2Gantry.m. Gantry frame: Xb in-plane, Yb out-of-plane (along -z), Zb along the beam axis.

cerr.imrtp.dosecalc.gantry2RTOGVectors(gantryVectorsM: ndarray, gantryAngle: float, couchAngle: float = 0.0) ndarray[source]

Rotate gantry-frame vectors back into the RTOG frame.

Port of gantry2RTOGVectors.m (including couch rotation).

class cerr.imrtp.dosecalc.ScanGrid(planC, scanNum: int)[source]

Bases: object

Cached geometry of a pyCERR scan, plus trilinear density sampling.

fracIndex(x, y, z)[source]

Fractional [row, col, slice] indices of (x, y, z) points.

sampleCTNums(x, y, z)[source]

Trilinear interpolation of CT numbers at (x, y, z), 0 outside.

cerr.imrtp.dosecalc.getTargetSurfacePoints(im, planC)[source]

Surface points of all target structures, expanded by PBMargin + 0.5 cm.

Returns (xS, yS, zS) coordinate vectors (cm) of the surface voxels of the (margin-expanded) union of the target structures, mirroring getSurface(structTargetV, PBMarginV + 0.5, ...) in IMRTP.m.

cerr.imrtp.dosecalc.getPBRays(xS, yS, zS, beam)[source]

Determine the pencil beams required to cover the target.

Port of getPBRays.m: target surface points are projected (through the source) onto the gantry frame at the isocenter distance, binned on the beamlet grid, row gaps are filled, and unit RTOG-frame direction vectors are generated for each pencil beam.

Returns:

(RTOGPBVectorsM, RTOGPBVectorsM_MC, PBMaskM, rowPBV, colPBV,

xPBPosV, yPBPosV)

cerr.imrtp.dosecalc.getPBRayData(xS, yS, zS, beam, numSamplePts: int, grid: ScanGrid)[source]

Pencil-beam geometry plus cumulative-density CT traces for one beam.

Port of getPBRayData.m. Returns (ctTraceList, RTOGPBVectorsM, RTOGPBVectorsM_MC, pbMask, rowPBV, colPBV, xPBPosV, yPBPosV).

cerr.imrtp.dosecalc.setBeamRayData(im, planC, statusCallback=None)[source]

Populate ray-trace fields on every beam of im.

Port of the getPBList helper in IMRTP.m: computes the PB direction matrix and CT traces, and sets the absolute source position (xRel/yRel/zRel + isocenter) on each beam.

class cerr.imrtp.dosecalc.CTTrace(distSamplePts: ndarray = None, densityRay: ndarray = None, cumDensityRay: ndarray = None)[source]

Bases: object

Cumulative radiological depth along one pencil-beam ray.

distSamplePts: ndarray = None
densityRay: ndarray = None
cumDensityRay: ndarray = None
cerr.imrtp.dosecalc.generateQIBInfluence(im, planC, statusCallback=None)[source]

Generate QIB beamlet influence for every goal structure and beam.

Port of generateQIBInfluence.m. Fills beam.beamlets on every beam of im and returns im.

cerr.imrtp.dosecalc.getIMDose(im, weightsV: Sequence[float] | None, structNumsV, planC) ndarray[source]

Assemble a 3-D dose distribution from beamlets and PB weights.

Port of getIMDose.m.

Parameters:
  • im – IMRTProblem with computed beamlets.

  • weightsV – one weight per pencil beam (flat, ordered beam-by-beam in PB order), or None for unit weights (open fields).

  • structNumsV – structure indices over which dose is assembled.

  • planC – plan container.

Returns:

np.ndarray of dose on the scan grid (rows x cols x slices).

Note

Where Matlab CERR fills sub-sampled structures with 3-D linear interpolation, this port uses nearest-sampled-point filling inside the structure mask.

class cerr.imrtp.dosecalc.MCRecompParams(leak: float = 0.032, spectrum_File: str = '6MV10x10MDA.spectrum', nhist: float = 100000.0, batch: int = 101, OutputError: int = 0, PBMaxWidth: float = 10.0, gradsense: float = 25.0, MCsolver: int = 1, saveIM: int = 0, sourceModel: int = 0, doseToWater: int = 0, fillWater: int = 0, useWedge: int = 0, inputPB: int = 0, inputIM: int = 1, Softening: int = 1, UseFlatFilter: int = 1, MLC: int = 0, TongueGroove: int = 0, interactiveMode: int = 0, LS_flag: int = 0, K: int = 1, scatterThreshold: float = 11.2)[source]

Bases: object

Inputs of beam2MCdose.m (defaults from call_doseRecal.m).

leak: float = 0.032
spectrum_File: str = '6MV10x10MDA.spectrum'
nhist: float = 100000.0
batch: int = 101
OutputError: int = 0
PBMaxWidth: float = 10.0
gradsense: float = 25.0
MCsolver: int = 1
saveIM: int = 0
sourceModel: int = 0
doseToWater: int = 0
fillWater: int = 0
useWedge: int = 0
inputPB: int = 0
inputIM: int = 1
Softening: int = 1
UseFlatFilter: int = 1
MLC: int = 0
TongueGroove: int = 0
interactiveMode: int = 0
LS_flag: int = 0
K: int = 1
scatterThreshold: float = 11.2
cerr.imrtp.dosecalc.beam2MCdose(im, planC, whichBeam: int, params: MCRecompParams = None, statusCallback=None)[source]

PLACEHOLDER – Monte-Carlo dose for one beam (DPM or VMC++).

Intended port of beam2MCdose.m / beam2MCdose_with_QIB.m from the CERR recompDose toolbox: build the beam fluence map (including MLC leaf sequences, transmission, wedge and source model) and run the selected Monte-Carlo solver, returning the per-beam IM dosimetry and beamlet weights.

cerr.imrtp.dosecalc.calcDoseByBeamMeterset(planC, nhist: float, batch: int)[source]

PLACEHOLDER – combine per-beam DPM doses using plan metersets.

Intended port of calcDoseByBeamMeterset.m (DPM path of call_doseRecal.m): reads the per-beam MC dose files written by the DPM solver and sums them weighted by the beam metersets.

cerr.imrtp.dosecalc.recalcDose(planC, im: IMRTProblem, structNumsV: Sequence[int] | None = None, sampleRateV: Sequence[int] | None = None, algorithm: str = 'QIB', imrtpName: str = 'QIB reCalc', beamletWeightsV: Sequence[float] | None = None, scatterThreshold: float | None = None, ptvStructNum: int | None = None, clinicDoseNum: int | None = None, scaleMetric: str = 'meanDose', dxPercent: float = 98.0, saveIM: bool = True, statusCallback=None)[source]

Recompute dose for an IMRT problem (port of call_doseRecal.m).

Parameters:
  • planC – pyCERR plan container.

  • im – IMRTProblem describing beams, goals and params (build one with the IMRTP GUI or cerr.imrtp.initIMRTProblem).

  • structNumsV – structure indices where dose is computed. If None, the structures of im.goals are used.

  • sampleRateV – per-structure in-plane sample rates (powers of 2; e.g. skin 8, targets/critical structures 2). Same length as structNumsV.

  • algorithm – ‘QIB’ (implemented), ‘DPM’ or ‘VMC++’ (placeholders; see dosecalc.montecarlo).

  • imrtpName – name of the IM dosimetry stored on planC.im.

  • beamletWeightsV – one weight per pencil beam (flat, beam-by-beam) or None for open (unit-weight) fields.

  • scatterThreshold – optional override of im.params.Scatter.Threshold (QIB default in call_doseRecal.m: 0.1).

  • ptvStructNum – target structure used to scale to the clinical dose (skipped when clinicDoseNum is None).

  • clinicDoseNum – index of the clinical dose in planC.dose to scale against.

  • scaleMetric – ‘meanDose’ (call_doseRecal.m default) or ‘Dx’.

  • dxPercent – x for the Dx metric (default 98).

  • saveIM – store the IM dosimetry on planC.im.

  • statusCallback – optional fn(msg, frac).

Returns:

index of the new dose in planC.dose, the dose array, and the updated IMRTProblem.

Return type:

(doseNum, dose3D, im)

Note

The Matlab script optionally downsamples the CT by a factor of 2 before computing (getplanCDownSample); CT downsampling is not yet available in pyCERR, so compute time is controlled through sampleRateV and the beamlet size instead.

cerr.imrtp.dosecalc.structDoseMetric(planC, structNum: int, doseNum: int, metric: str = 'meanDose', x: float = 98.0, binWidth: float = 0.01) float[source]

DVH metric of a structure for a given dose (Dx / meanDose helpers used by call_doseRecal.m for clinical-dose scaling).

cerr.imrtp.dosecalc.qibEngine(im, planC, statusCallback=None)[source]

QIB dose engine for the cerr.imrtp registry.

Computes the beamlet influence over all goal structures and returns the unit-weight (open-field) dose on the scan grid.

cerr.imrtp.dosecalc.imFromRTPlan(planC, beamsNum: int = 0, targetStructNum: int | None = None, structNumsV: Sequence[int] | None = None, sampleRateV: Sequence[int] | None = None, scanNum: int = 0, PBMargin: float = 0.5, beamletDelta: float = 1.0, name: str | None = None) IMRTProblem[source]

Build an IMRTProblem from the RTPLAN of planC.

Parameters:
  • planC – plan container (CT + RTSTRUCT + RTPLAN loaded).

  • beamsNum – index into planC.beams.

  • targetStructNumplanC.structure index of the target (PTV) that the beamlets must cover.

  • structNumsV – additional structures where dose is computed (default: target only); planC.structure indices.

  • sampleRateV – per-structure sample rates for structNumsV (default 2 each).

  • scanNum – associated scan.

  • PBMargin – beamlet margin around the target, cm (beam2MCdose.m uses 0.5).

  • beamletDelta – beamlet width at the isocenter, cm.

  • name – IM name (default: the RTPLAN label).

Returns:

IMRTProblem ready for recalcDose() / runIMRTP().

cerr.imrtp.dosecalc.beamsFromRTPlan(planC, beamsNum: int = 0, scanNum: int | None = None, energyMap: bool = True) List[IMBeam][source]

Convert the TREATMENT beams of planC.beams[beamsNum] to IMBeams.

Mirrors the geometry import of beam2MCdose.m: per beam the first control point supplies gantry / couch / collimator angles, nominal energy and isocenter; SAD comes from the beam sequence. Source positions are derived as xRel = SAD sin(g), yRel = SAD cos(g), zRel = 0, rotated by the couch angle.

Parameters:
  • planC – plan container with an RTPLAN loaded.

  • beamsNum – index into planC.beams.

  • scanNum – scan used for the DICOM->CERR isocenter transform (default 0).

  • energyMap – map nominal energies to the nearest QIB energy (6 / 18 MV). Set False to keep the RTPLAN value.

Returns:

list of IMBeam.

cerr.imrtp.dosecalc.dcmIsocenterToCerr(isoDcm_mm, planC, scanNum: int)[source]

DICOM patient coordinates (mm) -> pyCERR virtual coordinates (cm).

Uses the scan’s cerrToDcmTransM (pyCERR xyz cm -> DICOM mm); falls back to the simple HFS relation used by beam2MCdose.m (x/10, -y/10, -z/10) if the matrix is unavailable.