From 7e25693651e4533fd27084592de8426b4af5e8f2 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Thu, 2 Jul 2026 23:39:47 +0200 Subject: [PATCH 1/5] Improve the function to recalculate fractions from intensities. The previous version always assumed that PPs is defined as a first element. The current version should work for any order. Also, the pPs time is set to the value provided by user, before it was using some default constant. Still this function assumes that only one PPs is defined. --- source/physics/src/GatePositroniumHelper.cc | 40 ++++++++++++++++----- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/source/physics/src/GatePositroniumHelper.cc b/source/physics/src/GatePositroniumHelper.cc index 8685d0a87..aa9e1a844 100644 --- a/source/physics/src/GatePositroniumHelper.cc +++ b/source/physics/src/GatePositroniumHelper.cc @@ -8,6 +8,7 @@ #include #include #include +#include #include "GateMessageManager.hh" #include "GatePositroniumHelper.hh" @@ -19,41 +20,62 @@ PositroniumDecayModelParams GatePositroniumHelper::CalculateFractionsFromLifetim { PositroniumDecayModelParams paramsOut; bool pPsExist = false; + int pPsIndex = -1; for (unsigned i=0; i intens = CalcFractionsFromLifetime(params.fFractions.at(i), params.fLifetimes.at(i), params.fPositronInteractions.at(i)); + paramsOut.fFractions.push_back(intens.first); //2G intens paramsOut.fFractions.push_back(intens.second); //3G intens + paramsOut.fDecayKind.push_back(PositroniumDecayKind::k2Gamma); + paramsOut.fDecayKind.push_back(PositroniumDecayKind::k3Gamma); + paramsOut.fLifetimes.push_back(params.fLifetimes.at(i)); paramsOut.fLifetimes.push_back(params.fLifetimes.at(i)); paramsOut.fPromptGammaProbabilities.push_back(params.fPromptGammaProbabilities.at(i)); paramsOut.fPromptGammaProbabilities.push_back(params.fPromptGammaProbabilities.at(i)); paramsOut.fPromptGammaEnergy.push_back(params.fPromptGammaEnergy.at(i)); paramsOut.fPromptGammaEnergy.push_back(params.fPromptGammaEnergy.at(i)); - paramsOut.fDecayKind.push_back(PositroniumDecayKind::k2Gamma); - paramsOut.fDecayKind.push_back(PositroniumDecayKind::k3Gamma); paramsOut.fPositronInteractions.push_back(params.fPositronInteractions.at(i)); paramsOut.fPositronInteractions.push_back(params.fPositronInteractions.at(i)); + + if (params.fElectronCaptureProbabilities.size() > 0) { + paramsOut.fElectronCaptureProbabilities.push_back(params.fElectronCaptureProbabilities.at(i)); + paramsOut.fElectronCaptureProbabilities.push_back(params.fElectronCaptureProbabilities.at(i)); + } + if (params.fMeanPositronRange.size() > 0) { + paramsOut.fMeanPositronRange.push_back(params.fMeanPositronRange.at(i)); + paramsOut.fMeanPositronRange.push_back(params.fMeanPositronRange.at(i)); + } } } if (!paramsOut.fPositronInteractions.size()) { - GateError("GatePositroniumHelper::CalculateFractionsFromLifetimes: Could not calculate fraction from lifetimes"); + GateError("GatePositroniumHelper::CalculateFractionsFromLifetimes: Could not calculate fractions from lifetimes. fPositronInteractions is empty."); return params; } // setting pPs float pPsIntens = CalcPPsFractionFromOPs(paramsOut.fFractions, paramsOut.fPositronInteractions); if (pPsIntens > 0) { + assert(pPsIndex >=0); paramsOut.fFractions.push_back(pPsIntens); - paramsOut.fLifetimes.push_back(kParaPsLifetime_ns); - paramsOut.fPromptGammaProbabilities.push_back(paramsOut.fPromptGammaProbabilities.at(0)); - paramsOut.fPromptGammaEnergy.push_back(paramsOut.fPromptGammaEnergy.at(0)); + paramsOut.fLifetimes.push_back(params.fLifetimes.at(pPsIndex)); + paramsOut.fPromptGammaProbabilities.push_back(params.fPromptGammaProbabilities.at(pPsIndex)); + paramsOut.fPromptGammaEnergy.push_back(params.fPromptGammaEnergy.at(pPsIndex)); paramsOut.fDecayKind.push_back(PositroniumDecayKind::k2Gamma); paramsOut.fPositronInteractions.push_back(PositronElectronInteraction::kParaPs); + if (params.fElectronCaptureProbabilities.size() > 0) { + paramsOut.fElectronCaptureProbabilities.push_back(params.fElectronCaptureProbabilities.at(pPsIndex)); + } + if (params.fMeanPositronRange.size() > 0) { + paramsOut.fMeanPositronRange.push_back(params.fMeanPositronRange.at(pPsIndex)); + } } - paramsOut.fFractions = NormalizeFractions(paramsOut.fFractions); - return paramsOut; } From 12f595cf3e677f28248e26efb35fcda8b860d836 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Thu, 2 Jul 2026 23:58:58 +0200 Subject: [PATCH 2/5] Add check if pPs is missing --- source/physics/src/GatePositroniumHelper.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/physics/src/GatePositroniumHelper.cc b/source/physics/src/GatePositroniumHelper.cc index aa9e1a844..fe4cd09e9 100644 --- a/source/physics/src/GatePositroniumHelper.cc +++ b/source/physics/src/GatePositroniumHelper.cc @@ -60,6 +60,10 @@ PositroniumDecayModelParams GatePositroniumHelper::CalculateFractionsFromLifetim // setting pPs float pPsIntens = CalcPPsFractionFromOPs(paramsOut.fFractions, paramsOut.fPositronInteractions); + if (pPsIntens > 0 && pPsIndex < 0) { + GateError("GatePositroniumHelper::CalculateFractionsFromLifetimes: A non-zero para-Ps fraction was derived from the ortho-Ps components, but no kParaPs entry was provided in fPositronInteractions to supply its lifetime/prompt-gamma parameters."); + return params; + } if (pPsIntens > 0) { assert(pPsIndex >=0); paramsOut.fFractions.push_back(pPsIntens); From 32035339f17a9a93d12b63682a42e89f4d17f562 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Mon, 6 Jul 2026 10:52:27 +0200 Subject: [PATCH 3/5] Remove unused functions --- source/physics/src/GatePositroniumHelper.cc | 33 --------------------- 1 file changed, 33 deletions(-) diff --git a/source/physics/src/GatePositroniumHelper.cc b/source/physics/src/GatePositroniumHelper.cc index fe4cd09e9..ec3b7b124 100644 --- a/source/physics/src/GatePositroniumHelper.cc +++ b/source/physics/src/GatePositroniumHelper.cc @@ -111,39 +111,6 @@ std::pair GatePositroniumHelper::CalcFractionsFromLifetime(float i return std::make_pair(intens2G, intens3G); } -float GatePositroniumHelper::CalcFractionFromOPsLifetime(float intensity, float lifetime, PositroniumDecayKind decay) { - float nominator = 0.; - if (intensity > 0 && lifetime > 0) { - switch (decay) { - case PositroniumDecayKind::k2Gamma: - nominator = kOrthoPsMeanLifetime_ns - lifetime; - break; - case PositroniumDecayKind::k3Gamma: - nominator = lifetime; - break; -// If there will be more decays one needs to modify it - } - return intensity*nominator/kOrthoPsMeanLifetime_ns ; - } else - return nominator; -} - -float GatePositroniumHelper::CalcFractionFromDirectLifetime(float intensity, PositroniumDecayKind decay) { - float nominator = 0.; - if (intensity > 0) { - switch (decay) { - case PositroniumDecayKind::k2Gamma: - nominator = kHyperfineCoefficient - 1.; - break; - case PositroniumDecayKind::k3Gamma: - nominator = 1.; - break; - } - return intensity*nominator/kHyperfineCoefficient ; - } else - return nominator; -} - std::vector GatePositroniumHelper::NormalizeFractions(std::vector fractions) { std::vector normalizedVector = fractions; double sum = std::accumulate(fractions.begin(), fractions.end(), 0.0); From b960ed65898d791f2c1422e498d974b18674b65c Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Mon, 6 Jul 2026 10:57:00 +0200 Subject: [PATCH 4/5] Add handling of PositronElectronInteraction::kParaPsaraPositronium which should give error in this particular case. --- source/physics/src/GatePositroniumHelper.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/physics/src/GatePositroniumHelper.cc b/source/physics/src/GatePositroniumHelper.cc index ec3b7b124..568ff8168 100644 --- a/source/physics/src/GatePositroniumHelper.cc +++ b/source/physics/src/GatePositroniumHelper.cc @@ -107,6 +107,9 @@ std::pair GatePositroniumHelper::CalcFractionsFromLifetime(float i intens2G = intensity*(kOrthoPsMeanLifetime_ns - lifetime)/kOrthoPsMeanLifetime_ns; intens3G = intensity*lifetime/kOrthoPsMeanLifetime_ns; break; + case PositronElectronInteraction::kParaPs: + GateError("GatePositroniumHelper::CalcFractionsFromLifetimes: This function does not handle parapositrionium case. It should never be callded with inter == PositronElectronInteraction::kParaPs"); + break; } return std::make_pair(intens2G, intens3G); } From cdbfe316337db83f5edbf634baf94f1d1a6604ae Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Mon, 6 Jul 2026 11:20:01 +0200 Subject: [PATCH 5/5] Add information to the doc that ExtendedVSource is deprecated. --- docs/source_and_particle_management.rst | 269 ++++++++++++------------ 1 file changed, 136 insertions(+), 133 deletions(-) diff --git a/docs/source_and_particle_management.rst b/docs/source_and_particle_management.rst index fe6166d78..35b2b45e1 100644 --- a/docs/source_and_particle_management.rst +++ b/docs/source_and_particle_management.rst @@ -777,138 +777,6 @@ The data file must be a raw binary containing data in IEEE 32-bit floating point gate/source/mySource/setVoxelizedPhantomPosition -3.5 6.0 -10.0 cm -ExtendedVSource ---------------- - -The *ExtendedVSource* is an extension of the basic GATE's VSource class, which provides the model for the positronium decay. - -All properties of VSource source are inherited by ExtendedVSource. - -Two positronium decays channels are provided: two-photon decay (parapositronium (pPs) decay) and three-photon decay (orthopositronium (oPs) decay). Additionally, it is possible to add de-excitation (prompt) gamma to the decay model. - -Creating a source -~~~~~~~~~~~~~~~~~ - -To create a source use command:: - - /gate/source/addSource NAME Extended - -and choose the model:: - - /gate/source/NAME/setType MODEL - -There are 4 possible models: - -* **sg** - single photon emission -* **pPs** - two-photon emission from pPs decay -* **oPs** - three-photon emission from oPs decay -* **Ps** - mixed emission ( mixed photon emission from oPs and pPs decays ) - -Additionally one can add a prompt gamma emission (gamma emitted during the de-excitation). - -To do that use a command:: - - /gate/source/NAME/setEnableDeexcitation true - /gate/source/NAME/setPromptGammaEnergy ENERGY UNIT - -*ENERGY* is a float number, *UNIT* is energy unit (eV, keV, MeV, ...) - -Other commands -~~~~~~~~~~~~~~ - -For all models following commands are available: - -* **setFixedEmissionDirection** - set fixed emission direction of single gamma or prompt gamma:: - - /gate/source/NAME/setFixedEmissionDirection X Y Z - -* **setEnableFixedEmissionDirection** - set enable/disable fixed emission direction of single gamma or prompt gamma:: - - /gate/source/NAME/setEnableFixedEmissionDirection true - -For **sg** model there is a dedicated command to set emitted photon energy **setEmissionEnergy**:: - - /gate/source/NAME/setEmissionEnergy ENERGY UNIT - -The positronium mean lifetime can be set for the following models: **pPs**, **oPs** and **Ps** . The mean lifetime values in the vacuum are used as defaults. The lifetime per event is randomly chosen from the exponential distribution. Each annihilation gamma has time increased by this value. -To set the mean positronium lifetime use a command **setPostroniumLifetime**:: - - /gate/source/NAME/setPostroniumLifetime POSITRONIUM TIMEVALUE TIMEUNIT - -where *POSITRONIUM* is a name of positronium type (pPs or oPs), *TIMEVALUE* is float number, *TIMEUNIT* is time unit (ns, ps, ...). - -For the **Ps** model it is required to define a fraction (normalized to 1) of emitting photons from pPs and oPs decay. User must define a fraction for one type of positronium - the second one will be calculated automatically. -To do that use a command **setPositroniumFraction**:: - - /gate/source/NAME/setPositroniumFraction POSITRONIUM PROBABILITY - -where *POSITRONIUM* is a name of positronium type (pPs or oPs), *PROBABILITY* is a float number in range from 0 to 1. - - -Dedicated branches -~~~~~~~~~~~~~~~~~~ - -For this source 4 additional branches are present in the **Hits** tree: - -* **gammaType** - describes the type of the gamma - -* **sourceType** - describes the source type - -* **decayType** - describes from which decay channel of positronium is emitted gamma - -* **decayIndex** - branch present in the output format, but not filled by ``ExtendedVSource`` - -``Int`` is the type of value stored in all four branches. Meaning of values in ``gammaType``, ``sourceType`` and ``decayType`` is described in the tables below. - -.. table:: Description of values in gammaType branch - :widths: auto - :name: gammaType_branch_values - - +-------+---------------------------------------------------+ - | Value | Description | - +=======+===================================================+ - | 0 | photon/particle without emitted-gamma metadata | - +-------+---------------------------------------------------+ - | 1 | single photon (emitted by sg model) | - +-------+---------------------------------------------------+ - | 2 | annhilation gamma | - +-------+---------------------------------------------------+ - | 3 | prompt gamma | - +-------+---------------------------------------------------+ - -.. list-table:: Description of values in sourceType branch - :widths: 10 90 - :header-rows: 1 - :name: sourceType_branch_values - - * - Value - - Description - * - 0 - - photon/particle without emitted-gamma metadata - * - 1 - - single gamma emitter (sg model) - * - 2 - - parapositronium (pPs) - * - 3 - - orthopositronium (oPs) - * - 4 - - direct annihilation without positronium formation; not emitted by ExtendedVSouce - -.. table:: Description of values in decayType branch - :widths: auto - :name: decayType_branch_values - - +-------+---------------------------------------------------------------------------------+ - | Value | Description | - +=======+=================================================================================+ - | 0 | unknown decay | - +-------+---------------------------------------------------------------------------------+ - | 1 | standard decay channel (pPs-->2 gamma, oPs-->3 gamma) | - +-------+---------------------------------------------------------------------------------+ - | 2 | de-excitation and decay channel (pPs-->2 gamma + prompt, oPs-->3 gamma + prompt)| - +-------+---------------------------------------------------------------------------------+ - -For ``ExtendedVSource``, the ``decayIndex`` branch is not set by the source model and therefore always keeps the default value ``-1``. PositroniumSource @@ -1095,4 +963,139 @@ The example below defines a two-component source with explicit 2-gamma and 3-gam /gate/source/psSource/setElectronCaptureProbabilities 0.0 0.0 /gate/source/psSource/setMeanPositronRange 0.2 0.2 mm -In this setup, each sampled decay produces a prompt gamma at the source position and a delayed positronium annihilation. The first component annihilates into 2 gammas, while the second one annihilates into 3 gammas. \ No newline at end of file +In this setup, each sampled decay produces a prompt gamma at the source position and a delayed positronium annihilation. The first component annihilates into 2 gammas, while the second one annihilates into 3 gammas. + +ExtendedVSource +--------------- + +**Deprecated:** ``ExtendedVSource`` is an obsolete implementation and should not be used in new code. Use ``PositroniumSource`` instead, as it provides a much broader range of functionality. + +The *ExtendedVSource* is an extension of the basic GATE's VSource class, which provides the model for the positronium decay. + +All properties of VSource source are inherited by ExtendedVSource. + +Two positronium decays channels are provided: two-photon decay (parapositronium (pPs) decay) and three-photon decay (orthopositronium (oPs) decay). Additionally, it is possible to add de-excitation (prompt) gamma to the decay model. + +Creating a source +~~~~~~~~~~~~~~~~~ + +To create a source use command:: + + /gate/source/addSource NAME Extended + +and choose the model:: + + /gate/source/NAME/setType MODEL + +There are 4 possible models: + +* **sg** - single photon emission +* **pPs** - two-photon emission from pPs decay +* **oPs** - three-photon emission from oPs decay +* **Ps** - mixed emission ( mixed photon emission from oPs and pPs decays ) + +Additionally one can add a prompt gamma emission (gamma emitted during the de-excitation). + +To do that use a command:: + + /gate/source/NAME/setEnableDeexcitation true + /gate/source/NAME/setPromptGammaEnergy ENERGY UNIT + +*ENERGY* is a float number, *UNIT* is energy unit (eV, keV, MeV, ...) + +Other commands +~~~~~~~~~~~~~~ + +For all models following commands are available: + +* **setFixedEmissionDirection** - set fixed emission direction of single gamma or prompt gamma:: + + /gate/source/NAME/setFixedEmissionDirection X Y Z + +* **setEnableFixedEmissionDirection** - set enable/disable fixed emission direction of single gamma or prompt gamma:: + + /gate/source/NAME/setEnableFixedEmissionDirection true + +For **sg** model there is a dedicated command to set emitted photon energy **setEmissionEnergy**:: + + /gate/source/NAME/setEmissionEnergy ENERGY UNIT + +The positronium mean lifetime can be set for the following models: **pPs**, **oPs** and **Ps** . The mean lifetime values in the vacuum are used as defaults. The lifetime per event is randomly chosen from the exponential distribution. Each annihilation gamma has time increased by this value. +To set the mean positronium lifetime use a command **setPostroniumLifetime**:: + + /gate/source/NAME/setPostroniumLifetime POSITRONIUM TIMEVALUE TIMEUNIT + +where *POSITRONIUM* is a name of positronium type (pPs or oPs), *TIMEVALUE* is float number, *TIMEUNIT* is time unit (ns, ps, ...). + +For the **Ps** model it is required to define a fraction (normalized to 1) of emitting photons from pPs and oPs decay. User must define a fraction for one type of positronium - the second one will be calculated automatically. +To do that use a command **setPositroniumFraction**:: + + /gate/source/NAME/setPositroniumFraction POSITRONIUM PROBABILITY + +where *POSITRONIUM* is a name of positronium type (pPs or oPs), *PROBABILITY* is a float number in range from 0 to 1. + + +Dedicated branches +~~~~~~~~~~~~~~~~~~ + +For this source 4 additional branches are present in the **Hits** tree: + +* **gammaType** - describes the type of the gamma + +* **sourceType** - describes the source type + +* **decayType** - describes from which decay channel of positronium is emitted gamma + +* **decayIndex** - branch present in the output format, but not filled by ``ExtendedVSource`` + +``Int`` is the type of value stored in all four branches. Meaning of values in ``gammaType``, ``sourceType`` and ``decayType`` is described in the tables below. + +.. table:: Description of values in gammaType branch + :widths: auto + :name: gammaType_branch_values + + +-------+---------------------------------------------------+ + | Value | Description | + +=======+===================================================+ + | 0 | photon/particle without emitted-gamma metadata | + +-------+---------------------------------------------------+ + | 1 | single photon (emitted by sg model) | + +-------+---------------------------------------------------+ + | 2 | annhilation gamma | + +-------+---------------------------------------------------+ + | 3 | prompt gamma | + +-------+---------------------------------------------------+ + +.. list-table:: Description of values in sourceType branch + :widths: 10 90 + :header-rows: 1 + :name: sourceType_branch_values + + * - Value + - Description + * - 0 + - photon/particle without emitted-gamma metadata + * - 1 + - single gamma emitter (sg model) + * - 2 + - parapositronium (pPs) + * - 3 + - orthopositronium (oPs) + * - 4 + - direct annihilation without positronium formation; not emitted by ExtendedVSouce + +.. table:: Description of values in decayType branch + :widths: auto + :name: decayType_branch_values + + +-------+---------------------------------------------------------------------------------+ + | Value | Description | + +=======+=================================================================================+ + | 0 | unknown decay | + +-------+---------------------------------------------------------------------------------+ + | 1 | standard decay channel (pPs-->2 gamma, oPs-->3 gamma) | + +-------+---------------------------------------------------------------------------------+ + | 2 | de-excitation and decay channel (pPs-->2 gamma + prompt, oPs-->3 gamma + prompt)| + +-------+---------------------------------------------------------------------------------+ + +For ``ExtendedVSource``, the ``decayIndex`` branch is not set by the source model and therefore always keeps the default value ``-1``.