Question about beam energy layer decision

Dear OpenTPS developers,

The OpenTPS is a very powerful tool. Thanks a lot for your efforts.

I would like to ask how to decide the nominal energy of a proton beam layer.

> opentps_core/opentps/core/processing/doseCalculation/protons/MCsquare/BDL/BDL_default_UN_RangeShifter.txt · master · Open-MCsquare / OpenTPS · GitLab

from the above BDL, which seems the available energy is from 100MeV to 225MeV.

What about nominal energy below 100MeV? The clinical energy is usually between 70 MeV and 230 MeV.

And I checked the examples: the “layerSpacing” parameter seems to allow the nominal energy to be set to any value (e.g., 1mm, 5mm..). For example, if we want to use 102 MeV, we can adjust this parameter to a lower value.

So are those nominal energies obtained by linear interpolation in OpenTPS?
And it means the proton irradiation machine (IBA?) can change other nominal energies, except those listed in [BDL_default_UN_RangeShifter.txt], during real irradiation.

Is my understanding correct?

Second Question about the energy layer placement.
proximalLayers and distalLayers are two parameters that can be set to extend the energy layer.
But how to delete or shrink the layer in the proximal or distal direction?

Cheers,

Ye

Hi Ye!

Happy to see another user of OpenTPS :slight_smile:

You are right that these things are not explained in any documentation yet. We are working in a user-guide to collect all this information. For the moment, let me explain a few things, hopefully that will solve your questions:

Minimum beam energy and Beam model default in OpenTPS: The minimum energy for IBA systems is generally 70 MeV or 100 MeV, depending on the parameterization and calibration of the system. The default BDL in OpenTPS corresponds to a IBA Proteus®PLUS from UPenn. At the time we generated these BDLs (more than 5 years ago), the minimum energy of their system was 100 MeV.

Energy layers in OpenTPS: Energy layers in OpenTPS do not have a minimum or a maximum. They are computed with a raytracing algorithm to get the minimum and maximum WET (Water Equivalent Thickness) to cover the volume selected as target. Then, extra energy layers are added in the distal and proximal edge to be sure that we cover the target. The value by default is to add 1 energy layer both at proximal and distal, but this can be changed. Check the class ProtonPlanDesign :
self.spotSpacing = 5.0
self.layerSpacing = 5.0
self.proximalLayers = 1
self.distalLayers = 1

Note that the variable targetMargin is also there to have a margin around the target to place the spots in a sufficiently large area so that the target will be covered. If you are using robust optimisation with large systematic errors, this margin should probably be adapted and enlarged to cover the target in all your scenarios.

The WET is converted into MeV with the function rangeToEnergy, if values go below the energy in the BDL, a linear interpolation is done. For very small values you will get a warning “warnings.warn('Small proton ranges are used, accuracy of energy computation cannot be guaranteed.')". However, note that it has been tested for small animal cases with small energies and yet the MCsquare achieved quite decent dose calculation. Check lines 95-111 from def initializeBeam in the class BeamInitializer.

In OpenTPS we do not enforce to go discrete and use the energy layers in the BDL, we just use the final interpolated values computed after the process described above. After some clarifications from IBA team, they confirm that the machine is not restricted to deliver only the energies in the BDL, it is indeed almost continuous values that can be achieved (at least less than 0.1 mm resolution in range).

LayerSpacing parameter: The layerSpacing parameter has nothing to do with the minimal energy, this parameter determines how much you will separate the energy layers between them. If you want to use a specific value for your energy (like 102MeV) you better go to the layers and assign the value directly manually (hardcoded). Not sure now what would be the best way, but something like beam.appendLayer(PlanProtonLayer(energy))or overriding in your existing layers can work too.

To remove layers, the best might be to use the function beam.removeLayer()

Hi Ana,

Thank you very much for the kind and detailed explanation.

We believe users would be happy to have some documentation describing the algorithm or providing a guide. However, I understand that preparing such documentation requires a significant amount of time for your development team. Many thanks again to the whole team for their effort.

By the way, the first OpenTPS user meeting was great. I was happy to see the event.
As you mentioned, the small animal case with low beam energies, the user presented dose calucation was not bad.

Regarding the BDL, if IBA can achieve continuous energies, then the interpolation in OpenTPS makes sense to me. Thanks for the confirmation with the IBA team.

And I will follow your advice about adding or removing specific energy layers.

I have another question about multiple target optimization and the display of spot positions.

planInit.defineTargetMaskAndPrescription(target = roi, targetPrescription = 20.)

It seems to define only one target. Could you give me some advice on how to implement multiple target optimization?

I think the spot position can be displayed in the BEV (XY direction). But how to display the spot along the beam depth direction(XZ, or YZ)? This allows us to see the spot layers placed in the target. Maybe also help setting the your mentioned following parameters.

self.proximalLayers = 1
self.distalLayers = 1

Thanks again!

Have a nice weekend!

Cheers,

Ye

Hello Ye,

Regarding multiple-target optimization, this functionality is not fully implemented in OpenTPS yet. As a workaround, we recommend creating a union of the different target masks and using this combined ROI with the defineTargetMaskAndPrescription method. This method is used to place the spots within the selected region (the target), with an additional margin defined by the targetMargin parameter. The prescription is only used for spot weight initialization. Therefore, it is not necessary to provide multiple prescription

In practice, you can proceed as follows:

  1. Create a single ROI mask corresponding to the union of all target volumes.

  2. Pass this ROI mask to defineTargetMaskAndPrescription.

  3. Define the optimization objectives separately using the original individual target masks, allowing different objectives for each target.

Concerning visualization along the beam depth direction, this is unfortunately not possible. The Z direction is defined by the beam energy rather than spatial coordinates, which prevents displaying spot positions directly within the target. A visualization is possible in beam’s-eye view, but it remains quite restricted.

Dear Romain Schyns,

Sorry for late response.

Thank you very much for your advice regarding multiple-target optimization. I truly appreciate your insights.

I am also very grateful for your clear explanation of the visualization along the beam depth direction.

Cheers,
Ye