Source code for pyRadPlan.machines.photons._linac
from typing import Any, Optional, ClassVar, cast, List
import numpy as np
from numpy.typing import NDArray
from pydantic import (
Field,
field_validator,
ValidatorFunctionWrapHandler,
ValidationInfo,
ValidationError,
)
from pyRadPlan.machines.base import ExternalBeamMachine
from ._svd_kernel import PhotonSVDKernel
[docs]
class PhotonLINAC(ExternalBeamMachine):
"""
Base Class for Photon LINAC-like Machines.
Defines minimum meta-data a photon LINAC machine must hold
Provides multiple data storage formats, currently supported
- SVD Kernel Data (Bortfeld 1993, 10.1118/1.597070).
Attributes
----------
scd : float
The source-to-collimator distance of the machine
pb_kernels : dict[float, PhotonSVDKernel]
Pencil-beam kernels for the machine
of : np.ndarray
Output factor data
tpr : np.ndarray
Tissue-to-phantom ratio data
"""
# Defining some naming properties (must keep type annotations when overriding pydantic fields)
name: str = Field(default="Generic", alias="machine")
key: str = "photon_linac_generic"
radiation_mode: str = Field(default="photons", pattern="^(photons)$", validate_default=True)
_possible_radiation_modes: ClassVar[List[str]] = ["photons"]
# Machine geometry
scd: float = Field(ge=0.0, description="Source to collimator distance", alias="SCD")
# Optionally we can have pencil-beam kernels
pb_kernels: Optional[dict[float, PhotonSVDKernel]] = None
# Commissioning data
of: Optional[NDArray] = Field(default=None, description="Output Factor")
tpr: Optional[NDArray] = Field(default=None, description="Tissue-to-phantom-Ratio")
# TODO: Beam model descriptions for Monte Carlo
@classmethod
def _parse_tabulated_energy_data_from_mat(
cls, tabulated_energy_data: dict, returned_data: dict
):
"""Parse the tabulated energy data from a matRad machine file."""
super(PhotonLINAC, cls)._parse_tabulated_energy_data_from_mat(
tabulated_energy_data, returned_data
)
# parse kernel
# returned_data["scd"] = tabulated_energy_data.get("scd", 0.0)
returned_data["pb_kernels"] = tabulated_energy_data
[docs]
@field_validator("pb_kernels", mode="wrap")
@classmethod
def validate_pb_kernels(
cls, v: Any, handler: ValidatorFunctionWrapHandler, info: ValidationInfo
) -> PhotonSVDKernel:
try:
pb_kernels = handler(v, info)
except ValidationError as err:
# If we have a dict, we try to convert it to a PhotonSVDKernel object
if isinstance(v, dict):
# This check tries to check if we have a matRad kernel data set
if "kernel" in v and "kernel_data" not in v:
try:
kernel = cast(dict, v["kernel"])
v["kernel_ssds"] = np.asarray(kernel.pop("SSD"), dtype=np.float64)
kernel_list = [
np.asarray(kernel.pop(f"kernel{i}"), dtype=np.float64)
for i in range(1, len(kernel) + 1)
]
v["kernel_data"] = np.ascontiguousarray(
np.moveaxis(np.asarray(kernel_list), 0, 1)
)
# kernel dictionary by energy
v = {float(v["energy"]): v}
except Exception as exc:
raise ValueError("Could not parse matRad kernel data") from exc
pb_kernels = handler(v, info)
else:
# Otherwise we don't know what to do
raise err
else:
raise err
# Version specific handling
pb_kernels = cast(dict[float, PhotonSVDKernel], pb_kernels)
if info.data["version"] < 2:
# We need to rescale the pencil beam kernels by 4 due to their
# hardcoded implied 2D convolution resolution of (0.5 mm)^2
for _, kernel in pb_kernels.items():
kernel.kernel_data *= 4.0
return pb_kernels
[docs]
def get_kernel_by_index(self, ix_energy: int) -> PhotonSVDKernel:
"""Get the pencil beam kernel for a specific energy index.
Parameters
----------
ix_energy : int
Index of the energy level in the stored data
Returns
-------
IonPencilBeamKernel
Pencil beam kernel data
"""
return self.get_kernel_by_energy(self.energies[ix_energy])
[docs]
def get_kernel_by_energy(self, energy: float) -> PhotonSVDKernel:
"""Get the pencil beam kernel for a specific energy value.
Parameters
----------
energy : float
Energy value to search for
Returns
-------
IonPencilBeamKernel
Pencil beam kernel data
"""
if self.pb_kernels is None:
raise ValueError("No pencil beam kernels available for this machine.")
return self.pb_kernels[energy]