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
예제 #2
0
def beam_set_choices(ss):
    nr_targets = SSF.determine_nr_of_indexed_ptvs(ss)

    sep_plan = P.Property('Separate planer',
                          'sep_plan',
                          next_category='målvolum')
    sep_beamset_sep_iso = P.Property('Separate beam set - separate isosenter',
                                     'sep_beamset_sep_iso')
    sep_beamset_iso = P.Property('Separate beam set - felles isosenter',
                                 'sep_beamset_iso',
                                 default=True)
    beamset_iso = P.Property('Samme beam set - felles isosenter', 'beamset')

    for i in range(nr_targets):
        P.Property('CTV' + str(i + 1), 'CTV' + str(i + 1), parent=sep_plan)
    return [sep_plan, sep_beamset_sep_iso, sep_beamset_iso, beamset_iso]
예제 #3
0
def create_additional_stereotactic_beamsets_prescriptions_and_beams(
        plan,
        examination,
        ss,
        region_codes,
        prescription,
        external,
        energy_name,
        nr_existing_beams=1):
    nr_targets = SSF.determine_nr_of_indexed_ptvs(ss)
    i = 1
    if nr_targets > 1:
        if int(region_codes[0]) in RC.brain_codes + RC.lung_codes:
            for r in region_codes:
                # Set up beam set and prescription:
                region_code = int(r)
                beam_set = plan.AddNewBeamSet(
                    Name=BSF.label_s(region_code, prescription.fraction_dose,
                                     prescription.nr_fractions),
                    ExaminationName=examination.Name,
                    MachineName="ALVersa",
                    Modality='Photons',
                    TreatmentTechnique='VMAT',
                    PatientPosition='HeadFirstSupine',
                    NumberOfFractions=prescription.nr_fractions)
                BSF.add_prescription(beam_set, prescription,
                                     'PTV' + str(i + 1))
                # Determine the point which will be our isocenter:
                isocenter = SSF.determine_isocenter(examination, ss,
                                                    region_code, 'VMAT',
                                                    'PTV' + str(i + 1),
                                                    external)
                # Setup beams or arcs
                nr_beams = BEAMS.setup_beams(ss,
                                             examination,
                                             beam_set,
                                             isocenter,
                                             region_code,
                                             prescription.fraction_dose,
                                             'VMAT',
                                             energy_name,
                                             iso_index=str(i + 1),
                                             beam_index=nr_existing_beams + 1)
                nr_existing_beams += nr_beams
                i += 1
 def __init__(self, the_window, ss):
     Frame.__init__(self, the_window, bg='white')
     self.ok = False
     self.ss = ss
     self.create_widgets(self.ss)
     self.nr_targets = SSF.determine_nr_of_indexed_ptvs(self.ss)
     # Parameters for h = height, w = width of window, as well as location on the screen: x and y
     h = self.nr_targets * 30 + 110
     #w = 240
     w = 350
     x = 1100
     y = 500
     # Set these parameters
     the_window.geometry('%dx%d+%d+%d' % (w, h, x, y))
     # Title of window
     the_window.title("Regionkoder")
     # Set the background to white
     the_window.configure(bg='white')
