conrad¶
convex optimization in radiation therapy
Contents:¶
Case¶
Case¶
Medicine¶
Dose Constraints¶
Prescription¶
Define Prescription
and methods for parsing prescription data
from python objects as well as JSON- or YAML-formatted files.
Parsing methods expect the following formats.
YAML:
- name : PTV
label : 1
is_target: Yes
dose : 35.
constraints:
- "D90 >= 32.3Gy"
- "D1 <= 1.1rx"
- name : OAR1
label : 2
is_target: No
dose :
constraints:
- "D95 <= 20Gy"
- "V30 Gy <= 20%"
Python list
of dict
(JSON approximately the same):
[{
"name" : "PTV",
"label" : 1,
"is_target" : True,
"dose" : 35.,
"constraints" : ["D1 <= 1.1rx", "D90 >= 32.3Gy"]
}, {
"name" : "OAR1",
"label" : 2,
"is_target" : False,
"dose" : None,
"constraints" : ["D95 <= 20Gy"]
}]
- JSON verus Python syntax differences:
true
/false
instead ofTrue
/False
null
instead ofNone
-
class
prescription.
Prescription
(prescription_data=None)[source]¶ Class for specifying structures with dose targets and constraints.
-
add_structure_to_dictionaries
(structure)[source]¶ Add a new structure to internal representation of prescription.
Parameters: structure ( Structure
) – Structure added toPrescription.structure_dict
. An corresponding, empty constraint list is added toPrescription.constraint_dict
.Returns: None Raises: TypeError
– Ifstructure
not aStructure
.
-
constraints_by_label
¶ Dictionary of constraints in prescription, by structure label.
-
dict
¶ Dictionary of structures in prescription, by label.
-
digest
(prescription_data)[source]¶ Populate
Prescription
‘s structures and dose constraints.Specifically, for each entry in
prescription_data
, construct aStructure
to capture structure data (e.g., name, label), as well as a corresponding but separateConstraintList
object to capture any dose constraints specified for the structure.Add each such structure to
Prescription.structure_dict
, and each such constraint list toPrescription.constraint_dict
. Build or copy a “list of dictionaries” representation of the prescription data, assign toPrescription.rx_list
.Parameters: prescription_data – Input to be parsed for structure and dose constraint data. Accepted formats include str
specifying a valid path to a suitably-formatted JSON or YAML file, or a suitably-formattedlist
ofdict
objects.Returns: None Raises: TypeError
– If input not of typelist
or astr
specfying a valid path to file that can be loaded with thejson.load()
oryaml.safe_load()
methods.
-
list
¶ List of structures in prescription
-
report
(anatomy)[source]¶ Reports whether
anatomy
fulfills all prescribed constraints.Parameters: anatomy ( Antomy
) – Container of structures to compare against prescribed constraints.Returns: Dictionary keyed by structure label, with data on each dose constraint associated with that structure in this Prescription
. Reported data includes the constraint, whether it was satisfied, and the actual dose achieved at the percentile/threshold specified by the constraint.Return type: dict
Raises: TypeError
– Ifanatomy
not anAnatomy
.
-
Anatomy¶
Define Anatomy
, container for treatment planning structures.
-
class
anatomy.
Anatomy
(structures=None)[source]¶ Iterable container class for treatment planning structures.
Provides simple syntax via overloaded operators, including addition, retrieval, and removal of structures from anatomy:
anatomy = Anatomy() # target structure with label = 4 s1 = Structure(4, 'target', True) # non-target structure with label = 12 s2 = Structure(12, 'non-target', False) # non-target structure with label = 7 s3 = Structure(7, 'non-target 2', False) anatomy += s1 anatomy += s2 anatomy += s3 # remove structure s3 by name anatomy -= 'non-target 2' # remove structure s2 by label anatomy -= 12 # retrieve structure s1 by name anatomy[4] anatomy['target']
-
calculate_doses
(beam_intensities)[source]¶ Calculate voxel doses to each structure in
Anatomy
.Parameters: beam_intensities – Beam intensities to provide to each structure’s Structure.calculate_dose method. Returns: None
-
clear_constraints
()[source]¶ Clear all constraints from all structures in
Anatomy
.Parameters: None – Returns: None
-
dose_summary_data
(percentiles=[2, 98])[source]¶ Collimate dose summaries from each structure in
Anatomy
.Parameters: percentiles ( list
) – List of percentiles to include in dose summary queries.Returns: Dictionary of dose summaries obtained by calling Structure.summary for each structure. Return type: dict
-
dose_summary_string
¶ Collimate dose summary strings from each structure in
Anatomy
.Parameters: None – Returns: Dictionary of dose summaries obtained by calling Structure.summary_string for each structure. Return type: dict
-
label_order
¶ Ranked list of labels of structures in
Anatomy
.Raises: ValueError
– If input to setter contains labels for structures not contained in anatomy, or if the length of the input list does not match Anatomy.n_structures.
-
plannable
¶ True
if all structures plannable and at least one is a target.
-
plotting_data
(constraints_only=False, maxlength=None)[source]¶ Dictionary of
matplotlib
-compatible plotting data for all structures.Parameters: - constraints_only (
bool
, optional) – IfTrue
, return only the constraints associated with each structure. - maxlength (
int
, optional) – If specified, re-sample each structure’s DVH plotting data to have a maximum series length ofmaxlength
.
- constraints_only (
-
propagate_doses
(voxel_doses)[source]¶ Assign pre-calculated voxel doses to each structure in
Anatomy
Parameters: voxel_doses ( dict
) – Dictionary mapping structure labels to voxel dose subvectors.Returns: None
-
satisfies_prescription
(constraint_dict)[source]¶ Check whether anatomy satisfies supplied constraints.
:param
dict
: Dictionary ofConstraintList
objects :param keyed by structure labels.:Returns: True if each structure in Return type: int
-
structures
¶ Dictionary of structures in anatomy, keyed by label.
Setter method accepts any iterable collection of
Structure
objects.Raises: TypeError
– If input to setter is not iterable.ValueError
– If input to setter contains elements of a type other thanStructure
.
-
Define Structure
, building block of Anatomy
.
-
structure.
W_UNDER_DEFAULT
¶ float – Default objective weight for underdose penalty on target structures.
-
structure.
W_OVER_DEFAULT
¶ float – Default objective weight for underdose penalty on non-target structures.
-
structure.
W_NONTARG_DEFAULT
¶ float – Default objective weight for overdose penalty on non-target structures.
-
class
structure.
Structure
(label, name, is_target, size=None, **options)[source]¶ Structure
manages the dose information (including the dose influence matrix, dose calculations and dose volume histogram), as well as optimization objective information—including dose constraints—for a set of voxels (volume elements) in the patient volume to be treated as a logically homogeneous unit with respect to the optimization process.- There are usually three types of structures:
- Anatomical structures, such as a kidney or the spinal
- cord, termed organs-at-risk (OARs),
- Clinically delineated structures, such as a tumor or a target
- volume, and,
- Tissues grouped together by virtue of not being explicitly
- delineated by a clinician, typically lumped together under the catch-all category “body”.
Healthy tissue structures, including OARs and “body”, are treated as non-target, are prescribed zero dose, and only subject to an overdose penalty during optimization.
Target tissue structures are prescribed a non-zero dose, and subject to both an underdose and an overdose penalty.
-
label
¶ (
int
orstr
): Label, applied to each voxel in the structure, usually generated during CT contouring step in the clinical workflow for treatment planning.
-
name
¶ str
– Clinical or anatomical name.
-
is_target
¶ bool
–True
if structure is a target.
-
dvh
¶ DVH
– Dose volume histogram.
-
constraints
¶ ConstraintList
– Mutable collection of dose constraints to be applied to structure during optimization.
-
A
¶ Alias for
Structure.A_full
.
-
A_full
¶ Full dose matrix (dimensions = voxels x beams).
- Setter method will perform two additional tasks:
- If
Structure.size
is not set, set it based on - number of rows in
A_full
.
- If
- Trigger
Structure.A_mean
to be calculated from Structure.A_full
.
- Trigger
Raises: TypeError
– IfA_full
is not a matrix innp.ndarray
,sp.csc_matrix
, orsp.csr_matrix
formats.ValueError
– IfStructure.size
is set, and the number of rows inA_full
does not matchStructure.size
.
-
A_mean
¶ Mean dose matrix (dimensions =
1
xbeams
).Setter expects a one dimensional
np.ndarray
representing the mean dose matrix for the structure. If this optional argument is not provided, the method will attempt to calculate the mean dose fromStructure.A_full
.Raises: TypeError
– IfA_mean
provided and not of typenp.ndarray
, or if mean dose matrix is to be calculated fromStructure.A_full
, but full dose matrix is not aconrad
-recognized matrix type.ValueError
– IfA_mean
is not dimensioned as a row or column vector, or number of beams implied byA_mean
conflicts with number of beams implied byStructure.A_full
.
-
assign_dose
(y)[source]¶ Assign dose vector to structure.
Parameters: y – Vector-like input of voxel doses. Returns: None Raises: ValueError
– if structure size is known and incompatible with length ofy
.
-
boost
¶ Adjustment factor from precription dose to optimization dose.
-
calc_y
(x)[source]¶ Calculate voxel doses as: attr:Structure.y =
Structure.A
*x
.Parameters: x – Vector-like input of beam intensities. Returns: None
-
calculate_dose
(beam_intensities)[source]¶ Alias for
Structure.calc_y()
.
-
collapsable
¶ True
if optimization can be performed with mean dose only.
-
constraints_string
¶ String of structure header and constraints
-
dose
¶ Dose level targeted in structure’s optimization objective.
The dose has two components: the precribed dose,
Structure.dose_rx
, and a multiplicative adjustment factor,Structure.boost
.Once the structure’s dose has been initialized, setting
Structure.dose
will change the adjustment factor. This is to distinguish (and allow for differences) between the dose level prescribed to a structure by a clinician and the dose level request to a numerical optimization algorithm that yields a desirable distribution, since the latter may require some offset relative to the former. To change the reference dose level, use theStructure.dose_rx
setter.Setter is no-op for non-target structures, since zero dose is prescribed always.
Raises: TypeError
– If requested dose does not have units ofDeliveredDose
.ValueError
– If zero dose is requested to a target structure.
-
dose_rx
¶ Prescribed dose level.
Setting this field sets
Structure.dose
to the requested value andStructure.boost
to1
.
-
dose_unit
¶ One times the
DeliveredDose
unit of the structure dose.
-
max_dose
¶ Maximum dose to structure’s voxels.
-
mean_dose
¶ Average dose to structure’s voxels.
-
min_dose
¶ Minimum dose to structure’s voxels.
-
objective_string
¶ String of structure header and objectives
-
plannable
¶ True if structure’s attached data is sufficient for optimization.
- Minimum requirements:
- Structure size determined, and
- Dose matrix assigned, or
- Structure collapsable and mean dose matrix assigned.
-
plotting_data
(constraints_only=False, maxlength=None)[source]¶ Dictionary of
matplotlib
-compatible plotting data.Data includes DVH curve, constraints, and prescribed dose.
Parameters: - constraints_only (
bool
, optional) – IfTrue
, return only the constraints associated with the structure. - maxlength (
int
, optional) – If specified, re-sample the structure’s DVH plotting data to have a maximum series length ofmaxlength
.
- constraints_only (
-
satisfies
(constraint)[source]¶ Test whether structure’s voxel doses satisfy
constraint
.Parameters: constraint (
Constraint
) – Dose constraint to test against structure’s voxel doses.Returns: True
if structure’s voxel doses conform to the queried constraint.Return type: bool
Raises: TypeError
– Ifconstraint
not of typeConstraint
.ValueError
– IfStructure.dvh
not initialized or not populated with dose data.
-
set_constraint
(constr_id, threshold=None, relop=None, dose=None)[source]¶ Modify threshold, relop, and dose of an existing constraint.
Parameters: - constr_id (
str
) – Key to a constraint inStructure.constraints
. - threshold (optional) – Percentile threshold to assign if
queried constraint is of type
PercentileConstraint
, no-op otherwise. Must be compatible with the setter method forPercentileConstraint.percentile
. - relop (optional) – Inequality constraint sense. Must be
compatible with the setter method for
Constraint.relop
. - dose (optional) – Constraint dose. Must be compatible with
setter method for
Constraint.dose
.
Returns: None
Raises: ValueError
– Ifconstr_id
is not the key to a constraint in theConstraintlist
located atStructure.constraints
.- constr_id (
-
size
¶ Structure size (i.e., number of voxels in structure).
Raises: ValueError
– Ifsize
not anint
.
-
summary
(percentiles=[2, 25, 75, 98])[source]¶ Dictionary summarizing dose statistics.
Parameters: percentiles ( list
, optional) – Percentile levels at which to query the structure dose. If not provided, will query doses at default percentile levels of 2%, 25%, 75% and 98%.Returns: Dictionary of doses at requested percentiles, plus mean, minimum and maximum voxel doses. Return type: dict
-
summary_string
¶ String of structure header and dose summary
-
voxel_weights
¶ Voxel weights, or relative volumes of voxels.
The voxel weights are the
1
vector if the structure volume is regularly discretized, and some other set of integer values if voxels are clustered.Raises: ValueError
– IfStructure.voxel_weights
setter called beforeStructure.size
is defined, or if length of input does not matchStructure.size
, or if any of the provided weights are negative.
-
y
¶ Vector of structure’s voxel doses.
-
y_mean
¶ Value of structure’s mean voxel dose.
Physics¶
Define DoseFrame
and Physics
classes for treatment
planning.
-
class
physics.
DoseFrame
(voxels=None, beams=None, data=None, voxel_labels=None, beam_labels=None, voxel_weights=None, beam_weights=None, frame_name=None)[source]¶ Describe a reference frame (voxels x beams) for dosing physics.
A
DoseFrame
provides a description of the mathematical basis of the dosing physics, which usually consists of a matrix in \(\mathbf{R}^{\mbox{voxels} \times \mbox{beams}}\), mapping the space of beam intensities, \(\mathbf{R}^\mbox{beams}\) to the space of doses delivered to each voxel, \(\mathbf{R}^\mbox{voxels}\).For a given plan, we may require conversions between several related representations of the dose matrix. For instance, the beams may in fact be beamlets that can be coalesced into apertures, or—in order to accelerate the treatment plan optimization—may be clustered or sampled. Similarly, voxels may be clustered or sampled. For voxels, there is also a geometric frame, with
X
*Y
*Z
voxels, where the tuple (X
,Y
,Z
) gives the dimensions of a regularly discretized grid, the so-called dose grid used in Monte Carlo simulations or ray tracing calculations. Since many of the voxels in this rectangular volume necessarily lie outside of the patient volume, there is only some number of voxelsm
<X
*Y
*Z
that are actually relevant to treatment planning.Accordingly, each
DoseFrame
is intended to capture one such configuration of beams and voxels, with corresponding data on labels and/or weights attached to the configuration. Voxel labels allow each voxel to be mapped to an anatomical or clinical structure used in planning. The concept of beam labels is defined to allow beams to be gathered in logical groups (e.g. beamlets in fluence maps, or apertures in arcs) that may be constrained jointly or treated as a unit in some other way in an optimization context. Voxel and beam weights are defined for accounting purposes: if aDoseFrame
represents a set of clustered voxels or beams, the associated weights give the number of unitary voxels or beams in each cluster, so that optimization objective terms can be weighted appropriately.-
beam_labels
¶ Vector of labels mapping beams to beam groups.
Setter will also use dimension of input vector to set beam dimensions (
DoseFrame.beams
) if not already assigned at call time.Raises: ValueError
– If provided vector dimensions inconsistent with known frame dimensions.
-
beam_weights
¶ Vector of weights assigned to each (meta-)beam.
Setter will also use dimension of input vector to set voxel dimensions (
DoseFrame.beams
) if not already assigned at call time.Raises: ValueError
– If provided vector dimensions inconsistent with known frame dimensions.
-
beams
¶ Number of beams in dose frame.
If
DoseFrame.beam_weights
has not been assigned at call time, the setter will initialize it to the 1 vector.Raises: ValueError
– IfDoseFrame.beams
already determined. Beam dimension is a write-once property.
-
dose_matrix
¶ Dose matrix.
Setter will also use dimensions of input matrix to set any dimensions (
DoseFrame.voxels
orDoseFrame.beams
) that are not already assigned at call time.Raises: TypeError
– If input to setter is not a sparse or dense matrix type recognized byconrad
.ValueError
– If provided matrix dimensions inconsistent with known frame dimensions.
-
static
indices_by_label
(label_vector, label, vector_name)[source]¶ Retrieve indices of vector entries corresponding to a given value.
Parameters: - label_vector – Vector of values to search for entries corresponding
- label – Value to find.
- vector_name (
str
) – Name of vector, for use in exception messages.
Returns: Vector of indices at which the entries of
label_vector
are equal tolabel
.Return type: ndarray
Raises: ValueError
– Iflabel_vector
isNone
.KeyError
– Iflabel
not found inlabel_vector
.
-
plannable
¶ True if both dose matrix and voxel label data loaded.
This can be achieved by having a contiguous matrix and a vector of voxel labels indicating the identity of each row of the matrix, or a dictionary of submatrices that map label keys to submatrix values.
-
shape
¶ Frame dimensions, \(\{\mathbf{R}^\mbox{voxels} \times \mathbf{R}^\mbox{beams}\}\).
-
voxel_labels
¶ Vector of labels mapping voxels to structures.
Setter will also use dimension of input vector to set voxel dimensions (
DoseFrame.voxels
) if not already assigned at call time.Raises: ValueError
– If provided vector dimensions inconsistent with known frame dimensions.
-
voxel_weights
¶ Vector of weights assigned to each (meta-)voxel.
Setter will also use dimension of input vector to set voxel dimensions (
DoseFrame.voxels
) if not already assigned at call time.Raises: ValueError
– If provided vector dimensions inconsistent with known frame dimensions.
-
voxels
¶ Number of voxels in dose frame.
If
DoseFrame.voxel_weights
has not been assigned at call time, the setter will initialize it to the 1 vector.Raises: ValueError
– IfDoseFrame.voxels
already determined. Voxel dimension is a write-once property.
-
-
class
physics.
Physics
(voxels=None, beams=None, dose_matrix=None, dose_grid=None, voxel_labels=None, **options)[source]¶ Class managing all dose-related information for treatment planning.
A
Physics
instance includes one or moreDoseFrames
, each with attached data including voxel dimensions, beam dimensions, a voxel-to-structure mapping, and a dose influence matrix. The class also provides an interface for adding and switching between frames, and extracting data from the active frame.A
Physics
instance optionally has an associatedVoxelGrid
that represents the dose grid used for dose matrix calculation, and that provides the necessary geometric information for reconstructing and rendering the 3-D dose distribution (or 2-D slices thereof).-
add_dose_frame
(key, **frame_args)[source]¶ Add new
DoseFrame
representation of a dosing configuration.Parameters: Returns: None
Raises: ValueError
– Ifkey
corresponds to an existing key in thePhysics
object’s dictionary of dose frames.
-
beams
¶ Number of beams in current
Physics.frame
.
-
data_loaded
¶ True
if a client has seen data from the current dose frame.
-
dose_grid
¶ Three-dimensional grid.
-
dose_matrix
¶ Dose influence matrix for current
Physics.frame
.
-
dose_matrix_by_label
(voxel_label=None, beam_label=None)[source]¶ Submatrix of dose matrix, filtered by voxel and beam labels.
Parameters: - voxel_label (optional) – Label for which to build/retrieve
submatrix of current
Physics.dose_matrix
based on row indices for whichvoxel_label
matches the entries ofPhysics.voxel_labels
. All rows returned if no label provided. - beam_label (optional) – Label for which to build/retrieve
submatrix of current
Physics.dose_matrix
based on column indices for whichbeam_label
matches the entries ofPhysics.frame.beam_labels
. All columns returned if no label provided.
Returns: Submatrix of dose matrix attached to current
Physics.frame
, based on row indices for whichPhysics.frame.voxel_labels
matches the queriedvoxel_label
, and column indices for whichPhysics.frame.beam_labels
matches the queriedbeam_label
.- voxel_label (optional) – Label for which to build/retrieve
submatrix of current
-
plannable
¶ True if current frame has both dose matrix and voxel label data
-
voxel_labels
¶ Vector mapping voxels to structures in current
Physics.frame
.
-
voxels
¶ Number of voxels in current
Physics.frame
.
-
Optimization¶
Treatment Planning as a Convex Problem¶
Define PlanningProblem
, interface between Case
and solvers.
-
class
problem.
PlanningProblem
[source]¶ Interface between
Case
and convex solvers.Builds and solves specified treatment planning problem using fastest available solver, then extracts solution data and solver metadata (e.g., timing results) for use by clients of the
PlanningProblem
object (e.g., aCase
).-
solver_cvxpy
¶ SolverCVXPY
orNoneType
–cvxpy
-baed solver, if available.
-
solver_pogs
¶ SolverOptkit
orNoneType
– POGS solver, if available.
-
solve
(structures, run_output, slack=True, exact_constraints=False, **options)[source]¶ Run treatment plan optimization.
Parameters: - structures – Iterable collection of
Structure
objects with attached objective, constraint, and dose matrix information. Build convex model of treatment planning problem using these data. - run_output (
RunOutput
) – Container for saving solver results. - slack (
bool
, optional) – IfTrue
, build dose constraints with slack. - exact_constraints (
bool
, optional) – IfTrue
and at least one structure has a percentile-type dose constraint, execute the two-pass planning algorithm, using convex restrictions of the percentile constraints on the firstpass, and exact versions of the constraints on the second pass. - **options – Abitrary keyword arguments, passed through to
PlanningProblem.solver.init_problem()
andPlanningProblem.solver.build()
.
Returns: Number of feasible solver runs performed:
0
if first pass infeasible,1
if first pass feasible,2
if two-pass method requested and both passes feasible.Return type: int
Raises: ValueError
– If no solvers avaialable.- structures – Iterable collection of
-
solver
¶ Get active solver (CVXPY or OPTKIT/POGS).
-
Convex Solvers¶
Define solver using the cvxpy
module, if available.
For np.information on cvxpy
, see:
http://www.cvxpy.org/en/latest/
If conrad.defs.module_installed()
routine does not find the module
cvxpy
, the variable SolverCVXPY
is still defined in this
module’s namespace as a lambda returning None
with the same method
signature as the initializer for SolverCVXPY
. If cvxpy
is found, the class is defined normally.
-
solver_cvxpy.
SOLVER_DEFAULT
¶ str
– Default solver, set to ‘SCS’ if modulescs
is installed, otherwise set to ‘ECOS’.
Define POGS-based solver using optkit
, if available.
For information on POGS, see: https://foges.github.io/pogs/
For infromation on optkit
, see:
https://github.com/bungun/optkit
If conrad.defs.module_installed()
does not find the optkit
,
the variable SolverOptkit
is still defined in the module
namespace as a lambda returning None
with the same method signature
as the initializer for SolverOptkit
. If optkit
is found,
the class is defined normally.
# TODO: change backend switching syntax to check flag .precision_is_64bit instead of current .precision_is_32bit when optkit api updated
CVXPY solver interface¶
-
class
solver_cvxpy.
SolverCVXPY
(n_beams=None, **options)[source]¶ Interface between
conrad
andcvxpy
optimization library.SolverCVXPY
interpretsconrad
treatment planning problems (based on structures with attached objectives, dose constraints, and dose matrices) to build equivalent convex optimization problems usingcvxpy
‘s syntax.The class provides an interface to modify, run, and retrieve solutions from optimization problems that can be executed on a CPU (or GPU, if
scs
installed with appropriate backend libraries).-
problem
¶ cvxpy.Minimize
– CVXPY representation of optimization problem.
-
constraint_dual_vars
¶ dict
– Dictionary, keyed by constraint ID, of dual variables associated with each dose constraint in the CVXPY problem representation. The dual variables’ values are stored here after each optimization run for access by clients of theSolverCVXPY
object.
-
build
(structures, exact=False, **options)[source]¶ Update
cvxpy
optimization based on structure data.Extract dose matrix, target doses, and objective weights from structures.
Use doses and weights to add minimization terms to
SolverCVXPY.problem.objective
. Use dose constraints to extendSolverCVXPY.problem.constraints
.(When constraints include slack variables, a penalty on each slack variable is added to the objective.)
Parameters: structures – Iterable collection of Structure
objects.Returns: String documenting how data in structures
were parsed to form an optimization problem.Return type: str
-
clear
()[source]¶ Reset
cvxpy
problem to minimal representation.- The minmal representation consists of:
- An empty objective (Minimize 0),
- A nonnegativity constraint on the vector of beam intensities (\(x \ge 0\)).
- Reset dictionaries of:
- Slack variables (all dose constraints),
- Dual variables (all dose constraints), and
- Slope variables for convex restrictions (percentile dose constraints).
-
get_dual_value
(constr_id)[source]¶ Retrieve dual variable for queried constraint.
Parameters: constr_id ( str
) – ID of queried constraint.Returns: None
ifconstr_id
does not correspond to a registered dual variable. Value of dual variable otherwise.
-
get_dvh_slope
(constr_id)[source]¶ Retrieve slope variable for queried constraint.
Parameters: constr_id ( str
) – ID of queried constraint.Returns: None
ifconstr_id
does not correspond to a registered slope variable. ‘NaN’ (asnumpy.np.nan
) if constraint built as exact. Reciprocal of slope variable otherwise.
-
get_slack_value
(constr_id)[source]¶ Retrieve slack variable for queried constraint.
Parameters: constr_id ( str
) – ID of queried constraint.Returns: None
ifconstr_id
does not correspond to a registered slack variable.0
if corresponding constraint built without slack. Value of slack variable if constraint built with slack.
-
init_problem
(n_beams, use_slack=True, use_2pass=False, **options)[source]¶ Initialize
cvxpy
variables and problem components.Create a
cvxpy.Variable
of length-n_beams
to representthe beam intensities. InvokeSolverCVXPY.clear()
to build minimal problem.Parameters: - n_beams (
int
) – Number of candidate beams in plan. - use_slack (
bool
, optional) – IfTrue
, next invocation ofSolverCVXPY.build()
will build dose constraints with slack variables. - use_2pass (
bool
, optional) – IfTrue
, next invocation ofSolverCVXPY.build()
will build percentile-type dose constraints as exact constraints instead of convex restrictions thereof, assuming other requirements are met. - **options – Arbitrary keyword arguments.
Returns: None
- n_beams (
-
n_beams
¶ Number of candidate beams in treatment plan.
-
objective_value
¶ Objective value at end of solve.
-
solve
(**options)[source]¶ Execute optimization of a previously built planning problem.
Parameters: **options – Keyword arguments specifying solver options, passed to cvxpy.Problem.solve()
.Returns: True
ifcvxpy
solver converged.Return type: bool
Raises: ValueError
– If specified solver is neither ‘SCS’ nor ‘ECOS’.
-
solveiters
¶ Number of solver iterations performed.
-
solvetime
¶ Solver run time.
-
status
¶ Solver status.
-
x
¶ Vector variable of beam intensities, x.
-
x_dual
¶ Dual variable corresponding to constraint x >= 0.
-
Define Case
, the top level interface for treatment planning.
-
class
case.
Case
(anatomy=None, physics=None, prescription=None, suppress_rx_constraints=False)[source]¶ Top level interface for treatment planning.
A
Case
has four major components.Case.physics
is of typePhysics
, and contains physical information for the case, including the number of voxels, beams, beam layout, voxel labels and dose influence matrix.Case.anatomy
is of typeAntomy
, and manages the structures in the patient anatomy, including optimization objectives and dose constraints applied to each structure.Case.prescription
is of typePrescription
, and specifies a clinical prescription for the case, including prescribed doses for target structures and prescribed dose constraints (e.g., RTOG recommendations).Case.problem
is of typePlanningProblem
, and is a tool that forms and manages the mathematical representation of treatment planning problem specified by case anatomy, physics and prescription; it serves as the interface to convex solvers that run the treatment plan optimization.-
A
¶ Dose matrix from current planning frame of
Case.physics
.
-
add_constraint
(structure_label, constraint)[source]¶ Add
constraint
to structure specified bystructure_label
.Parameters: - structure_label – Must correspond to label or name of a
Structure
inCase.anatomy
. - constraint (
conrad.medicine.Constraint
) – Dose constraint to add to constraint list of specified structure.
Returns: None
- structure_label – Must correspond to label or name of a
-
anatomy
¶ Container for all planning structures.
-
calculate_doses
(x)[source]¶ Calculate voxel doses for each structure in
Case.anatomy
.Parameters: x – Vector-like np.array of beam intensities. Returns: None
-
change_constraint
(constr_id, threshold=None, direction=None, dose=None)[source]¶ Modify constraint in
Case
.If
constr_id
is a valid key to a constraint in theConstraintList
attached to one of the structures inCase.anatomy
, that constraint will be modified according to the remaining arguments. Call is no-op if key does not exist.Parameters: - constr_id – Key to a constraint on one of the structures in
Case.anatomy
. - threshold (optional) – If constraint in question is a
PercentileConstraint
, percentile threshold set to this value. No effect otherwise. - direction (
str
, optional) – Constraint direction set to this value. Should be one of: ‘<’ or ‘>’. - dose (
DeliveredDose
, optional) – Constraint dose level set to this value.
Returns: None
- constr_id – Key to a constraint on one of the structures in
-
change_objective
(label, **objective_parameters)[source]¶ Modify objective for structure in
Case
.Parameters: - label – Label or name of a
Structure
inCase.anatomy
. - **options –
Returns: None
- label – Label or name of a
-
clear_constraints
()[source]¶ Remove all constraints from all structures in
Case
.Parameters: None – Returns: None
-
drop_constraint
(constr_id)[source]¶ Remove constraint from case.
If
constr_id
is a valid key to a constraint in theConstraintList
attached to one of the structures inCase.anatomy
, that constraint will be removed from the structure’s constraint list. Call is no-op if key does not exist.Parameters: constr_id – Key to a constraint on one of the structures in Case.anatomy
.Returns: None
-
gather_physics_from_anatomy
()[source]¶ Gather dose matrices from structures.
Parameters: None – Returns: None Raises: AttributeError
– Ifcase.physics.dose_matrix
is already set.
-
load_physics_to_anatomy
(overwrite=False)[source]¶ Transfer data from physics to each structure.
The label associated with each structure in
Case.anatomy
is used to retrieve the dose matrix data and voxel weights fromCase.physics
for the voxels bearing that label.The method marks the
Case.physics.dose_matrix
as seen, in order to prevent redundant data transfers.Parameters: overwrite ( bool
, optional) – IfTrue
, dose matrix data fromCase.physics
will overwrite dose matrices assigned to each structure inCase.anatomy
.Returns: None Raises: ValueError
– IfCase.anatomy
has assigned dose matrices,Case.physics
not marked as having updated dose matrix data, and flagoverwrite
set toFalse
.
-
n_beams
¶ Number of beams in current planning frame of
Case.physics
.
-
n_structures
¶ Number of structures in
Case.anatomy
.
-
n_voxels
¶ Number of voxels in current planning frame of
Case.physics
.
-
physics
¶ Patient anatomy, contains all dose physics information.
-
plan
(use_slack=True, use_2pass=False, **options)[source]¶ Invoke numerical solver to optimize plan, given state of
Case
.At call time, the objectives, dose constraints, dose matrix, and other relevant data associated with each structure in
Case.anatomy
is passed toCase.problem
to build and solve a convex optimization problem.Parameters: - use_slack (
bool
, optional) – Allow slacks on each dose constraint. - use_2pass (
bool
, optional) – Execute two-pass planing method to enforce exact versions, rather than convex restrictions of any percentile-type dose constraints included in the plan. - **options – Arbitrary keyword arguments. Passed through to
Case.problem.solve()
.
Returns: Tuple with
bool
indicator of planning problem feasibility and aRunRecord
with data from the setup, execution and output of the planning run.Return type: tuple
Raises: ValueError
– If case not plannable due to missing information.- use_slack (
-
plannable
¶ True
if case meets minimum requirements forCase.plan()
call.Parameters: None – Returns: True
if anatomy has one or more target structures and dose matrices from the case physics.Return type: bool
-
plotting_data
(x=None, constraints_only=False, maxlength=None)[source]¶ Dictionary of
matplotlib
-compatible plotting data.Includes data for dose volume histograms, prescribed doses, and dose volume (percentile) constraints for each structure in
Case.anatomy
.Parameters: - x (optional) – Vector of beam intensities from which to calculate structure doses prior to emitting plotting data.
- constraints_only (
bool
, optional) – IfTrue
, only include each structure’s constraint data in returned dictionary. - maxlength (
int
, optional) – If specified, re-sample each structure’s DVH plotting data to have a maximum series length ofmaxlength
.
Returns: Plotting data for each structure, keyed by structure label.
Return type: dict
-
prescription
¶ Container for clinical goals and limits.
Structure list from prescription used to populate
Case.anatomy
if anatomy is empty whenCase.prescription
setter is invoked.
-
problem
¶ Object managing numerical optimization setup and results.
-
propagate_doses
(y)[source]¶ Split voxel dose vector
y
into doses for each structure inCase.anatomy
.Parameters: y – Vector-like np.array of voxel doses, or dictionary mapping structure labels to voxel dose subvectors,
-
structures
¶ Dictionary of structures contained in
Case.anatomy
.
-
transfer_rx_constraints_to_anatomy
()[source]¶ Push constraints in prescription onto structures in anatomy.
Assume each structure label represented in
Case.prescription
is represented inCase.anatomy
. Any existing constraints on structures inCase.anatomy
are preserved.Parameters: None – Returns: None
-
Treatment Planning Workflow¶
Treatment Planning Workflow¶
Planning History¶
Define classes used to record solver inputs/outputs and maintain a treatment planning history.
-
class
history.
PlanningHistory
[source]¶ Class for tracking treatment plans generated by a
Case
.dict
– Dictionary mapping tags of named plans to their respective indices inPlanningHistory.runs
-
last_feasible
¶ Solver feasibility flag from most recent treatment plan.
-
last_info
¶ Solver info from most recent treatment plan.
-
last_solvetime
¶ Solver runtime from most recent treatment plan.
-
last_solvetime_exact
¶ Second-pass solver runtime from most recent treatment plan.
-
last_x
¶ Vector of beam intensities from most recent treatment plan.
-
last_x_exact
¶ Second-pass beam intensities from most recent treatment plan.
-
no_run_check
(property_name)[source]¶ Test whether history includes any treatment plans.
Helper method for property getter methods.
Parameters: property_name ( str
) – Name to use in error message if exception raised.Returns: None Raises: ValueError
– If no treatment plans exist in history, i.e.,PlanningHistory.runs
has length zero.
-
tag_last
(tag)[source]¶ Tag most recent treatment plan in history.
Parameters: tag – Name to apply to most recently added treatment plan. Plan can then be retrieved with slicing syntax:
# (history is a :class:`PlanningHistory` instance) history[tag]
Returns: None Raises: ValueError
– If no treatment plans exist in history.
-
class
history.
RunOutput
[source]¶ Record of solver outputs associated with a treatment planning run.
-
optimal_variables
¶ dict
– Dictionary of optimal variables returned by solver. At a minimum, has entries for the beam intensity vectors for the first-pass and second-pass solver runs. May include entries for:- x (beam intensities),
- y (voxel doses),
- mu (dual variable for constraint x>= 0), and
- nu (dual variable for constraint Ax == y).
-
optimal_dvh_slopes
¶ dict
– Dictionary of optimal slopes associated with the convex restriction of each percentile-type dose constraint. Keyed by constraint ID.
-
solver_info
¶ dict
– Dictionary of solver information. At a minimum, has entries solver run time (first pass/restricted constraints, and second pass/exact constraints).
-
solvetime
¶ Run time for first-pass solve (restricted dose constraints).
-
solvetime_exact
¶ Run time for second-pass solve (exact dose constraints).
-
x
¶ Optimal beam intensities from first-pass solve.
-
x_exact
¶ Optimal beam intensities from second-pass solve.
-
-
class
history.
RunProfile
(structures=None, use_slack=True, use_2pass=False, gamma='default')[source]¶ Record of solver input associated with a treatment planning run.
-
use_slack
¶ bool
–True
if solver allowed to construct convex problem with slack variables for each dose constraint.
-
use_2pass
¶ bool
–True
if solver requested to construct and solve two problems, one incorporating convex restrictions of all percentile-type dose constraints, and a second problem formulating exact constraints based on the feasible output of the first solver run.
-
objectives
¶ dict
– Dictionary of objective data associated with each structure in plan, keyed by structure labels.
-
constraints
¶ dict
– Dictionary of constraint data for each dose constraint on each structure in plan, keyed by constraint ID.
-
gamma
¶ Master scaling applied to slack penalty term in objective when dose constraint slacks allowed.
-
-
class
history.
RunRecord
(structures=None, use_slack=True, use_2pass=False, gamma='default')[source]¶ -
profile
¶ RunProfile
– Record of the objective weights, dose constraints, and relevant solver options passed to the convex solver prior to planning.
-
output
¶ RunOutput
– Output from the solver, including optimal beam intensities, i.e., the treatment plan.
-
plotting_data
¶ dict
– Dictionary of plotting data from case, with entries corresponding to the first (and potentially only) plan formed by the solver, as well as the exact-constraint version of the same plan, if the two-pass planning method was invoked.
-
feasible
¶ Solver feasibility flag from solver output.
-
info
¶ Solver information from solver output.
-
nonzero_beam_count
¶ Number of active beams in first-pass solution.
-
nonzero_beam_count_exact
¶ Number of active beams in second-pass solution.
-
solvetime
¶ Run time for first-pass solve (restricted dose constraints).
-
solvetime_exact
¶ Run time for second-pass solve (exact dose constraints).
-
x
¶ Optimal beam intensitites from first-pass solution.
-
x_exact
¶ Optimal beam intensitites from second-pass solution.
-
x_pass1
¶ Alias for
RunRecord.x
.
-
x_pass2
¶ Alias for
RunRecord.x_exact
.
-
Visualization¶
Dose volume histogram plotting utilities.
Provides CasePlotter
for conveniently
plotting DVH curve data generated by calling Case.plan()
.
If matplotlib
is available, plotting types such as
CasePlotter
types are defined normally.
This switch allows conrad
to install, load and operate without
Python plotting capabilities, and exempts matplotlib
from being
a load-time requirement.