private void set_beams(List <VVector> isoLocations) { //DRR parameters (dummy parameters to generate DRRs for each field) DRRCalculationParameters DRR = new DRRCalculationParameters(); DRR.DRRSize = 500.0; DRR.FieldOutlines = true; DRR.StructureOutlines = true; DRR.SetLayerParameters(1, 1.0, 100.0, 1000.0); //place the beams for the VMAT plan //unfortunately, all of Nataliya's requirements for beam placement meant that this process couldn't simply draw from beam placement templates. Some of the beam placements for specific isocenters //and under certain conditions needed to be hard-coded into the script. I'm not really a fan of this, but it was the only way to satisify Nataliya's requirements. int count = 0; string beamName; VRect <double> jp; for (int i = 0; i < numVMATIsos; i++) { for (int j = 0; j < numBeams[i]; j++) { //second isocenter and third beam requires the x-jaw positions to be mirrored about the y-axis (these jaw positions are in the fourth element of the jawPos list) //this is generally the isocenter located in the pelvis and we want the beam aimed at the kidneys-area if (i == 1 && j == 2) { jp = jawPos.ElementAt(j + 1); } else if (i == 1 && j == 3) { jp = jawPos.ElementAt(j - 1); } else { jp = jawPos.ElementAt(j); } Beam b; beamName = ""; beamName += String.Format("{0} ", count + 1); //zero collimator rotations of two main fields for beams in isocenter immediately superior to matchline. Adjust the third beam such that collimator rotation is 90 degrees. Do not adjust 4th beam double coll = collRot[j]; if ((numIsos > numVMATIsos) && (i == (numVMATIsos - 1))) { if (j < 2) { coll = 0.0; } else if (j == 2) { coll = 90.0; } } //all even beams (e.g., 2, 4, etc.) will be CCW and all odd beams will be CW if (count % 2 == 0) { b = plan.AddArcBeam(ebmpArc, jp, coll, CCW[0], CCW[1], GantryDirection.CounterClockwise, 0, isoLocations.ElementAt(i)); if (j >= 2) { beamName += String.Format("CCW {0}{1}", isoNames.ElementAt(i), 90); } else { beamName += String.Format("CCW {0}{1}", isoNames.ElementAt(i), ""); } } else { b = plan.AddArcBeam(ebmpArc, jp, coll, CW[0], CW[1], GantryDirection.Clockwise, 0, isoLocations.ElementAt(i)); if (j >= 2) { beamName += String.Format("CW {0}{1}", isoNames.ElementAt(i), 90); } else { beamName += String.Format("CW {0}{1}", isoNames.ElementAt(i), ""); } } b.Id = beamName; b.CreateOrReplaceDRR(DRR); count++; } } //add additional plan for ap/pa legs fields (all ap/pa isocenter fields will be contained within this plan) if (numIsos > numVMATIsos) { //6-10-2020 EAS, checked if exisiting _Legs plan is present in createPlan method legs_planUpper = tbi.AddExternalPlanSetup(selectedSS); if (singleAPPAplan) { legs_planUpper.Id = String.Format("_Legs"); } else { legs_planUpper.Id = String.Format("{0} Upper Legs", numVMATIsos + 1); } //100% dose prescribed in plan legs_planUpper.SetPrescription(prescription.Item1, prescription.Item2, 1.0); legs_planUpper.SetCalculationModel(CalculationType.PhotonVolumeDose, calculationModel); Structure target; if (useFlash) { target = selectedSS.Structures.FirstOrDefault(x => x.Id.ToLower() == "ts_flash_target"); } else { target = selectedSS.Structures.FirstOrDefault(x => x.Id.ToLower() == "ptv_body"); } //adjust x2 jaw (furthest from matchline) so that it covers edge of target volume double x2 = isoLocations.ElementAt(numVMATIsos).z - (target.MeshGeometry.Positions.Min(p => p.Z) - 20.0); if (x2 > 200.0) { x2 = 200.0; } else if (x2 < 10.0) { x2 = 10.0; } //AP field //set MLC positions. First row is bank number 0 (X1 leaves) and second row is bank number 1 (X2). float[,] MLCpos = new float[2, 60]; for (int i = 0; i < 60; i++) { MLCpos[0, i] = (float)-200.0; MLCpos[1, i] = (float)(x2); } Beam b = legs_planUpper.AddMLCBeam(ebmpStatic, MLCpos, new VRect <double>(-200.0, -200.0, x2, 200.0), 90.0, 0.0, 0.0, isoLocations.ElementAt(numVMATIsos)); b.Id = String.Format("{0} AP Upper Legs", ++count); b.CreateOrReplaceDRR(DRR); //PA field b = legs_planUpper.AddMLCBeam(ebmpStatic, MLCpos, new VRect <double>(-200.0, -200.0, x2, 200.0), 90.0, 180.0, 0.0, isoLocations.ElementAt(numVMATIsos)); b.Id = String.Format("{0} PA Upper Legs", ++count); b.CreateOrReplaceDRR(DRR); if ((numIsos - numVMATIsos) == 2) { VVector infIso = new VVector(); //the element at numVMATIsos in isoLocations vector is the first AP/PA isocenter infIso.x = isoLocations.ElementAt(numVMATIsos).x; infIso.y = isoLocations.ElementAt(numVMATIsos).y; double x1 = -200.0; //if the distance between the matchline and the inferior edge of the target is < 600 mm, set the beams in the second isocenter (inferior-most) to be half-beam blocks if (selectedSS.Structures.First(x => x.Id.ToLower() == "matchline").CenterPoint.z - target.MeshGeometry.Positions.Min(p => p.Z) < 600.0) { infIso.z = isoLocations.ElementAt(numVMATIsos).z - 200.0; x1 = 0.0; } else { infIso.z = isoLocations.ElementAt(numVMATIsos).z - 390.0; } //fit x1 jaw to extend of patient x2 = infIso.z - (target.MeshGeometry.Positions.Min(p => p.Z) - 20.0); if (x2 > 200.0) { x2 = 200.0; } else if (x2 < 10.0) { x2 = 10.0; } //set MLC positions MLCpos = new float[2, 60]; for (int i = 0; i < 60; i++) { MLCpos[0, i] = (float)(x1); MLCpos[1, i] = (float)(x2); } //AP field if (singleAPPAplan) { b = legs_planUpper.AddMLCBeam(ebmpStatic, MLCpos, new VRect <double>(x1, -200.0, x2, 200.0), 90.0, 0.0, 0.0, infIso); b.Id = String.Format("{0} AP Lower Legs", ++count); b.CreateOrReplaceDRR(DRR); //PA field b = legs_planUpper.AddMLCBeam(ebmpStatic, MLCpos, new VRect <double>(x1, -200.0, x2, 200.0), 90.0, 180.0, 0.0, infIso); b.Id = String.Format("{0} PA Lower Legs", ++count); b.CreateOrReplaceDRR(DRR); } else { //create a new legs plan if the user wants to separate the two APPA isocenters into separate plans ExternalPlanSetup legs_planLower = tbi.AddExternalPlanSetup(selectedSS); legs_planLower.Id = String.Format("{0} Lower Legs", numIsos); legs_planLower.SetPrescription(prescription.Item1, prescription.Item2, 1.0); legs_planLower.SetCalculationModel(CalculationType.PhotonVolumeDose, calculationModel); b = legs_planLower.AddMLCBeam(ebmpStatic, MLCpos, new VRect <double>(x1, -200.0, x2, 200.0), 90.0, 0.0, 0.0, infIso); b.Id = String.Format("{0} AP Lower Legs", ++count); b.CreateOrReplaceDRR(DRR); //PA field b = legs_planLower.AddMLCBeam(ebmpStatic, MLCpos, new VRect <double>(x1, -200.0, x2, 200.0), 90.0, 180.0, 0.0, infIso); b.Id = String.Format("{0} PA Lower Legs", ++count); b.CreateOrReplaceDRR(DRR); } } //MessageBox.Show("calculating dose"); //legs_planUpper.CalculateDose(); //const int nChars = 256; //StringBuilder Buff = new StringBuilder(nChars); //IntPtr handle = GetForegroundWindow(); //if (GetWindowText(handle, Buff, nChars) > 0) //{ // MessageBox.Show(Buff.ToString()); //} } MessageBox.Show("Beams placed successfully!\nPlease proceed to the optimization setup tab!"); }
static void Planning(Application curapp, Patient curpat, StructureSet curstructset, List <Tuple <string, string, float, string> > TargetStructures, List <string> AllowedNonTargetStructures, float RxDose, int NFractions) { curpat.BeginModifications(); string IDofptv_low = string.Empty; string IDofptv_high = string.Empty; // StringBuilder sb = new StringBuilder(); //Check structure nameing sb.AppendLine("Check Structure Naming"); sb.AppendLine("Structure ID \tIs Standard Name?"); foreach (Structure curstruct in curstructset.Structures.OrderBy(x => x.Id)) { if (curstruct.DicomType == "PTV" | curstruct.DicomType == "CTV" | curstruct.DicomType == "GTV") { if (TargetStructures.Where(x => x.Item1 == curstruct.Id).Any() || TargetStructures.Where(x => curstruct.DicomType.ToString() + "_" + x.Item3 + (x.Item4 == "Gy" ? "00" : string.Empty) == curstruct.Id).Any()) { sb.AppendLine(curstruct.Id.PadRight(curstruct.Id.Length < 20 ? 20 - curstruct.Id.Length : 0) + "\tYes"); } else { sb.AppendLine(curstruct.Id.PadRight(curstruct.Id.Length < 20 ? 20 - curstruct.Id.Length : 0) + "\tNo"); } } else { if (AllowedNonTargetStructures.Where(x => x == curstruct.Id).Any() || curstruct.Id.ToString().StartsWith("z")) { sb.AppendLine(curstruct.Id.PadRight(curstruct.Id.Length < 20 ? 20 - curstruct.Id.Length : 0) + "\tYes"); } else { sb.AppendLine(curstruct.Id.PadRight(curstruct.Id.Length < 20 ? 20 - curstruct.Id.Length : 0) + "\tNo"); } } } sb.AppendLine(); sb.AppendLine("Press OK to continue with creating optimization structures"); System.Windows.MessageBox.Show(sb.ToString()); //Create optimization structures if (TargetStructures.Where(x => x.Item1 == "PTV_Low").Any()) { IDofptv_low = TargetStructures.Where(x => x.Item1 == "PTV_Low").Select(x => x.Item2).First(); //Get the ID of the structure identified as PTV_Low } if (TargetStructures.Where(x => x.Item1 == "PTV_High").Any()) { IDofptv_high = TargetStructures.Where(x => x.Item1 == "PTV_High").Select(x => x.Item2).First(); //Get the ID of the structure identified as PTV_High } Structure ptv_low = null; Structure ptv_high = null; //Check that PT_High structure exists, issue warning if it does not. if (curstructset.Structures.Where(x => x.Id == IDofptv_low).Any()) { ptv_low = curstructset.Structures.Where(x => x.Id == IDofptv_low).First(); } else { System.Windows.MessageBox.Show("Did not find a PTV_High Structure. Fix before proceeding"); } //Check that PT_Low structure exists, issue warning if it does not. if (curstructset.Structures.Where(x => x.Id == IDofptv_high).Any()) { ptv_high = curstructset.Structures.Where(x => x.Id == IDofptv_high).First(); } else { System.Windows.MessageBox.Show("Did not find a PTV_Low Structure. Fix before proceeding"); } //Creation of optimization structures. If a copy already exitst delete it first. //Optimization structure for the PTV_Low volume is named zPTV_Low^Opt if (curstructset.Structures.Where(x => x.Id.Contains("zPTV_Low^Opt")).Any()) { curstructset.RemoveStructure(curstructset.Structures.Where(x => x.Id.Contains("zPTV_Low^Opt")).First()); } Structure zptvlowopt = curstructset.AddStructure("ORGAN", "zPTV_Low^Opt"); //Dose limiting annulus (DLA) structure is used to make the prescribed dose conformal. DLA for PTV_Low is named zDLA_Low if (curstructset.Structures.Where(x => x.Id.Contains("zDLA__Low")).Any()) { curstructset.RemoveStructure(curstructset.Structures.Where(x => x.Id.Contains("zDLA__Low")).First()); } Structure zdlalow = curstructset.AddStructure("ORGAN", "zDLA__Low"); //Optimization structure for the PTV_High volume is named zPTV_High^Opt if (curstructset.Structures.Where(x => x.Id.Contains("zPTV_High^Opt")).Any()) { curstructset.RemoveStructure(curstructset.Structures.Where(x => x.Id.Contains("zPTV_High^Opt")).First()); } Structure zptvhighopt = curstructset.AddStructure("ORGAN", "zPTV_High^Opt"); //Dose limiting annulus (DLA) structure is used to make the prescribed dose conformal. DLA for PTV_High is named zDLA_High if (curstructset.Structures.Where(x => x.Id.Contains("zDLA__High")).Any()) { curstructset.RemoveStructure(curstructset.Structures.Where(x => x.Id.Contains("zDLA__High")).First()); } Structure zdlahigh = curstructset.AddStructure("ORGAN", "zDLA__High"); //Make zPTV_High^Opt from PTV_High and boolean out the rectum zptvhighopt.SegmentVolume = ptv_high.Margin(0.0f); zptvhighopt.SegmentVolume = zptvhighopt.Sub(curstructset.Structures.Where(x => x.Id.Contains("Rectum")).Single());//Boolean the Rectum out of the high dose ptv optimization structure //Make zPTV_Low^Opt from PTV_Low and boolean out the PTV_High structure zptvlowopt.SegmentVolume = ptv_low.Margin(0.0f); zptvlowopt.SegmentVolume = zptvlowopt.Sub(ptv_high.Margin(1.0f));//Boolean the ptv_high out of ptv_low optimization structure //Make a dose limiting annulus arround the low dose ptv optimization structure zdlalow.SegmentVolume = zptvlowopt.SegmentVolume; zdlalow.SegmentVolume = zdlalow.Margin(10.0f); zdlalow.SegmentVolume = zdlalow.Sub(zptvlowopt.Margin(1.0f)); zdlalow.SegmentVolume = zdlalow.Sub(zptvhighopt.Margin(5.0f)); //Make a dose limiting annulus arround the high dose ptv optimization structure zdlahigh.SegmentVolume = zptvhighopt.SegmentVolume; zdlahigh.SegmentVolume = zdlahigh.Margin(10.0f); zdlahigh.SegmentVolume = zdlahigh.Sub(zptvhighopt.Margin(1.0f)); sb = new StringBuilder(); sb.AppendLine("Done with creating optimization strutures"); sb.AppendLine("Click OK to proceed with setting up course and VMAT plan"); System.Windows.MessageBox.Show(sb.ToString()); //Add course Course curcourse; if (curpat.Courses.Where(x => x.Id == "AutoPlan").Any()) { curcourse = curpat.Courses.Where(x => x.Id == "AutoPlan").Single(); } else { curcourse = curpat.AddCourse(); curcourse.Id = "AutoPlan"; } //Remove PlanSetup if it exists then create new plan setup if (curcourse.PlanSetups.Where(x => x.Id == "AutoPlanVMAT").Any()) { curcourse.RemovePlanSetup(curcourse.PlanSetups.Where(x => x.Id == "AutoPlanVMAT").Single()); } ExternalPlanSetup cureps = curcourse.AddExternalPlanSetup(curstructset); cureps.Id = "AutoPlanVMAT"; //Add VMAT Beams VVector isocenter = new VVector(Math.Round(ptv_high.CenterPoint.x / 10.0f) * 10.0f, Math.Round(ptv_high.CenterPoint.y / 10.0f) * 10.0f, Math.Round(ptv_high.CenterPoint.z / 10.0f) * 10.0f); ExternalBeamMachineParameters ebmp = new ExternalBeamMachineParameters("Truebeam", "6X", 600, "ARC", null); Beam VMAT1 = cureps.AddArcBeam(ebmp, new VRect <double>(-100, -100, 100, 100), 30, 181, 179, GantryDirection.Clockwise, 0, isocenter); Beam VMAT2 = cureps.AddArcBeam(ebmp, new VRect <double>(-100, -100, 100, 100), 330, 179, 181, GantryDirection.CounterClockwise, 0, isocenter); VMAT1.Id = "CW"; VMAT2.Id = "CCW"; VMAT1.FitCollimatorToStructure(new FitToStructureMargins(10), ptv_low, true, true, false); VMAT2.FitCollimatorToStructure(new FitToStructureMargins(10), ptv_low, true, true, false); cureps.SetCalculationModel(CalculationType.PhotonVMATOptimization, "PO_15014"); cureps.SetPrescription(NFractions, new DoseValue(RxDose / NFractions, "Gy"), 1); curapp.SaveModifications(); sb = new StringBuilder(); sb.AppendLine("Done with setting up course and VMAT plan"); sb.AppendLine("Click OK to proceed with plan optimization"); System.Windows.MessageBox.Show(sb.ToString()); float doseobjectivevalue_low = TargetStructures.Where(x => x.Item1 == "PTV_Low").Select(x => x.Item3).First(); float doseobjectivevalue_high = TargetStructures.Where(x => x.Item1 == "PTV_High").Select(x => x.Item3).First(); cureps.OptimizationSetup.AddPointObjective(zptvlowopt, OptimizationObjectiveOperator.Lower, new DoseValue(doseobjectivevalue_low, "Gy"), 100, 100); cureps.OptimizationSetup.AddPointObjective(zptvlowopt, OptimizationObjectiveOperator.Upper, new DoseValue(doseobjectivevalue_low + 3.0f, "Gy"), 30, 50); cureps.OptimizationSetup.AddPointObjective(zdlalow, OptimizationObjectiveOperator.Upper, new DoseValue(doseobjectivevalue_low, "Gy"), 0, 50); cureps.OptimizationSetup.AddPointObjective(zptvhighopt, OptimizationObjectiveOperator.Lower, new DoseValue(doseobjectivevalue_high, "Gy"), 100, 120); cureps.OptimizationSetup.AddPointObjective(zptvhighopt, OptimizationObjectiveOperator.Upper, new DoseValue(doseobjectivevalue_high + 2.0f, "Gy"), 0, 100); cureps.OptimizationSetup.AddPointObjective(zdlahigh, OptimizationObjectiveOperator.Upper, new DoseValue(doseobjectivevalue_high, "Gy"), 0, 50); cureps.OptimizationSetup.AddNormalTissueObjective(80.0f, 0.0f, 100.0f, 40.0f, 0.05f); OptimizerResult optresult = cureps.OptimizeVMAT(new OptimizationOptionsVMAT(OptimizationIntermediateDoseOption.NoIntermediateDose, string.Empty)); sb = new StringBuilder(); sb.AppendLine("VMAT optimization is done"); sb.AppendLine("NIterattions:" + optresult.NumberOfIMRTOptimizerIterations.ToString() + " , ObjectiveFunctionValue: " + optresult.TotalObjectiveFunctionValue.ToString()); sb.AppendLine("Click OK to proceed with optimization"); cureps.OptimizeVMAT(); curapp.SaveModifications(); sb = new StringBuilder(); sb.AppendLine("Done with optimization"); sb.AppendLine("Click OK to proceed with dose calculation"); System.Windows.MessageBox.Show(sb.ToString()); cureps.CalculateDose(); //cureps.PlanNormalizationValue = 99.0f; curapp.SaveModifications(); sb = new StringBuilder(); sb.AppendLine("Done with dose calculation"); if (cureps.StructureSet.Structures.Where(x => x.Id == "Rectum").Any()) { Structure reportstructure = cureps.StructureSet.Structures.Where(x => x.Id == "Rectum").First(); sb.AppendLine("Rectum:V65Gy[%]:" + cureps.GetVolumeAtDose(reportstructure, new DoseValue(65.0f, DoseValue.DoseUnit.Gy), VolumePresentation.Relative).ToString()); } sb.AppendLine("PTV_High:D95%[Gy]:" + cureps.GetDoseAtVolume(ptv_high, 95.0f, VolumePresentation.Relative, DoseValuePresentation.Absolute).ToString()); sb.AppendLine("Click OK to finish script"); System.Windows.MessageBox.Show(sb.ToString()); }
public static void BeamMaker(ref ExternalPlanSetup plan, StructureSet ss, double prescriptionDose, Tuple <string, string, bool> beamParams) { //First check if beams already exist foreach (Beam beam in plan.Beams.ToList()) { plan.RemoveBeam(beam); } string treatmentCenter = beamParams.Item1; string treatmentArea = beamParams.Item2; string beamName = ""; int doseRate = 600; //Get the right beam name if (treatmentCenter == "BC Cancer - Surrey") { beamName = "fv" + treatmentArea.Replace(" ", "") + "TB"; beamName = beamName.ToUpper(); doseRate = 400; } else if (treatmentCenter == "BC Cancer - Vancouver") { beamName = "Va" + treatmentArea.Replace(" ", ""); } //need to create two arc beams, and make sure they fit to PTVs. ExternalBeamMachineParameters ebmp = new ExternalBeamMachineParameters(beamName, "6X", doseRate, "ARC", null); //First need to find the isocentre, which will be in the main PTV //find all the ptvs List <Structure> ptvs = new List <Structure>(); List <Structure> mainPTVs = new List <Structure>(); foreach (Structure structure in ss.Structures) { if (structure.Name.ToLower().Contains("ptv")) { ptvs.Add(structure); if (StringOperations.FindPTVNumber(structure.Name) == prescriptionDose / 100) //Check if receiving prescription dose { mainPTVs.Add(structure); } } } //Check if it's receiving the prescription dose. If so, set isocentre here. If there is more than //One receiving the prescription dose, set at the average between the two. VVector isocentre; if (mainPTVs.Count > 0) { double x = 0; double y = 0; double z = 0; //Find average x,y,z for (int i = 0; i < mainPTVs.Count; i++) { x += Math.Round(mainPTVs[i].CenterPoint.x / 10.0f) * 10.0f / mainPTVs.Count; y += Math.Round(mainPTVs[i].CenterPoint.y / 10.0f) * 10.0f / mainPTVs.Count; z += Math.Round(mainPTVs[i].CenterPoint.z / 10.0f) * 10.0f / mainPTVs.Count; } isocentre = new VVector(x, y, z); } else { isocentre = new VVector(0, 0, 0); } //Create two VMAT beams //First get the right jaw dimensions: VRect <double> jaws1 = FitJawsToTarget(isocentre, plan, ptvs, 30, 0); VRect <double> jaws2 = FitJawsToTarget(isocentre, plan, ptvs, 330, 0); Beam vmat1 = plan.AddArcBeam(ebmp, jaws1, 30, 180.1, 179.9, GantryDirection.Clockwise, 0, isocentre); Beam vmat2 = plan.AddArcBeam(ebmp, jaws2, 330, 179.9, 180.1, GantryDirection.CounterClockwise, 0, isocentre); vmat1.Id = "PC_vmat1"; vmat2.Id = "PC_vmat2"; }