예제 #5
0
class Plan(object):
    def __init__(self, patient, case, mq_patient):
        self.patient = patient
        self.case = case
        self.mq_patient = mq_patient

        # Load patient model, examination and structure set:
        pm = case.PatientModel
        examination = get_current("Examination")
        ss = PMF.get_structure_set(pm, examination)

        # Determine if a target volume is present (raises error if not):
        if not PMF.has_defined_ctv_or_ptv(pm, examination):
            GUIF.handle_missing_ctv_or_ptv()

        # Check if the last CT has been set as primary, and display a warning if not.
        success = TS_C.TSCase(case).last_examination_used_test()
        if not success:
            GUIF.handle_primary_is_not_most_recent_ct()

        # Setup and run GUI:
        my_window = Tk()
        (region_code, fraction_dose, nr_fractions, initials,
         total_dose) = GUIF.collect_fractionation_choices(my_window)

        # Load list of region codes and corresponding region names and get the region name for our particular region code (raise error if a name is not retrieved):
        regions = REGIONS.RegionList(
            "C:\\temp\\raystation-scripts\\settings\\regions.tsv")
        region_text = regions.get_text(region_code)
        assert region_text != None

        # For SBRT brain or lung, if there are multiple targets, an extra form appear where
        # the user has to type region code of the other targets.
        # FIXME: Bruke funksjon for test fx?
        # FIXME: Vurder hvor denne koden bør ligge.
        target = None
        palliative_choices = None
        nr_targets = SSF.determine_nr_of_indexed_ptvs(ss)
        if nr_targets > 1:
            if region_code in RC.brain_codes + RC.lung_codes:
                if PF.is_stereotactic(nr_fractions, fraction_dose):
                    region_codes = GUIF.multiple_beamset_form(ss, Toplevel())
                    GUIF.check_region_codes(region_code, region_codes)
            elif region_code in RC.palliative_codes:
                # For palliative cases with multiple targets:
                palliative_choices = GUIF.palliative_beamset_form(
                    ss, Toplevel())
                if palliative_choices[0] in [
                        'sep_beamset_iso', 'sep_beamset_sep_iso'
                ]:
                    region_codes = GUIF.multiple_beamset_form(ss, Toplevel())
                    GUIF.check_region_codes(region_code, region_codes)
                    if SSF.has_roi_with_shape(ss, ROIS.ctv1.name):
                        target = ROIS.ctv1.name
                    elif SSF.has_roi_with_shape(ss, ROIS.ctv2.name):
                        target = ROIS.ctv2.name
                    elif SSF.has_roi_with_shape(ss, ROIS.ctv3.name):
                        target = ROIS.ctv3.name
                    elif SSF.has_roi_with_shape(ss, ROIS.ctv4.name):
                        target = ROIS.ctv4.name
                elif palliative_choices[0] == 'sep_plan':
                    target = palliative_choices[1]

        # Set up plan, making sure the plan name does not already exist. If the plan name exists, (1), (2), (3) etc is added behind the name.
        plan = CF.create_plan(case, examination, region_text)

        # Check that the number of fractions and fraction dose is among those expected for the given region code
        GUIF.check_input(ss, region_code, nr_fractions, fraction_dose)

        # Set planners initials
        plan.PlannedBy = initials

        # Set dose grid, 0.2x0.2x0.2 cm3 for stereotactic treatments and for prostate and 0.3x03x0.3 cm3 otherwise
        PF.set_dose_grid(plan, region_code, nr_fractions, fraction_dose)

        my_window = Toplevel()
        # Determine which technique and optimization choices which will appear in the form
        results = GUIF.determine_choices(region_code, nr_fractions,
                                         fraction_dose, my_window, [])
        # Chosen technique value, 'VMAT' or 'Conformal'
        technique = results[0]
        # Chosen technique name, 'VMAT' or '3D-CRT'
        technique_name = results[1]
        # Chosen optimization value
        opt = results[2]

        # Determine prescription target:
        if not target:
            roi_dict = SSF.create_roi_dict(ss)
            target = SSF.determine_target(ss, roi_dict, nr_fractions,
                                          fraction_dose)

        # Translate the couch in the longitudinal direction according to the target position
        if SSF.has_roi_with_shape(ss, ROIS.couch.name):
            PMF.translate_couch_long(pm, ss, examination, target)

        # Create 'Mask_PTV' for partial brain and stereotactic brain
        if region_code in RC.brain_codes and region_code not in RC.brain_whole_codes:
            if nr_targets > 1:
                targets = [ROIS.ptv1, ROIS.ptv2, ROIS.ptv3, ROIS.ptv4]
                for i in range(nr_targets):
                    SSF.create_expanded_and_intersected_volume(
                        pm, examination, ss, targets[i], ROIS.external,
                        ROIS.mask_ptv.name + str(i + 1), 1600)
                    patient.SetRoiVisibility(RoiName=ROIS.mask_ptv.name +
                                             str(i + 1),
                                             IsVisible=False)
            else:
                SSF.create_expanded_and_intersected_volume(
                    pm, examination, ss, ROIS.ptv, ROIS.external,
                    ROIS.mask_ptv.name, 1600)
                patient.SetRoiVisibility(RoiName=ROIS.mask_ptv.name,
                                         IsVisible=False)

        # Create 'Mask_PTV' for stereotactic lung
        if region_code in RC.lung_codes and PF.is_stereotactic(
                nr_fractions, fraction_dose):
            if nr_targets > 1:
                targets = [ROIS.ptv1, ROIS.ptv2, ROIS.ptv3]
                for i in range(nr_targets):
                    created = SSF.create_roi_subtraction(
                        pm, examination, ss, targets[i], ROIS.chestwall,
                        ROIS.mask_ptv.name + str(i + 1), 0)
                    if created:
                        patient.SetRoiVisibility(RoiName=ROIS.mask_ptv.name +
                                                 str(i + 1),
                                                 IsVisible=False)
            else:
                created = SSF.create_roi_subtraction(pm, examination, ss,
                                                     ROIS.ptv, ROIS.chestwall,
                                                     ROIS.mask_ptv.name, 0)
                if created:
                    patient.SetRoiVisibility(RoiName=ROIS.mask_ptv.name,
                                             IsVisible=False)

        # Determine name of the body contour ('External' or 'Body'):
        external = SSF.body_roi_name(ss)
        if not external:
            GUIF.handle_missing_external()

        # Determine the machine name from the size of the target volume, only one target is taken into consideration here.
        # For those situations where you have two targets and you want to have separate isocenters, then you what to evaluate the targets separately.
        if target in [
                ROIS.ctv1.name, ROIS.ctv2.name, ROIS.ctv3.name, ROIS.ctv4.name
        ] and palliative_choices[0] in ['sep_beamset_sep_iso', 'sep_plan']:
            energy_name = SSF.determine_energy_single_target(ss, target)
        elif region_code in RC.breast_codes:
            energy_name = '6'
        else:
            # Determine the machine name from the size of the target volume:
            energy_name = SSF.determine_energy(ss, target)

        # Create the name of the beamset
        beam_set_name = BSF.label(region_code, fraction_dose, nr_fractions,
                                  technique)

        # Create primary beam set:
        beam_set = PF.create_beam_set(plan, beam_set_name, examination,
                                      technique, nr_fractions)
        # Add prescription:
        BSF.add_prescription(beam_set, nr_fractions, fraction_dose, target)
        # Determine the point which will be our isocenter:
        if nr_targets > 1:
            if palliative_choices and palliative_choices[0] in [
                    'sep_beamset_iso', 'beamset'
            ]:
                # Consider all targets when determining isocenter:
                isocenter = SSF.determine_isocenter(examination,
                                                    ss,
                                                    region_code,
                                                    technique_name,
                                                    target,
                                                    external,
                                                    multiple_targets=True)
            else:
                # Set isocenter for PTV1:
                isocenter = SSF.determine_isocenter(examination, ss,
                                                    region_code,
                                                    technique_name, target,
                                                    external)
        else:
            # Consider all targets when determining isocenter:
            isocenter = SSF.determine_isocenter(examination,
                                                ss,
                                                region_code,
                                                technique_name,
                                                target,
                                                external,
                                                multiple_targets=True)
        # Determine if this patient has any previous beams in Mosaiq (which impacts which beam number is to be used with this plan):
        beam_nr = 1
        if self.mq_patient:
            beam_nr = self.mq_patient.next_available_field_number()
        # Setup beams or arcs
        nr_beams = BEAMS.setup_beams(ss,
                                     examination,
                                     beam_set,
                                     isocenter,
                                     region_code,
                                     fraction_dose,
                                     technique_name,
                                     energy_name,
                                     beam_index=beam_nr)

        # For SBRT brain or lung, if there are multiple targets, create beam sets for all targets
        # FIXME: Bruke funksjon for test fx?
        if nr_targets > 1:
            if region_code in RC.brain_codes + RC.lung_codes and region_code not in RC.brain_whole_codes:
                if PF.is_stereotactic(nr_fractions, fraction_dose):
                    PF.create_additional_stereotactic_beamsets_prescriptions_and_beams(
                        plan,
                        examination,
                        ss,
                        region_codes,
                        fraction_dose,
                        nr_fractions,
                        external,
                        energy_name,
                        nr_existing_beams=nr_beams)
            elif region_code in RC.palliative_codes:
                # Palliative cases with multiple targets:
                if palliative_choices[0] in [
                        'sep_beamset_iso', 'sep_beamset_sep_iso'
                ]:
                    if palliative_choices[0] == 'sep_beamset_iso':
                        PF.create_additional_palliative_beamsets_prescriptions_and_beams(
                            plan,
                            examination,
                            ss,
                            region_codes,
                            fraction_dose,
                            nr_fractions,
                            external,
                            energy_name,
                            nr_existing_beams=nr_beams,
                            isocenter=isocenter)
                    else:
                        PF.create_additional_palliative_beamsets_prescriptions_and_beams(
                            plan,
                            examination,
                            ss,
                            region_codes,
                            fraction_dose,
                            nr_fractions,
                            external,
                            energy_name,
                            nr_existing_beams=nr_beams)

        # If there is a 2Gy x 8 boost for breast patients
        if SSF.has_roi_with_shape(
                ss, ROIS.ctv_sb.name) and SSF.has_roi_with_shape(
                    ss, ROIS.ptv_c.name) and region_code in RC.breast_codes:
            PF.create_breast_boost_beamset(
                ss,
                plan,
                examination,
                isocenter,
                region_code,
                ROIS.ctv_sb.name,
                background_dose=int(round(fraction_dose * nr_fractions)))
            # Make sure that the original beam set (not this boost beam set) is loaded in the GUI:
            infos = plan.QueryBeamSetInfo(
                Filter={'Name': '^' + beam_set_name + '$'})
            plan.LoadBeamSet(BeamSetInfo=infos[0])

        # Determines and sets up isodoses based on region code and fractionation
        CF.determine_isodoses(case, ss, region_code, nr_fractions,
                              fraction_dose)

        # Determine site
        site = SF.site(pm, examination, ss, plan, nr_fractions, total_dose,
                       region_code, target, technique_name)

        # Set up Clinical Goals:
        es = plan.TreatmentCourse.EvaluationSetup
        CG.setup_clinical_goals(ss, es, site, total_dose, nr_fractions, target)
        # Loads the plan, done after beam set is created, as this is the only way the CT-images appears in Plan Design and Plan Optimization when the plan is loaded
        CF.load_plan(case, plan)

        # Set up beams and optimization for breast patients
        if technique_name == 'VMAT' and region_code in RC.breast_reg_codes:
            # Use robust optimization for VMAT breast:
            OBJF.set_robustness_breast(plan, region_code)
        elif technique_name == '3D-CRT' and region_code in RC.breast_codes:
            if region_code in RC.breast_reg_codes:
                BSF.set_up_beams_and_optimization_for_regional_breast(
                    plan, beam_set, ROIS.ptv_c.name, region_code)
            else:
                BSF.set_up_beams_and_optimization_for_tangential_breast(
                    plan, beam_set, plan.PlanOptimizations[0],
                    target.replace("C", "P") + "c")
                # If there is a 2Gy x 8 boost for breast patients
                if SSF.has_roi_with_shape(
                        ss, ROIS.ctv_sb.name) and SSF.has_roi_with_shape(
                            ss, ROIS.ptv_c.name):
                    BSF.set_up_beams_and_optimization_for_tangential_breast(
                        plan, plan.BeamSets[1], plan.PlanOptimizations[1],
                        ROIS.ptv_sbc.name)

        # Set up treat and protect for stereotactic lung
        #if region_code in RC.lung_codes and PF.is_stereotactic(nr_fractions, fraction_dose):
        #  BSF.set_up_treat_and_protect_for_stereotactic_lung(beam_set, target, 0.5)

        # Run first optimization on each beam set:
        for po in plan.PlanOptimizations:
            po.OptimizationParameters.DoseCalculation.ComputeFinalDose = True
            # Set 'Constrain leaf motion' to 0.3 for stereotactic patients
            if PF.is_stereotactic(nr_fractions, fraction_dose):
                acp = po.OptimizationParameters.TreatmentSetupSettings[
                    0].SegmentConversion.ArcConversionProperties
                acp.UseMaxLeafTravelDistancePerDegree = True
                acp.MaxLeafTravelDistancePerDegree = 0.3
            po.RunOptimization()

        # Start adaptive optimization if indicated
        if opt == 'oar':
            OBJF.adapt_optimization_oar(ss, plan, site.oar_objectives,
                                        region_code)
            if region_code in RC.breast_codes:
                if region_code in RC.breast_reg_codes:
                    # Need to close leafs behind jaw for breast regional patients
                    BSF.close_leaves_behind_jaw_for_regional_breast(beam_set)
                # Create 2.5 cm margin to air for breast patient planned with a 3D-CRT technique for robustness purpuses
                BSF.create_margin_air_for_3dcrt_breast(ss, beam_set,
                                                       region_code)
                # Compute dose
                beam_set.ComputeDose(DoseAlgorithm='CCDose')
            # Auto scale to prescription
            for po in plan.PlanOptimizations:
                po.AutoScaleToPrescription = True
        # Load plan
        CF.load_plan(case, plan)

        # Save
        patient.Save()
