Is it possible to configure multiple CTVs?

E.g. CTV high, CTV elective, CTV intermediate etc.

Hi,

Yes it is possible to work with multiple CTVs.
For example, to set up contours and optimization objectives with two CTVs, a possible way could be:

ctImagePath = r"/auto/globalscratch/users/b/r/brober/Arcpt/Patients/HAN_02_05"
dataList = readData(ctImagePath, maxDepth=0)
ct = dataList[-1]
contours = dataList[-2]

# 1st CTV
CTVp_7000 = contours.getContourByName('CTVp_7000')
CTVp_7000_Mask = CTVp_7000.getBinaryMask(origin=ct.origin, gridSize=ct.gridSize, spacing=ct.spacing)

# 2nd CTV
CTVn_5400 = contours.getContourByName('CTVn_5400')
CTVn_5400_Mask = CTVn_5400.getBinaryMask(origin=ct.origin, gridSize=ct.gridSize, spacing=ct.spacing)

CTV_all_Mask = CTVp_7000.getBinaryMask(origin=ct.origin, gridSize=ct.gridSize, spacing=ct.spacing)
CTV_all_Mask.imageArray=np.logical_or(CTV_all_Mask.imageArray, CTVn_5400_Mask.imageArray)

.
.
.

planDesign = PlanDesign()
planDesign.ct = ct
planDesign.targetMask = CTV_all_Mask

.
.
.

plan = planDesign.buildPlan()  # Spot placement

#----
beamlets = mc2.computeBeamlets(ct, plan) # If computation needed
#----
plan.planDesign.beamlets = beamlets


#Optimization objectives
plan.planDesign.objectives = ObjectivesList()
plan.planDesign.objectives.setTarget(CTVp_7000.name, 70)
plan.planDesign.objectives.targetMask = plan.planDesign.targetMask
plan.planDesign.objectives.fidObjList = []

plan.planDesign.objectives.addFidObjective(CTVp_7000_Mask, FidObjective.Metrics.DMIN, 70, 100) #dose prescription: 70Gy
plan.planDesign.objectives.addFidObjective(CTVp_7000_Mask, FidObjective.Metrics.DMAX, 70, 100)
plan.planDesign.objectives.addFidObjective(CTVn_5400_Mask, FidObjective.Metrics.DMAX, 54.25, 100)
plan.planDesign.objectives.addFidObjective(CTVn_5400_Mask, FidObjective.Metrics.DMIN, 54.25, 100) #dose prescription: 54.25Gy

solver = IMPTPlanOptimizer(method='Scipy-LBFGS', plan=plan, maxit=500)
doseImage, ps = solver.optimize()

Tell us if you need any further details.

Best,
Benjamin

2 Likes

Thank you so much Benjamin :slight_smile:

1 Like

@Broberfroid. could you also explain/ include a snippet to demonstrate how you make sure that the dose deposition matrices are calculated also for a given set of robustness scenarios?

Sure,
when defining your planDesign you can specify your treatment uncertainties like this:

planDesign.robustness.setupSystematicError = [4.0, 4.0, 4.0]  # mm
planDesign.robustness.setupRandomError = [0.0, 0.0, 0.0]  # mm (sigma)
planDesign.robustness.rangeSystematicError = 2.6  # %
planDesign.robustness.selectionStrategy = planDesign.robustness.Strategies.ERRORSPACE_REGULAR

Then, you can compute your beamlets for the different robust scenarios like this:

nominal, scenarios = mc2.computeRobustScenarioBeamlets(ct, plan, roi=[roi], storePath=output_path) #dose will be cropped at roi, use it carefully to save disk space
plan.planDesign.beamlets = nominal
plan.planDesign.robustness.scenarios = scenarios
plan.planDesign.robustness.numScenarios = len(scenarios)

If you have already computed the different robust beamlets, don’t hesitate to use the loadBeamlets function to load them directly.

from opentps.core.io.serializedObjectIO import loadBeamlets
from glob import glob

scenarios=[]
scenarios_list=glob(os.path.join(output_path, "*Scenario_*"))
for i,j in enumerate(scenarios_list):
    bl = loadBeamlets(j)
    scenarios.append(bl)
plan.planDesign.robustness.scenarios = scenarios
plan.planDesign.robustness.numScenarios = 21

Then, for the optimization, you can use the “robust” argument to specify whether the objective should be robustly optimized.

plan.planDesign.objectives.addFidObjective(roi, FidObjective.Metrics.DMAX, 20.0, 1.0, robust=True)
plan.planDesign.objectives.addFidObjective(roi, FidObjective.Metrics.DMIN, 20.5, 1.0, robust=True)

Note that beamlets are currently computed and stored for each scenario, it is thus quite time-consuming. We are currently investigating methods to improve the whole robust optimization process.

1 Like

Hi @Wens , code has been refactored so that one can use the following function planDesign.defineTargetMaskAndPrescription(target = [primaryTumor,secondaryTumor], targetPrescription = [50.,30.])

and define more than one CTV for the spot placement instead of doing it all manually. Objectives still have to be added for each CTV independently.