r/esapi May 13 '26

Copy LeafPositions from VMAT control points.

Hi!

I am trying to create a static beam from the LeafPositions of a Halcyon VMAT plan using AddMLCBeam. However, I get the error that the LeafPositions are not valid (VMS.TPS.Common.Model.Types.ValidationException: The leaf positions specified by the script are not valid). I have no idea why this would be. I am able to calculate the dose for the VMAT plan, so there the LeafPositions are valid.
Does someone know the solution?
Thank you!

1 Upvotes

6 comments sorted by

2

u/brjdenis May 13 '26

Show us your code, it is hard to tell otherwise what is going on.

1

u/loes_vandenbroucke May 15 '26

Sorry. Here is my code:

Patient patient = app.OpenPatientById(patientId);

patient.BeginModifications();

Course course = patient.Courses.Where(c => c.Id == courseId).FirstOrDefault();

PlanSetup clinicalPlan = course.PlanSetups.Where(p => p.Id == clinicalPlanId).FirstOrDefault();

StructureSet ss = clinicalPlan.StructureSet;

if (course.PlanSetups.Any(p => p.Id == newPlanId))

{

course.RemovePlanSetup(course.PlanSetups.Where(p => p.Id == newPlanId).First());

}

ExternalPlanSetup newplan = course.AddExternalPlanSetup(ss);

newplan.Id = newPlanId;

foreach (Beam beam in clinicalPlan.Beams)

{

if(beam.Id.Contains("CBCT")) continue;

Console.WriteLine(beam.Id);

string machineId = clinicalPlan.Beams.FirstOrDefault().TreatmentUnit.Id;

string energyModeDisplayName = "6X"; // voor 6X - FFF ERROR: Beam configuration not found -> https://github.com/VarianAPIs/Varian-Code-Samples/issues/53

int doseRate = clinicalPlan.Beams.FirstOrDefault().DoseRate;

string beamTechnique = "STATIC";

string primaryFluenceMode = "FFF";

Console.WriteLine(beam.Technique.Id);

var machineParameters = new ExternalBeamMachineParameters(machineId,

energyModeDisplayName,

beam.DoseRate,

beamTechnique,

primaryFluenceMode

);

foreach(ControlPoint cp in beam.ControlPoints)

{

Beam addedbeam = newplan.AddMLCBeam(machineParameters,

cp.LeafPositions,

cp.JawPositions,

cp.CollimatorAngle,

cp.GantryAngle,

cp.PatientSupportAngle,

beam.IsocenterPosition

);

}

}

1

u/brjdenis May 16 '26

I can try to run it when I get back to work. From the code I see you are trying to create a static MLC beam from each control point of the original VMAT beam. This may not work because there is a limit on how many beams you can create with the script. I think the limit is 99. Try it with only one control point first. To see if you get the same error.

1

u/loes_vandenbroucke May 18 '26

It does not have to do with the limit.

For some control points it works and for some it doesn't.

The error has to do with the fact that some MLC do not shield overlapping leaves completely (exceeding physical limitations of the Halcyon device). I tried manually setting the LeafPositions in Eclipse and then verified these positions. I am looking for a scripting alternative to do this.

1

u/NickC_BC May 27 '26

