def energy_of_arc_test(self): t = TEST.Test("Skal normalt ikke bruke 15 MV ved VMAT", '6 eller 10', self.energy) if self.is_vmat(): if self.beam.BeamQualityId == '15': return t.fail(self.beam.BeamQualityId) else: return t.succeed()
def mu_beam_vmat_test(self): t = TEST.Test("Skal være høyere enn nedre praktiserte grense", '>2', self.mu) if self.is_vmat(): if self.beam.BeamMU < 2: return t.fail(self.beam.BeamMU) else: return t.succeed()
def collimator_angle_of_arc_test(self): t = TEST.Test("Skal normalt unngå rette vinkler for VMAT buer", 'Ulik [0, 90, 180, 270]', self.collimator) if self.beam.ArcRotationDirection != 'None': if self.beam.InitialCollimatorAngle in [0, 90, 180, 270]: return t.fail(self.beam.InitialCollimatorAngle) else: return t.succeed()
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 prosthesis_titanium_test(self): t = TEST.Test( "Hvis proteser er til stedet, skal disse hete " + ROIS.prosthesis.name + ", " + ROIS.prosthesis_r.name + " eller " + ROIS.prosthesis_l.name + " og skal være satt til 'Titanium'", True, self.geometry) # Run test if this structure set corresponds to the examination used for the treatment plan: match = False for rg in self.structure_set.RoiGeometries: if rg.OfRoi.Name in [ ROIS.prosthesis.name, ROIS.prosthesis_r.name, ROIS.prosthesis_l.name ]: if rg.OfRoi.Name in [ ROIS.prosthesis.name, ROIS.prosthesis_r.name, ROIS.prosthesis_l.name ] and rg.OfRoi.RoiMaterial.OfMaterial.Name == 'Titanium' and rg.OfRoi.RoiMaterial.OfMaterial.MassDensity == 4.54: match = True else: match = False else: match = True for rg in self.structure_set.RoiGeometries: if rg.OfRoi.RoiMaterial: if rg.OfRoi.RoiMaterial.OfMaterial.Name == 'Titanium' and rg.OfRoi.Name not in [ ROIS.prosthesis.name, ROIS.prosthesis_r.name, ROIS.prosthesis_l.name ]: match = False else: match = True if match: return t.succeed() else: return t.fail()
def external_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) # Run test if this structure set corresponds to the examination used for the treatment plan: has_external_at_bounding_box = False for rg in self.structure_set.RoiGeometries: if rg.OfRoi.Type == ROIS.external.type: # Check if the external bounding box equals that of its image series # (which indicates that the patient was cropped with the chosen FOV, # and that dose computation will not be accurate). ext_box = rg.GetBoundingBox() for series in self.structure_set.OnExamination.Series: img_box = series.ImageStack.GetBoundingBox() # We check in lateral (x) and vertical (y) directions: if abs(ext_box[0].x - img_box[0].x) < 0.1: has_external_at_bounding_box = True if abs(ext_box[1].x - img_box[1].x) < 0.1: has_external_at_bounding_box = True if abs(ext_box[0].y - img_box[0].y) < 0.1: has_external_at_bounding_box = True if abs(ext_box[1].y - img_box[1].y) < 0.1: has_external_at_bounding_box = True if has_external_at_bounding_box: return t.fail() else: return t.succeed()
def update_tests(self, tests): new_tests = [] for testname in tests: file_md5 = testf.md5(testname) if testname not in self.tests: test = testf.Test(name=testname, id=self.get_count_tests(), file_md5=file_md5) self.tests[testname] = test self.is_changed = True new_tests.append(test) else: test = self.tests[testname] if test.md5 != file_md5: new_tests.append(test) self.is_changed = True test.md5 = file_md5 for solution_name in self.all_solutions.keys(): solution = self.all_solutions[solution_name] solution.times[test.id] = None if test.id in solution.rt: solution.rt.remove(test.id) if test.id in solution.tl: solution.tl.remove(test.id) return new_tests
def dose_grid_test(self): # Default voxel size: max_voxel_size = 0.3 # Expected dose grid resolution depends on whether the beam set is stereotactic or not and which region is treated: if self.is_stereotactic(): if self.ts_beam_set.ts_label.label.region in RC.brain_codes: max_voxel_size = 0.1 elif self.ts_beam_set.ts_label.label.region in RC.lung_and_mediastinum_codes: max_voxel_size = 0.2 else: # Bone mets SBRT: max_voxel_size = 0.1 else: # Conventional treatment: if self.ts_beam_set.ts_label.label.region in RC.brain_codes: max_voxel_size = 0.2 elif self.ts_beam_set.ts_label.label.region in RC.prostate_codes: max_voxel_size = 0.2 # Create the test: t = TEST.Test( "Skal i utgangspunktet bruke dose grid med maks voksel størrelse: " + str(max_voxel_size) + " cm", "<=" + str(max_voxel_size), self.grid) # Determine actual value: grid = self.optimization.OptimizationParameters.TreatmentSetupSettings[ 0].ForTreatmentSetup.FractionDose.InDoseGrid.VoxelSize grid_max = max([grid.x, grid.y, grid.z]) # Test: if grid_max <= max_voxel_size: return t.succeed() else: return t.fail(grid_max)
def mlc_corner_validity_test(self): t = TEST.Test("Skal ha hjørne-posisjoner som er leverbare på Elekta", True, self.mlc) violated = False # Agility/Versa HD: limits = [20.0 for i in range(80)] limits[0] = limits[79] = 16.1 limits[1] = limits[78] = 16.7 limits[2] = limits[77] = 17.3 limits[3] = limits[76] = 17.8 limits[4] = limits[75] = 18.3 limits[5] = limits[74] = 18.8 limits[6] = limits[73] = 19.2 limits[7] = limits[72] = 19.7 # Iterate leaf positions and check against limits: #for i in range(len(limits)): for i in it.chain(range(0, 7), range(72, 79)): if self.segment.LeafPositions[0][i] < -limits[i]: violated = True if self.segment.LeafPositions[1][i] > limits[i]: violated = True if violated: return t.fail(False) else: return t.succeed()
def stereotactic_mu_constraints_for_single_beam(self): t = TEST.Test( "Skal i utgangspunktet bruke begrensninger på antall MU per bue <= 1.4*fraksjonsdose (cGy).", True, self.mu) beam_start = 0 beam_stop = 0 beam_length = 0 total_beam_length = 0 if self.ts_beam_set.has_prescription(): if len(list(self.ts_beam_set.beam_set.Beams)) == 1: if self.ts_beam_set.ts_label.label.technique: if self.ts_beam_set.ts_label.label.technique.upper( ) == 'S': for po in self.ts_beam_set.ts_plan.plan.PlanOptimizations: for beam_settings in po.OptimizationParameters.TreatmentSetupSettings[ 0].BeamSettings: if self.beam.Number == beam_settings.ForBeam.Number and beam_settings.ArcConversionPropertiesPerBeam.MaxArcMU: t.expected = "<" + str( RSU.fraction_dose( self.ts_beam_set.beam_set) * 140) if beam_settings.ArcConversionPropertiesPerBeam.MaxArcMU <= ( RSU.fraction_dose( self.ts_beam_set.beam_set) * 140 * 1.15): return t.succeed() else: return t.fail( round( beam_settings. ArcConversionPropertiesPerBeam. MaxArcMU, 1))
def breast_oar_defined_test(self): failed_geometries = [] t = TEST.Test("Regionen må ha definert volum:", True, self.defined_roi) t.expected = None ss = self.ts_beam_sets[0].ts_structure_set().structure_set roi_not_contours_dict = SSF.create_roi_dict_not_contours(ss) roi_and_region_dict = { ROIS.a_lad.name: RC.breast_r_codes, ROIS.breast_r_draft.name: RC.breast_l_codes, ROIS.breast_r.name: RC.breast_l_codes, ROIS.breast_l.name: RC.breast_r_codes } if self.ts_case.ts_plan.ts_beam_sets[0].ts_label.label.region: for key, value in roi_and_region_dict.items(): if roi_not_contours_dict.get(key) and self.ts_beam_sets[ 0].ts_label.label.region in value: del roi_not_contours_dict[key] structure_sets = self.ts_case.case.PatientModel.StructureSets for i in range(len(structure_sets)): if structure_sets[i].OnExamination.Name != self.ts_beam_sets[ 0].ts_structure_set().structure_set.OnExamination.Name: roi = structure_sets[i].RoiGeometries for j in range(len(roi)): if roi[j].HasContours() and roi_not_contours_dict.get( roi[j].OfRoi.Name): del roi_not_contours_dict[roi[j].OfRoi.Name] if len(roi_not_contours_dict.keys()) >= 1: return t.fail(list(roi_not_contours_dict.keys())) else: return t.succeed()
def setup_beams_test(self): t = TEST.Test("'Create setup beams' skal ikke være aktivert", False, self.param) if self.beam_set.PatientSetup.UseSetupBeams: return t.fail(True) else: return t.succeed()
def dose_grid_test(self): correct_grid = 0.3 match = True grid = self.optimization.OptimizationParameters.TreatmentSetupSettings[ 0].ForTreatmentSetup.FractionDose.InDoseGrid.VoxelSize if self.ts_beam_set.ts_label.label.technique: if self.ts_beam_set.ts_label.label.technique.upper( ) == 'S' and self.ts_beam_set.ts_label.label.region in RC.brain_partial_codes: if grid.x != 0.1 or grid.y != 0.1 or grid.z != 0.1: correct_grid = 0.1 match = False elif self.ts_beam_set.ts_label.label.region in RC.brain_partial_codes or self.ts_beam_set.ts_label.label.region in RC.prostate_codes or self.ts_beam_set.ts_label.label.technique.upper( ) == 'S' and self.ts_beam_set.ts_label.label.region in RC.lung_codes: if grid.x != 0.2 or grid.y != 0.2 or grid.z != 0.2: correct_grid = 0.2 match = False elif grid.x != 0.3 or grid.y != 0.3 or grid.z != 0.3: correct_grid = 0.3 match = False t = TEST.Test( "Skal i utgangspunktet bruke dosegrid: " + str(correct_grid) + "x" + str(correct_grid) + "x" + str(correct_grid) + " cm^3", True, self.grid) if match: return t.succeed() else: return t.fail(grid.x)
def name_capitalization_test(self): t = TEST.Test("Skal være navngitt med stor forbokstav", None, self.name) if self.beam.Name[0].isupper() or not self.beam.Name[0].isalpha(): return t.succeed() else: t.expected = self.beam.Name[0].upper() return t.fail(self.beam.Name[0])
def bolus_set_test(self): t = TEST.Test("Bolus forventes å være aktivert for feltet når en bolus ROI eksisterer i struktursettet", None, self.param) if PMF.bolus(self.ts_beam_set.ts_plan.ts_case.case.PatientModel): t.expected = str(PMF.bolus_names(self.ts_beam_set.ts_plan.ts_case.case.PatientModel)) if len(self.beam.Boli) > 0: return t.succeed() else: return t.fail(None)
def gantry_angle_test(self): t = TEST.Test( "Gantryvinkel lik 180 grader bør unngås, da denne vinkelen er tvetydig. Bruk < eller > enn 180.0, og velg den siden som best passer overens med øvrige feltvinkler samt planlagt XVI-protokoll.", '!= 180.0', self.gantry) if self.beam.GantryAngle == 180: return t.fail(self.beam.GantryAngle) else: return t.succeed()
def technique_test(self): t = TEST.Test("Plan-teknikk skal være en av disse", ['3D-CRT', 'SMLC', 'VMAT'], self.technique) if not self.beam_set.DeliveryTechnique in ('Arc', 'SMLC', '3DCRT', 'DynamicArc'): return t.fail(self.beam_set.DeliveryTechnique) else: return t.succeed()
def dose_is_clinical_test(self): t = TEST.Test("Beregning skal være markert som 'klinisk'", True, self.dose) if self.has_dose(): if self.beam_set.FractionDose.DoseValues.IsClinical: return t.succeed() else: return t.fail()
def number_of_segments_of_static_beam_test(self): t = TEST.Test("Skal i utgangspunktet være tilbakeholden med å bruke mange segmenter (ved IMRT)", '<8', self.segments) if self.beam.ArcRotationDirection == 'None': nr_segments = RSU.soc_length(self.beam.Segments) if nr_segments >= 8: return t.fail(nr_segments) else: return t.succeed()
def mu_segment_3dcrt_test(self): t = TEST.Test("Skal være høyere enn nedre praktiserte grense", '>2', self.mu) if self.beam.ArcRotationDirection == 'None': for segment in self.beam.Segments: if self.beam.BeamMU*segment.RelativeWeight < 2: return t.fail(round(self.beam.BeamMU*segment.RelativeWeight, 2)) else: return t.succeed()
def id_space_test(self): t = TEST.Test( 'Skal være mellomrom mellom fødselsdato og personnr (ddmmåå xxxxx)', ' ', self.id) if len(self.patient.PatientID) != 12: return t.fail(len(self.patient.PatientID)) else: return t.succeed()
def prescription_type_test(self): t = TEST.Test("Skal være en av disse", ['MedianDose', 'DoseAtPoint', 'DoseAtVolume'], self.type) if self.prescription.PrimaryDosePrescription.PrescriptionType in ( 'MedianDose', 'DoseAtPoint', 'DoseAtVolume'): return t.succeed() else: return t.fail()
def nr_parts_test(self): t = TEST.Test( "Skal være 3 deler adskilt av kolon (eks '342V:70-78:4')", 3, self.param) if len(self.label.parts) == 3: return t.succeed() else: return t.fail(len(self.label.parts))
def planned_by_test(self): t = TEST.Test( 'Doseplanleggerens initialer bør være fylt inn her (Planned by)', True, self.planned_by) if self.plan.PlannedBy: return t.succeed() else: return t.fail()
def prescription_poi_target_volume_test(self): t = TEST.Test( "Punktnormering skal kun benyttes for planer som ikke har målvolum definert", False, self.type) if self.prescription.PrimaryDosePrescription.PrescriptionType == 'DoseAtPoint': if self.ts_beam_set.ts_plan.ts_case.has_target_volume: return t.fail(True) else: return t.succeed()
def middle_part_separator_test(self): t = TEST.Test( "Midterste del skal inneholde to deler adskilt av bindestrek (eks '342V:70-78:4')", True, self.param) if len(self.label.parts) == 3: if len(self.label.middle_parts) == 2: return t.succeed() else: return t.fail(len(self.label.middle_parts))
def region_test(self): t = TEST.Test( "Første del (av første del) skal være et heltall for regionkode (eks '342V:70-78:4')", True, self.param) if len(self.label.parts) == 3: if self.label.region: return t.succeed() else: return t.fail(self.label.region)
def technique_test(self): t = TEST.Test( "Siste tegn av første del skal være en bokstav for plan-teknikk (eks '342V:70-78:4')", True, self.param) if len(self.label.parts) == 3: if self.label.technique: return t.succeed() else: return t.fail(self.label.technique)
def nr_fractions_test(self): t = TEST.Test( "Siste del skal være heltall for antall fraksjoner (eks '342V:70-78:4')", True, self.param) if len(self.label.parts) == 3: if self.label.nr_fractions: return t.succeed() else: return t.fail(self.label.nr_fractions)
def is_not_zero_test(self): t = TEST.Test("Skal ikke ha koordinater i punktet: 0,0,0", True, self.coordinates) if self.poi_geometry.Point: # When the poi geometry is undefined, RayStation seems to represent this with the min float value: if self.poi_geometry.Point.x == 0 and self.poi_geometry.Point.y == 0 and self.poi_geometry.Point.z == 0: return t.fail() else: return t.succeed()