예제 #6
0
def create_lung_stereotactic_objectives(ss, plan, region_code, total_dose):
    nr_targets = SSF.determine_nr_of_indexed_ptvs(ss)
    for i in range(0, nr_targets):
        OF.fall_off(ss,
                    plan,
                    ROIS.external.name,
                    total_dose * 100,
                    total_dose * 100 / 2,
                    3,
                    5,
                    beam_set_index=i)
        OF.max_dose(ss,
                    plan,
                    ROIS.external.name,
                    total_dose * 130,
                    15,
                    beam_set_index=i)
        OF.max_dose(ss, plan, ROIS.skin.name, 30 * 100, 10, beam_set_index=i)
        OF.max_dose(ss,
                    plan,
                    ROIS.spinal_canal.name,
                    13 * 100,
                    5,
                    beam_set_index=i)
        OF.max_dvh(ss,
                   plan,
                   ROIS.chestwall.name,
                   30 * 100,
                   2,
                   100,
                   beam_set_index=i)
        OF.max_eud(ss,
                   plan,
                   ROIS.lungs.name,
                   4.5 * 100,
                   1,
                   1,
                   beam_set_index=i)
        if region_code in RC.lung_r_codes:
            OF.max_eud(ss,
                       plan,
                       ROIS.lung_r.name,
                       6.5 * 100,
                       1,
                       3,
                       beam_set_index=i)
            OF.max_dose(ss,
                        plan,
                        ROIS.ribs_r.name,
                        total_dose * 120,
                        10,
                        beam_set_index=i)
        else:
            OF.max_eud(ss,
                       plan,
                       ROIS.lung_l.name,
                       6.5 * 100,
                       1,
                       3,
                       beam_set_index=i)
            OF.max_dose(ss,
                        plan,
                        ROIS.ribs_l.name,
                        total_dose * 120,
                        10,
                        beam_set_index=i)
    if nr_targets == 1:
        OF.min_dose(ss, plan, ROIS.ptv.name, total_dose * 100, 250)
        OF.fall_off(ss, plan, ROIS.wall_ptv.name, total_dose * 100,
                    0.7 * total_dose * 100, 0.8, 5)
    else:
        for i in range(0, nr_targets):
            OF.min_dose(ss,
                        plan,
                        ROIS.ptv.name + str(i + 1),
                        total_dose * 100,
                        250,
                        beam_set_index=i)
            OF.fall_off(ss,
                        plan,
                        "zPTV" + str(i + 1) + "_Wall",
                        total_dose * 100,
                        0.7 * total_dose * 100,
                        0.8,
                        5,
                        beam_set_index=i)
