PQM - Targets - Referencing "totalPrescribedDose.Dose"

Nov 3, 2016 at 6:26 PM
In the PQM example code, you can call totalPrescribedDose.Dose only when working with the TARGET volumes. When working with the OARs this property is not available.

Would anyone know a way to get the prescription dose when working in the OAR portion?

Thanks,

m
Nov 7, 2016 at 2:07 PM
I ran into the same problem..... hoping someone can point me in the right direction.
Nov 8, 2016 at 7:24 PM
Edited Nov 8, 2016 at 7:43 PM
Hi m,

Very fun question you have here. The first thing I'd like to mention is that the PlanQualityMetrics script was designed to handle both plans and plan sums efficiently, so there will be quite a bit of changes that need to be made in order to allow the functionality you ask for because it will need to be done for both plans and plansums. If you take a look into the code, the first thing we need to understand to resolve your question is how the target knows the prescription dose at all. Take a look inside of the cs file "PQMReporter.cs":

How target gets prescription dose:
It seems that lines 173 - 234 are dealing with plan sums while line 245 starts the same method but with a single plan. If you want this to work for both, you'll have to add what's coming below to both, but I'm just going to look into the single plan for simplicity. If you check the WriteDoseStatisticsXML_Target method on line 244, you'll notice on line 259, the following code snippet starts.
if(target != null)
{
    PlanQualityMetric[] PQMs = Target.getPQMs(plan.TotalPrescribedDose);
    string[] search = { target.Id };
    addStructurePQM(plan, ss, search, PQMs, writer);
}
Looking at line 3 of the code snippet (line 261 of the PQM code), we can see that the TotalPrescribedDose is a property of the plan, not the target structure. Upon looking at the script called "UserDefinedMetrics.cs", we can see how this implementation is used.
public class Target
{
    public static PlanQualityMetric[] getPQMs(DoseValue totalPrescribedDose)
    {
        PlanQualityMetric[] PQMs =
        {
            new VolumeAtDose("V[95%(Rx) > 95%]", new DoseValue(totalPrescribedDose.Dose*.95, totalPrescribedDose.Unit), 95.0, 1.0, VolumePresentation.Relative, PQMUtilities.LimitType.lower)
        };
    }
}
So here we can see there is a method implemented within the Target class that the prescription dose is being passed to. Looking directly below at the Rectum class, we see no such method, but we can certainly create one by mimicking what we've seen in the target.
Applying the prescription dose to the rectum:
The first thing to note is that in the WriteDoseStatisticsXML_Prostate2GyOrLess method, there is a class called PlanningItem. The planning Item class does not contain a TotalPrescribedDose property, so you need to change this to PlanSetup.
i.e. change
protected void WriteDoseStatisticsXML_Prostate2GyOrLess(Patient patient, StructureSet ss, PlanningItem plan, XMLWriter writer)
to
protected void WriteDoseStatisticsXML_Prostate2GyOrLess(Patient patient, StructureSet ss, PlanSetup plan, XMLWriter writer)
Now we have a method that will work if we pass in plan, but what happens if we pass in a plan sum? The IDE should be telling you there is something wrong with a line of code, and that line way at the bottom is stating that the code cannot pass a plansum. So, copy the entire WriteDoseStatisticsXML_Prostate2GyOrLess method (from line 276- 293) and paste it below line 293. Now change the WriteDoseSatisticsXML_Prosate2GyOrLess to accept a PlanSum instead of a PlanSetup.

Also in the "PQMReporter.cs" file, we can add the same method of getting PQMs to the Rectum portion. See line 279: if we change
addStructurePQM(plan, ss, Rectum.SearchIds, Rectum.PQMs, writer);
to
addStructurePQM(plan, ss, Rectum.SearchIds, Rectum.getPQMs(plan.TotalPrescribedDose), writer);
then we pass the prescription dose to the "UserDefinedMetrics.cs" file. For the method that takes the plansum, you're going to have to sum the doses to get the overall prescribed dose. like this (line 297).
var doses = from PlanSetup p in plan.PlanSetups
                      select p.TotalPrescribedDose;
            double totalPrescribedDose = doses.Sum(x => (x.Unit == DoseValue.DoseUnit.Gy) ? x.Dose : x.Dose / 100);
           addStructurePQM(plan, ss, Rectum.searchIds, Rectum.getPQMs(new DoseValue(totalPrescribedDose, DoseValue.DoseUnit.Gy)), writer);
