Optimization#
pyRadPlan separates the optimization workflow into three orthogonal concepts:
Problems — define what is being optimized (variables, constraints, structure of the objective function).
Solvers — define how the mathematical program is solved (algorithm, convergence criteria).
Objectives — define clinical goals attached to individual structures (DVH goals, dose targets, dose limits).
This separation allows objectives to be reused across different problem formulations and solvers to be swapped without touching clinical goal definitions.
Running optimization#
The high-level entry point is fluence_optimization():
from pyRadPlan import fluence_optimization
fluence = fluence_optimization(ct, cst, stf, dij, pln)
Under the hood, this function:
Instantiates the planning problem configured in
pln.prop_opt["problem"].Reads objectives from each VOI in
cst.Resolves required quantities from
dij.Calls the solver and returns the optimal fluence vector.
Planning problems#
A planning problem defines the optimization variable and the structure of the objective
function. The problem class is selected via pln.prop_opt["problem"]:
Key |
Description |
|---|---|
|
Nonlinear beamlet fluence optimization. Variables are non-negative beamlet weights; the objective is the weighted sum of clinical-goal penalty functions. |
pln.prop_opt = {"problem": "nonlin_fluence"}
Problems are registered at import time and can be extended by registering additional
PlanningProblem subclasses with register_problem().
Solvers#
A solver implements the mathematical optimization algorithm. The solver is selected via the
"solver" key inside pln.prop_opt or directly on the problem object:
Key |
Description |
|---|---|
|
(default) IPOPT interior-point optimizer. Handles large-scale nonlinear programs efficiently. Suitable for the full fluence optimization problem. |
|
SciPy minimization ( |
pln.prop_opt = {
"problem": "nonlin_fluence",
"solver": "scipy",
}
The currently registered fluence problem constrains fluence weights to be non-negative.
Objectives#
Objectives are penalty or constraint functions that express clinical goals. They are attached
directly to VOI objects inside the structure set and are collected
automatically during optimization.
Each objective targets a quantity (e.g. "physical_dose", "rbe_x_dose"), has a
priority (weight in the combined objective), and may reference a dose level or DVH
parameter.
Available objectives#
Class |
Description |
|---|---|
|
Penalizes squared deviations from a reference dose. Good all-rounder for PTV coverage. |
|
Penalizes only dose below the reference (one-sided). Use for target coverage without over-irradiation penalty. |
|
Penalizes only dose above the reference. Use for OAR sparing without coverage trade-off. |
|
Penalizes the mean dose in the structure. |
|
Penalizes violation of a maximum DVH constraint (Dx < limit). |
|
Penalizes violation of a minimum DVH constraint (Dx > limit). |
|
Equivalent Uniform Dose penalty (generalized EUD formulation). |
|
Penalizes deviation from a reference dose distribution (plan mimicking). |
Attaching objectives to structures#
from pyRadPlan.optimization.objectives import SquaredDeviation, SquaredOverdosing
ptv = next(v for v in cst.vois if v.voi_type == "TARGET")
ptv.objectives = [
SquaredDeviation(priority=1000, d_ref=60.0, quantity="physical_dose"),
]
oar = next(v for v in cst.vois if v.name == "Spinal_Cord")
oar.objectives = [
SquaredOverdosing(priority=500, d_max=45.0, quantity="physical_dose"),
]
Objectives are pydantic models, so they can be serialized and shared as JSON.
Compute backend#
The optimization problem runs internally against the Python Array API standard, so the compute backend can be switched without modifying any algorithm code:
from pyRadPlan import xp_utils
xp_utils.PREFER_GPU = False
xp_utils.PREFERRED_CPU_ARRAY_BACKEND = "numpy"
xp_utils.PREFER_GPU = True
xp_utils.PREFERRED_GPU_ARRAY_BACKEND = "cupy"
The quantity resolver chooses the current preferred namespace through
pyRadPlan.core.xp_utils.choose_array_api_namespace() and converts Dij matrices into
that namespace when quantities are resolved.