예제 #7
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)
예제 #8
0
class Plan(object):
  def __init__(self, patient, case, mq_patient):
    self.patient = patient
    self.case = case
    self.mq_patient = mq_patient


    # Load patient model, examination and structure set:
    pm = case.PatientModel
    examination = get_current("Examination")
    ss = PMF.get_structure_set(pm, examination)


    # Determine if a target volume is present (raises error if not):
    if not PMF.has_defined_ctv_or_ptv(pm, examination):
      GUIF.handle_missing_ctv_or_ptv()


    # Check if the last CT has been set as primary, and display a warning if not:
    success = TS_C.TSCase(case).last_examination_used_test()
    if not success:
      GUIF.handle_primary_is_not_most_recent_ct()


    # Setup and run GUI:
    my_window = Tk()
    (region_code, fraction_dose, nr_fractions, initials, total_dose) = GUIF.collect_fractionation_choices(my_window)


    # Load list of region codes and corresponding region names, and get the region name for our particular region code (raise error if a name is not retrieved):
    regions = REGIONS.RegionList("C:\\temp\\raystation-scripts\\settings\\regions.tsv")
    region_text = regions.get_text(region_code)
    assert region_text != None
    
    # Establish the number of target volumes:
    nr_targets = SSF.determine_nr_of_indexed_ptvs(ss)
    
    # Create the prescription object:
    prescription = PRES.create_prescription(total_dose, nr_fractions, region_code)
    # Validate the prescription:
    valid = PRES.validate_prescription(prescription, region_code)
    if not valid:
      GUIF.handle_invalid_prescription(prescription, region_code, region_text)


    # For SBRT brain or lung, if there are multiple targets, display an extra form
    # where the user can specify the region code(s) of the other target(s).
    target, palliative_choices, region_codes = GUIF.collect_target_strategy_and_region_codes(ss, nr_targets, region_code, prescription)

    
    # Set up plan, making sure the plan name does not already exist. If the plan name exists, (1), (2), (3) etc is added behind the name:
    plan = CF.create_plan(case, examination, region_text)


    # Set planners initials:
    plan.PlannedBy = initials

    my_window = Toplevel()

    # Determine which technique and optimization choices which will appear in the form:
    results = GUIF.determine_choices(region_code, prescription, my_window, [])
    # Chosen technique value ('VMAT' or 'Conformal'):
    technique = results[0]
    # Chosen technique name ('VMAT' or '3D-CRT'):
    technique_name = results[1]
    # Chosen optimization value:
    opt = results[2]
    

    # Determine the prescription target volume:
    if not target:
      roi_dict = SSF.create_roi_dict(ss)
      target = SSF.determine_target(ss, roi_dict, prescription)


    # Create 'Mask_PTV' for partial brain and stereotactic brain:
    if region_code in RC.brain_codes and region_code not in RC.brain_whole_codes:
      PMF.create_mask_ptv_brain(patient, pm, examination, ss, nr_targets)


    # Create 'Mask_PTV' for stereotactic lung:
    if region_code in RC.lung_codes and prescription.is_stereotactic():
      PMF.create_mask_ptv_lung(patient, pm, examination, ss, nr_targets)


    # Determine name of the body contour ('External' or 'Body'):
    external = SSF.body_roi_name(ss)
    if not external:
      GUIF.handle_missing_external()


    # Determine the energy quality from the size of the target volume (note that only one target is taken into consideration here).
    # For those situations where you have two targets and you want to have separate isocenters, then you what to evaluate the targets separately.
    if target in [ROIS.ctv1.name, ROIS.ctv2.name, ROIS.ctv3.name, ROIS.ctv4.name] and palliative_choices[0] in ['sep_beamset_sep_iso', 'sep_plan']:
      energy_name = SSF.determine_energy_single_target(ss, target)
    elif region_code in RC.breast_codes:
      energy_name = '6'
    else:
      # Determine the energy quality from the size of the target volume:
      energy_name = SSF.determine_energy(ss, target)

    # Create beamset name:
    beam_set_name = BSF.label(region_code, prescription, technique)


    # Create primary beam set:
    beam_set = PF.create_beam_set(plan, beam_set_name, examination, technique, prescription.nr_fractions)
    # Add prescription:
    BSF.add_prescription(beam_set, prescription, target)

    # Set beam set dose grid:
    BSF.set_dose_grid(beam_set, region_code, prescription)

    # Determine the point which will be our isocenter:
    if nr_targets > 1:
      if palliative_choices and palliative_choices[0] in ['sep_beamset_iso', 'beamset']:
        # Consider all targets when determining isocenter:
        isocenter = SSF.determine_isocenter(examination, ss, region_code, technique_name, target, external, multiple_targets = True)
      else:
        # Set isocenter for PTV1:
        isocenter = SSF.determine_isocenter(examination, ss, region_code, technique_name, target, external)
    else:
      # Consider all targets when determining isocenter:
      isocenter = SSF.determine_isocenter(examination, ss, region_code, technique_name, target, external, multiple_targets = True)
    
    
    # Determine if this patient has any previous beams in Mosaiq (which impacts which beam number is to be used with this plan):
    beam_nr = 1
    if self.mq_patient:
      beam_nr = self.mq_patient.next_available_field_number()
    # Setup beams (or arcs):
    nr_beams = BEAMS.setup_beams(ss, examination, beam_set, isocenter, region_code, prescription.fraction_dose, technique_name, energy_name, beam_index=beam_nr)
    last_beam_index = beam_nr + nr_beams - 1


    # For SBRT brain or lung, if there are multiple targets, create beam sets for all targets:
    if nr_targets > 1:
      if region_code in RC.brain_codes + RC.lung_codes and region_code not in RC.brain_whole_codes:
        if prescription.is_stereotactic():
          PF.create_additional_stereotactic_beamsets_prescriptions_and_beams(plan, examination, ss, region_codes, prescription, external, energy_name, nr_existing_beams = last_beam_index)
      elif region_code in RC.palliative_codes:
        # Palliative cases with multiple targets:
        if palliative_choices[0] in ['sep_beamset_iso', 'sep_beamset_sep_iso']:
          if palliative_choices[0] == 'sep_beamset_iso':
            PF.create_additional_palliative_beamsets_prescriptions_and_beams(plan, examination, ss, region_codes, prescription, external, energy_name, nr_existing_beams = last_beam_index, isocenter = isocenter)
          else:
            PF.create_additional_palliative_beamsets_prescriptions_and_beams(plan, examination, ss, region_codes, prescription, external, energy_name, nr_existing_beams = last_beam_index)


    # Creates a 2 Gy x 8 boost beam set for breast patients, if indicated:
    if SSF.breast_boost_is_indicated(ss, region_code):
      PF.create_breast_boost_beamset(ss, plan, examination, isocenter, region_code, ROIS.ctv_sb.name, background_dose=int(round(prescription.total_dose)))
      # Make sure that the original beam set (not this boost beam set) is loaded in the GUI:
      infos = plan.QueryBeamSetInfo(Filter={'Name':'^'+beam_set_name+'$'})
      plan.LoadBeamSet( BeamSetInfo=infos[0])


    # Determines and sets up isodoses based on region code and fractionation:
    CF.determine_isodoses(case, ss, region_code, prescription)


    # Determine site:
    site = SF.site(pm, examination, ss, plan, prescription, region_code, target, technique_name)


    # Set up Clinical Goals:
    es = plan.TreatmentCourse.EvaluationSetup
    CG.setup_clinical_goals(ss, es, site, prescription, target)
    # Loads the plan (done after beam set is created, as this is the only way the CT-images appears in Plan Design and Plan Optimization when the plan is loaded):
    CF.load_plan(case, plan)


    # Set up beams and optimization for breast patients:
    if technique_name == 'VMAT' and region_code in RC.breast_reg_codes:
      # Use robust optimization for VMAT breast:
      OBJF.set_robustness_breast(plan, region_code)
    elif technique_name == '3D-CRT' and region_code in RC.breast_codes:
      if region_code in RC.breast_reg_codes:
        BSF.set_up_beams_and_optimization_for_regional_breast(plan, beam_set, ROIS.ptv_c.name, region_code)
      else:
        BSF.set_up_beams_and_optimization_for_tangential_breast(plan, beam_set, plan.PlanOptimizations[0], target.replace("C", "P")+"c")
        # Configures the 2 Gy x 8 boost for breast patients, if indicated:
        if SSF.breast_boost_is_indicated(ss, region_code):
          BSF.set_up_beams_and_optimization_for_tangential_breast(plan, plan.BeamSets[1], plan.PlanOptimizations[1], ROIS.ptv_sbc.name)


    # Set up treat and protect for stereotactic lung:
    #if region_code in RC.lung_codes and prescription.is_stereotactic():
    #  BSF.set_up_treat_and_protect_for_stereotactic_lung(beam_set, target, 0.5)


    # Run first optimization on each beam set:
    for plan_optimization in plan.PlanOptimizations:
      # Optimization parameters to be used on all cases (3D-CRT and VMAT):
      plan_optimization.OptimizationParameters.DoseCalculation.ComputeFinalDose = True
      # Configure optimization parameters for VMAT only:
      if "Arc" in plan_optimization.OptimizedBeamSets[0].DeliveryTechnique:
        optimization_parameters = OPT.optimization_parameters(region_code, prescription.fraction_dose)
        optimization_parameters.apply_to(plan_optimization)
      # Run the optimization (may crash if GPU for computation is not available):
      try:
        plan_optimization.RunOptimization()
      except:
        GUIF.handle_failed_dose_computation()


    # Start adaptive optimization if indicated:
    if opt == 'oar':
      try:
        OBJF.adapt_optimization_oar(ss, plan, site.oar_objectives, region_code)
      except:
        GUIF.handle_failed_dose_computation()
      if region_code in RC.breast_codes and technique_name != 'VMAT':
        # Modify leaves for the open fields in non-VMAT breast:
        if region_code in RC.breast_reg_codes:
          # Close leaves behind jaw for breast regional patients:
          BSF.close_leaves_behind_jaw_for_regional_breast(beam_set)
        # Create 2.5 cm margin to air for breast patient planned with a 3D-CRT technique (for robustness purpuses):
        BSF.create_margin_air_for_3dcrt_breast(ss, beam_set, region_code)
        # Compute dose:
        try:
          beam_set.ComputeDose(DoseAlgorithm = 'CCDose')
        except:
          GUIF.handle_failed_dose_computation()
      # Auto scale to prescription:
      for plan_optimization in plan.PlanOptimizations:
        plan_optimization.AutoScaleToPrescription = True
    # Load plan:
    CF.load_plan(case, plan)


    # Save:
    patient.Save()
    def create_widgets(self, ss):
        # Parameter for width of the label in characters
        w = 13
        # Parameter that limit the number of characters in each line by setting this option to the desired number
        wl = 300
        # Create label
        self.l1 = Label(
            self,
            text="Regionkode, flere målvolum (må være forskjellig!)",
            font=("TkDefaultFont", 10),
            anchor=W,
            width=36,
            bg='white')
        # Number of targets (PTVs)
        self.nr_targets = SSF.determine_nr_of_indexed_ptvs(ss)
        # Declear list to hold the input text
        self.textbox = []
        # Create the number of textboxes and labels based on number of PTVs
        for i in range(self.nr_targets - 1):
            # Label
            l2 = Label(self,
                       text="PTV" + str(i + 2) + ":",
                       font=("TkDefaultFont", 10),
                       width=w,
                       bg='white')
            # Textbox entry
            entry = Entry(self,
                          textvariable="",
                          width=13,
                          bg='white',
                          font=("TkDefaultFont", 10))
            # Place label and entry
            l2.grid(row=i + 1, column=0, padx=15, pady=10)
            entry.grid(row=i + 1, column=1, padx=15, pady=10)
            # Store entry in list
            self.textbox.append(entry)

        # Create OK and Cancel buttons
        self.okButton = Button(self,
                               text="OK",
                               command=self.ok_clicked,
                               font=("TkDefaultFont", 10),
                               bg='white')
        self.quitButton = Button(self,
                                 text="Cancel",
                                 command=self.cancel_clicked,
                                 font=("TkDefaultFont", 10),
                                 bg='white')

        # Place label
        self.l1.grid(row=0, column=0, padx=10, pady=10, columnspan=2)

        # Place OK and Cancel buttons
        self.okButton.grid(row=self.nr_targets,
                           column=0,
                           padx=20,
                           pady=15,
                           ipadx=10,
                           sticky=E)
        self.quitButton.grid(row=self.nr_targets,
                             column=1,
                             padx=20,
                             pady=15,
                             sticky=W)