Overall line 276 -314 should look like this
protected void WriteDoseStatisticsXML_Prostate2GyOrLess(Patient patient, StructureSet ss, PlanSetup plan, XmlWriter writer)
    {
      // find stats for the rectum structure
      addStructurePQM(plan, ss, Rectum.searchIds, Rectum.getPQMs(plan.TotalPrescribedDose), writer);

      // find stats for the bladder structure
      addStructurePQM(plan, ss, Bladder.searchIds, Bladder.PQMs, writer);

      // find stats for the penile bulb structure
      addStructurePQM(plan, ss, PenileBulb.searchIds, PenileBulb.PQMs, writer);

      // find stats for the small bowel
      addStructurePQM(plan, ss, SmallBowel.searchIds, SmallBowel.PQMs, writer);

      // find stats for femurs
      addStructurePQM(plan, ss, FemurLeft.searchIds, FemurLeft.PQMs, writer);
      addStructurePQM(plan, ss, FemurRight.searchIds, FemurRight.PQMs, writer);
    }
    protected void WriteDoseStatisticsXML_Prostate2GyOrLess(Patient patient, StructureSet ss, PlanSum plan, XmlWriter writer)
    {
            // find stats for the rectum structure
            var doses = from PlanSetup p in plan.PlanSetups
                      select p.TotalPrescribedDose;
            double totalPrescribedDose = doses.Sum(x => (x.Unit == DoseValue.DoseUnit.Gy) ? x.Dose : x.Dose / 100);
            addStructurePQM(plan, ss, Rectum.searchIds, Rectum.getPQMs(new DoseValue(totalPrescribedDose, DoseValue.DoseUnit.Gy)), writer);

            // find stats for the bladder structure
            addStructurePQM(plan, ss, Bladder.searchIds, Bladder.PQMs, writer);

            // find stats for the penile bulb structure
            addStructurePQM(plan, ss, PenileBulb.searchIds, PenileBulb.PQMs, writer);

            // find stats for the small bowel
            addStructurePQM(plan, ss, SmallBowel.searchIds, SmallBowel.PQMs, writer);

            // find stats for femurs
            addStructurePQM(plan, ss, FemurLeft.searchIds, FemurLeft.PQMs, writer);
            addStructurePQM(plan, ss, FemurRight.searchIds, FemurRight.PQMs, writer);
    }
Nov 8, 2016 at 7:42 PM
Edited Nov 8, 2016 at 7:54 PM
Not done yet. The debugger is telling us that we still have some issues with our Rectum.getPQMs method. It doesn't exist. Now we need to fix the rectum class to look like the target class by implementing this method into the "UserDefinedMetrics.cs" file. I'll just post before and after since this is getting long. On line 59 before
    public static class Rectum
    {
      public static PlanQualityMetric[] PQMs = 
    {
        new VolumeAtDose("V50 < 50%", new DoseValue(5000.0, DoseValue.DoseUnit.cGy), 50.0, 1.05),
        new VolumeAtDose("V60 < 35%", new DoseValue(6000.0, DoseValue.DoseUnit.cGy), 35.0, 1.05),
        new VolumeAtDose("V65 < 25%", new DoseValue(6500.0, DoseValue.DoseUnit.cGy), 25.0, 1.05),
        new VolumeAtDose("V70 < 20%", new DoseValue(7000.0, DoseValue.DoseUnit.cGy), 20.0, 1.0),
        new VolumeAtDose("V75 < 15%", new DoseValue(7500.0, DoseValue.DoseUnit.cGy), 15.0, 1.0)
    };
public static string[] searchIds = { "Rectum", "CT_RECTUM" };
        // Put a comma separated list here of all variations of the IDs used for the "Rectum"
        // structure in your clinic. The search for matching IDs is case insensitive, the PQM 
        // script searches for all case variations of "rectum", "RECTUM", etc.
    };
After
        public static class Rectum
        {
            public static PlanQualityMetric[] getPQMs(DoseValue totalPrescribedDose)
            {
                PlanQualityMetric[] PQMs =
              {
                    new VolumeAtDose("V[50%(Rx) < 50%]", new DoseValue(totalPrescribedDose.Dose*0.50, totalPrescribedDose.Unit), 50.0, 1.0),
                new VolumeAtDose("V50 < 50%", new DoseValue(5000.0, DoseValue.DoseUnit.cGy), 50.0, 1.05),
                new VolumeAtDose("V60 < 35%", new DoseValue(6000.0, DoseValue.DoseUnit.cGy), 35.0, 1.05),
                new VolumeAtDose("V65 < 25%", new DoseValue(6500.0, DoseValue.DoseUnit.cGy), 25.0, 1.05),
                new VolumeAtDose("V70 < 20%", new DoseValue(7000.0, DoseValue.DoseUnit.cGy), 20.0, 1.0),
                new VolumeAtDose("V75 < 15%", new DoseValue(7500.0, DoseValue.DoseUnit.cGy), 15.0, 1.0)
            };
                return PQMs;
        }
      public static string[] searchIds = { "Rectum", "CT_RECTUM" };
        // Put a comma separated list here of all variations of the IDs used for the "Rectum"
        // structure in your clinic. The search for matching IDs is case insensitive, the PQM 
        // script searches for all case variations of "rectum", "RECTUM", etc.
    };
This was a little more work than I thought it would be reading the question, but it seems as though you can copy this for each structure. You already have your multiple implementations of the WriteDoseStatistics_Prostate2GyorLess Method, and you won't have to recalculate the prescribed dose for a plan sum for each separate structure, thus for additional structures, it will be much easier to change to make them act as the Rectum structure does.

Thanks,
Nov 18, 2016 at 6:08 PM
Wow no way I would have figured that out on my own! Thanks a bunch!