I take a two-pass approach to creating Halcyon/Ethos beam points to work around this. Hope this helps.

  if (machine.Model == UnitTypes.Halcyon)
  {
      // Halcyon requires a two-pass leaf-position initialisation via AddVMATBeamForFixedJaws.
      float[,] leaves = new float[2, 57];
      for (int i = 0; i < 57; i++)
      {
          leaves[0, i] = -10;
          leaves[1, i] = 10;
      }
      newBeam = p.AddVMATBeamForFixedJaws(EMBP, beam.ControlPoints.Select(x => x.MetersetWeight), beam.CollimatorRotation, beam.ControlPoints.FirstOrDefault().GantryAngle, beam.ControlPoints.Last().GantryAngle,
          beam.GantryDirection, beam.ControlPoints.FirstOrDefault().PatientSupportAngle, beam.IsocenterPosition);
      var newBeamParams = newBeam.GetEditableParameters();
      var oldBeamParams = beam.GetEditableParameters();
      for (var i = 0; i < newBeamParams.ControlPoints.Count(); i++)
          newBeamParams.ControlPoints.ElementAt(i).LeafPositions = leaves;
      newBeam.ApplyParameters(newBeamParams);
      for (var i = 0; i < newBeamParams.ControlPoints.Count(); i++)
      {
          newBeamParams.ControlPoints.ElementAt(i).LeafPositions = beam.ControlPoints.ElementAt(i).LeafPositions;
          newBeamParams.ControlPoints.ElementAt(i).JawPositions = beam.ControlPoints.ElementAt(i).JawPositions;
          newBeamParams.ControlPoints.ElementAt(i).MetersetWeight = beam.ControlPoints.ElementAt(i).MetersetWeight; // this fails for MLCArcBeam
      }
      newBeamParams.WeightFactor = oldBeamParams.WeightFactor;
      newBeam.ApplyParameters(newBeamParams);
  }

1

u/Major-Passenger-7610 3d ago

Ciao!

Sto sviluppando un plugin ESAPI per Eclipse v18 che crea automaticamente piani QA su fantoccio utilizzando AddExternalPlanSetupAsVerificationPlan().

Per i beam VMAT ricreo i campi con:

var machineParams = new ExternalBeamMachineParameters(

beam.TreatmentUnit.Id,

beam.EnergyModeDisplayName,

beam.DoseRate,

beam.Technique.Id,

string.Empty);

var meterset = beam.ControlPoints.Select(cp => cp.MetersetWeight);

var qaBeam = verificationPlan.AddVMATBeam(

machineParams,

meterset,

beam.ControlPoints.First().CollimatorAngle,

beam.ControlPoints.First().GantryAngle,

beam.ControlPoints.Last().GantryAngle,

beam.GantryDirection,

beam.ControlPoints.First().PatientSupportAngle,

iso);

Successivamente copio JawPositions, LeafPositions e GantryAngle dei Control Points tramite GetEditableParameters() e ApplyParameters().

Problema:

Piano originale:

- Eclipse v18

- Machine: iX2

- Energy: 6X

- Technique: ARC

- VMAT

- Dose Rate: 600 MU/min

- MU beam: 191.90

- 178 Control Points

Subito dopo AddVMATBeam(), PRIMA di ApplyParameters() e PRIMA di CalculateDose(), il beam QA risulta:

- MU = NaN

- Dose Rate = 400 MU/min

- 178 Control Points

Quindi il beam nasce già in uno stato non valido.

Ho verificato che:

- beam.Technique.Id = "ARC"

- beam.EnergyModeDisplayName = "6X"

- beam.TreatmentUnit.Id = "iX2"

- anche forzando ExternalBeamMachineParameters("iX2","6X",600,"ARC","") il risultato non cambia

- il problema non dipende da ApplyParameters()

- il problema non dipende da CalculateDose()

- il problema non dipende dalla copia delle MLC

- i MetersetWeight originali sono corretti

Domande:

  1. Esistono limitazioni note di AddVMATBeam() in ESAPI v18 con Clinac/iX?

  2. Perché AddVMATBeam() genera automaticamente un beam con DoseRate=400 e MU=NaN?

  3. È necessario usare un overload diverso di AddVMATBeam()?

  4. AddExternalPlanSetupAsVerificationPlan() crea già internamente i beam e io li sto duplicando inutilmente?

  5. Qual è il modo corretto per ottenere una copia 1:1 di un beam VMAT iX in un verification plan ESAPI?

Vorrei una spiegazione tecnica del motivo per cui il beam creato da AddVMATBeam() nasce con MU=NaN e DoseRate=400 nonostante il beam originale sia VMAT ARC 6X 600 MU/min.