def create_cornea(pm, examination, ss, source_roi, box_roi, roi, subtraction_roi): if SSF.has_named_roi_with_contours(ss, source_roi.name): center_x = SSF.roi_center_x(ss, source_roi.name) center_z = SSF.roi_center_z(ss, source_roi.name) source_roi_box = ss.RoiGeometries[source_roi.name].GetBoundingBox() y_min = source_roi_box[1].y if y_min > 0: y_min = -source_roi_box[1].y delete_roi(pm, box_roi.name) box = pm.CreateRoi(Name=box_roi.name, Color=box_roi.color, Type=box_roi.type) pm.RegionsOfInterest[box_roi.name].CreateBoxGeometry( Size={ 'x': 5, 'y': 5, 'z': 4 }, Examination=examination, Center={ 'x': center_x, 'y': y_min + 2.5, 'z': center_z }) exclude_roi_from_export(pm, box_roi.name) if source_roi.name == ROIS.lens_l.name: wall_roi = ROIS.z_eye_l elif source_roi.name == ROIS.lens_r.name: wall_roi = ROIS.z_eye_r delete_roi(pm, wall_roi.name) create_wall_roi(pm, examination, ss, wall_roi) exclude_roi_from_export(pm, wall_roi.name) if not SSF.is_approved_roi_structure(ss, roi.name): if is_approved_roi_structure_in_one_of_all_structure_sets( pm, roi.name): intersection = ROI.ROIAlgebra(roi.name + "1", roi.type, roi.color, sourcesA=[source_roi], sourcesB=[box_roi], operator='Intersection') # In the rare case that this ROI already exists, delete it (to avoid a crash): delete_roi(pm, intersection.name) create_algebra_roi(pm, examination, ss, intersection) GUIF.handle_creation_of_new_roi_because_of_approved_structure_set( intersection.name) else: subtraction = ROI.ROIAlgebra(subtraction_roi.name, subtraction_roi.type, subtraction_roi.color, sourcesA=[wall_roi], sourcesB=[box_roi], operator='Subtraction') # In the rare case that this ROI already exists, delete it (to avoid a crash): delete_roi(pm, subtraction.name) create_algebra_roi(pm, examination, ss, subtraction) else: GUIF.handle_missing_roi_for_derived_rois(subtraction_roi.name, source_roi.name)
def create_additional_palliative_beamsets_prescriptions_and_beams(plan, examination, ss, region_codes, fraction_dose, nr_fractions, external, energy_name, nr_existing_beams=1, isocenter=False): nr_targets = SSF.determine_nr_of_indexed_ptvs(ss) common_isocenter = False if isocenter: common_isocenter = True i = 1 if nr_targets > 1: for r in region_codes: region_code = int(r) # Determine the point which will be our isocenter: if not isocenter: if SSF.has_named_roi_with_contours(ss, 'PTV' + str(i+1)): isocenter = SSF.determine_isocenter(examination, ss, region_code, 'VMAT', 'PTV' + str(i+1), external) energy_name = SSF.determine_energy_single_target(ss, 'PTV'+str(i+1)) else: GUIF.handle_missing_ptv() # Set up beam set and prescription: beam_set = plan.AddNewBeamSet( Name=BSF.label(region_code, fraction_dose, nr_fractions, 'VMAT'), ExaminationName=examination.Name, MachineName= "ALVersa", Modality='Photons', TreatmentTechnique='VMAT', PatientPosition=CF.determine_patient_position(examination), NumberOfFractions=nr_fractions ) BSF.add_prescription(beam_set, nr_fractions, fraction_dose, 'CTV' + str(i+1)) # Setup beams or arcs nr_beams = BEAMS.setup_beams(ss, examination, beam_set, isocenter, region_code, fraction_dose, 'VMAT', energy_name, iso_index=str(i+1), beam_index=nr_existing_beams+1) nr_existing_beams += nr_beams OBJ.create_palliative_objectives_for_additional_beamsets(ss, plan, fraction_dose*nr_fractions, i) i += 1 if not common_isocenter: isocenter=False
def isocenter_centered_test(self): t = TEST.Test( "Skal i utgangspunktet være mest mulig sentrert i pasientens aksial-snitt", '<= 12 cm', self.isocenter) photon_iso = None if self.beam_set.Modality == 'Photons': ss = self.ts_structure_set().structure_set for beam in self.beam_set.Beams: if not photon_iso: photon_iso = beam.Isocenter.Position # For photon plans, isosenter should be somewhat centered in the patient to avoid gantry collisions. # Compare isocenter x and y coordinates to the center coordinates of the external ROI: if SSF.has_named_roi_with_contours(ss, ROIS.external.name): # Determine x and y coordinate: patient_center_x = SSF.roi_center_x( ss, ROIS.external.name) patient_center_y = SSF.roi_center_y( ss, ROIS.external.name) # Reset large patient center x values (Why do we do this?!) if abs(patient_center_x) > 3: patient_center_x = 0 # Determine the distance between patient center and isocenter: diff = round( ((photon_iso.x - patient_center_x)**2 + (photon_iso.y - patient_center_y)**2)**0.5, 1) if diff > 12: return t.fail(diff) else: return t.succeed()
def create_bottom_part_x_cm(pm, examination, ss, source_roi, roi, distance): if SSF.has_named_roi_with_contours(ss, source_roi.name): center_x = SSF.roi_center_x(ss, source_roi.name) center_y = SSF.roi_center_y(ss, source_roi.name) center_z = SSF.roi_center_z(ss, source_roi.name) source_roi_box = ss.RoiGeometries[source_roi.name].GetBoundingBox() x_min = source_roi_box[0].x x_max = source_roi_box[1].x x = source_roi_box[1].x - source_roi_box[0].x y_min = source_roi_box[0].y y_max = source_roi_box[1].y y = source_roi_box[1].y - source_roi_box[0].y z_min = source_roi_box[0].z z = source_roi_box[1].z - source_roi_box[0].z z_cutoff = z_min + distance / 2 delete_roi(pm, ROIS.box.name) box = pm.CreateRoi(Name=ROIS.box.name, Color=ROIS.box.color, Type=ROIS.box.type) pm.RegionsOfInterest[ROIS.box.name].CreateBoxGeometry( Size={ 'x': x, 'y': y, 'z': distance }, Examination=examination, Center={ 'x': center_x, 'y': center_y, 'z': z_cutoff }) if not SSF.is_approved_roi_structure(ss, roi.name): if is_approved_roi_structure_in_one_of_all_structure_sets( pm, roi.name): intersection = ROI.ROIAlgebra(roi.name + "1", roi.type, roi.color, sourcesA=[source_roi], sourcesB=[ROIS.box], operator='Intersection') # In the rare case that this ROI already exists, delete it (to avoid a crash): delete_roi(pm, intersection.name) create_algebra_roi(pm, examination, ss, intersection) GUIF.handle_creation_of_new_roi_because_of_approved_structure_set( intersection.name) else: intersection = ROI.ROIAlgebra(roi.name, roi.type, roi.color, sourcesA=[source_roi], sourcesB=[ROIS.box], operator='Intersection') # In the rare case that this ROI already exists, delete it (to avoid a crash): delete_roi(pm, intersection.name) create_algebra_roi(pm, examination, ss, intersection) delete_roi(pm, ROIS.box.name) else: GUIF.handle_missing_roi_for_derived_rois(roi.name, source_roi.name)
def create_margin_air_for_3dcrt_breast(ss, beam_set, region_code): roi_dict = SSF.create_roi_dict(ss) for beam in beam_set.Beams: # Modify first segment (if beam has segments): if len(beam.Segments) > 0: segment = beam.Segments[0] leaf_positions = segment.LeafPositions jaw = segment.JawPositions y1 = jaw[2] if region_code in RC.breast_reg_codes: if SSF.has_named_roi_with_contours( ss, ROIS.ptv_pc.name) and SSF.has_named_roi_with_contours( ss, ROIS.ptv_nc.name) and beam.Name in ['RPO', 'LPO']: nodes = ss.RoiGeometries[ROIS.ptv_nc.name].GetBoundingBox() breast = ss.RoiGeometries[ ROIS.ptv_pc.name].GetBoundingBox() y2 = jaw[3] - (nodes[1].z - breast[1].z + 1) elif SSF.has_named_roi_with_contours( ss, ROIS.ptv_50c.name) and SSF.has_named_roi_with_contours( ss, ROIS.ptv_47c.name) and beam.Name in ['RPO', 'LPO']: nodes = ss.RoiGeometries[ ROIS.ptv_47c.name].GetBoundingBox() breast = ss.RoiGeometries[ ROIS.ptv_50c.name].GetBoundingBox() y2 = jaw[3] - (nodes[1].z - breast[1].z + 1) else: y2 = jaw[3] else: y2 = jaw[3] # (Don't forget that MLC number 50 has index leafPositions[x][49]) mlcY1 = int(math.floor((y1 + 20) * 2) + 1.0) mlcY2 = int(math.ceil((y2 + 20) * 2)) for leaf in range(mlcY1 - 1, mlcY2 + 1): if beam.Name == 'LAO' and region_code in RC.breast_r_codes: leaf_positions[0][leaf] = leaf_positions[0][leaf] - 2.5 elif beam.Name == 'RAO' and region_code in RC.breast_l_codes: leaf_positions[1][leaf] = leaf_positions[1][leaf] + 2.5 elif beam.Name in ['RPO', 'RPO 1']: leaf_positions[1][leaf] = leaf_positions[1][leaf] + 2.5 elif beam.Name in ['LPO', 'LPO 1']: leaf_positions[0][leaf] = leaf_positions[0][leaf] - 2.5 segment.LeafPositions = leaf_positions
def create_posterior_half(pm, examination, ss, source_roi, roi): if SSF.has_named_roi_with_contours(ss, source_roi.name): center_x = SSF.roi_center_x(ss, source_roi.name) center_y = SSF.roi_center_y(ss, source_roi.name) center_z = SSF.roi_center_z(ss, source_roi.name) source_roi_box = ss.RoiGeometries[source_roi.name].GetBoundingBox() x_min = source_roi_box[0].x x_max = source_roi_box[1].x x = source_roi_box[1].x - source_roi_box[0].x boxes = [] boxes2 = [] for [contour_index, contour] in enumerate( ss.RoiGeometries[source_roi.name].PrimaryShape.Contours): y_min = 9999 y_max = -9999 for coordinate in contour: if coordinate.y > y_max: y_max = coordinate.y elif coordinate.y < y_min: y_min = coordinate.y length = round((abs(y_max - y_min)), 1) center_y = y_max delete_roi(pm, ROIS.box.name + str(contour_index)) box = pm.CreateRoi(Name=ROIS.box.name + str(contour_index), Color=ROIS.box.color, Type=ROIS.box.type) pm.RegionsOfInterest[ROIS.box.name + str(contour_index)].CreateBoxGeometry( Size={ 'x': x, 'y': length, 'z': 0.3 }, Examination=examination, Center={ 'x': center_x, 'y': center_y, 'z': coordinate.z }) boxes.append(box) boxes2.append( ROI.ROI(ROIS.box.name + str(contour_index), ROIS.box.type, ROIS.box.color)) subtraction = ROI.ROIAlgebra(roi.name, roi.type, roi.color, sourcesA=[source_roi], sourcesB=boxes2, operator='Intersection') # In the rare case that this ROI already exists, delete it (to avoid a crash): delete_roi(pm, subtraction.name) create_algebra_roi(pm, examination, ss, subtraction) for i in range(0, len(boxes)): delete_roi(pm, boxes[i].Name) else: GUIF.handle_missing_roi_for_derived_rois(roi.name, source_roi.name)
def uniform_dose(ss, plan, roi_name, dose_level, weigth, beam_set_index=0): if SSF.has_named_roi_with_contours(ss, roi_name): po = plan.PlanOptimizations[beam_set_index] o = po.AddOptimizationFunction(FunctionType="UniformDose", RoiName=roi_name) o.DoseFunctionParameters.DoseLevel = dose_level o.DoseFunctionParameters.Weight = weigth return o else: GUIF.handle_missing_roi_for_objective(roi_name)
def min_dvh(ss, plan, roi_name, dose_level, percent_volume, weigth, beam_set_index=0): if SSF.has_named_roi_with_contours(ss, roi_name): po = plan.PlanOptimizations[beam_set_index] o = po.AddOptimizationFunction(FunctionType="MinDvh", RoiName=roi_name) o.DoseFunctionParameters.DoseLevel = dose_level o.DoseFunctionParameters.PercentVolume = percent_volume o.DoseFunctionParameters.Weight = weigth return o else: GUIF.handle_missing_roi_for_objective(roi_name)
def external_ptv_bounding_test(self): t = TEST.Test( "External ligger helt på yttergrensen av bildeopptaket. Dette kan tyde på at CT-bildene er beskjært uheldig, og at deler av pasienten mangler. Dersom denne beskjæringen forekommer i nærheten av målvolumet, vil det resultere i feil doseberegning.", True, self.external_bounding) match = False # Run test if this structure set corresponds to the examination used for the treatment plan: if SSF.has_named_roi_with_contours(self.structure_set, ROIS.external.name): ext_box = self.structure_set.RoiGeometries[ ROIS.external.name].GetBoundingBox() img_box = self.structure_set.OnExamination.Series[ 0].ImageStack.GetBoundingBox() # Iterate all ROI geometries: for rg in self.structure_set.RoiGeometries: # We have quite a few criterias on which ROI geometries to test with. It should be a GTV or CTV, it should have contours # (unfotunately a cornercase has been discovered with an underived but unedited ROI which means we also have to check for the attribute 'Contours'), # and finally we dont check on derived ROIs: if rg.OfRoi.Type in [ 'Gtv', 'Ctv' ] and rg.HasContours() and hasattr( rg.PrimaryShape, 'Contours') and not rg.PrimaryShape.DerivedRoiStatus: roi_box = rg.GetBoundingBox() if abs(roi_box[0].z - img_box[0].z) < 1.5 or abs(roi_box[1].z - img_box[1].z) < 1.5: match = True elif abs(ext_box[0].x - img_box[0].x) < 0.2 or abs(ext_box[1].x - img_box[1].x) < 0.2: for [contour_index, contour] in enumerate(rg.PrimaryShape.Contours): for coordinate in contour: if round(ext_box[0].x, 1) == round(coordinate.x, 1): z = round(coordinate.z, 1) if (ptv_box[0].z - 5 < z) or (ptv_box[1].z + 5 > z): match = True if match: return t.fail() else: return t.succeed()
def fall_off(ss, plan, roi_name, high_dose_level, low_dose_level, distance, weigth, beam_set_index=0): if SSF.has_named_roi_with_contours(ss, roi_name): po = plan.PlanOptimizations[beam_set_index] o = po.AddOptimizationFunction(FunctionType="DoseFallOff", RoiName=roi_name) o.DoseFunctionParameters.HighDoseLevel = high_dose_level o.DoseFunctionParameters.LowDoseLevel = low_dose_level o.DoseFunctionParameters.LowDoseDistance = distance o.DoseFunctionParameters.Weight = weigth return o else: GUIF.handle_missing_roi_for_objective(roi_name)
def clinical_max_test(self): ss = self.ts_beam_set.ts_structure_set().structure_set if SSF.has_named_roi_with_contours(ss, ROIS.external.name): # Get the external ROI and its volume: external = ss.OutlineRoiGeometry volume = external.GetRoiVolume() # Determine which max dose level and volume fraction is to be used: if self.is_stereotactic(): # Use point dose (0 cc): fraction = 0 # For stereotactic treatments, max allowed dose is dependent on site: if self.ts_beam_set.ts_label.label.region in RC.brain_codes: # Brain SRT: max_factor = 1.7 else: # Other SBRT: max_factor = 1.5 else: # Use the fraction which gives a 2 cc volume: fraction = 2 / volume # For conventional treatment max allowed dose is 105 %: max_factor = 1.05 # The differential dose of this prescription: diff_pr_dose = RSU.differential_prescription_dose( self.ts_beam_set.ts_plan.plan, self.ts_beam_set.beam_set) # Create the test: expected = "<" + str(round(diff_pr_dose * max_factor, 2)) t = TEST.Test( "Skal i utgangspunktet være mindre enn {} % av normeringsdosen" .format(round(max_factor * 100)), expected, self.max) # Get the clinical max dose: clinical_max_dose = RSU.gy( self.ts_beam_set.beam_set.FractionDose. GetDoseAtRelativeVolumes(RoiName=external.OfRoi.Name, RelativeVolumes=[fraction])[0] ) * self.ts_beam_set.beam_set.FractionationPattern.NumberOfFractions # Is it outside the set limit? if clinical_max_dose > diff_pr_dose * max_factor: return t.fail(round(clinical_max_dose, 2)) else: return t.succeed()
def clinical_max_test(self): t = TEST.Test( "Skal i utgangspunktet være mindre enn 105% av normeringsdosen", True, self.maks) ts = TEST.Test( "Skal i utgangspunktet være mindre enn 150% av normeringsdosen", True, self.maks) diff_pr_dose = RSU.differential_prescription_dose( self.ts_beam_set.ts_plan.plan, self.ts_beam_set.beam_set) ss = self.ts_beam_set.ts_structure_set().structure_set if SSF.has_named_roi_with_contours(ss, ROIS.external.name): #external = RSU.ss_roi_geometry(self.ts_beam_set.beam_set, self.ts_beam_set.ts_plan.ts_case.case.PatientModel.RegionsOfInterest[ROIS.external.name]) external = ss.OutlineRoiGeometry volume = external.GetRoiVolume() # Determine the fraction corresponding to a 2cc volume: fraction = 2 / volume #clinical_max_dose = RSU.gy(self.ts_beam_set.beam_set.FractionDose.GetDoseAtRelativeVolumes(RoiName = external.OfRoi.Name, RelativeVolumes = [fraction])[0]) * self.ts_beam_set.beam_set.FractionationPattern.NumberOfFractions if self.ts_beam_set.ts_label.label.technique: if not self.ts_beam_set.ts_label.label.technique.upper( ) == 'S': clinical_max_dose = RSU.gy( self.ts_beam_set.beam_set.FractionDose. GetDoseAtRelativeVolumes(RoiName=external.OfRoi.Name, RelativeVolumes=[fraction])[0] ) * self.ts_beam_set.beam_set.FractionationPattern.NumberOfFractions if clinical_max_dose > diff_pr_dose * 1.05: t.expected = "<" + str(round(diff_pr_dose * 1.05, 2)) return t.fail(round(clinical_max_dose, 2)) else: return t.succeed() elif self.ts_beam_set.ts_label.label.technique.upper() == 'S': clinical_max_dose = RSU.gy( self.ts_beam_set.beam_set.FractionDose. GetDoseAtRelativeVolumes(RoiName=external.OfRoi.Name, RelativeVolumes=[0])[0] ) * self.ts_beam_set.beam_set.FractionationPattern.NumberOfFractions if clinical_max_dose > diff_pr_dose * 1.50: ts.expected = "<" + str(round(diff_pr_dose * 1.50, 2)) ts.fail(round(clinical_max_dose, 2)) else: ts.succeed()
'Right': 0, 'Left': 0 }) bowel_bag.UpdateDerivedGeometry(Examination=examination, Algorithm="Auto") # Non-organs: # Prostate seed markers: # Does markers exist already? (If so, we'll set up our marker ROI as a union of those) marker_candidates = [ 'Markers', 'Seed iso', 'Seed 1', 'Seed 2', 'Seed 3', 'Seed 4', 'Marker1', 'Marker2', 'Marker3' ] marker_rois = [] # Add ones that exist to our list: for candidate in marker_candidates: if SSF.has_named_roi_with_contours(ss, candidate): marker_rois.append(candidate) # If we have a list of verified marker ROIs, create a ROI algebra based on it (if not, create empty ROI): markers = create_roi(name='Markers', color='Blue', type='Marker', alternatives=[]) pm.RegionsOfInterest['Markers'].OrganData.OrganType = "Other" if len(marker_rois) > 1: markers.SetAlgebraExpression(ExpressionA={ 'Operation': "Union", 'SourceRoiNames': marker_rois, 'MarginSettings': { 'Type': "Expand", 'Superior': 0, 'Inferior': 0,
def create_brain_objectives(pm, examination, ss, plan, total_dose, nr_fractions): if nr_fractions in [1, 3]: # Stereotactic brain nr_targets = SSF.determine_nr_of_indexed_ptvs(ss) for i in range(0, nr_targets): OF.max_eud(ss, plan, ROIS.brain_ptv.name, 0.08 * total_dose * 100, 1.3, 1, beam_set_index=i) OF.max_eud(ss, plan, ROIS.brain_ptv.name, 0.06 * total_dose * 100, 1, 1, beam_set_index=i) OF.fall_off(ss, plan, ROIS.body.name, total_dose * 100, total_dose * 100 / 2, 0.8, 25, beam_set_index=i) if nr_targets == 1: # one target OF.min_dose(ss, plan, ROIS.ptv.name, total_dose * 100, 200, beam_set_index=0) OF.fall_off(ss, plan, ROIS.z_ptv_wall.name, total_dose * 100, 0.7 * total_dose * 100, 0.6, 25, beam_set_index=0) else: for i in range(0, nr_targets): OF.min_dose(ss, plan, ROIS.ptv.name + str(i + 1), total_dose * 100, 200, beam_set_index=i) OF.fall_off(ss, plan, "zPTV" + str(i + 1) + "_Wall", total_dose * 100, 0.7 * total_dose * 100, 0.6, 25, beam_set_index=i) else: # Partial brain OF.max_dose(ss, plan, ROIS.ptv.name, total_dose * 100 * 1.05, 80) OF.fall_off(ss, plan, ROIS.external.name, total_dose * 100, total_dose * 100 / 2, 1.5, 30) OF.max_dose(ss, plan, ROIS.external.name, total_dose * 100 * 1.05, 30) # Objectives for prioritized OARs: OF.max_dose( ss, plan, ROIS.brainstem_surface.name, (TOL.brainstem_surface_v003_adx.equivalent(nr_fractions) * 100) - 50, 60) OF.max_dose( ss, plan, ROIS.brainstem_core.name, (TOL.brainstem_core_v003_adx.equivalent(nr_fractions) * 100) - 50, 80) OF.max_dose( ss, plan, ROIS.optic_chiasm.name, (TOL.optic_chiasm_v003_adx.equivalent(nr_fractions) * 100) - 50, 40) OF.max_dose(ss, plan, ROIS.optic_nrv_l.name, (TOL.optic_nrv_v003_adx.equivalent(nr_fractions) * 100) - 50, 20) OF.max_dose(ss, plan, ROIS.optic_nrv_r.name, (TOL.optic_nrv_v003_adx.equivalent(nr_fractions) * 100) - 50, 20) prioritized_oars = [ ROIS.brainstem_core, ROIS.brainstem_surface, ROIS.optic_chiasm, ROIS.optic_nrv_l, ROIS.optic_nrv_r ] tolerances = [ TOL.brainstem_core_v003_adx, TOL.brainstem_surface_v003_adx, TOL.optic_chiasm_v003_adx, TOL.optic_nrv_v003_adx, TOL.optic_nrv_v003_adx ] conflict_oars = [] for i in range(len(prioritized_oars)): if tolerances[i].equivalent(nr_fractions) < total_dose * 0.95: conflict_oars.append(prioritized_oars[i]) # Setup of min and uniform doses depends on presence of critical overlaps or not: if len(conflict_oars) > 0: # Create subtraction and intersect ROIs for planning of conflicting sites: ctv_oars = ROI.ROIAlgebra(ROIS.ctv_oars.name, ROIS.ctv_oars.type, ROIS.ctv.color, sourcesA=[ROIS.ctv], sourcesB=conflict_oars, operator='Subtraction', marginsA=MARGINS.zero, marginsB=MARGINS.uniform_2mm_expansion) ptv_oars = ROI.ROIAlgebra(ROIS.ptv_oars.name, ROIS.ptv_oars.type, ROIS.ptv.color, sourcesA=[ROIS.ptv], sourcesB=conflict_oars, operator='Subtraction', marginsA=MARGINS.zero, marginsB=MARGINS.uniform_2mm_expansion) ptv_and_oars = ROI.ROIAlgebra(ROIS.ptv_and_oars.name, ROIS.ptv_and_oars.type, ROIS.other_ptv.color, sourcesA=[ROIS.ptv], sourcesB=conflict_oars, operator='Intersection') rois = [ctv_oars, ptv_oars, ptv_and_oars] PMF.delete_matching_rois(pm, rois) for i in range(len(rois)): PMF.create_algebra_roi(pm, examination, ss, rois[i]) PMF.exclude_roi_from_export(pm, rois[i].name) # Create objectives for the subtraction/intersect ROIs: OF.uniform_dose( ss, plan, ROIS.ptv_and_oars.name, (tolerances[0].equivalent(nr_fractions) * 100 - 50), 5 ) # (Note that this assumes our OARs have the same tolerance dose...) OF.uniform_dose(ss, plan, ROIS.ctv_oars.name, total_dose * 100, 30) OF.min_dose(ss, plan, ROIS.ptv_oars.name, total_dose * 100 * 0.95, 150) else: OF.uniform_dose(ss, plan, ROIS.ctv.name, total_dose * 100, 30) OF.min_dose(ss, plan, ROIS.ptv.name, total_dose * 100 * 0.95, 150) # Setup of objectives for less prioritized OARs: other_oars = [ ROIS.cochlea_l, ROIS.cochlea_r, ROIS.hippocampus_l, ROIS.hippocampus_r, ROIS.lens_l, ROIS.lens_r, ROIS.lacrimal_l, ROIS.lacrimal_r, ROIS.retina_l, ROIS.retina_r, ROIS.cornea_r, ROIS.cornea_l, ROIS.pituitary ] tolerances = [ TOL.cochlea_mean_tinnitus, TOL.cochlea_mean_tinnitus, TOL.hippocampus_v40, TOL.hippocampus_v40, TOL.lens_v003_adx, TOL.lens_v003_adx, TOL.lacrimal_mean, TOL.lacrimal_mean, TOL.retina_v003_adx, TOL.retina_v003_adx, TOL.cornea_v003_adx, TOL.cornea_v003_adx, TOL.pituitary_mean ] for i in range(len(other_oars)): if SSF.has_named_roi_with_contours(ss, other_oars[i].name): weight = None # Conflict with dose? if tolerances[i].equivalent(nr_fractions) < total_dose * 0.95: # Conflict with dose: if not SSF.roi_overlap(pm, examination, ss, ROIS.ptv, other_oars[i], 2): if ROIF.roi_vicinity_approximate( SSF.rg(ss, ROIS.ptv.name), SSF.rg(ss, other_oars[i].name), 2): # OAR is close, but not overlapping: weight = 2 else: weight = 20 else: # No conflict with dose: weight = 20 # Create objective if indicated: if weight: if other_oars[i].name in [ ROIS.cochlea_r.name, ROIS.cochlea_l.name, ROIS.lacrimal_l.name, ROIS.lacrimal_r.name, ROIS.hippocampus_l.name, ROIS.hippocampus_r.name ]: OF.max_eud( ss, plan, other_oars[i].name, tolerances[i].equivalent(nr_fractions) * 100 - 50, 1, weight) else: OF.max_dose( ss, plan, other_oars[i].name, (tolerances[i].equivalent(nr_fractions) * 100) - 50, weight) else: GUIF.handle_missing_roi_for_objective(other_oars[i].name)