// Compute dose public void ComputeDose() { // Set normalization value verifPln.PlanNormalizationValue = currPln.PlanNormalizationValue; // Set the plan calculation model verifPln.SetCalculationModel(CalculationType.PhotonVolumeDose, currPln.PhotonCalculationModel); Dictionary <String, String> currPlnCalcModels = currPln.GetCalculationOptions(currPln.PhotonCalculationModel); foreach (KeyValuePair <String, String> calcModel in currPlnCalcModels) { verifPln.SetCalculationOption(currPln.PhotonCalculationModel, calcModel.Key, calcModel.Value); } // Compute dose // For plan containing non-IMRT beams if (verifPln.Beams.Any(b => b.MLCPlanType == MLCPlanType.ArcDynamic || b.MLCPlanType == MLCPlanType.Static)) { verifPln.CalculateDose(); // Compute dose for non-IMRT type beams // Correct for MU by changing beam weighting foreach (Beam verifBm in verifPln.Beams) { BeamParameters verifBmParam = verifBm.GetEditableParameters(); verifBmParam.WeightFactor = muValues.First(mv => mv.Key == verifBm.Id).Value.Value / verifBm.Meterset.Value; verifBm.ApplyParameters(verifBmParam); } } // For all other IMRT plans else { verifPln.CalculateDoseWithPresetValues(muValues); // Compute dose for IMRT type beams } }
static void CopyJawAndLeafPositions(Beam from, BeamParameters to) { IEnumerable <ControlPointParameters> cps = to.ControlPoints; int ix = 0; foreach (var cp in from.ControlPoints) { cps.ElementAt(ix).JawPositions = cp.JawPositions; cps.ElementAt(ix).LeafPositions = cp.LeafPositions; ix++; } }
// Copy the MLC and jaw positions from the beam in approved plan to the beam in verificiation plan private BeamParameters copyControlPoints(Beam currBm, Beam verifBm) { BeamParameters verifBmParam = verifBm.GetEditableParameters(); for (int i_CP = 0; i_CP < verifBm.ControlPoints.Count(); i_CP++) { verifBmParam.ControlPoints.ElementAt(i_CP).LeafPositions = currBm.ControlPoints.ElementAt(i_CP).LeafPositions; verifBmParam.ControlPoints.ElementAt(i_CP).JawPositions = currBm.ControlPoints.ElementAt(i_CP).JawPositions; } verifBmParam.WeightFactor = currBm.WeightFactor; return(verifBmParam); }
// Compute isocenter shift on verification plan public VVector ComputeIsoShift(QASettings qaSet) { verifPlnIso = qaSet.pIso; // Set iso center from settings List <Double> infFldEdges = new List <Double>(); // create list to store inferior field edge for each beam foreach (Beam bm in currPln.Beams) { if (bm.MetersetPerGy > 0) { BeamParameters bmParam = bm.GetEditableParameters(); Double collAng = bmParam.ControlPoints.First().CollimatorAngle; VRect <Double> collPos = bmParam.ControlPoints.First().JawPositions; // Compute the approximate inferior field edge position shaped by the collimator and MLC Double cosAng = Math.Cos(Math.PI * collAng / 180.0); Double sinAng = Math.Sin(Math.PI * collAng / 180.0); Double DelX = 400.0; //field edge can be defined by one X and one Y jaw Double DelY = 400.0; if (cosAng > 0.01) { DelY = -collPos.Y1 / cosAng; } if (cosAng < -0.01) { DelY = -collPos.Y2 / cosAng; } //past 90 deg, Y2 defines inferior edge if (sinAng > 0.01) { DelX = -collPos.X1 / sinAng; } //ccw rotation (respect to BEV), X1 defines inferior edge if (sinAng < -0.01) { DelX = -collPos.X2 / sinAng; } //cw rotation (respect to BEV), X2 defines inferior edge infFldEdges.Add(Math.Min(DelX, DelY)); //use the minimum between DelX and DelY as the approximate inferior field edge } } if (infFldEdges.Count == 0) { return(new VVector(Double.NaN, Double.NaN, Double.NaN)); } // no field edge found, either beam parameters invalid or no MU Double MedInfFldEdge = GetMedian(infFldEdges); if (MedInfFldEdge > qaSet.pLen) // if the field edge is longer than the length of qa phantom { verifPlnIso.z = verifPlnIso.z + Math.Ceiling(MedInfFldEdge / 10 - qaSet.pLen / 10) * 10; // shift the iso superiorly (cm as minimum shift unit) } return(verifPlnIso); }
// Add beam to the plan public Beam AddBeamToVerifPlan(Beam currBm) { if (verifPln == null) { return(null); } else { bmTech = getMLCBmTechnique(currBm); // find beam technique // Create machine parameters String energy = currBm.EnergyModeDisplayName; String fluence = null; Match EMode = Regex.Match(currBm.EnergyModeDisplayName, @"^([0-9]+[A-Z]+)-?([A-Z]+)?", RegexOptions.IgnoreCase); //format is... e.g. 6X(-FFF) if (EMode.Success) { if (EMode.Groups[2].Length > 0) // fluence mode { energy = EMode.Groups[1].Value; fluence = EMode.Groups[2].Value; } // else normal modes uses default in decleration } ExternalBeamMachineParameters machParam = new ExternalBeamMachineParameters(currBm.TreatmentUnit.Id.ToString(), energy, currBm.DoseRate, currBm.Technique.Id.ToString(), fluence); // Define collimator, gantry and couch angles // Double gantryAng = currBm.ControlPoints.First().GantryAngle; Double collAng = currBm.ControlPoints.First().CollimatorAngle; Double couchAng = 0.0; // MU values for each control point // IEnumerable <double> muSet = currBm.ControlPoints.Select(cp => cp.MetersetWeight).ToList(); // Add beam MU to the list of MU values // muValues.Add(new KeyValuePair <string, MetersetValue>(currBm.Id, currBm.Meterset)); // Start adding beam based on beam technique // if (bmTech == "StaticMLC") { Beam verifBm = verifPln.AddMLCBeam(machParam, new float[2, 60], new VRect <double>(-10.0, -10.0, 10.0, 10.0), collAng, gantryAng, couchAng, verifPlnIso); verifBm.Id = currBm.Id; BeamParameters ctrPtParam = copyControlPoints(currBm, verifBm); verifBm.ApplyParameters(ctrPtParam); return(verifBm); } else if (bmTech == "StaticSegWin") { Beam verifBm = verifPln.AddMultipleStaticSegmentBeam(machParam, muSet, collAng, gantryAng, couchAng, verifPlnIso); verifBm.Id = currBm.Id; BeamParameters ctrPtParam = copyControlPoints(currBm, verifBm); verifBm.ApplyParameters(ctrPtParam); return(verifBm); } else if (bmTech == "StaticSlidingWin") { Beam verifBm = verifPln.AddSlidingWindowBeam(machParam, muSet, collAng, gantryAng, couchAng, verifPlnIso); verifBm.Id = currBm.Id; BeamParameters ctrPtParam = copyControlPoints(currBm, verifBm); verifBm.ApplyParameters(ctrPtParam); return(verifBm); } else if (bmTech == "ConformalArc") { Beam verifBm = verifPln.AddConformalArcBeam(machParam, collAng, currBm.ControlPoints.Count(), currBm.ControlPoints.First().GantryAngle, currBm.ControlPoints.Last().GantryAngle, currBm.GantryDirection, couchAng, verifPlnIso); verifBm.Id = currBm.Id; BeamParameters ctrPtParam = copyControlPoints(currBm, verifBm); verifBm.ApplyParameters(ctrPtParam); return(verifBm); } else if (bmTech == "VMAT") { Beam verifBm = verifPln.AddVMATBeam(machParam, muSet, collAng, currBm.ControlPoints.First().GantryAngle, currBm.ControlPoints.Last().GantryAngle, currBm.GantryDirection, couchAng, verifPlnIso); verifBm.Id = currBm.Id; BeamParameters ctrPtParam = copyControlPoints(currBm, verifBm); verifBm.ApplyParameters(ctrPtParam); return(verifBm); } else // null { return(null); } } }
private void newPlan_btn_Click(object sender, RoutedEventArgs e) { Course c2 = null; if (p.Courses.Where(x => x.Id == course_txt.Text).Count() == 0) { c2 = p.AddCourse(); c2.Id = course_txt.Text; } else { c2 = p.Courses.First(x => x.Id == course_txt.Text); } ExternalPlanSetup ps2 = c2.AddExternalPlanSetup(ps.StructureSet); //doses should all be the same.t //ps2.DosePerFraction = ps.DosePerFraction; //read only //ps2.TotalDose = ps.TotalDose;//read only ps2.SetPrescription( (int)ps.NumberOfFractions, ps.DosePerFraction, ps.TreatmentPercentage); //I've chnaged this down below. Currently, the calculation will take place with preset monitor units //making it the same as the plan its copied from but then I scale the normaliztation factor by 1.3% because the discover is not in the beam. ps2.PlanNormalizationValue = val * ps.PlanNormalizationValue; //val = (double)Convert.ToDouble(Input.Text); bool valid = double.TryParse(Input.Text.ToString(), out val); ps2.PlanNormalizationValue = val + no_norm; //ps2.TreatmentPercentage = ps.TreatmentPercentage;//read only //ps2.AddMLCBeam() ps2.Id = plan_txt.Text; List <KeyValuePair <string, MetersetValue> > mu_list = new List <KeyValuePair <string, MetersetValue> >(); /*foreach (FieldInfo fi in fields)########################*/ for (int t = 0; t < fields.Count(); t++) { FieldInfo fi = fields[t]; Beam b2; if (fi.gantry_direction == 0) { b2 = ps2.AddSlidingWindowBeam(fi.Ebmp, fi.cpInfos.Select(x => x.meterSet), fi.collAngle, fi.gantry, fi.couch, fi.isocenter); } else { b2 = ps2.AddVMATBeam(fi.Ebmp, fi.cpInfos.Select(x => x.meterSet), fi.collAngle, fi.gantry, fi.gantry_stop, fi.gantry_direction, fi.couch, fi.isocenter); } int cploc = 0; //if (fi.applicator != null) { b2.Applicator = fi.applicator; } BeamParameters beamp = fi.bp; //b2.ApplyParameters(new BeamParameters(ControlPoint cp)) //int cploc = 0; //foreach (cpInfo cpi in fi.cpInfos) //double MU_old = 0; foreach (ControlPointParameters cpp in beamp.ControlPoints) //for(int xx=0; xx< beamp.ControlPoints.Count(); xx++) { /* ControlPointParameters cpp= beamp.ControlPoints[xx];*/ float[,] leafPos = new float[2, 60]; int leafloc = 0; double x1 = cpp.JawPositions.X1; double x2 = cpp.JawPositions.X2; cpInfo cpi = fi.cpInfos[cploc]; //foreach (cpDetail cpd in cpi.cpDetails)##################### for (int dd = 0; dd < cpi.cpDetails.Count(); dd++) { cpDetail cpd = cpi.cpDetails[dd]; //sometimes the errors show that the difference will overlap the leaves. //here we check for the overla[p and if there is n overlap, leaf B just gets set to 0.1 less than the leaf A position. //thus ignoring the deviation fort that leaf pair. if (cpd.leafB + Convert.ToSingle(cpd.deviationB) > cpd.leafA + Convert.ToSingle(cpd.deviationA)) { leafPos[1, leafloc] = cpd.leafA + (float)cpd.deviationA; leafPos[0, leafloc] = leafPos[1, leafloc] - (float)0.1; } else { /*if (cpd.leafA + (float)cpd.deviationA < x1) * { * * leafPos[1, leafloc] = (float)x1 + (float)0.5; * leafPos[0, leafloc] = (float)x1; * } * else if (cpd.leafA + (float)cpd.deviationA > x2) * { * leafPos[1, leafloc] = (float)x2; * if(cpd.leafB + (float)cpd.deviationB > x2) * { * leafPos[0, leafloc] = (float)x2 - (float)0.5; * } * else * { * leafPos[0, leafloc] = cpd.leafB + (float)cpd.deviationB; * } * } * else * { * leafPos[1, leafloc] = cpd.leafA + Convert.ToSingle(cpd.deviationA); * leafPos[0, leafloc] = cpd.leafB + (float)cpd.deviationB; * }*/ leafPos[1, leafloc] = cpd.leafA + (float)cpd.deviationA; leafPos[0, leafloc] = cpd.leafB + (float)cpd.deviationB; //leafPos[0, leafloc] = cpd.leafA + Convert.ToSingle(cpd.deviationA); //leafPos[1, leafloc] = cpd.leafB + Convert.ToSingle(cpd.deviationB); } leafloc++; } ////start with the first leaf position, and then interoplate all the rest. //float leaf_oldA = 0; //float leaf_oldB = 0; //for (int i = 0; i < cpi.cpDetails.Count(); i++) //{ // if (i == 0) // { // leafPos[0, i] = cpi.cpDetails[i].leafA; // leafPos[1, i] = cpi.cpDetails[i + 1].leafB; // leaf_oldA = leafPos[0, i]; // leaf_oldB = leafPos[1, i]; // //mU_old = cpi.meterSet[i]; // } // else // { // //let the interpolation begin. // //first the MU // } //} //beamp.SetAllLeafPositions(leafPos); //ControlPointParameters cpp = beamp.ControlPoints[cploc] cpp.LeafPositions = leafPos; //double check to see if this has to be applied every time. VMAT code is taking a long time. //********************************** b2.ApplyParameters(beamp); //********************************** cploc++; } //calculate the dose for each of the fields. mu_list.Add(new KeyValuePair <string, MetersetValue>(b2.Id, fi.MU)); } ps2.CalculateDoseWithPresetValues(mu_list); //ps2.PlanNormalizationMethod = ps.PlanNormalizationMethod;\ //need to renormalize by 1.3% in order to take into account the Discover that we cannot add to the newly calculated plan. //ps2.PlanNormalizationValue = val * ps2.PlanNormalizationValue; //val = (double)Convert.ToDouble(Input.Text); if (double.TryParse(Input.Text.ToString(), out val)) { ps2.PlanNormalizationValue = val + no_norm; } MessageBox.Show($"{plan_txt.Text} created successfully."); }