From 863ecc10acb7be2d5676969afb8e6acc69966012 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Thu, 11 Sep 2025 18:11:36 +0200 Subject: [PATCH 001/144] Remove unused header --- source/physics/src/GatePositroniumDecayModel.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/source/physics/src/GatePositroniumDecayModel.cc b/source/physics/src/GatePositroniumDecayModel.cc index a30e64a4e..645566ef7 100644 --- a/source/physics/src/GatePositroniumDecayModel.cc +++ b/source/physics/src/GatePositroniumDecayModel.cc @@ -8,7 +8,6 @@ #include "Randomize.hh" #include #include -#include #include "G4DecayProducts.hh" #include "G4LorentzVector.hh" #include "G4ParticleTable.hh" From 3d264e3a519f6b2a62bc39117297a0c33c3b8a60 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Thu, 11 Sep 2025 18:13:53 +0200 Subject: [PATCH 002/144] Remove commented code --- source/physics/src/GatePositroniumDecayModel.cc | 6 ------ 1 file changed, 6 deletions(-) diff --git a/source/physics/src/GatePositroniumDecayModel.cc b/source/physics/src/GatePositroniumDecayModel.cc index 645566ef7..23eaeea75 100644 --- a/source/physics/src/GatePositroniumDecayModel.cc +++ b/source/physics/src/GatePositroniumDecayModel.cc @@ -126,12 +126,6 @@ G4int GatePositroniumDecayModel::GeneratePrimaryVertices(G4Event* event, G4doubl event->AddPrimaryVertex( GetPrimaryVertexFromPositroniumAnnihilation(particle_time, particle_position) ); - //Do testu - /*G4PrimaryVertex* vertex = new G4PrimaryVertex(particle_position, particle_time); - std::vector gammas_ps = GetGammasFromPositroniumAnnihilation(); - vertex->SetPrimary( GetGammaFromDeexcitation() ); - std::for_each( gammas_ps.begin(), gammas_ps.end(), [&]( G4PrimaryParticle* gamma ) { vertex->SetPrimary( gamma ); } );*/ - return vertexes_number; } From e43e51c00c6004bb4da15af2c8c34a80d75e834e Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Thu, 11 Sep 2025 20:19:29 +0200 Subject: [PATCH 003/144] Small refactoring of GateParaPositronium --- source/physics/include/GateParaPositronium.hh | 15 +++++++++++---- source/physics/src/GateParaPositronium.cc | 10 +++++----- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/source/physics/include/GateParaPositronium.hh b/source/physics/include/GateParaPositronium.hh index 56e14c771..f2f78a546 100644 --- a/source/physics/include/GateParaPositronium.hh +++ b/source/physics/include/GateParaPositronium.hh @@ -16,14 +16,21 @@ **/ class GateParaPositronium : public G4ParticleDefinition { - private: - static GateParaPositronium* theInstance; - GateParaPositronium() {} - ~GateParaPositronium() {} public: static GateParaPositronium* Definition(); static GateParaPositronium* ParaPositroniumDefinition(); static GateParaPositronium* ParaPositronium(); + + private: + static GateParaPositronium* theInstance; + + GateParaPositronium() = default; + ~GateParaPositronium() override = default; + + public: + GateParaPositronium(const GateParaPositronium&) = delete; + GateParaPositronium& operator=(const GateParaPositronium&) = delete; + }; #endif diff --git a/source/physics/src/GateParaPositronium.cc b/source/physics/src/GateParaPositronium.cc index 1f304c4e6..014272af4 100644 --- a/source/physics/src/GateParaPositronium.cc +++ b/source/physics/src/GateParaPositronium.cc @@ -12,15 +12,15 @@ #include "GatePositroniumDecayChannel.hh" -GateParaPositronium* GateParaPositronium::theInstance = 0; +GateParaPositronium* GateParaPositronium::theInstance = nullptr; GateParaPositronium* GateParaPositronium::Definition() { - if (theInstance !=0) return theInstance; + if (theInstance) return theInstance; const G4String name = "pPs"; const G4double mass = 2.0 * electron_mass_c2; - const G4int spin = 0; + const G4int spin = 0; const G4int parity = 1; const G4double lifetime = 0.1244 * ns; const G4double BR = 1.0; @@ -49,9 +49,9 @@ GateParaPositronium* GateParaPositronium::Definition() nullptr, false, "e" ); //create Decay Table - G4DecayTable* table = new G4DecayTable(); + auto table = new G4DecayTable(); // create a decay channel - G4VDecayChannel* mode = new GatePositroniumDecayChannel( name, BR ); + auto mode = new GatePositroniumDecayChannel(name, BR); table->Insert(mode); anInstance->SetDecayTable(table); } From 2d850ee966d603d4a8332c7c1bc09fa3a68a6154 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Thu, 11 Sep 2025 20:29:21 +0200 Subject: [PATCH 004/144] Small refactorization of Positronium classes --- source/physics/include/GateOrthoPositronium.hh | 14 ++++++++++---- source/physics/src/GateOrthoPositronium.cc | 10 +++++----- source/physics/src/GateParaPositronium.cc | 2 +- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/source/physics/include/GateOrthoPositronium.hh b/source/physics/include/GateOrthoPositronium.hh index 73b224c0c..e55a05162 100644 --- a/source/physics/include/GateOrthoPositronium.hh +++ b/source/physics/include/GateOrthoPositronium.hh @@ -16,14 +16,20 @@ **/ class GateOrthoPositronium : public G4ParticleDefinition { - private: - static GateOrthoPositronium* theInstance; - GateOrthoPositronium() {} - ~GateOrthoPositronium() {} public: static GateOrthoPositronium* Definition(); static GateOrthoPositronium* OrthoPositroniumDefinition(); static GateOrthoPositronium* OrthoPositronium(); + + private: + static GateOrthoPositronium* theInstance; + + GateOrthoPositronium() = default; + ~GateOrthoPositronium() override = default; + + public: + GateOrthoPositronium(const GateOrthoPositronium&) = delete; + GateOrthoPositronium& operator=(const GateOrthoPositronium&) = delete; }; #endif diff --git a/source/physics/src/GateOrthoPositronium.cc b/source/physics/src/GateOrthoPositronium.cc index 9c3fcf9ff..710a7e9e1 100644 --- a/source/physics/src/GateOrthoPositronium.cc +++ b/source/physics/src/GateOrthoPositronium.cc @@ -12,11 +12,11 @@ #include "GatePositroniumDecayChannel.hh" -GateOrthoPositronium* GateOrthoPositronium::theInstance = 0; +GateOrthoPositronium* GateOrthoPositronium::theInstance = nullptr; GateOrthoPositronium* GateOrthoPositronium::Definition() { - if (theInstance !=0) return theInstance; + if (theInstance) return theInstance; const G4String name = "oPs"; const G4double mass = 2.0 * electron_mass_c2; @@ -29,7 +29,7 @@ GateOrthoPositronium* GateOrthoPositronium::Definition() G4ParticleTable* pTable = G4ParticleTable::GetParticleTable(); G4ParticleDefinition* anInstance = pTable->FindParticle(name); - if ( anInstance == 0 ) + if (!anInstance) { // create particle @@ -49,9 +49,9 @@ GateOrthoPositronium* GateOrthoPositronium::Definition() nullptr, false, "e" ); //create Decay Table - G4DecayTable* table = new G4DecayTable(); + auto table = new G4DecayTable(); // create a decay channel - G4VDecayChannel* mode = new GatePositroniumDecayChannel( name, BR ); + auto mode = new GatePositroniumDecayChannel( name, BR ); table->Insert(mode); anInstance->SetDecayTable(table); } diff --git a/source/physics/src/GateParaPositronium.cc b/source/physics/src/GateParaPositronium.cc index 014272af4..44e2875fe 100644 --- a/source/physics/src/GateParaPositronium.cc +++ b/source/physics/src/GateParaPositronium.cc @@ -29,7 +29,7 @@ GateParaPositronium* GateParaPositronium::Definition() G4ParticleTable* pTable = G4ParticleTable::GetParticleTable(); G4ParticleDefinition* anInstance = pTable->FindParticle(name); - if ( anInstance == 0 ) + if (!anInstance) { // create particle From 3c0cf21ae2dee79fa5cf25a042aaee351974efff Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Thu, 11 Sep 2025 21:24:48 +0200 Subject: [PATCH 005/144] Small refactorization of PositroniumDecayChannel Also, fix some comments. --- .../include/GatePositroniumDecayChannel.hh | 37 +++--- .../src/GatePositroniumDecayChannel.cc | 115 ++++++++---------- 2 files changed, 74 insertions(+), 78 deletions(-) diff --git a/source/physics/include/GatePositroniumDecayChannel.hh b/source/physics/include/GatePositroniumDecayChannel.hh index 290970cf3..7d5671a0f 100644 --- a/source/physics/include/GatePositroniumDecayChannel.hh +++ b/source/physics/include/GatePositroniumDecayChannel.hh @@ -7,16 +7,16 @@ #ifndef GatePositroniumDecayChannel_hh #define GatePositroniumDecayChannel_hh -#include "globals.hh" +//#include "globals.hh" #include "G4GeneralPhaseSpaceDecay.hh" #include "G4PhysicalConstants.hh" -#include "G4SystemOfUnits.hh" +//#include "G4SystemOfUnits.hh" /** Author: Mateusz Bała * Email: bala.mateusz@gmail.com - * Theorem author for oPs decay: Daria Kamińska ( Eur. Phys. J. C (2016) 76:445 ) + * Original author of the oPs decay model: Daria Kamińska et al. ( Eur. Phys. J. C (2016) 76:445 ) * Organization: J-PET (http://koza.if.uj.edu.pl/pet/) - * About class: Implements decay of positronium ( pPs and oPs ). Provides support for polarization. + * About class: Implements pPs and oPs positronium decays. Provides support for polarization. **/ class GatePositroniumDecayChannel : public G4GeneralPhaseSpaceDecay { @@ -25,8 +25,8 @@ class GatePositroniumDecayChannel : public G4GeneralPhaseSpaceDecay //Describes for which positronium we need decay enum PositroniumKind { NotDefined, ParaPositronium, OrthoPositronium }; - GatePositroniumDecayChannel( const G4String& theParentName, G4double theBR); - virtual ~GatePositroniumDecayChannel(); + GatePositroniumDecayChannel( const G4String& parentName, G4double BR); + virtual ~GatePositroniumDecayChannel() = default; /** Return gammas from positronium decay **/ virtual G4DecayProducts* DecayIt(G4double) override; @@ -41,7 +41,7 @@ class GatePositroniumDecayChannel : public G4GeneralPhaseSpaceDecay /** Calculate cross section Mij matrix element * Based on "Quantum electrodynamics" V. B. BERESTETSKY. * Chapter: 89. Annihilation of positronium - * Exquantation: 89.14 + * Equation: 89.14 **/ G4double GetOrthoPsM( const G4double w1, const G4double w2, const G4double w3 ) const; /** Calculate polarization orthogonal to momentum direction @@ -52,17 +52,20 @@ class GatePositroniumDecayChannel : public G4GeneralPhaseSpaceDecay G4ThreeVector GetPerpendicularVector(const G4ThreeVector& v) const; protected: - //Decay constants - const G4String kParaPositroniumName = "pPs"; - const G4String kOrthoPositroniumName = "oPs"; - const G4String kDaughterName = "gamma"; - const G4int kParaPositroniumAnnihilationGammasNumber = 2; - const G4int kOrthoPositroniumAnnihilationGammasNumber = 3; - const G4double kPositroniumMass = 2.0 * electron_mass_c2; + static inline const G4String kParaPositroniumName = "pPs"; + static inline const G4String kOrthoPositroniumName = "oPs"; + static inline const G4String kDaughterName = "gamma"; + + //Decay paramters + static constexpr G4int kParaPositroniumAnnihilationGammasNumber = 2; + static constexpr G4int kOrthoPositroniumAnnihilationGammasNumber = 3; + + static constexpr G4double kPositroniumMass = 2.0 * electron_mass_c2; + ///This is maximal number which can be calculated by function GetOrthoPsM() - determined based on 10^7 iterations + static constexpr G4double kOrthoPsMMax = 7.65928; + static constexpr G4double kElectronMass = electron_mass_c2; //[MeV] + PositroniumKind fPositroniumKind = PositroniumKind::NotDefined; - ///This is maxiaml number which can be calculated by function GetOrthoPsM() - based on 10^7 iterations - const G4double kOrthoPsMMax = 7.65928; - const G4double kElectronMass = electron_mass_c2; //[MeV] }; #endif diff --git a/source/physics/src/GatePositroniumDecayChannel.cc b/source/physics/src/GatePositroniumDecayChannel.cc index 70b5bc282..a220ba4a4 100644 --- a/source/physics/src/GatePositroniumDecayChannel.cc +++ b/source/physics/src/GatePositroniumDecayChannel.cc @@ -10,55 +10,51 @@ #include "Randomize.hh" #include "G4LorentzVector.hh" -GatePositroniumDecayChannel::GatePositroniumDecayChannel(const G4String& theParentName, G4double theBR) +GatePositroniumDecayChannel::GatePositroniumDecayChannel(const G4String& parentName, G4double BR) { - G4int daughters_number = 0; - - if ( theParentName == kParaPositroniumName ) - { - daughters_number = kParaPositroniumAnnihilationGammasNumber; - fPositroniumKind = GatePositroniumDecayChannel::PositroniumKind::ParaPositronium; - } - else if ( theParentName == kOrthoPositroniumName ) - { - daughters_number = kOrthoPositroniumAnnihilationGammasNumber; - fPositroniumKind = GatePositroniumDecayChannel::PositroniumKind::OrthoPositronium; - } - else - { - #ifdef G4VERBOSE - if (GetVerboseLevel()>0) - { - G4cout << "GatePositroniumDecayChannel:: constructor :"; - G4cout << " parent particle is not positronium (pPs,oPs) but "; - G4cout << theParentName << G4endl; + G4int daughters_number = 0; + + if (parentName == kParaPositroniumName) { + daughters_number = kParaPositroniumAnnihilationGammasNumber; + fPositroniumKind = + GatePositroniumDecayChannel::PositroniumKind::ParaPositronium; + } else if (parentName == kOrthoPositroniumName) { + daughters_number = kOrthoPositroniumAnnihilationGammasNumber; + fPositroniumKind = + GatePositroniumDecayChannel::PositroniumKind::OrthoPositronium; + } else { +#ifdef G4VERBOSE + if (GetVerboseLevel() > 0) { + G4cout << "GatePositroniumDecayChannel:: constructor :"; + G4cout << " parent particle is not positronium (pPs,oPs) but "; + G4cout << parentName << G4endl; + } +#endif } - #endif - } - SetParentMass( kPositroniumMass ); - SetBR( theBR ); - SetParent( theParentName ); - SetNumberOfDaughters( daughters_number ); - for ( G4int daughter_index = 0; daughter_index < daughters_number; ++daughter_index ) { SetDaughter( daughter_index, kDaughterName ); } + SetParentMass(kPositroniumMass); + SetBR(BR); + SetParent(parentName); + SetNumberOfDaughters(daughters_number); + for (G4int daughter_index = 0; daughter_index < daughters_number; + ++daughter_index) { + SetDaughter(daughter_index, kDaughterName); + } } -GatePositroniumDecayChannel::~GatePositroniumDecayChannel() {} - G4DecayProducts* GatePositroniumDecayChannel::DecayIt(G4double) { - switch ( fPositroniumKind ) - { - case GatePositroniumDecayChannel::PositroniumKind::ParaPositronium: - return DecayParaPositronium(); - case GatePositroniumDecayChannel::PositroniumKind::OrthoPositronium: - return DecayOrthoPositronium(); - default: - G4cout << "GatePositroniumDecayChannel::DecayIt "; - G4cout << *parent_name << " can not decay " << G4endl; - DumpInfo(); - return nullptr; - }; + switch (fPositroniumKind) { + case GatePositroniumDecayChannel::PositroniumKind::ParaPositronium: + return DecayParaPositronium(); + case GatePositroniumDecayChannel::PositroniumKind::OrthoPositronium: + return DecayOrthoPositronium(); + default: + G4cout << "GatePositroniumDecayChannel::DecayIt "; + G4cout << *parent_name << " can not decay " << G4endl; + DumpInfo(); + return nullptr; + } } G4DecayProducts* GatePositroniumDecayChannel::DecayParaPositronium() @@ -89,7 +85,10 @@ G4DecayProducts* GatePositroniumDecayChannel::DecayOrthoPositronium() do { - if ( decay_products != nullptr ) { delete decay_products; } + if (decay_products) { + delete decay_products; + decay_products = nullptr; + } decay_products = G4GeneralPhaseSpaceDecay::DecayIt(); @@ -104,17 +103,15 @@ G4DecayProducts* GatePositroniumDecayChannel::DecayOrthoPositronium() weight = GetOrthoPsM( lv_gamma_1.e(), lv_gamma_2.e(), lv_gamma_3.e() ); random_weight = kOrthoPsMMax * G4UniformRand(); } - while( random_weight > weight ); + while (random_weight > weight); ///Polarization - G4ThreeVector polarization_gamma_1 = GetPolarization( gamma_1->GetMomentumDirection() ); - G4ThreeVector polarization_gamma_2 = GetPolarization( gamma_2->GetMomentumDirection() ); - G4ThreeVector polarization_gamma_3 = GetPolarization( gamma_3->GetMomentumDirection() ); - - gamma_1->SetPolarization( polarization_gamma_1.x(), polarization_gamma_1.y(), polarization_gamma_1.z() ); - gamma_2->SetPolarization( polarization_gamma_2.x(), polarization_gamma_2.y(), polarization_gamma_2.z() ); - gamma_3->SetPolarization( polarization_gamma_3.x(), polarization_gamma_3.y(), polarization_gamma_3.z() ); - + for (size_t i = 0; i < decay_products->entries(); i++) + { + auto* gamma = (*decay_products)[i]; + auto polarization = GetPolarization(gamma->GetMomentumDirection()); + gamma->SetPolarization(polarization.x(), polarization.y(), polarization.z()); + } return decay_products; } @@ -125,16 +122,12 @@ G4double GatePositroniumDecayChannel::GetOrthoPsM( const G4double w1, const G4do G4ThreeVector GatePositroniumDecayChannel::GetPolarization( const G4ThreeVector& momentum ) const { - G4ThreeVector polarization(0.0,0.0,0.0); - - G4ThreeVector a0,b0,d0; - d0 = momentum.unit(); - a0 = GetPerpendicularVector( d0 ).unit(); - b0 = d0.cross( a0 ).unit(); + auto d0 = momentum.unit(); + auto a0 = GetPerpendicularVector( d0 ).unit(); + auto b0 = d0.cross( a0 ).unit(); G4double angle_radians = G4UniformRand() * M_PI; - polarization = std::cos( angle_radians ) * a0 + std::sin( angle_radians ) * b0; - polarization.unit(); - return polarization; + auto polarization = std::cos( angle_radians ) * a0 + std::sin( angle_radians ) * b0; + return polarization.unit(); } G4ThreeVector GatePositroniumDecayChannel::GetPerpendicularVector(const G4ThreeVector& v) const From c93e9d913a2a2e006717454cd666c0d3eae5ca1c Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Thu, 11 Sep 2025 21:31:12 +0200 Subject: [PATCH 006/144] Remove unecessary & while passing basic types --- source/physics/include/GateExtendedVSource.hh | 12 ++++++------ source/physics/src/GateExtendedVSource.cc | 13 ++++++------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/source/physics/include/GateExtendedVSource.hh b/source/physics/include/GateExtendedVSource.hh index be4375824..d4a995d70 100644 --- a/source/physics/include/GateExtendedVSource.hh +++ b/source/physics/include/GateExtendedVSource.hh @@ -55,19 +55,19 @@ public: /** Set enable emission of additional gamma - from deexcitation ( prompt gamma ) **/ - void SetEnableDeexcitation( const G4bool& enable_deexcitation ); + void SetEnableDeexcitation( const G4bool enable_deexcitation ); /** Set fixed direction of single gamma ( or prompt gamma ) **/ void SetFixedEmissionDirection( const G4ThreeVector& fixed_emission_direction ); /** Set enable/disable emission of single gamma with fixed direction **/ - void SetEnableFixedEmissionDirection( const G4bool& enable_fixed_emission_direction ); + void SetEnableFixedEmissionDirection( const G4bool enable_fixed_emission_direction ); /** Set single gamma kinetic energy **/ - void SetEmissionEnergy( const G4double& energy ); + void SetEmissionEnergy( const G4double energy ); /** Set seed for Randomize.hh generatores **/ - void SetSeed( const G4long& seed ); + void SetSeed( const G4long seed ); /** Set positronium lifetime - which is included as constant for exponential distribution ( G4RandExponential ) * @param: positronium_name - for example: pPs, oPs * @param: life_time - in ns @@ -77,13 +77,13 @@ public: /** Set prompt gamma energy ( deexcictation energy ). * If user set enable emission of prompt gamma without set prompt energy then il wii be default value used ( deexcitation of Na22 ) **/ - void SetPromptGammaEnergy( const G4double& energy ); + void SetPromptGammaEnergy( const G4double energy ); /** Set propability of gammas emission from different positronium. * @param: positronium_kind - positronium name: pPs, oPs * @param: fraction - number in range from 0.0 to 1.0 * You have to call this method for only one kind of positronium - for the second one propability will be calculated as: 1.0 - fraction. **/ - void SetPositroniumFraction( const G4String& positronium_kind, const G4double& fraction ); + void SetPositroniumFraction( const G4String& positronium_kind, const G4double fraction ); protected: /** Set model used for this source. If is not defined then this class will behave like GateVSource. diff --git a/source/physics/src/GateExtendedVSource.cc b/source/physics/src/GateExtendedVSource.cc index cb80afd2f..1a3436f7c 100644 --- a/source/physics/src/GateExtendedVSource.cc +++ b/source/physics/src/GateExtendedVSource.cc @@ -5,7 +5,6 @@ See LICENSE.md for further details ----------------------*/ #include "GateExtendedVSource.hh" -#include #include "GatePositroniumDecayModel.hh" #include "G4Event.hh" @@ -32,15 +31,15 @@ void GateExtendedVSource::SetModel( const G4String& model_name ) G4cout << "GateExtendedVSource::SetModel : Unknown gamma source model. Enable: sg, pPs, oPs, Ps. Switching to GateVSource behavour." << G4endl; } } -void GateExtendedVSource::SetEnableDeexcitation( const G4bool& enable_deexcitation ) { fEnableDeexcitation.Set( enable_deexcitation ); } +void GateExtendedVSource::SetEnableDeexcitation( const G4bool enable_deexcitation ) { fEnableDeexcitation.Set( enable_deexcitation ); } void GateExtendedVSource::SetFixedEmissionDirection( const G4ThreeVector& fixed_emission_direction ) { fFixedEmissionDirection.Set( fixed_emission_direction ); } -void GateExtendedVSource::SetEnableFixedEmissionDirection( const G4bool& enable_fixed_emission_direction ) { fEnableFixedEmissionDirection.Set( enable_fixed_emission_direction ); } +void GateExtendedVSource::SetEnableFixedEmissionDirection( const G4bool enable_fixed_emission_direction ) { fEnableFixedEmissionDirection.Set( enable_fixed_emission_direction ); } -void GateExtendedVSource::SetEmissionEnergy( const G4double& energy ) { fEmissionEnergy.Set( energy ); } +void GateExtendedVSource::SetEmissionEnergy( const G4double energy ) { fEmissionEnergy.Set( energy ); } -void GateExtendedVSource::SetSeed( const G4long& seed ) { fSeed.Set( seed ); } +void GateExtendedVSource::SetSeed( const G4long seed ) { fSeed.Set( seed ); } void GateExtendedVSource::SetPostroniumLifetime( const G4String& positronium_name, const G4double& life_time ) { @@ -49,9 +48,9 @@ void GateExtendedVSource::SetPostroniumLifetime( const G4String& positronium_nam else { GateError( "GateExtendedVSource::SetPostroniumLifetime : incorrect positronium name - try: pPs or oPs" ); } } -void GateExtendedVSource::SetPromptGammaEnergy( const G4double& energy ) { fPromptGammaEnergy.Set( energy ); } +void GateExtendedVSource::SetPromptGammaEnergy( const G4double energy ) { fPromptGammaEnergy.Set( energy ); } -void GateExtendedVSource::SetPositroniumFraction( const G4String& positronium_kind, const G4double& fraction ) +void GateExtendedVSource::SetPositroniumFraction( const G4String& positronium_kind, const G4double fraction ) { if ( fraction > 1.0 || fraction < 0.0 ) { From cef56fb2acfe5635341f7a9ca3d54813024bf4c7 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Thu, 11 Sep 2025 22:06:35 +0200 Subject: [PATCH 007/144] Refactor ExtendedVSource Replace in-house code by std::optional. Also, removing const and & from basic type arguments in setters. --- source/physics/include/GateExtendedVSource.hh | 75 +++++++---------- source/physics/src/GateExtendedVSource.cc | 83 +++++++++++-------- 2 files changed, 75 insertions(+), 83 deletions(-) diff --git a/source/physics/include/GateExtendedVSource.hh b/source/physics/include/GateExtendedVSource.hh index d4a995d70..edfce6eab 100644 --- a/source/physics/include/GateExtendedVSource.hh +++ b/source/physics/include/GateExtendedVSource.hh @@ -7,28 +7,12 @@ #ifndef GateExtendedVSource_hh #define GateExtendedVSource_hh +#include + #include "GateVSource.hh" #include "GateExtendedVSourceMessenger.hh" #include "GateGammaEmissionModel.hh" -/** About class: this is helper class to control if setting is in use - **/ -template -class ModelSetting -{ - public: - void Set( T value ) - { - fValue = value; - fIsSetted = true; - } - T Get() const { return fValue; } - bool IsSetted() const { return fIsSetted; } - private: - T fValue; - bool fIsSetted = false; -}; - /** Author: Mateusz Bała * Email: bala.mateusz@gmail.com * Organization: J-PET (http://koza.if.uj.edu.pl/pet/) @@ -46,52 +30,48 @@ public: Positronium //generate gammas from mixed model ( from pPs and oPs decay with setted ratio ) }; - GateExtendedVSource( G4String name ); + explicit GateExtendedVSource(const G4String& name); virtual ~GateExtendedVSource(); /** Generate gammas for event **/ virtual G4int GeneratePrimaries( G4Event* event ) override; - /** Set enable emission of additional gamma - from deexcitation ( prompt gamma ) - **/ - void SetEnableDeexcitation( const G4bool enable_deexcitation ); + void SetEnableDeexcitation(G4bool enable_deexcitation); /** Set fixed direction of single gamma ( or prompt gamma ) **/ - void SetFixedEmissionDirection( const G4ThreeVector& fixed_emission_direction ); + void SetFixedEmissionDirection(const G4ThreeVector &fixed_emission_direction); /** Set enable/disable emission of single gamma with fixed direction **/ - void SetEnableFixedEmissionDirection( const G4bool enable_fixed_emission_direction ); + void SetEnableFixedEmissionDirection(G4bool enable_fixed_emission_direction); /** Set single gamma kinetic energy **/ - void SetEmissionEnergy( const G4double energy ); - /** Set seed for Randomize.hh generatores - **/ - void SetSeed( const G4long seed ); + void SetEmissionEnergy(G4double energy); + void SetSeed(G4long seed); /** Set positronium lifetime - which is included as constant for exponential distribution ( G4RandExponential ) * @param: positronium_name - for example: pPs, oPs * @param: life_time - in ns * Lifetime value will have inpact of vertex time set for annihilation gammas **/ - void SetPostroniumLifetime( const G4String& positronium_name ,const G4double& life_time ); + void SetPostroniumLifetime(const G4String &positronium_name, G4double life_time); /** Set prompt gamma energy ( deexcictation energy ). * If user set enable emission of prompt gamma without set prompt energy then il wii be default value used ( deexcitation of Na22 ) **/ - void SetPromptGammaEnergy( const G4double energy ); + void SetPromptGammaEnergy(G4double energy); /** Set propability of gammas emission from different positronium. * @param: positronium_kind - positronium name: pPs, oPs * @param: fraction - number in range from 0.0 to 1.0 * You have to call this method for only one kind of positronium - for the second one propability will be calculated as: 1.0 - fraction. **/ - void SetPositroniumFraction( const G4String& positronium_kind, const G4double fraction ); + void SetPositroniumFraction(const G4String& positronium_kind, G4double fraction); protected: /** Set model used for this source. If is not defined then this class will behave like GateVSource. **/ - void SetModel( const G4String& model_name ); - /** Prepare model to work - set all settings from user to model - **/ - void PrepareModel(); + void SetModel(const G4String &model_name); + /** Prepare model to work - set all settings from user to model + **/ + void PrepareModel(); protected: //Gamma emission model @@ -100,21 +80,22 @@ public: GateExtendedVSourceMessenger* pMessenger = nullptr; //User settings: ModelKind fModelKind = ModelKind::NotDefined; - ModelSetting fEnableDeexcitation; - ModelSetting fFixedEmissionDirection; - ModelSetting fEnableFixedEmissionDirection; - ModelSetting fEmissionEnergy; - ModelSetting fSeed; - ModelSetting fParaPostroniumLifetime; - ModelSetting fOrthoPostroniumLifetime; - ModelSetting fPromptGammaEnergy; - ModelSetting fParaPositroniumFraction; - //Constants for Set(...) methods - const G4String kParaPositroniumName = "pPs"; - const G4String kOrthoPositroniumName = "oPs"; + std::optional fEnableDeexcitation; + std::optional fFixedEmissionDirection; + std::optional fEnableFixedEmissionDirection; + std::optional fEmissionEnergy; + std::optional fSeed; + std::optional fParaPostroniumLifetime; + std::optional fOrthoPostroniumLifetime; + std::optional fPromptGammaEnergy; + std::optional fParaPositroniumFraction; + // //Set by PrepareModel() and used in GeneratePrimaries() G4bool fBehaveLikeVSource = false; + //Constants for Set(...) methods + static inline const G4String kParaPositroniumName = "pPs"; + static inline const G4String kOrthoPositroniumName = "oPs"; }; #endif diff --git a/source/physics/src/GateExtendedVSource.cc b/source/physics/src/GateExtendedVSource.cc index 1a3436f7c..7a597a7c8 100644 --- a/source/physics/src/GateExtendedVSource.cc +++ b/source/physics/src/GateExtendedVSource.cc @@ -8,7 +8,9 @@ #include "GatePositroniumDecayModel.hh" #include "G4Event.hh" -GateExtendedVSource::GateExtendedVSource( G4String name ) : GateVSource( name ) +#include + +GateExtendedVSource::GateExtendedVSource(const G4String& name ) : GateVSource( name ) { pMessenger = new GateExtendedVSourceMessenger( this ); } @@ -19,38 +21,46 @@ GateExtendedVSource::~GateExtendedVSource() if ( pModel != nullptr ) { delete pModel; } } -void GateExtendedVSource::SetModel( const G4String& model_name ) +void GateExtendedVSource::SetModel(const G4String &model_name) { - if ( model_name == "sg" ) { fModelKind = GateExtendedVSource::ModelKind::SingleGamma; } - else if ( model_name == "pPs" ) { fModelKind = GateExtendedVSource::ModelKind::ParaPositronium; } - else if ( model_name == "oPs" ) { fModelKind = GateExtendedVSource::ModelKind::OrthoPositronium; } - else if ( model_name == "Ps" ) { fModelKind = GateExtendedVSource::ModelKind::Positronium; } - else - { - fBehaveLikeVSource = true; - G4cout << "GateExtendedVSource::SetModel : Unknown gamma source model. Enable: sg, pPs, oPs, Ps. Switching to GateVSource behavour." << G4endl; } + static const std::map models{ + {"sg", GateExtendedVSource::ModelKind::SingleGamma}, + {"pPs", GateExtendedVSource::ModelKind::ParaPositronium}, + {"oPs", GateExtendedVSource::ModelKind::OrthoPositronium}, + {"Ps", GateExtendedVSource::ModelKind::Positronium}}; + + auto it = models.find(model_name); + if (it != models.end()) + { + fModelKind = it->second; + } else { + fBehaveLikeVSource = true; + G4cout << "GateExtendedVSource::SetModel : Unknown gamma source model. " + "Enable: sg, pPs, oPs, Ps. Switching to GateVSource behavour." + << G4endl; + } } -void GateExtendedVSource::SetEnableDeexcitation( const G4bool enable_deexcitation ) { fEnableDeexcitation.Set( enable_deexcitation ); } +void GateExtendedVSource::SetEnableDeexcitation(G4bool enable_deexcitation) { fEnableDeexcitation = enable_deexcitation; } -void GateExtendedVSource::SetFixedEmissionDirection( const G4ThreeVector& fixed_emission_direction ) { fFixedEmissionDirection.Set( fixed_emission_direction ); } +void GateExtendedVSource::SetFixedEmissionDirection(const G4ThreeVector& fixed_emission_direction) { fFixedEmissionDirection = fixed_emission_direction; } -void GateExtendedVSource::SetEnableFixedEmissionDirection( const G4bool enable_fixed_emission_direction ) { fEnableFixedEmissionDirection.Set( enable_fixed_emission_direction ); } +void GateExtendedVSource::SetEnableFixedEmissionDirection(G4bool enable_fixed_emission_direction) { fEnableFixedEmissionDirection = enable_fixed_emission_direction; } -void GateExtendedVSource::SetEmissionEnergy( const G4double energy ) { fEmissionEnergy.Set( energy ); } +void GateExtendedVSource::SetEmissionEnergy(G4double energy) { fEmissionEnergy =energy; } -void GateExtendedVSource::SetSeed( const G4long seed ) { fSeed.Set( seed ); } +void GateExtendedVSource::SetSeed(G4long seed) { fSeed = seed; } -void GateExtendedVSource::SetPostroniumLifetime( const G4String& positronium_name, const G4double& life_time ) +void GateExtendedVSource::SetPostroniumLifetime(const G4String& positronium_name, G4double life_time) { - if ( positronium_name == kParaPositroniumName ) { fParaPostroniumLifetime.Set( life_time ); } - else if ( positronium_name == kOrthoPositroniumName ) { fOrthoPostroniumLifetime.Set( life_time ); } + if ( positronium_name == kParaPositroniumName ) { fParaPostroniumLifetime = life_time; } + else if ( positronium_name == kOrthoPositroniumName ) { fOrthoPostroniumLifetime = life_time; } else { GateError( "GateExtendedVSource::SetPostroniumLifetime : incorrect positronium name - try: pPs or oPs" ); } } -void GateExtendedVSource::SetPromptGammaEnergy( const G4double energy ) { fPromptGammaEnergy.Set( energy ); } +void GateExtendedVSource::SetPromptGammaEnergy(G4double energy) { fPromptGammaEnergy = energy; } -void GateExtendedVSource::SetPositroniumFraction( const G4String& positronium_kind, const G4double fraction ) +void GateExtendedVSource::SetPositroniumFraction( const G4String& positronium_kind, G4double fraction ) { if ( fraction > 1.0 || fraction < 0.0 ) { @@ -63,46 +73,47 @@ void GateExtendedVSource::SetPositroniumFraction( const G4String& positronium_ki else if ( positronium_kind == kOrthoPositroniumName ) { pPs_fraction = 1.0 - fraction; } else { GateError( "GateExtendedVSource::SetPositroniumFraction : incorrect positronium kind - enable are: pPs, oPs" ); } - fParaPositroniumFraction.Set( pPs_fraction ); + fParaPositroniumFraction = pPs_fraction; } void GateExtendedVSource::PrepareModel() { SetModel( GetType() ); - if ( fBehaveLikeVSource ) { return; } + if ( fBehaveLikeVSource ) return; + if ( fModelKind == GateExtendedVSource::ModelKind::ParaPositronium || fModelKind == GateExtendedVSource::ModelKind::OrthoPositronium || fModelKind == GateExtendedVSource::ModelKind::Positronium ) { pModel = new GatePositroniumDecayModel(); GatePositroniumDecayModel* model = dynamic_cast( pModel ); if ( fModelKind == GateExtendedVSource::ModelKind::OrthoPositronium ) { model->SetPositroniumKind( GatePositroniumDecayModel::PositroniumKind::oPs ); } - if ( fModelKind == GateExtendedVSource::ModelKind::Positronium && fParaPositroniumFraction.IsSetted() ) { model->SetParaPositroniumFraction( fParaPositroniumFraction.Get() ); } + if ( fModelKind == GateExtendedVSource::ModelKind::Positronium && fParaPositroniumFraction.has_value() ) { model->SetParaPositroniumFraction( fParaPositroniumFraction.value() ); } - if ( fEnableDeexcitation.IsSetted() && fEnableDeexcitation.Get() ) { model->SetDecayModel( GatePositroniumDecayModel::DecayModel::WithPrompt ); } - if ( fParaPostroniumLifetime.IsSetted() ) { model->SetPostroniumLifetime( kParaPositroniumName, fParaPostroniumLifetime.Get() ); } - if ( fOrthoPostroniumLifetime.IsSetted() ) { model->SetPostroniumLifetime( kOrthoPositroniumName, fOrthoPostroniumLifetime.Get() ); } - if ( fPromptGammaEnergy.IsSetted() ) { model->SetPromptGammaEnergy( fPromptGammaEnergy.Get() ); } + if ( fEnableDeexcitation.has_value() && fEnableDeexcitation.value() ) { model->SetDecayModel( GatePositroniumDecayModel::DecayModel::WithPrompt ); } + if ( fParaPostroniumLifetime.has_value() ) { model->SetPostroniumLifetime( kParaPositroniumName, fParaPostroniumLifetime.value() ); } + if ( fOrthoPostroniumLifetime.has_value() ) { model->SetPostroniumLifetime( kOrthoPositroniumName, fOrthoPostroniumLifetime.value() ); } + if ( fPromptGammaEnergy.has_value() ) { model->SetPromptGammaEnergy( fPromptGammaEnergy.value() ); } } else if ( fModelKind == GateExtendedVSource::ModelKind::SingleGamma ) { pModel = new GateGammaEmissionModel(); } else { GateError( "GateExtendedVSource::PrepareModel - unknown model." ); } - if ( fFixedEmissionDirection.IsSetted() ) { pModel->SetFixedEmissionDirection( fFixedEmissionDirection.Get() ); } - if ( fEnableFixedEmissionDirection.IsSetted() ) { pModel->SetEnableFixedEmissionDirection( fEnableFixedEmissionDirection.Get() ); } - if ( fEmissionEnergy.IsSetted() ) { pModel->SetEmissionEnergy( fEmissionEnergy.Get() ); } - if ( fSeed.IsSetted() ) { pModel->SetSeed( fSeed.Get() ); } + if ( fFixedEmissionDirection.has_value() ) { pModel->SetFixedEmissionDirection( fFixedEmissionDirection.value() ); } + if ( fEnableFixedEmissionDirection.has_value() ) { pModel->SetEnableFixedEmissionDirection( fEnableFixedEmissionDirection.value() ); } + if ( fEmissionEnergy.has_value() ) { pModel->SetEmissionEnergy( fEmissionEnergy.value() ); } + if ( fSeed.has_value() ) { pModel->SetSeed( fSeed.value() ); } } -G4int GateExtendedVSource::GeneratePrimaries( G4Event* event ) +G4int GateExtendedVSource::GeneratePrimaries(G4Event* event) { - if ( !fBehaveLikeVSource && pModel == nullptr ) { PrepareModel(); } - if ( fBehaveLikeVSource ) { return GateVSource::GeneratePrimaries( event ); } + if (!fBehaveLikeVSource && !pModel) { PrepareModel(); } + if (fBehaveLikeVSource) { return GateVSource::GeneratePrimaries(event); } G4double particle_time = GetTime(); G4ThreeVector particle_position = GetPosDist()->GenerateOne(); - ChangeParticlePositionRelativeToAttachedVolume( particle_position ); - return pModel->GeneratePrimaryVertices( event, particle_time, particle_position); + ChangeParticlePositionRelativeToAttachedVolume(particle_position); + return pModel->GeneratePrimaryVertices(event, particle_time, particle_position); } From a6700d3c21bb868798c5b1970759a34a97376005 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Mon, 6 Oct 2025 07:41:48 +0200 Subject: [PATCH 008/144] Just reformatting --- source/physics/src/GateParaPositronium.cc | 98 +++++++++++------------ 1 file changed, 48 insertions(+), 50 deletions(-) diff --git a/source/physics/src/GateParaPositronium.cc b/source/physics/src/GateParaPositronium.cc index 44e2875fe..24316820e 100644 --- a/source/physics/src/GateParaPositronium.cc +++ b/source/physics/src/GateParaPositronium.cc @@ -5,60 +5,58 @@ See LICENSE.md for further details ----------------------*/ #include "GateParaPositronium.hh" +#include "G4DecayTable.hh" +#include "G4ParticleTable.hh" #include "G4PhysicalConstants.hh" #include "G4SystemOfUnits.hh" -#include "G4ParticleTable.hh" -#include "G4DecayTable.hh" #include "GatePositroniumDecayChannel.hh" - -GateParaPositronium* GateParaPositronium::theInstance = nullptr; - -GateParaPositronium* GateParaPositronium::Definition() -{ - if (theInstance) return theInstance; - - const G4String name = "pPs"; - const G4double mass = 2.0 * electron_mass_c2; - const G4int spin = 0; - const G4int parity = 1; - const G4double lifetime = 0.1244 * ns; - const G4double BR = 1.0; - - // search in particle table - G4ParticleTable* pTable = G4ParticleTable::GetParticleTable(); - G4ParticleDefinition* anInstance = pTable->FindParticle(name); - - if (!anInstance) - { - // create particle - - // Arguments for constructor: - // name, mass, width, charge, - // spin, parity, C-conjugation, - // isospin, isospin3, G-parity, - // type, lepton number, baryon number, - // PDG encoding, stable, lifetime, - // decay table, shortlived, subType - anInstance = new G4ParticleDefinition( - name, mass, 0.0, 0, - spin, parity, 0, - 0, 0, 0, - "lepton", 2, 0, - 0, false, lifetime, - nullptr, false, "e" ); - - //create Decay Table - auto table = new G4DecayTable(); - // create a decay channel - auto mode = new GatePositroniumDecayChannel(name, BR); - table->Insert(mode); - anInstance->SetDecayTable(table); - } - theInstance = dynamic_cast(anInstance); - return theInstance; +GateParaPositronium *GateParaPositronium::theInstance = nullptr; + +GateParaPositronium *GateParaPositronium::Definition() { + if (theInstance) + return theInstance; + + const G4String name = "pPs"; + const G4double mass = 2.0 * electron_mass_c2; + const G4int spin = 0; + const G4int parity = 1; + const G4double lifetime = 0.1244 * ns; + const G4double BR = 1.0; + + // search in particle table + G4ParticleTable *pTable = G4ParticleTable::GetParticleTable(); + G4ParticleDefinition *anInstance = pTable->FindParticle(name); + + if (!anInstance) { + // create particle + + // Arguments for constructor: + // name, mass, width, charge, + // spin, parity, C-conjugation, + // isospin, isospin3, G-parity, + // type, lepton number, baryon number, + // PDG encoding, stable, lifetime, + // decay table, shortlived, subType + anInstance = new G4ParticleDefinition(name, mass, 0.0, 0, spin, parity, 0, + 0, 0, 0, "lepton", 2, 0, 0, false, + lifetime, nullptr, false, "e"); + + // create Decay Table + auto table = new G4DecayTable(); + // create a decay channel + auto mode = new GatePositroniumDecayChannel(name, BR); + table->Insert(mode); + anInstance->SetDecayTable(table); + } + theInstance = dynamic_cast(anInstance); + return theInstance; } -GateParaPositronium* GateParaPositronium::ParaPositroniumDefinition() { return Definition(); } +GateParaPositronium *GateParaPositronium::ParaPositroniumDefinition() { + return Definition(); +} -GateParaPositronium* GateParaPositronium::ParaPositronium() { return Definition(); } +GateParaPositronium *GateParaPositronium::ParaPositronium() { + return Definition(); +} From 90aba8686bc700d26165689446d4497a6b76f8ab Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Mon, 6 Oct 2025 07:45:14 +0200 Subject: [PATCH 009/144] Small improvement and formatting E.g. calling sstringstream constructor instead of adding the value later. --- .../src/GateExtendedVSourceMessenger.cc | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/source/physics/src/GateExtendedVSourceMessenger.cc b/source/physics/src/GateExtendedVSourceMessenger.cc index ff9c72f5d..145af0dc8 100644 --- a/source/physics/src/GateExtendedVSourceMessenger.cc +++ b/source/physics/src/GateExtendedVSourceMessenger.cc @@ -112,14 +112,15 @@ void GateExtendedVSourceMessenger::SetNewValue( G4UIcommand* command, G4String n } else if ( command == upCmdSetPostroniumLifetime.get() ) { - G4String positronium_name = ""; - G4String units = ""; + G4String positronium_name; + G4String units; G4double value = 0.0; - std::stringstream ss; - ss << new_value; + + std::stringstream ss(new_value); ss >> positronium_name >> value >> units; - G4String new_value_lifetime = std::to_string( value ) + " " + units; - pSource->SetPostroniumLifetime( positronium_name, upCmdSetLifetime->GetNewDoubleValue( new_value_lifetime ) ); + + G4String new_lifetime_value = std::to_string( value ) + " " + units; + pSource->SetPostroniumLifetime(positronium_name, upCmdSetLifetime->GetNewDoubleValue(new_lifetime_value )); } else if ( command == upCmdSetPromptGammaEnergy.get() ) @@ -129,15 +130,16 @@ void GateExtendedVSourceMessenger::SetNewValue( G4UIcommand* command, G4String n else if ( command == upCmdSetPositroniumFraction.get() ) { G4double fraction = 0.0; - G4String positronium_kind = ""; - std::stringstream ss; - ss << new_value; + G4String positronium_kind; + + std::stringstream ss(new_value); ss >> positronium_kind >> fraction; - pSource->SetPositroniumFraction( positronium_kind, fraction ); + + pSource->SetPositroniumFraction(positronium_kind, fraction); } else { - GateVSourceMessenger::SetNewValue( command, new_value ); + GateVSourceMessenger::SetNewValue(command, new_value); } } From 2767e625e990ffe49da5701f6f074551130cfd80 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Mon, 6 Oct 2025 09:39:20 +0200 Subject: [PATCH 010/144] Fix missing braces --- .../include/GatePositroniumDecayChannel.hh | 2 - source/physics/src/GateExtendedVSource.cc | 77 +++++++++++++------ 2 files changed, 53 insertions(+), 26 deletions(-) diff --git a/source/physics/include/GatePositroniumDecayChannel.hh b/source/physics/include/GatePositroniumDecayChannel.hh index 7d5671a0f..b489c6de5 100644 --- a/source/physics/include/GatePositroniumDecayChannel.hh +++ b/source/physics/include/GatePositroniumDecayChannel.hh @@ -7,10 +7,8 @@ #ifndef GatePositroniumDecayChannel_hh #define GatePositroniumDecayChannel_hh -//#include "globals.hh" #include "G4GeneralPhaseSpaceDecay.hh" #include "G4PhysicalConstants.hh" -//#include "G4SystemOfUnits.hh" /** Author: Mateusz Bała * Email: bala.mateusz@gmail.com diff --git a/source/physics/src/GateExtendedVSource.cc b/source/physics/src/GateExtendedVSource.cc index 7a597a7c8..11274973d 100644 --- a/source/physics/src/GateExtendedVSource.cc +++ b/source/physics/src/GateExtendedVSource.cc @@ -76,34 +76,63 @@ void GateExtendedVSource::SetPositroniumFraction( const G4String& positronium_ki fParaPositroniumFraction = pPs_fraction; } -void GateExtendedVSource::PrepareModel() +void GateExtendedVSource::PrepareModel() { - SetModel( GetType() ); + SetModel(GetType()); - if ( fBehaveLikeVSource ) return; - - - if ( fModelKind == GateExtendedVSource::ModelKind::ParaPositronium || fModelKind == GateExtendedVSource::ModelKind::OrthoPositronium || fModelKind == GateExtendedVSource::ModelKind::Positronium ) - { - pModel = new GatePositroniumDecayModel(); - GatePositroniumDecayModel* model = dynamic_cast( pModel ); - - if ( fModelKind == GateExtendedVSource::ModelKind::OrthoPositronium ) { model->SetPositroniumKind( GatePositroniumDecayModel::PositroniumKind::oPs ); } - if ( fModelKind == GateExtendedVSource::ModelKind::Positronium && fParaPositroniumFraction.has_value() ) { model->SetParaPositroniumFraction( fParaPositroniumFraction.value() ); } - - if ( fEnableDeexcitation.has_value() && fEnableDeexcitation.value() ) { model->SetDecayModel( GatePositroniumDecayModel::DecayModel::WithPrompt ); } - if ( fParaPostroniumLifetime.has_value() ) { model->SetPostroniumLifetime( kParaPositroniumName, fParaPostroniumLifetime.value() ); } - if ( fOrthoPostroniumLifetime.has_value() ) { model->SetPostroniumLifetime( kOrthoPositroniumName, fOrthoPostroniumLifetime.value() ); } - if ( fPromptGammaEnergy.has_value() ) { model->SetPromptGammaEnergy( fPromptGammaEnergy.value() ); } - } - else if ( fModelKind == GateExtendedVSource::ModelKind::SingleGamma ) { pModel = new GateGammaEmissionModel(); } - else { GateError( "GateExtendedVSource::PrepareModel - unknown model." ); } + if (fBehaveLikeVSource) { + return; + } - if ( fFixedEmissionDirection.has_value() ) { pModel->SetFixedEmissionDirection( fFixedEmissionDirection.value() ); } - if ( fEnableFixedEmissionDirection.has_value() ) { pModel->SetEnableFixedEmissionDirection( fEnableFixedEmissionDirection.value() ); } - if ( fEmissionEnergy.has_value() ) { pModel->SetEmissionEnergy( fEmissionEnergy.value() ); } - if ( fSeed.has_value() ) { pModel->SetSeed( fSeed.value() ); } + if (fModelKind == GateExtendedVSource::ModelKind::ParaPositronium || + fModelKind == GateExtendedVSource::ModelKind::OrthoPositronium || + fModelKind == GateExtendedVSource::ModelKind::Positronium) { + pModel = new GatePositroniumDecayModel(); + GatePositroniumDecayModel *model = + dynamic_cast(pModel); + + if (fModelKind == GateExtendedVSource::ModelKind::OrthoPositronium) { + model->SetPositroniumKind( + GatePositroniumDecayModel::PositroniumKind::oPs); + } + if (fModelKind == GateExtendedVSource::ModelKind::Positronium && + fParaPositroniumFraction.has_value()) { + model->SetParaPositroniumFraction(fParaPositroniumFraction.value()); + } + + if (fEnableDeexcitation.has_value() && fEnableDeexcitation.value()) { + model->SetDecayModel(GatePositroniumDecayModel::DecayModel::WithPrompt); + } + if (fParaPostroniumLifetime.has_value()) { + model->SetPostroniumLifetime(kParaPositroniumName, + fParaPostroniumLifetime.value()); + } + if (fOrthoPostroniumLifetime.has_value()) { + model->SetPostroniumLifetime(kOrthoPositroniumName, + fOrthoPostroniumLifetime.value()); + } + if (fPromptGammaEnergy.has_value()) { + model->SetPromptGammaEnergy(fPromptGammaEnergy.value()); + } + } else if (fModelKind == GateExtendedVSource::ModelKind::SingleGamma) { + pModel = new GateGammaEmissionModel(); + } else { + GateError("GateExtendedVSource::PrepareModel - unknown model."); + } + if (fFixedEmissionDirection.has_value()) { + pModel->SetFixedEmissionDirection(fFixedEmissionDirection.value()); + } + if (fEnableFixedEmissionDirection.has_value()) { + pModel->SetEnableFixedEmissionDirection( + fEnableFixedEmissionDirection.value()); + } + if (fEmissionEnergy.has_value()) { + pModel->SetEmissionEnergy(fEmissionEnergy.value()); + } + if (fSeed.has_value()) { + pModel->SetSeed(fSeed.value()); + } } G4int GateExtendedVSource::GeneratePrimaries(G4Event* event) From bec4acb6b56de5573bbc78b79be4807887a59246 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Mon, 6 Oct 2025 10:34:16 +0200 Subject: [PATCH 011/144] Add smart pointers Also, update the contribution section --- source/physics/include/GateExtendedVSource.hh | 9 ++++---- .../include/GatePositroniumDecayChannel.hh | 2 +- source/physics/src/GateExtendedVSource.cc | 21 +++++++------------ 3 files changed, 13 insertions(+), 19 deletions(-) diff --git a/source/physics/include/GateExtendedVSource.hh b/source/physics/include/GateExtendedVSource.hh index edfce6eab..4014b776e 100644 --- a/source/physics/include/GateExtendedVSource.hh +++ b/source/physics/include/GateExtendedVSource.hh @@ -8,6 +8,7 @@ #define GateExtendedVSource_hh #include +#include #include "GateVSource.hh" #include "GateExtendedVSourceMessenger.hh" @@ -15,7 +16,7 @@ /** Author: Mateusz Bała * Email: bala.mateusz@gmail.com - * Organization: J-PET (http://koza.if.uj.edu.pl/pet/) + * Refactored by: Wojciech Krzemien * About class: Extended version of GateVSource. It focuses on generating gammas from positronium decay. **/ class GateExtendedVSource : public GateVSource @@ -31,7 +32,7 @@ public: }; explicit GateExtendedVSource(const G4String& name); - virtual ~GateExtendedVSource(); + virtual ~GateExtendedVSource() = default; /** Generate gammas for event **/ @@ -75,9 +76,9 @@ public: protected: //Gamma emission model - GateGammaEmissionModel* pModel = nullptr; + std::unique_ptr pModel; //Source messanger - GateExtendedVSourceMessenger* pMessenger = nullptr; + std::unique_ptr pMessenger; //User settings: ModelKind fModelKind = ModelKind::NotDefined; std::optional fEnableDeexcitation; diff --git a/source/physics/include/GatePositroniumDecayChannel.hh b/source/physics/include/GatePositroniumDecayChannel.hh index b489c6de5..4222a9ec5 100644 --- a/source/physics/include/GatePositroniumDecayChannel.hh +++ b/source/physics/include/GatePositroniumDecayChannel.hh @@ -13,7 +13,7 @@ /** Author: Mateusz Bała * Email: bala.mateusz@gmail.com * Original author of the oPs decay model: Daria Kamińska et al. ( Eur. Phys. J. C (2016) 76:445 ) - * Organization: J-PET (http://koza.if.uj.edu.pl/pet/) + * Refactored by: Wojciech Krzemien * About class: Implements pPs and oPs positronium decays. Provides support for polarization. **/ class GatePositroniumDecayChannel : public G4GeneralPhaseSpaceDecay diff --git a/source/physics/src/GateExtendedVSource.cc b/source/physics/src/GateExtendedVSource.cc index 11274973d..8c05d947f 100644 --- a/source/physics/src/GateExtendedVSource.cc +++ b/source/physics/src/GateExtendedVSource.cc @@ -10,16 +10,10 @@ #include -GateExtendedVSource::GateExtendedVSource(const G4String& name ) : GateVSource( name ) -{ - pMessenger = new GateExtendedVSourceMessenger( this ); -} - -GateExtendedVSource::~GateExtendedVSource() -{ - if ( pMessenger != nullptr ) { delete pMessenger; } - if ( pModel != nullptr ) { delete pModel; } -} +GateExtendedVSource::GateExtendedVSource(const G4String &name) + : GateVSource(name), + pMessenger(std::make_unique(this)) +{} void GateExtendedVSource::SetModel(const G4String &model_name) { @@ -87,9 +81,7 @@ void GateExtendedVSource::PrepareModel() if (fModelKind == GateExtendedVSource::ModelKind::ParaPositronium || fModelKind == GateExtendedVSource::ModelKind::OrthoPositronium || fModelKind == GateExtendedVSource::ModelKind::Positronium) { - pModel = new GatePositroniumDecayModel(); - GatePositroniumDecayModel *model = - dynamic_cast(pModel); + auto model = std::make_unique(); if (fModelKind == GateExtendedVSource::ModelKind::OrthoPositronium) { model->SetPositroniumKind( @@ -114,8 +106,9 @@ void GateExtendedVSource::PrepareModel() if (fPromptGammaEnergy.has_value()) { model->SetPromptGammaEnergy(fPromptGammaEnergy.value()); } + pModel = std::move(model); } else if (fModelKind == GateExtendedVSource::ModelKind::SingleGamma) { - pModel = new GateGammaEmissionModel(); + pModel = std::make_unique(); } else { GateError("GateExtendedVSource::PrepareModel - unknown model."); } From 837328c0f38493703fb103fb1b1eb1f72becc52e Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Mon, 6 Oct 2025 11:12:31 +0200 Subject: [PATCH 012/144] Ordering the header list --- .../include/GatePositroniumDecayModel.hh | 17 +++++------------ source/physics/src/GateExtendedVSource.cc | 6 ++++-- source/physics/src/GatePositroniumDecayModel.cc | 12 +++++++++--- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/source/physics/include/GatePositroniumDecayModel.hh b/source/physics/include/GatePositroniumDecayModel.hh index b64c6b97c..dbfcee259 100644 --- a/source/physics/include/GatePositroniumDecayModel.hh +++ b/source/physics/include/GatePositroniumDecayModel.hh @@ -8,25 +8,18 @@ #define PositroniumDecayModel_hh #include -#include "G4DecayTable.hh" -#include "G4ParticleTable.hh" -#include "G4PrimaryParticle.hh" -#include "G4GeneralPhaseSpaceDecay.hh" -#include "G4DecayTable.hh" -#include "G4ParticleDefinition.hh" -#include "GateEmittedGammaInformation.hh" -#include "GateGammaEmissionModel.hh" -#include "G4PhysicalConstants.hh" +#include "G4PrimaryParticle.hh" #include "G4SystemOfUnits.hh" - #include "G4VDecayChannel.hh" -#include "G4ParticleDefinition.hh" #include "G4PrimaryVertex.hh" +#include "GateEmittedGammaInformation.hh" +#include "GateGammaEmissionModel.hh" + + /** Author: Mateusz Bała * Email: bala.mateusz@gmail.com - * Organization: J-PET (http://koza.if.uj.edu.pl/pet/) * About class: Class generate gammas from positronium decay with including deexcitation gamma ( prompt gamma ) if is required. **/ class GatePositroniumDecayModel : public GateGammaEmissionModel diff --git a/source/physics/src/GateExtendedVSource.cc b/source/physics/src/GateExtendedVSource.cc index 8c05d947f..a609d00d0 100644 --- a/source/physics/src/GateExtendedVSource.cc +++ b/source/physics/src/GateExtendedVSource.cc @@ -4,11 +4,13 @@ of the GNU Lesser General Public Licence (LGPL) See LICENSE.md for further details ----------------------*/ +#include + +#include "G4Event.hh" + #include "GateExtendedVSource.hh" #include "GatePositroniumDecayModel.hh" -#include "G4Event.hh" -#include GateExtendedVSource::GateExtendedVSource(const G4String &name) : GateVSource(name), diff --git a/source/physics/src/GatePositroniumDecayModel.cc b/source/physics/src/GatePositroniumDecayModel.cc index 23eaeea75..a5bea94ee 100644 --- a/source/physics/src/GatePositroniumDecayModel.cc +++ b/source/physics/src/GatePositroniumDecayModel.cc @@ -4,15 +4,21 @@ of the GNU Lesser General Public Licence (LGPL) See LICENSE.md for further details ----------------------*/ -#include "GatePositroniumDecayModel.hh" -#include "Randomize.hh" #include #include + +#include "Randomize.hh" #include "G4DecayProducts.hh" #include "G4LorentzVector.hh" #include "G4ParticleTable.hh" +#include "G4DecayTable.hh" +#include "G4ParticleTable.hh" +#include "G4DecayTable.hh" +#include "G4ParticleDefinition.hh" + +#include "GatePositroniumDecayModel.hh" -GatePositroniumDecayModel::Positronium::Positronium( G4String name, G4double life_time, G4int annihilation_gammas_number ) : fName( name ), fLifeTime( life_time ), fAnnihilationGammasNumber( annihilation_gammas_number ) +GatePositroniumDecayModel::Positronium::Positronium(const G4String name, G4double life_time, G4int annihilation_gammas_number ) : fName( name ), fLifeTime( life_time ), fAnnihilationGammasNumber( annihilation_gammas_number ) { G4ParticleDefinition* positronium_def = G4ParticleTable::GetParticleTable()->FindParticle( name ); G4DecayTable* positronium_decay_table = positronium_def->GetDecayTable(); From e63f8ddd635e9ddeb2789b859f4ce2c0c1231733 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Mon, 6 Oct 2025 12:10:37 +0200 Subject: [PATCH 013/144] Starting refactorization of Positronium Decay Model !Work in Progress! Add the stub of the proposed solution --- .../include/GatePositroniumDecayModel.hh | 35 +++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/source/physics/include/GatePositroniumDecayModel.hh b/source/physics/include/GatePositroniumDecayModel.hh index dbfcee259..566b9fe1f 100644 --- a/source/physics/include/GatePositroniumDecayModel.hh +++ b/source/physics/include/GatePositroniumDecayModel.hh @@ -109,7 +109,7 @@ class GatePositroniumDecayModel : public GateGammaEmissionModel //Positronium model - for current event Positronium* pInfoPs = nullptr; //Default deexcitation gamma energy - if user didn't set prompt gamma energy this value will be used - const G4double kSodium22DeexcitationGammaEnergy = 1.022 * MeV;//[MeV] + static constexpr G4double kSodium22DeexcitationGammaEnergy = 1.022 * MeV; //Which positronium use for gammas generation PositroniumKind fPositroniumKind = PositroniumKind::pPs; @@ -121,7 +121,38 @@ class GatePositroniumDecayModel : public GateGammaEmissionModel G4double fParaPositroniumFraction = 1.0; //It is required to generate mixed positronium decays ( pPs and oPs witch propability controled by varaible fParaPositroniumFraction ) G4bool fUsePositroniumFractions = false; - + + ///wk: We want to use std::vector fractions + ///wk: std::vector 2G or 3G + ///wk std::vector lifetimes + ///wk std::vector + /// + enum PositroniumDecayKind { k2g, k3orth, k3isotropic}; + struct PositroniumDecayModelParams + { + std::vector fFractions; + std::vector fLifetimes; + std::vector fDecayKind; + }; + class MiniPositroniumDecayModel + { + public: + int getPositroniumDecayParamsForEvent() { + //r = G4UniformRand(); + float r =0.3; //probability + float curr_frac_cumulative = 0.0; + for (int i = 0; i < fModelParams.fFractions.size(); ++i) { + curr_frac_cumulative = curr_frac_cumulative +fModelParams.fFractions[i]; + if(r<= curr_frac_cumulative) return i; + } + return -1; + } + MiniPositroniumDecayModel(const PositroniumDecayModelParams& modelParams):fModelParams(modelParams) + {} + private: + PositroniumDecayModelParams fModelParams; + }; }; + #endif From d3233d409dc4f2b26e3505faab67f31afe714d9b Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Thu, 9 Oct 2025 22:40:07 +0200 Subject: [PATCH 014/144] Add stub of two unit tests that can be run with ctest !Work in progress! We can actually run some simple unit tests for GatePositroniumDecayModel --- CMakeLists.txt | 14 +++++ .../include/GatePositroniumDecayModel.hh | 61 ++++++++++--------- tests/test_GatePositroniumDecayModel.cpp | 24 ++++++++ tests/test_dummy.cpp | 22 +++++++ 4 files changed, 91 insertions(+), 30 deletions(-) create mode 100644 tests/test_GatePositroniumDecayModel.cpp create mode 100644 tests/test_dummy.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 1abf44bef..604522754 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,9 @@ CMAKE_POLICY(SET CMP0012 NEW) # if() recognizes numbers and boolean constants. CMAKE_POLICY(SET CMP0025 NEW) # Compiler id for Apple Clang is now AppleClang. CMAKE_POLICY(SET CMP0057 NEW) # Support new if() IN_LIST operator. CMAKE_POLICY(SET CMP0072 NEW) # FindOpenGL prefers GLVND by default when available. +#========================================================= +#set(CMAKE_CXX_COMPILER "/usr/bin/x86_64-linux-gnu-g++-13") +#set(CMAKE_EXPORT_COMPILE_COMMANDS ON) #========================================================= PROJECT(Gate) @@ -20,6 +23,17 @@ IF(BUILD_TESTING) ENABLE_TESTING() SET(BUILDNAME "${BUILDNAME}" CACHE STRING "Name of build on the dashboard") MARK_AS_ADVANCED(BUILDNAME) + #Stupid solution cause it links to everything but for now on + ADD_EXECUTABLE(test_dummy tests/test_dummy.cpp) + TARGET_LINK_LIBRARIES(test_dummy GateLib) + + ADD_TEST(NAME test_dummy COMMAND test_dummy) + + ADD_EXECUTABLE(test_GatePositroniumDecayModel tests/test_GatePositroniumDecayModel.cpp) + TARGET_LINK_LIBRARIES(test_GatePositroniumDecayModel GateLib) + + ADD_TEST(NAME test_GatePositroniumDecayModel COMMAND test_GatePositroniumDecayModel) + ENDIF(BUILD_TESTING) #========================================================= diff --git a/source/physics/include/GatePositroniumDecayModel.hh b/source/physics/include/GatePositroniumDecayModel.hh index 566b9fe1f..62330fb98 100644 --- a/source/physics/include/GatePositroniumDecayModel.hh +++ b/source/physics/include/GatePositroniumDecayModel.hh @@ -122,37 +122,38 @@ class GatePositroniumDecayModel : public GateGammaEmissionModel //It is required to generate mixed positronium decays ( pPs and oPs witch propability controled by varaible fParaPositroniumFraction ) G4bool fUsePositroniumFractions = false; - ///wk: We want to use std::vector fractions - ///wk: std::vector 2G or 3G - ///wk std::vector lifetimes - ///wk std::vector - /// - enum PositroniumDecayKind { k2g, k3orth, k3isotropic}; - struct PositroniumDecayModelParams - { - std::vector fFractions; - std::vector fLifetimes; - std::vector fDecayKind; - }; - class MiniPositroniumDecayModel - { - public: - int getPositroniumDecayParamsForEvent() { - //r = G4UniformRand(); - float r =0.3; //probability - float curr_frac_cumulative = 0.0; - for (int i = 0; i < fModelParams.fFractions.size(); ++i) { - curr_frac_cumulative = curr_frac_cumulative +fModelParams.fFractions[i]; - if(r<= curr_frac_cumulative) return i; - } - return -1; - } - MiniPositroniumDecayModel(const PositroniumDecayModelParams& modelParams):fModelParams(modelParams) - {} - private: - PositroniumDecayModelParams fModelParams; - }; }; +///wk: We want to use std::vector fractions +///wk: std::vector 2G or 3G +///wk std::vector lifetimes +///wk std::vector +/// +enum PositroniumDecayKind { k2g, k3orth, k3isotropic}; +struct PositroniumDecayModelParams +{ + std::vector fFractions; + std::vector fLifetimes; + std::vector fDecayKind; +}; + +class MiniPositroniumDecayModel +{ + public: + int getPositroniumDecayParamsForEvent() { + //r = G4UniformRand(); + float r =0.3; //probability + float curr_frac_cumulative = 0.0; + for (int i = 0; i < fModelParams.fFractions.size(); ++i) { + curr_frac_cumulative = curr_frac_cumulative +fModelParams.fFractions[i]; + if(r<= curr_frac_cumulative) return i; + } + return -1; + } + MiniPositroniumDecayModel(const PositroniumDecayModelParams& modelParams):fModelParams(modelParams) + {} + private: + PositroniumDecayModelParams fModelParams; +}; #endif diff --git a/tests/test_GatePositroniumDecayModel.cpp b/tests/test_GatePositroniumDecayModel.cpp new file mode 100644 index 000000000..3beb23d40 --- /dev/null +++ b/tests/test_GatePositroniumDecayModel.cpp @@ -0,0 +1,24 @@ +#include +#include +#include + +bool run_tests() +{ + PositroniumDecayModelParams params; + MiniPositroniumDecayModel model(params); + auto res = model.getPositroniumDecayParamsForEvent(); + return true; +} + +int main() +{ + bool res = true; + res = res & run_tests(); + if (res) { + std::cout << "All tests have passed" << std::endl; + return 0; + } else { + std::cerr << "Some tests failed" << std::endl; + return -1; + } +} diff --git a/tests/test_dummy.cpp b/tests/test_dummy.cpp new file mode 100644 index 000000000..98a6b13e1 --- /dev/null +++ b/tests/test_dummy.cpp @@ -0,0 +1,22 @@ +#include +#include +bool run_tests() +{ + if (1==0) { + return true; + } + return false; +} + +int main() +{ + bool res = true; + res = res & run_tests(); + if (res) { + std::cout << "All tests have passed" << std::endl; + return 0; + } else { + std::cerr << "Some tests failed" << std::endl; + return -1; + } +} From 6d9f2d80734fa87e645f1d3f24929e698df0a9c9 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 10 Oct 2025 16:26:08 +0200 Subject: [PATCH 015/144] Remove unused includes --- source/physics/include/GateGammaEmissionModel.hh | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/physics/include/GateGammaEmissionModel.hh b/source/physics/include/GateGammaEmissionModel.hh index c747cc4d4..b25136071 100644 --- a/source/physics/include/GateGammaEmissionModel.hh +++ b/source/physics/include/GateGammaEmissionModel.hh @@ -8,8 +8,6 @@ #define GateGammaEmissionModel_hh #include "G4PrimaryParticle.hh" -#include -#include #include "GateEmittedGammaInformation.hh" #include "G4Event.hh" #include "G4SystemOfUnits.hh" From 8006a6dd46289d6136c9ec4ba236874a10003fa7 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 10 Oct 2025 16:26:42 +0200 Subject: [PATCH 016/144] Extend the test for GatePositroniumDecayModel --- tests/test_GatePositroniumDecayModel.cpp | 38 ++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/tests/test_GatePositroniumDecayModel.cpp b/tests/test_GatePositroniumDecayModel.cpp index 3beb23d40..80bbf09cc 100644 --- a/tests/test_GatePositroniumDecayModel.cpp +++ b/tests/test_GatePositroniumDecayModel.cpp @@ -1,13 +1,47 @@ #include #include +#include #include +/// It should be separated in several subtests bool run_tests() { + bool res = true; PositroniumDecayModelParams params; MiniPositroniumDecayModel model(params); - auto res = model.getPositroniumDecayParamsForEvent(); - return true; + int index = MiniPositroniumDecayModel::getPositroniumDecayIndex({1}); + if (index != 0) { + res = false; + std::cerr << "getPositroniumDecayDecayIndex({1})!=0" << std::endl; + } + index = MiniPositroniumDecayModel::getPositroniumDecayIndex({}); + if (index != -1) { + res = false; + std::cerr << "getPositroniumDecayDecayIndex({})!=-1" << std::endl; + } + + float epsilon = 1e-3; + int num_of_trials = 1e6; + std::vector fractions={0.5,0.4,0.1}; + std::vector indices = {0,0,0}; + for (int i = 0; i < num_of_trials; i++) { + index = MiniPositroniumDecayModel::getPositroniumDecayIndex(fractions); + indices[index] = indices[index] +1; + } + std::vector estimated_fractions = {0.,0., 0.}; + for (int i = 0; i < estimated_fractions.size(); i++) { + estimated_fractions[i] = float(indices[i])/num_of_trials; + std::cout << "estimated_fractions[i]="<epsilon) + { + res = false; + std::cerr << "assumed fractions and estimated fractions differ: std::abs(fractions[i] - estimated_fractions[i])" << std::endl; + } + } + return res; } int main() From b6daec2c8c82c7518a968abf5cb8d24f10a7da06 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 10 Oct 2025 16:27:28 +0200 Subject: [PATCH 017/144] Add new functionality to GetPositroniumDecayModel !Work in Progress! Add some overlapping functionality e.g. MiniPositroniumDecayModel to be able to test the new functionality independently. This must be cleaned up later. --- .../include/GatePositroniumDecayModel.hh | 39 +++++++++----- .../physics/src/GatePositroniumDecayModel.cc | 53 +++++++++++++++++++ 2 files changed, 78 insertions(+), 14 deletions(-) diff --git a/source/physics/include/GatePositroniumDecayModel.hh b/source/physics/include/GatePositroniumDecayModel.hh index 62330fb98..dffb4609d 100644 --- a/source/physics/include/GatePositroniumDecayModel.hh +++ b/source/physics/include/GatePositroniumDecayModel.hh @@ -16,6 +16,7 @@ #include "GateEmittedGammaInformation.hh" #include "GateGammaEmissionModel.hh" +#include "Randomize.hh" // to be removed later /** Author: Mateusz Bała @@ -129,7 +130,7 @@ class GatePositroniumDecayModel : public GateGammaEmissionModel ///wk std::vector lifetimes ///wk std::vector /// -enum PositroniumDecayKind { k2g, k3orth, k3isotropic}; +enum PositroniumDecayKind { k2Gamma, k3Gamma}; struct PositroniumDecayModelParams { std::vector fFractions; @@ -137,23 +138,33 @@ struct PositroniumDecayModelParams std::vector fDecayKind; }; -class MiniPositroniumDecayModel +class MiniPositroniumDecayModel:public GateGammaEmissionModel { public: - int getPositroniumDecayParamsForEvent() { - //r = G4UniformRand(); - float r =0.3; //probability - float curr_frac_cumulative = 0.0; - for (int i = 0; i < fModelParams.fFractions.size(); ++i) { - curr_frac_cumulative = curr_frac_cumulative +fModelParams.fFractions[i]; - if(r<= curr_frac_cumulative) return i; - } - return -1; + static int getPositroniumDecayIndex(const std::vector& fractions); + explicit MiniPositroniumDecayModel(const PositroniumDecayModelParams& modelParams):fModelParams(modelParams) + { + auto num_of_decay_channels = fModelParams.fDecayKind.size(); + for (int i = 0; i < num_of_decay_channels; i++) { + if (fModelParams.fDecayKind[i] == PositroniumDecayKind::k2Gamma) + { + fPositroniumDecayChannel.push_back(std::move(GatePositroniumDecayModel::Positronium("pPs", fModelParams.fLifetimes[i]* ns, 2))); + } else { + fPositroniumDecayChannel.push_back(std::move(GatePositroniumDecayModel::Positronium("oPs", fModelParams.fLifetimes[i]* ns, 3))); + } + + } } - MiniPositroniumDecayModel(const PositroniumDecayModelParams& modelParams):fModelParams(modelParams) - {} - private: + + protected: + virtual G4int GeneratePrimaryVertices(G4Event* event, G4double& particle_time, G4ThreeVector& particle_position) override; + G4PrimaryVertex *GetPrimaryVertexFromPositroniumAnnihilation(G4double particle_time, const G4ThreeVector &particle_position, int decayIndex); + std::vector GetGammasFromPositroniumAnnihilation(int decayIndex); + +private: PositroniumDecayModelParams fModelParams; + std::vector fPositroniumDecayChannel; }; + #endif diff --git a/source/physics/src/GatePositroniumDecayModel.cc b/source/physics/src/GatePositroniumDecayModel.cc index a5bea94ee..bdf126a1d 100644 --- a/source/physics/src/GatePositroniumDecayModel.cc +++ b/source/physics/src/GatePositroniumDecayModel.cc @@ -180,4 +180,57 @@ std::vector GatePositroniumDecayModel::GetGammasFromPositron return gammas; } +int MiniPositroniumDecayModel::getPositroniumDecayIndex(const std::vector& fractions) { + auto r = G4UniformRand(); + float curr_frac_cumulative = 0.0; + for (int i = 0; i < fractions.size(); ++i) { + curr_frac_cumulative = curr_frac_cumulative + fractions[i]; + if(r<= curr_frac_cumulative) return i; + } + return -1; +} + +G4PrimaryVertex* MiniPositroniumDecayModel::GetPrimaryVertexFromPositroniumAnnihilation(G4double particle_time, const G4ThreeVector& particle_position, int decayIndex) +{ + + G4double shifted_particle_time = particle_time + G4RandExponential::shoot(fModelParams.fLifetimes[decayIndex]); + + G4PrimaryVertex* vertex = new G4PrimaryVertex( particle_position, shifted_particle_time ); + std::vector gammas = GetGammasFromPositroniumAnnihilation(decayIndex); + std::for_each( gammas.begin(), gammas.end(), [&]( G4PrimaryParticle* gamma ) { vertex->SetPrimary( gamma ); } ); + return vertex; +} + +// For this moment without prompt +G4int MiniPositroniumDecayModel::GeneratePrimaryVertices(G4Event* event, G4double& particle_time, G4ThreeVector& particle_position) +{ + auto decayIndex = MiniPositroniumDecayModel::getPositroniumDecayIndex(fModelParams.fFractions); + G4int number_of_vertices = 1; + event->AddPrimaryVertex(GetPrimaryVertexFromPositroniumAnnihilation(particle_time, particle_position, decayIndex)); + return number_of_vertices; +} + +std::vector MiniPositroniumDecayModel::GetGammasFromPositroniumAnnihilation(int decayIndex) +{ + int annihilation_gammas_number = fPositroniumDecayChannel[decayIndex].GetAnnihilationGammasNumber(); + //std::vector gammas( pInfoPs->GetAnnihilationGammasNumber() ) ; + std::vector gammas(annihilation_gammas_number); + + G4DecayProducts* decay_products = fPositroniumDecayChannel[decayIndex].GetDecayProducts(); + //G4DecayProducts* decay_products = nullptr; + for ( G4int i = 0; i < annihilation_gammas_number; ++i ) + { + G4PrimaryParticle* gamma = new G4PrimaryParticle( pGammaDefinition ); + + G4DynamicParticle* dynamic_gamma = (*decay_products)[i]; + G4LorentzVector lv = dynamic_gamma->Get4Momentum(); + gamma->Set4Momentum( lv.px(), lv.py(), lv.pz(), lv.e() ); + gamma->SetPolarization( dynamic_gamma->GetPolarization() ); + gamma->SetUserInformation( GetPrimaryParticleInformation( gamma, GateEmittedGammaInformation::GammaKind::Annihilation ) ); + gammas[i] = gamma; + } + delete decay_products; + + return gammas; +} From 924e161f090b57596a717724796a8869e4360560 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 10 Oct 2025 23:36:28 +0200 Subject: [PATCH 018/144] Improve the test Add Gate initialization --- .../include/GatePositroniumDecayModel.hh | 7 +++- tests/test_GatePositroniumDecayModel.cpp | 37 ++++++++++++++++++- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/source/physics/include/GatePositroniumDecayModel.hh b/source/physics/include/GatePositroniumDecayModel.hh index dffb4609d..98c1e30b7 100644 --- a/source/physics/include/GatePositroniumDecayModel.hh +++ b/source/physics/include/GatePositroniumDecayModel.hh @@ -16,7 +16,6 @@ #include "GateEmittedGammaInformation.hh" #include "GateGammaEmissionModel.hh" -#include "Randomize.hh" // to be removed later /** Author: Mateusz Bała @@ -145,14 +144,18 @@ class MiniPositroniumDecayModel:public GateGammaEmissionModel explicit MiniPositroniumDecayModel(const PositroniumDecayModelParams& modelParams):fModelParams(modelParams) { auto num_of_decay_channels = fModelParams.fDecayKind.size(); + std::cout << num_of_decay_channels << std::endl; for (int i = 0; i < num_of_decay_channels; i++) { if (fModelParams.fDecayKind[i] == PositroniumDecayKind::k2Gamma) { + std::cout << "creating pPs" << std::endl; fPositroniumDecayChannel.push_back(std::move(GatePositroniumDecayModel::Positronium("pPs", fModelParams.fLifetimes[i]* ns, 2))); + std::cout << "after creating pPs" << std::endl; } else { + std::cout << "creating oPs" << std::endl; fPositroniumDecayChannel.push_back(std::move(GatePositroniumDecayModel::Positronium("oPs", fModelParams.fLifetimes[i]* ns, 3))); + std::cout << "after creating oPs" << std::endl; } - } } diff --git a/tests/test_GatePositroniumDecayModel.cpp b/tests/test_GatePositroniumDecayModel.cpp index 80bbf09cc..c32274395 100644 --- a/tests/test_GatePositroniumDecayModel.cpp +++ b/tests/test_GatePositroniumDecayModel.cpp @@ -1,8 +1,40 @@ #include #include #include +#include + #include +#include "GateRunManager.hh" +#include "GatePhysicsList.hh" +#include "GateDetectorConstruction.hh" + +void initializeGateRunManager(GateRunManager* runManager) +{ + // Set the DetectorConstruction + GateDetectorConstruction* gateDC = new GateDetectorConstruction(); + runManager->SetUserInitialization( gateDC ); + // Set the PhysicsList + runManager->SetUserInitialization( GatePhysicsList::GetInstance() ); + // Initialize G4 kernel + runManager->InitializeAll(); +} + +bool run_tests2() +{ + std::unique_ptr runManager(new GateRunManager); + initializeGateRunManager(runManager.get()); + + PositroniumDecayModelParams params; + params.fFractions={0.3,0.7}; + params.fLifetimes={0.1244 ,138.6}; + params.fDecayKind={PositroniumDecayKind::k2Gamma, PositroniumDecayKind::k3Gamma}; + MiniPositroniumDecayModel model(params); + + + return true; +} + /// It should be separated in several subtests bool run_tests() { @@ -31,14 +63,13 @@ bool run_tests() std::vector estimated_fractions = {0.,0., 0.}; for (int i = 0; i < estimated_fractions.size(); i++) { estimated_fractions[i] = float(indices[i])/num_of_trials; - std::cout << "estimated_fractions[i]="<epsilon) { res = false; - std::cerr << "assumed fractions and estimated fractions differ: std::abs(fractions[i] - estimated_fractions[i])" << std::endl; + std::cerr << "assumed fractions and estimated fractions differ:" << std::abs(fractions[i] - estimated_fractions[i]) << std::endl; } } return res; @@ -48,6 +79,8 @@ int main() { bool res = true; res = res & run_tests(); + res = res & run_tests2(); + if (res) { std::cout << "All tests have passed" << std::endl; return 0; From 0828e8e9caaabc652164fee8724fa515626c992d Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Tue, 14 Oct 2025 16:22:02 +0200 Subject: [PATCH 019/144] Small refactoring Fix typo in method name, also ordering of the code etc. --- .../include/GateExtendedVSourceMessenger.hh | 26 ++++++++++--------- .../src/GateExtendedVSourceMessenger.cc | 26 +++++++++---------- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/source/physics/include/GateExtendedVSourceMessenger.hh b/source/physics/include/GateExtendedVSourceMessenger.hh index e3cd9ffad..0990ca46e 100644 --- a/source/physics/include/GateExtendedVSourceMessenger.hh +++ b/source/physics/include/GateExtendedVSourceMessenger.hh @@ -8,7 +8,6 @@ #define GateExtendedVSourceMessenger_hh #include "GateVSourceMessenger.hh" -#include "G4UImessenger.hh" #include "G4UIcmdWithAnInteger.hh" #include "G4UIcmdWithADoubleAndUnit.hh" #include "G4UIcmdWithABool.hh" @@ -21,36 +20,39 @@ class GateExtendedVSource; /** Author: Mateusz Bała * Email: bala.mateusz@gmail.com - * Organization: J-PET (http://koza.if.uj.edu.pl/pet/) + * Refactored: Wojciech Krzemien * About class: Messenger for GateExtendedVSource class **/ class GateExtendedVSourceMessenger: public GateVSourceMessenger { public: - GateExtendedVSourceMessenger( GateExtendedVSource* source ); - ~GateExtendedVSourceMessenger(); + explicit GateExtendedVSourceMessenger(GateExtendedVSource *source); + ~GateExtendedVSourceMessenger()=default; - void SetNewValue( G4UIcommand* command, G4String newValue ); + void SetNewValue(G4UIcommand *command, G4String newValue) override; protected: void InitCommands(); - G4UIcmdWithABool* GetBoolCmd( const G4String cmd_name, const G4String cmd_guidance ); - G4UIcmdWithADoubleAndUnit* GetDoubleCmdWithUnit( const G4String cmd_name, const G4String cmd_guidance, const G4String default_unit, const G4String unit_candidates ); - G4UIcmdWith3Vector* GetVectorCmd( const G4String cmd_name, const G4String cmd_guidance ); - G4UIcmdWithAnInteger* GetIntCmd( const G4String cmd_name, const G4String cmd_guidance ); - G4UIcmdWithAString* GetStringCmd(const G4String cmd_name, const G4String cmd_guidance ); - G4UIcmdWith3VectorAndUnit* GetVectorCmdWithUnit( const G4String cmd_name, const G4String cmd_guidance, const G4String default_unit, const G4String unit_candidates ); + + G4UIcmdWithABool* GetBoolCmd(const G4String& cmd_name, const G4String& cmd_guidance); + G4UIcmdWithADoubleAndUnit* GetDoubleCmdWithUnit(const G4String& cmd_name, const G4String& cmd_guidance, const G4String& default_unit, const G4String& unit_candidates); + G4UIcmdWith3Vector* GetVectorCmd(const G4String& cmd_name, const G4String& cmd_guidance); + G4UIcmdWithAnInteger* GetIntCmd(const G4String& cmd_name, const G4String& cmd_guidance); + G4UIcmdWithAString* GetStringCmd(const G4String& cmd_name, const G4String& cmd_guidance); + G4UIcmdWith3VectorAndUnit* GetVectorCmdWithUnit(const G4String& cmd_name, const G4String& cmd_guidance, const G4String& default_unit, const G4String& unit_candidates); protected: GateExtendedVSource* pSource = nullptr; + std::unique_ptr upCmdSetEnableDeexcitation; std::unique_ptr upCmdSetFixedEmissionDirection; std::unique_ptr upCmdSetEnableFixedEmissionDirection; std::unique_ptr upCmdSetEmissionEnergy; std::unique_ptr upCmdSetSeed; - std::unique_ptr upCmdSetPostroniumLifetime; + std::unique_ptr upCmdSetPositroniumLifetime; std::unique_ptr upCmdSetPromptGammaEnergy; std::unique_ptr upCmdSetPositroniumFraction; + //Supporting commands - disable for user std::unique_ptr upCmdSetLifetime; diff --git a/source/physics/src/GateExtendedVSourceMessenger.cc b/source/physics/src/GateExtendedVSourceMessenger.cc index 145af0dc8..0e276529b 100644 --- a/source/physics/src/GateExtendedVSourceMessenger.cc +++ b/source/physics/src/GateExtendedVSourceMessenger.cc @@ -5,19 +5,17 @@ See LICENSE.md for further details ----------------------*/ +#include + #include "GateExtendedVSourceMessenger.hh" #include "GateExtendedVSource.hh" -#include -GateExtendedVSourceMessenger::GateExtendedVSourceMessenger( GateExtendedVSource* source ) : GateVSourceMessenger( source ) +GateExtendedVSourceMessenger::GateExtendedVSourceMessenger(GateExtendedVSource *source): GateVSourceMessenger(source), pSource(source) { - pSource = source; - InitCommands(); + InitCommands(); } -GateExtendedVSourceMessenger::~GateExtendedVSourceMessenger() {} - -G4UIcmdWithABool* GateExtendedVSourceMessenger::GetBoolCmd( const G4String cmd_name, const G4String cmd_guidance ) +G4UIcmdWithABool* GateExtendedVSourceMessenger::GetBoolCmd(const G4String& cmd_name, const G4String& cmd_guidance ) { G4String cmd_path = GetDirectoryName() + cmd_name; G4UIcmdWithABool* cmd = new G4UIcmdWithABool( cmd_path, this ); @@ -26,7 +24,7 @@ G4UIcmdWithABool* GateExtendedVSourceMessenger::GetBoolCmd( const G4String cmd_n return cmd; } -G4UIcmdWithADoubleAndUnit* GateExtendedVSourceMessenger::GetDoubleCmdWithUnit( const G4String cmd_name, const G4String cmd_guidance, const G4String default_unit, const G4String unit_candidates ) +G4UIcmdWithADoubleAndUnit* GateExtendedVSourceMessenger::GetDoubleCmdWithUnit( const G4String& cmd_name, const G4String& cmd_guidance, const G4String& default_unit, const G4String& unit_candidates ) { G4String cmd_path = GetDirectoryName() + cmd_name; G4UIcmdWithADoubleAndUnit* cmd = new G4UIcmdWithADoubleAndUnit( cmd_path , this ); @@ -37,7 +35,7 @@ G4UIcmdWithADoubleAndUnit* GateExtendedVSourceMessenger::GetDoubleCmdWithUnit( c return cmd; } -G4UIcmdWith3Vector* GateExtendedVSourceMessenger::GetVectorCmd( const G4String cmd_name, const G4String cmd_guidance ) +G4UIcmdWith3Vector* GateExtendedVSourceMessenger::GetVectorCmd( const G4String& cmd_name, const G4String& cmd_guidance ) { G4String cmd_path = GetDirectoryName() + cmd_name; G4UIcmdWith3Vector* cmd = new G4UIcmdWith3Vector( cmd_path, this ); @@ -46,7 +44,7 @@ G4UIcmdWith3Vector* GateExtendedVSourceMessenger::GetVectorCmd( const G4String c return cmd; } -G4UIcmdWithAnInteger* GateExtendedVSourceMessenger::GetIntCmd( const G4String cmd_name, const G4String cmd_guidance ) +G4UIcmdWithAnInteger* GateExtendedVSourceMessenger::GetIntCmd( const G4String& cmd_name, const G4String& cmd_guidance ) { G4String cmd_path = GetDirectoryName() + cmd_name; G4UIcmdWithAnInteger* cmd = new G4UIcmdWithAnInteger( cmd_path, this ); @@ -55,7 +53,7 @@ G4UIcmdWithAnInteger* GateExtendedVSourceMessenger::GetIntCmd( const G4String cm return cmd; } -G4UIcmdWithAString* GateExtendedVSourceMessenger::GetStringCmd(const G4String cmd_name, const G4String cmd_guidance ) +G4UIcmdWithAString* GateExtendedVSourceMessenger::GetStringCmd(const G4String& cmd_name, const G4String& cmd_guidance ) { G4String cmd_path = GetDirectoryName() + cmd_name; G4UIcmdWithAString* cmd = new G4UIcmdWithAString( cmd_path, this ); @@ -64,7 +62,7 @@ G4UIcmdWithAString* GateExtendedVSourceMessenger::GetStringCmd(const G4String cm return cmd; } -G4UIcmdWith3VectorAndUnit* GateExtendedVSourceMessenger::GetVectorCmdWithUnit( const G4String cmd_name, const G4String cmd_guidance, const G4String default_unit, const G4String unit_candidates ) +G4UIcmdWith3VectorAndUnit* GateExtendedVSourceMessenger::GetVectorCmdWithUnit( const G4String& cmd_name, const G4String& cmd_guidance, const G4String& default_unit, const G4String& unit_candidates ) { G4String cmd_path = GetDirectoryName() + cmd_name; G4UIcmdWith3VectorAndUnit* cmd = new G4UIcmdWith3VectorAndUnit( cmd_path , this ); @@ -82,7 +80,7 @@ void GateExtendedVSourceMessenger::InitCommands() upCmdSetEnableFixedEmissionDirection.reset( GetBoolCmd( "setEnableFixedEmissionDirection", "Set fixed direction enable/disable." ) ); upCmdSetEmissionEnergy.reset( GetDoubleCmdWithUnit( "setEmissionEnergy", "Set energy for single gamma.", "keV", "keV MeV GeV" ) ); upCmdSetSeed.reset( GetIntCmd("setSeed", "Set seed for random generator" ) ); - upCmdSetPostroniumLifetime.reset( GetStringCmd( "setPostroniumLifetime", "Set life-time of positronium." ) ); + upCmdSetPositroniumLifetime.reset( GetStringCmd( "setPositroniumLifetime", "Set life-time of positronium." ) ); upCmdSetLifetime.reset( GetDoubleCmdWithUnit( "setLifetime", "Set life-time of positronium - disable for user.", "ns", "ps ns" ) ); upCmdSetPromptGammaEnergy.reset( GetDoubleCmdWithUnit( "setPromptGammaEnergy", "Set energy for prompt gamma.", "keV", "keV MeV GeV" ) ); upCmdSetPositroniumFraction.reset( GetStringCmd( "setPositroniumFraction", "\"positronium_kind fraction\" - where positronium_kind = {pPs, oPs} and fraction in [0.0, 1.0]" ) ); @@ -110,7 +108,7 @@ void GateExtendedVSourceMessenger::SetNewValue( G4UIcommand* command, G4String n { pSource->SetSeed( static_cast( upCmdSetSeed->GetNewIntValue( new_value ) ) ); } - else if ( command == upCmdSetPostroniumLifetime.get() ) + else if ( command == upCmdSetPositroniumLifetime.get() ) { G4String positronium_name; G4String units; From a5ac434a936951f52e44eee4a4e4766d34504901 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Tue, 14 Oct 2025 21:44:07 +0200 Subject: [PATCH 020/144] Extension of the decay model to include the excitation function --- source/physics/include/GateExtendedVSource.hh | 3 ++- .../include/GatePositroniumDecayModel.hh | 9 +++---- source/physics/src/GateExtendedVSource.cc | 25 ++++++++++++++++--- .../physics/src/GatePositroniumDecayModel.cc | 21 ++++++++++++++-- tests/test_GatePositroniumDecayModel.cpp | 1 - 5 files changed, 46 insertions(+), 13 deletions(-) diff --git a/source/physics/include/GateExtendedVSource.hh b/source/physics/include/GateExtendedVSource.hh index 4014b776e..31664b758 100644 --- a/source/physics/include/GateExtendedVSource.hh +++ b/source/physics/include/GateExtendedVSource.hh @@ -28,7 +28,8 @@ public: SingleGamma, // generate single gamma ParaPositronium, //generate gammas from para-positronium decay OrthoPositronium, //generate gammas from ortho-positronium decay - Positronium //generate gammas from mixed model ( from pPs and oPs decay with setted ratio ) + Positronium, //generate gammas from mixed model ( from pPs and oPs decay with setted ratio ) + MiniPositronium //generate gammas from extended mixed model }; explicit GateExtendedVSource(const G4String& name); diff --git a/source/physics/include/GatePositroniumDecayModel.hh b/source/physics/include/GatePositroniumDecayModel.hh index 98c1e30b7..5d2193f6a 100644 --- a/source/physics/include/GatePositroniumDecayModel.hh +++ b/source/physics/include/GatePositroniumDecayModel.hh @@ -134,6 +134,8 @@ struct PositroniumDecayModelParams { std::vector fFractions; std::vector fLifetimes; + std::vector fIsPromptPhoton; + std::vector fPromptPhotonEnergy; std::vector fDecayKind; }; @@ -144,24 +146,21 @@ class MiniPositroniumDecayModel:public GateGammaEmissionModel explicit MiniPositroniumDecayModel(const PositroniumDecayModelParams& modelParams):fModelParams(modelParams) { auto num_of_decay_channels = fModelParams.fDecayKind.size(); - std::cout << num_of_decay_channels << std::endl; for (int i = 0; i < num_of_decay_channels; i++) { if (fModelParams.fDecayKind[i] == PositroniumDecayKind::k2Gamma) { - std::cout << "creating pPs" << std::endl; fPositroniumDecayChannel.push_back(std::move(GatePositroniumDecayModel::Positronium("pPs", fModelParams.fLifetimes[i]* ns, 2))); - std::cout << "after creating pPs" << std::endl; } else { - std::cout << "creating oPs" << std::endl; fPositroniumDecayChannel.push_back(std::move(GatePositroniumDecayModel::Positronium("oPs", fModelParams.fLifetimes[i]* ns, 3))); - std::cout << "after creating oPs" << std::endl; } } } protected: virtual G4int GeneratePrimaryVertices(G4Event* event, G4double& particle_time, G4ThreeVector& particle_position) override; + G4PrimaryVertex* GetPrimaryVertexFromDeexcitation(G4double particle_time, const G4ThreeVector& particle_position, int decayIndex); G4PrimaryVertex *GetPrimaryVertexFromPositroniumAnnihilation(G4double particle_time, const G4ThreeVector &particle_position, int decayIndex); + G4PrimaryParticle* GetGammaFromDeexcitation(int decayIndex); std::vector GetGammasFromPositroniumAnnihilation(int decayIndex); private: diff --git a/source/physics/src/GateExtendedVSource.cc b/source/physics/src/GateExtendedVSource.cc index a609d00d0..bfb9af889 100644 --- a/source/physics/src/GateExtendedVSource.cc +++ b/source/physics/src/GateExtendedVSource.cc @@ -23,7 +23,8 @@ void GateExtendedVSource::SetModel(const G4String &model_name) {"sg", GateExtendedVSource::ModelKind::SingleGamma}, {"pPs", GateExtendedVSource::ModelKind::ParaPositronium}, {"oPs", GateExtendedVSource::ModelKind::OrthoPositronium}, - {"Ps", GateExtendedVSource::ModelKind::Positronium}}; + {"Ps", GateExtendedVSource::ModelKind::Positronium}, + {"mPs", GateExtendedVSource::ModelKind::MiniPositronium}}; auto it = models.find(model_name); if (it != models.end()) @@ -109,10 +110,26 @@ void GateExtendedVSource::PrepareModel() model->SetPromptGammaEnergy(fPromptGammaEnergy.value()); } pModel = std::move(model); - } else if (fModelKind == GateExtendedVSource::ModelKind::SingleGamma) { - pModel = std::make_unique(); } else { - GateError("GateExtendedVSource::PrepareModel - unknown model."); + if (fModelKind == GateExtendedVSource::ModelKind::SingleGamma) { + pModel = std::make_unique(); + } else { + if(fModelKind == GateExtendedVSource::ModelKind::MiniPositronium) { + std::cout << "initializing MiniPositronium model" << std::endl; + PositroniumDecayModelParams params; + //params.fFractions={0.4,0.3, 0.2, 0.1}; + //params.fLifetimes={0.1244 ,138.6, 2, 3}; + //params.fDecayKind={PositroniumDecayKind::k2Gamma, PositroniumDecayKind::k3Gamma, PositroniumDecayKind::k2Gamma, PositroniumDecayKind::k2Gamma}; + params.fFractions={1}; + params.fLifetimes={5 * ns}; + params.fPromptPhotonEnergy={1.274 * MeV}; + params.fIsPromptPhoton={true}; + params.fDecayKind={PositroniumDecayKind::k2Gamma}; + pModel = std::make_unique(params); + } else { + GateError("GateExtendedVSource::PrepareModel - unknown model."); + } + } } if (fFixedEmissionDirection.has_value()) { diff --git a/source/physics/src/GatePositroniumDecayModel.cc b/source/physics/src/GatePositroniumDecayModel.cc index bdf126a1d..c43bd73d3 100644 --- a/source/physics/src/GatePositroniumDecayModel.cc +++ b/source/physics/src/GatePositroniumDecayModel.cc @@ -206,18 +206,35 @@ G4int MiniPositroniumDecayModel::GeneratePrimaryVertices(G4Event* event, G4doubl { auto decayIndex = MiniPositroniumDecayModel::getPositroniumDecayIndex(fModelParams.fFractions); G4int number_of_vertices = 1; + if(fModelParams.fIsPromptPhoton[decayIndex]) + { + ++number_of_vertices ; + event->AddPrimaryVertex(GetPrimaryVertexFromDeexcitation(particle_time, particle_position, decayIndex)); + } event->AddPrimaryVertex(GetPrimaryVertexFromPositroniumAnnihilation(particle_time, particle_position, decayIndex)); return number_of_vertices; } +G4PrimaryParticle* MiniPositroniumDecayModel::GetGammaFromDeexcitation(int decayIndex) +{ + G4PrimaryParticle* gamma = GetSingleGamma(fModelParams.fPromptPhotonEnergy[decayIndex]); + gamma->SetUserInformation( GetPrimaryParticleInformation( gamma, GateEmittedGammaInformation::GammaKind::Prompt ) ); + return gamma; +} + +G4PrimaryVertex* MiniPositroniumDecayModel::GetPrimaryVertexFromDeexcitation(G4double particle_time, const G4ThreeVector& particle_position, int decayIndex) +{ + G4PrimaryVertex* vertex = new G4PrimaryVertex(particle_position, particle_time); + vertex->SetPrimary(GetGammaFromDeexcitation(decayIndex)); + return vertex; +} + std::vector MiniPositroniumDecayModel::GetGammasFromPositroniumAnnihilation(int decayIndex) { int annihilation_gammas_number = fPositroniumDecayChannel[decayIndex].GetAnnihilationGammasNumber(); - //std::vector gammas( pInfoPs->GetAnnihilationGammasNumber() ) ; std::vector gammas(annihilation_gammas_number); G4DecayProducts* decay_products = fPositroniumDecayChannel[decayIndex].GetDecayProducts(); - //G4DecayProducts* decay_products = nullptr; for ( G4int i = 0; i < annihilation_gammas_number; ++i ) { G4PrimaryParticle* gamma = new G4PrimaryParticle( pGammaDefinition ); diff --git a/tests/test_GatePositroniumDecayModel.cpp b/tests/test_GatePositroniumDecayModel.cpp index c32274395..1bb2b4a53 100644 --- a/tests/test_GatePositroniumDecayModel.cpp +++ b/tests/test_GatePositroniumDecayModel.cpp @@ -31,7 +31,6 @@ bool run_tests2() params.fDecayKind={PositroniumDecayKind::k2Gamma, PositroniumDecayKind::k3Gamma}; MiniPositroniumDecayModel model(params); - return true; } From c02abed79dad2584883131b7d2010aa690187313 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Tue, 14 Oct 2025 21:59:05 +0200 Subject: [PATCH 021/144] Remove obsolete comment --- source/physics/src/GateExtendedVSource.cc | 2 +- source/physics/src/GatePositroniumDecayModel.cc | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/source/physics/src/GateExtendedVSource.cc b/source/physics/src/GateExtendedVSource.cc index bfb9af889..fc04eb0d1 100644 --- a/source/physics/src/GateExtendedVSource.cc +++ b/source/physics/src/GateExtendedVSource.cc @@ -121,7 +121,7 @@ void GateExtendedVSource::PrepareModel() //params.fLifetimes={0.1244 ,138.6, 2, 3}; //params.fDecayKind={PositroniumDecayKind::k2Gamma, PositroniumDecayKind::k3Gamma, PositroniumDecayKind::k2Gamma, PositroniumDecayKind::k2Gamma}; params.fFractions={1}; - params.fLifetimes={5 * ns}; + params.fLifetimes={5}; params.fPromptPhotonEnergy={1.274 * MeV}; params.fIsPromptPhoton={true}; params.fDecayKind={PositroniumDecayKind::k2Gamma}; diff --git a/source/physics/src/GatePositroniumDecayModel.cc b/source/physics/src/GatePositroniumDecayModel.cc index c43bd73d3..5e4faeebe 100644 --- a/source/physics/src/GatePositroniumDecayModel.cc +++ b/source/physics/src/GatePositroniumDecayModel.cc @@ -201,7 +201,6 @@ G4PrimaryVertex* MiniPositroniumDecayModel::GetPrimaryVertexFromPositroniumAnnih return vertex; } -// For this moment without prompt G4int MiniPositroniumDecayModel::GeneratePrimaryVertices(G4Event* event, G4double& particle_time, G4ThreeVector& particle_position) { auto decayIndex = MiniPositroniumDecayModel::getPositroniumDecayIndex(fModelParams.fFractions); From 8d9119889d0c363db7d0e83a30162f300196a6e9 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Thu, 23 Oct 2025 00:33:34 +0200 Subject: [PATCH 022/144] Add separate include file for positronium model parameters --- .../GatePositroniumDecayModelParams.hh | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 source/physics/include/GatePositroniumDecayModelParams.hh diff --git a/source/physics/include/GatePositroniumDecayModelParams.hh b/source/physics/include/GatePositroniumDecayModelParams.hh new file mode 100644 index 000000000..7fe4ea1fa --- /dev/null +++ b/source/physics/include/GatePositroniumDecayModelParams.hh @@ -0,0 +1,23 @@ +/** ---------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + ----------------------*/ +#ifndef GatePositroniumDecayModelParams_hh +#define GatePositroniumDecayModelParams_hh + +#include + +enum PositroniumDecayKind { k2Gamma, k3Gamma}; + +struct PositroniumDecayModelParams +{ + std::vector fFractions; + std::vector fLifetimes; + std::vector fIsPromptPhoton; + std::vector fPromptPhotonEnergy; + std::vector fDecayKind; +}; + +#endif From 331906a4f5a5289b566ae08edacfe842f13556f0 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 24 Oct 2025 00:17:14 +0200 Subject: [PATCH 023/144] Add commands to set positronium values from macros Warning this is Work in Progress commit. Some part of code must be still improved --- source/physics/include/GateExtendedVSource.hh | 12 ++++ .../include/GateExtendedVSourceMessenger.hh | 10 ++- .../include/GatePositroniumDecayModel.hh | 10 +-- source/physics/src/GateExtendedVSource.cc | 56 +++++++++++++--- .../src/GateExtendedVSourceMessenger.cc | 67 +++++++++++++++++++ .../physics/src/GatePositroniumDecayModel.cc | 11 +-- 6 files changed, 143 insertions(+), 23 deletions(-) diff --git a/source/physics/include/GateExtendedVSource.hh b/source/physics/include/GateExtendedVSource.hh index 31664b758..f594d0fd6 100644 --- a/source/physics/include/GateExtendedVSource.hh +++ b/source/physics/include/GateExtendedVSource.hh @@ -13,6 +13,7 @@ #include "GateVSource.hh" #include "GateExtendedVSourceMessenger.hh" #include "GateGammaEmissionModel.hh" +#include "GatePositroniumDecayModelParams.hh" /** Author: Mateusz Bała * Email: bala.mateusz@gmail.com @@ -98,6 +99,17 @@ public: //Constants for Set(...) methods static inline const G4String kParaPositroniumName = "pPs"; static inline const G4String kOrthoPositroniumName = "oPs"; + + public: + + std::optional> fPositroniumFractions; + std::optional> fPositroniumLifetimes; + std::optional> fIsPromptPhoton; + std::optional> fPromptPhotonEnergies; + std::optional> fDecayKinds; + + + }; #endif diff --git a/source/physics/include/GateExtendedVSourceMessenger.hh b/source/physics/include/GateExtendedVSourceMessenger.hh index 0990ca46e..fb81fe84e 100644 --- a/source/physics/include/GateExtendedVSourceMessenger.hh +++ b/source/physics/include/GateExtendedVSourceMessenger.hh @@ -7,6 +7,8 @@ #ifndef GateExtendedVSourceMessenger_hh #define GateExtendedVSourceMessenger_hh +#include + #include "GateVSourceMessenger.hh" #include "G4UIcmdWithAnInteger.hh" #include "G4UIcmdWithADoubleAndUnit.hh" @@ -14,7 +16,6 @@ #include "G4UIcmdWith3Vector.hh" #include "G4UIcmdWithAString.hh" #include "G4UIcmdWith3VectorAndUnit.hh" -#include class GateExtendedVSource; @@ -55,6 +56,13 @@ class GateExtendedVSourceMessenger: public GateVSourceMessenger //Supporting commands - disable for user std::unique_ptr upCmdSetLifetime; + + //New commands + std::unique_ptr upCmdSetPositroniumFractions; + std::unique_ptr upCmdSetPositroniumLifetimes; + std::unique_ptr upCmdSetIsPromptPhoton; + std::unique_ptr upCmdSetPromptPhotonEnergies; + std::unique_ptr upCmdSetDecayKinds; }; diff --git a/source/physics/include/GatePositroniumDecayModel.hh b/source/physics/include/GatePositroniumDecayModel.hh index 5d2193f6a..442680d78 100644 --- a/source/physics/include/GatePositroniumDecayModel.hh +++ b/source/physics/include/GatePositroniumDecayModel.hh @@ -16,6 +16,7 @@ #include "GateEmittedGammaInformation.hh" #include "GateGammaEmissionModel.hh" +#include "GatePositroniumDecayModelParams.hh" /** Author: Mateusz Bała @@ -129,15 +130,6 @@ class GatePositroniumDecayModel : public GateGammaEmissionModel ///wk std::vector lifetimes ///wk std::vector /// -enum PositroniumDecayKind { k2Gamma, k3Gamma}; -struct PositroniumDecayModelParams -{ - std::vector fFractions; - std::vector fLifetimes; - std::vector fIsPromptPhoton; - std::vector fPromptPhotonEnergy; - std::vector fDecayKind; -}; class MiniPositroniumDecayModel:public GateGammaEmissionModel { diff --git a/source/physics/src/GateExtendedVSource.cc b/source/physics/src/GateExtendedVSource.cc index fc04eb0d1..48d6f7f55 100644 --- a/source/physics/src/GateExtendedVSource.cc +++ b/source/physics/src/GateExtendedVSource.cc @@ -115,17 +115,57 @@ void GateExtendedVSource::PrepareModel() pModel = std::make_unique(); } else { if(fModelKind == GateExtendedVSource::ModelKind::MiniPositronium) { + /// WK: todo extract a dedicated function that creates proper params std::cout << "initializing MiniPositronium model" << std::endl; PositroniumDecayModelParams params; - //params.fFractions={0.4,0.3, 0.2, 0.1}; - //params.fLifetimes={0.1244 ,138.6, 2, 3}; - //params.fDecayKind={PositroniumDecayKind::k2Gamma, PositroniumDecayKind::k3Gamma, PositroniumDecayKind::k2Gamma, PositroniumDecayKind::k2Gamma}; - params.fFractions={1}; - params.fLifetimes={5}; - params.fPromptPhotonEnergy={1.274 * MeV}; - params.fIsPromptPhoton={true}; - params.fDecayKind={PositroniumDecayKind::k2Gamma}; + if(fPositroniumFractions.has_value()) { + std::cout <<"we get some values from fParaPositroniumFractions" << std::endl; + params.fFractions=fPositroniumFractions.value(); + } else { + params.fFractions={0.4, 0.3, 0.2, 0.1}; + } + + if(fPositroniumLifetimes.has_value()) { + std::cout <<"we get some values from fPositroniumLifetimes" << std::endl; + params.fLifetimes=fPositroniumLifetimes.value(); + } else { + params.fLifetimes={0.1244 ,6, 2, 3}; + } + + if(fDecayKinds.has_value()) { + std::cout <<"we get some values from fDecayKinds" << std::endl; + params.fDecayKind=fDecayKinds.value(); + } else { + params.fDecayKind={PositroniumDecayKind::k2Gamma, PositroniumDecayKind::k3Gamma, PositroniumDecayKind::k2Gamma, PositroniumDecayKind::k2Gamma}; + } + + if(fIsPromptPhoton.has_value()) { + std::cout <<"we get some values from fIsPromptPhoton" << std::endl; + params.fIsPromptPhoton=fIsPromptPhoton.value(); + } else { + params.fIsPromptPhoton={true,true, true, true}; + } + + if(fPromptPhotonEnergies.has_value()) { + std::cout <<"we get some values from fPromptPhotonEnergies" << std::endl; + params.fPromptPhotonEnergy=fPromptPhotonEnergies.value(); + } else { + params.fPromptPhotonEnergy={1.274 * MeV,1.274 * MeV,1.274 * MeV, 1.274 * MeV}; + } + + + //params.fFractions={0.6, 0.4}; + //params.fLifetimes={5, 2}; + //params.fDecayKind={PositroniumDecayKind::k2Gamma, PositroniumDecayKind::k2Gamma}; + //params.fIsPromptPhoton={false,false}; + //params.fFractions={1}; + //params.fLifetimes={5}; + //params.fPromptPhotonEnergy={1.274 * MeV}; + ////params.fIsPromptPhoton={false,false, false, false}; + //params.fIsPromptPhoton={true}; + //params.fDecayKind={PositroniumDecayKind::k2Gamma}; pModel = std::make_unique(params); + std::cout << "initializing MiniPositronium model 2" << std::endl; } else { GateError("GateExtendedVSource::PrepareModel - unknown model."); } diff --git a/source/physics/src/GateExtendedVSourceMessenger.cc b/source/physics/src/GateExtendedVSourceMessenger.cc index 0e276529b..34d271924 100644 --- a/source/physics/src/GateExtendedVSourceMessenger.cc +++ b/source/physics/src/GateExtendedVSourceMessenger.cc @@ -84,6 +84,11 @@ void GateExtendedVSourceMessenger::InitCommands() upCmdSetLifetime.reset( GetDoubleCmdWithUnit( "setLifetime", "Set life-time of positronium - disable for user.", "ns", "ps ns" ) ); upCmdSetPromptGammaEnergy.reset( GetDoubleCmdWithUnit( "setPromptGammaEnergy", "Set energy for prompt gamma.", "keV", "keV MeV GeV" ) ); upCmdSetPositroniumFraction.reset( GetStringCmd( "setPositroniumFraction", "\"positronium_kind fraction\" - where positronium_kind = {pPs, oPs} and fraction in [0.0, 1.0]" ) ); + + + upCmdSetPositroniumFractions.reset(GetStringCmd( "setPositroniumFractions", "\"f1, f2, f3 .., fn\" - where fi in [0.0, 1.0] and sum of all fi ==1" ) ); + upCmdSetPositroniumLifetimes.reset(GetStringCmd( "setPositroniumLifetimes", "\"t1, t2, t3 .., tn\" - where" ) ); + upCmdSetDecayKinds.reset(GetStringCmd( "setDecayKinds", "\"k1, k2, k3 .., kn\" - where ki " ) ); } void GateExtendedVSourceMessenger::SetNewValue( G4UIcommand* command, G4String new_value ) @@ -135,6 +140,68 @@ void GateExtendedVSourceMessenger::SetNewValue( G4UIcommand* command, G4String n pSource->SetPositroniumFraction(positronium_kind, fraction); } + else if(command == upCmdSetPositroniumFractions.get()) + { + std::vector fractions; + std::stringstream ss(new_value); + G4double num; + while (ss >> num) { + fractions.push_back(num); + } + pSource->fPositroniumFractions=fractions; + } + else if(command == upCmdSetPositroniumFractions.get()) + { + std::vector fractions; + std::stringstream ss(new_value); + G4double num; + while (ss >> num) { + fractions.push_back(num); + } + pSource->fPositroniumFractions=fractions; + } + else if(command == upCmdSetPositroniumLifetimes.get()) + { + std::vector lifetimes; + std::stringstream ss(new_value); + G4double num; + while (ss >> num) { + lifetimes.push_back(num); + } + pSource->fPositroniumLifetimes=lifetimes; + } + else if(command == upCmdSetIsPromptPhoton.get()) + { + std::vector isPromptPhoton; + std::stringstream ss(new_value); + bool flag; + while (ss >> flag) { + isPromptPhoton.push_back(flag); + } + pSource->fIsPromptPhoton=isPromptPhoton; + } + else if(command == upCmdSetPromptPhotonEnergies.get()) { + std::vector promptPhotonEnergies; + std::stringstream ss(new_value); + float energy; + while (ss >> energy) { + promptPhotonEnergies.push_back(energy); + } + pSource->fPromptPhotonEnergies=promptPhotonEnergies; + } + else if(command == upCmdSetDecayKinds.get()) { + std::vector decayKinds; + std::stringstream ss(new_value); + std::string kind; + while (ss >> kind) { + if (kind == "k2Gamma") { + decayKinds.push_back(k2Gamma); + } else { + decayKinds.push_back(k3Gamma); + } + } + pSource->fDecayKinds=decayKinds; + } else { GateVSourceMessenger::SetNewValue(command, new_value); diff --git a/source/physics/src/GatePositroniumDecayModel.cc b/source/physics/src/GatePositroniumDecayModel.cc index 5e4faeebe..b99f3be62 100644 --- a/source/physics/src/GatePositroniumDecayModel.cc +++ b/source/physics/src/GatePositroniumDecayModel.cc @@ -204,12 +204,13 @@ G4PrimaryVertex* MiniPositroniumDecayModel::GetPrimaryVertexFromPositroniumAnnih G4int MiniPositroniumDecayModel::GeneratePrimaryVertices(G4Event* event, G4double& particle_time, G4ThreeVector& particle_position) { auto decayIndex = MiniPositroniumDecayModel::getPositroniumDecayIndex(fModelParams.fFractions); + G4int number_of_vertices = 1; - if(fModelParams.fIsPromptPhoton[decayIndex]) - { - ++number_of_vertices ; - event->AddPrimaryVertex(GetPrimaryVertexFromDeexcitation(particle_time, particle_position, decayIndex)); - } + if(fModelParams.fIsPromptPhoton[decayIndex]) + { + ++number_of_vertices; + event->AddPrimaryVertex(GetPrimaryVertexFromDeexcitation(particle_time, particle_position, decayIndex)); + } event->AddPrimaryVertex(GetPrimaryVertexFromPositroniumAnnihilation(particle_time, particle_position, decayIndex)); return number_of_vertices; } From 73286fa47cd237e12af30755b066979eb0c982db Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Sun, 26 Oct 2025 16:43:51 +0100 Subject: [PATCH 024/144] Add separated GatePositronium class --- source/physics/include/GatePositronium.hh | 28 +++++++++++++++++++ .../include/GatePositroniumDecayModel.hh | 6 ---- source/physics/src/GatePositronium.cc | 28 +++++++++++++++++++ 3 files changed, 56 insertions(+), 6 deletions(-) create mode 100644 source/physics/include/GatePositronium.hh create mode 100644 source/physics/src/GatePositronium.cc diff --git a/source/physics/include/GatePositronium.hh b/source/physics/include/GatePositronium.hh new file mode 100644 index 000000000..a3694f7ae --- /dev/null +++ b/source/physics/include/GatePositronium.hh @@ -0,0 +1,28 @@ +/** ---------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + ----------------------*/ +#ifndef GatePositronium_hh +#define GatePositronium_hh + +#include "G4VDecayChannel.hh" +#include "G4DecayProducts.hh" + +class GatePositronium { +public: + GatePositronium(const G4String& name, G4double life_time, + G4int annihilation_gammas_number); + G4double GetLifeTime() const; + G4String GetName() const; + G4int GetAnnihilationGammasNumber() const; + G4DecayProducts *GetDecayProducts() const; + +private: + G4String fName; + G4double fLifeTime = 0.0; //[ns] + G4int fAnnihilationGammasNumber = 0; + G4VDecayChannel *pDecayChannel = nullptr; +}; +#endif diff --git a/source/physics/include/GatePositroniumDecayModel.hh b/source/physics/include/GatePositroniumDecayModel.hh index 442680d78..c2a69af94 100644 --- a/source/physics/include/GatePositroniumDecayModel.hh +++ b/source/physics/include/GatePositroniumDecayModel.hh @@ -125,12 +125,6 @@ class GatePositroniumDecayModel : public GateGammaEmissionModel }; -///wk: We want to use std::vector fractions -///wk: std::vector 2G or 3G -///wk std::vector lifetimes -///wk std::vector -/// - class MiniPositroniumDecayModel:public GateGammaEmissionModel { public: diff --git a/source/physics/src/GatePositronium.cc b/source/physics/src/GatePositronium.cc new file mode 100644 index 000000000..f714f2eb1 --- /dev/null +++ b/source/physics/src/GatePositronium.cc @@ -0,0 +1,28 @@ +/** ---------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + ----------------------*/ + +#include "G4DecayProducts.hh" +#include "G4ParticleTable.hh" +#include "G4DecayTable.hh" +#include "G4ParticleDefinition.hh" + +#include "GatePositronium.hh" + +GatePositronium::GatePositronium(const G4String& name, G4double life_time, G4int annihilation_gammas_number ): fName(name), fLifeTime(life_time), fAnnihilationGammasNumber(annihilation_gammas_number) +{ + G4ParticleDefinition *positronium_def = G4ParticleTable::GetParticleTable()->FindParticle(name); + G4DecayTable *positronium_decay_table = positronium_def->GetDecayTable(); + pDecayChannel = positronium_decay_table->GetDecayChannel(0); +} + +G4double GatePositronium::GetLifeTime() const { return fLifeTime; } + +G4String GatePositronium::GetName() const { return fName; } + +G4int GatePositronium::GetAnnihilationGammasNumber() const { return fAnnihilationGammasNumber; } + +G4DecayProducts* GatePositronium::GetDecayProducts() const { return pDecayChannel->DecayIt(); } From e1561235a71975e18fe26adfb0243ef7cfd7bfb6 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Sun, 26 Oct 2025 17:00:40 +0100 Subject: [PATCH 025/144] Add non-copy and move logic to GatePositronium It contains a raw pointer to fDecayChannel which it does not own. --- source/physics/include/GatePositronium.hh | 12 ++++++++++-- source/physics/src/GatePositronium.cc | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/source/physics/include/GatePositronium.hh b/source/physics/include/GatePositronium.hh index a3694f7ae..0ade5d3f4 100644 --- a/source/physics/include/GatePositronium.hh +++ b/source/physics/include/GatePositronium.hh @@ -14,8 +14,16 @@ class GatePositronium { public: GatePositronium(const G4String& name, G4double life_time, G4int annihilation_gammas_number); + ~GatePositronium() = default; + + GatePositronium(const GatePositronium&) = delete; + GatePositronium& operator=(const GatePositronium&) = delete; + GatePositronium(GatePositronium&&) noexcept = default; + GatePositronium& operator=(GatePositronium&&) noexcept = default; + + G4double GetLifeTime() const; - G4String GetName() const; + const G4String& GetName() const; G4int GetAnnihilationGammasNumber() const; G4DecayProducts *GetDecayProducts() const; @@ -23,6 +31,6 @@ private: G4String fName; G4double fLifeTime = 0.0; //[ns] G4int fAnnihilationGammasNumber = 0; - G4VDecayChannel *pDecayChannel = nullptr; + G4VDecayChannel *pDecayChannel = nullptr; // Todo check who owns pDecayChannel? }; #endif diff --git a/source/physics/src/GatePositronium.cc b/source/physics/src/GatePositronium.cc index f714f2eb1..f74927c16 100644 --- a/source/physics/src/GatePositronium.cc +++ b/source/physics/src/GatePositronium.cc @@ -21,7 +21,7 @@ GatePositronium::GatePositronium(const G4String& name, G4double life_time, G4int G4double GatePositronium::GetLifeTime() const { return fLifeTime; } -G4String GatePositronium::GetName() const { return fName; } +const G4String& GatePositronium::GetName() const { return fName; } G4int GatePositronium::GetAnnihilationGammasNumber() const { return fAnnihilationGammasNumber; } From 61d4b38a618dced0a14a512f62b24337451ad8a7 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Sun, 26 Oct 2025 17:23:53 +0100 Subject: [PATCH 026/144] Add tests of GatePositronium --- tests/test_GatePositronium.cpp | 87 ++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 tests/test_GatePositronium.cpp diff --git a/tests/test_GatePositronium.cpp b/tests/test_GatePositronium.cpp new file mode 100644 index 000000000..c210df9d2 --- /dev/null +++ b/tests/test_GatePositronium.cpp @@ -0,0 +1,87 @@ +#include +#include + +#include + +#include + +#include "GateRunManager.hh" +#include "GatePhysicsList.hh" +#include "GateDetectorConstruction.hh" + +void initializeGateRunManager(GateRunManager* runManager) +{ + // Set the DetectorConstruction + GateDetectorConstruction* gateDC = new GateDetectorConstruction(); + runManager->SetUserInitialization( gateDC ); + // Set the PhysicsList + runManager->SetUserInitialization( GatePhysicsList::GetInstance() ); + // Initialize G4 kernel + runManager->InitializeAll(); +} + +//bool run_tests2() +//{ + //std::unique_ptr runManager(new GateRunManager); + //initializeGateRunManager(runManager.get()); + + //PositroniumDecayModelParams params; + //params.fFractions={0.3,0.7}; + //params.fLifetimes={0.1244 ,138.6}; + //params.fDecayKind={PositroniumDecayKind::k2Gamma, PositroniumDecayKind::k3Gamma}; + //MiniPositroniumDecayModel model(params); + + //return true; +//} + +bool run_tests() +{ + bool res = true; + GatePositronium pPs("pPs", 0.1 , 2); + if (pPs.GetLifeTime() != 0.1) { + return false; + } + if (pPs.GetName() != "pPs") { + return false; + } + if (pPs.GetAnnihilationGammasNumber() != 2) { + return false; + } + return res; +} + +bool run_tests2() +{ + bool res = true; + GatePositronium oPs("oPs", 1000, 3); + if (oPs.GetLifeTime() != 1000) { + return false; + } + if (oPs.GetName() != "oPs") { + return false; + } + if (oPs.GetAnnihilationGammasNumber() != 3) { + return false; + } + return res; +} + + +int main() +{ + // Initialization + std::unique_ptr runManager(new GateRunManager); + initializeGateRunManager(runManager.get()); + + bool res = true; + res = res & run_tests(); + res = res & run_tests2(); + + if (res) { + std::cout << "All tests have passed" << std::endl; + return 0; + } else { + std::cerr << "Some tests failed" << std::endl; + return -1; + } +} From b6ecfa6c089e6a3e36cbc78008e8b83e86e44e56 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Sun, 26 Oct 2025 17:27:40 +0100 Subject: [PATCH 027/144] Remove commented code --- tests/test_GatePositronium.cpp | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/tests/test_GatePositronium.cpp b/tests/test_GatePositronium.cpp index c210df9d2..b1f4ff4ce 100644 --- a/tests/test_GatePositronium.cpp +++ b/tests/test_GatePositronium.cpp @@ -20,20 +20,6 @@ void initializeGateRunManager(GateRunManager* runManager) runManager->InitializeAll(); } -//bool run_tests2() -//{ - //std::unique_ptr runManager(new GateRunManager); - //initializeGateRunManager(runManager.get()); - - //PositroniumDecayModelParams params; - //params.fFractions={0.3,0.7}; - //params.fLifetimes={0.1244 ,138.6}; - //params.fDecayKind={PositroniumDecayKind::k2Gamma, PositroniumDecayKind::k3Gamma}; - //MiniPositroniumDecayModel model(params); - - //return true; -//} - bool run_tests() { bool res = true; From 9cf74c0f41785d8c21f5a90574a3407bf9eba7d3 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Sun, 26 Oct 2025 17:28:14 +0100 Subject: [PATCH 028/144] Add tests to ctest in CMakeLists.txt --- CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 604522754..f5fa8281c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,11 @@ IF(BUILD_TESTING) ADD_TEST(NAME test_GatePositroniumDecayModel COMMAND test_GatePositroniumDecayModel) + ADD_EXECUTABLE(test_GatePositronium tests/test_GatePositronium.cpp) + TARGET_LINK_LIBRARIES(test_GatePositronium GateLib) + + ADD_TEST(NAME test_GatePositronium COMMAND test_GatePositronium) + ENDIF(BUILD_TESTING) #========================================================= From 79c5a57a72af32461a804f9a9a9498cf9a78f768 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Sun, 26 Oct 2025 17:29:17 +0100 Subject: [PATCH 029/144] Extract separated GateMiniPositroniumDecayModel It is meant to replace the GatePositroniumDecayModel later. It uses cleaned up GatePositronium class. --- .../include/GateMiniPositroniumDecayModel.hh | 41 ++++++++ .../src/GateMiniPositroniumDecayModel.cc | 99 +++++++++++++++++++ tests/test_GatePositroniumDecayModel.cpp | 2 +- 3 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 source/physics/include/GateMiniPositroniumDecayModel.hh create mode 100644 source/physics/src/GateMiniPositroniumDecayModel.cc diff --git a/source/physics/include/GateMiniPositroniumDecayModel.hh b/source/physics/include/GateMiniPositroniumDecayModel.hh new file mode 100644 index 000000000..a515aaf14 --- /dev/null +++ b/source/physics/include/GateMiniPositroniumDecayModel.hh @@ -0,0 +1,41 @@ +/** ---------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + ----------------------*/ +#ifndef GateMiniPositroniumDecayModel_hh +#define GateMiniPositroniumDecayModel_hh + +#include + +#include "G4PrimaryParticle.hh" +#include "G4PrimaryVertex.hh" + +#include "GateGammaEmissionModel.hh" +#include "GatePositroniumDecayModelParams.hh" +#include "GatePositronium.hh" + +// Todo change the name to GatePositroniumDecayModel +// Todo2: add docs +class MiniPositroniumDecayModel:public GateGammaEmissionModel +{ + public: + static int getPositroniumDecayIndex(const std::vector& fractions); + + public: + explicit MiniPositroniumDecayModel(const PositroniumDecayModelParams& modelParams); + + protected: + virtual G4int GeneratePrimaryVertices(G4Event* event, G4double& particle_time, G4ThreeVector& particle_position) override; + G4PrimaryVertex* GetPrimaryVertexFromDeexcitation(G4double particle_time, const G4ThreeVector& particle_position, int decayIndex); + G4PrimaryVertex *GetPrimaryVertexFromPositroniumAnnihilation(G4double particle_time, const G4ThreeVector &particle_position, int decayIndex); + G4PrimaryParticle* GetGammaFromDeexcitation(int decayIndex); + std::vector GetGammasFromPositroniumAnnihilation(int decayIndex); + +private: + PositroniumDecayModelParams fModelParams; + std::vector fPositroniumDecayChannel; +}; + +#endif diff --git a/source/physics/src/GateMiniPositroniumDecayModel.cc b/source/physics/src/GateMiniPositroniumDecayModel.cc new file mode 100644 index 000000000..b1966c0cb --- /dev/null +++ b/source/physics/src/GateMiniPositroniumDecayModel.cc @@ -0,0 +1,99 @@ +/** ---------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + ----------------------*/ +#include +#include + +#include "Randomize.hh" +#include "G4DecayProducts.hh" +#include "G4LorentzVector.hh" + +#include "GateMiniPositroniumDecayModel.hh" + +int MiniPositroniumDecayModel::getPositroniumDecayIndex(const std::vector& fractions) { + auto r = G4UniformRand(); + float curr_frac_cumulative = 0.0; + for (int i = 0; i < fractions.size(); ++i) { + curr_frac_cumulative = curr_frac_cumulative + fractions[i]; + if(r<= curr_frac_cumulative) return i; + } + return -1; +} + +MiniPositroniumDecayModel::MiniPositroniumDecayModel(const PositroniumDecayModelParams& modelParams):fModelParams(modelParams) +{ + auto num_of_decay_channels = fModelParams.fDecayKind.size(); + for (int i = 0; i < num_of_decay_channels; i++) { + if (fModelParams.fDecayKind[i] == PositroniumDecayKind::k2Gamma) + { + fPositroniumDecayChannel.push_back(std::move(GatePositronium("pPs", fModelParams.fLifetimes[i]* ns, 2))); + } else { + fPositroniumDecayChannel.push_back(std::move(GatePositronium("oPs", fModelParams.fLifetimes[i]* ns, 3))); + } + } +} + +G4PrimaryVertex* MiniPositroniumDecayModel::GetPrimaryVertexFromPositroniumAnnihilation(G4double particle_time, const G4ThreeVector& particle_position, int decayIndex) +{ + + G4double shifted_particle_time = particle_time + G4RandExponential::shoot(fModelParams.fLifetimes[decayIndex]); + + G4PrimaryVertex* vertex = new G4PrimaryVertex( particle_position, shifted_particle_time ); + std::vector gammas = GetGammasFromPositroniumAnnihilation(decayIndex); + std::for_each( gammas.begin(), gammas.end(), [&]( G4PrimaryParticle* gamma ) { vertex->SetPrimary( gamma ); } ); + return vertex; +} + +G4int MiniPositroniumDecayModel::GeneratePrimaryVertices(G4Event* event, G4double& particle_time, G4ThreeVector& particle_position) +{ + auto decayIndex = MiniPositroniumDecayModel::getPositroniumDecayIndex(fModelParams.fFractions); + + G4int number_of_vertices = 1; + if(fModelParams.fIsPromptPhoton[decayIndex]) + { + ++number_of_vertices; + event->AddPrimaryVertex(GetPrimaryVertexFromDeexcitation(particle_time, particle_position, decayIndex)); + } + event->AddPrimaryVertex(GetPrimaryVertexFromPositroniumAnnihilation(particle_time, particle_position, decayIndex)); + return number_of_vertices; +} + +G4PrimaryParticle* MiniPositroniumDecayModel::GetGammaFromDeexcitation(int decayIndex) +{ + G4PrimaryParticle* gamma = GetSingleGamma(fModelParams.fPromptPhotonEnergy[decayIndex]); + gamma->SetUserInformation( GetPrimaryParticleInformation( gamma, GateEmittedGammaInformation::GammaKind::Prompt ) ); + return gamma; +} + +G4PrimaryVertex* MiniPositroniumDecayModel::GetPrimaryVertexFromDeexcitation(G4double particle_time, const G4ThreeVector& particle_position, int decayIndex) +{ + G4PrimaryVertex* vertex = new G4PrimaryVertex(particle_position, particle_time); + vertex->SetPrimary(GetGammaFromDeexcitation(decayIndex)); + return vertex; +} + +std::vector MiniPositroniumDecayModel::GetGammasFromPositroniumAnnihilation(int decayIndex) +{ + int annihilation_gammas_number = fPositroniumDecayChannel[decayIndex].GetAnnihilationGammasNumber(); + std::vector gammas(annihilation_gammas_number); + + G4DecayProducts* decay_products = fPositroniumDecayChannel[decayIndex].GetDecayProducts(); + for ( G4int i = 0; i < annihilation_gammas_number; ++i ) + { + G4PrimaryParticle* gamma = new G4PrimaryParticle( pGammaDefinition ); + + G4DynamicParticle* dynamic_gamma = (*decay_products)[i]; + G4LorentzVector lv = dynamic_gamma->Get4Momentum(); + gamma->Set4Momentum( lv.px(), lv.py(), lv.pz(), lv.e() ); + gamma->SetPolarization( dynamic_gamma->GetPolarization() ); + gamma->SetUserInformation( GetPrimaryParticleInformation( gamma, GateEmittedGammaInformation::GammaKind::Annihilation ) ); + gammas[i] = gamma; + } + delete decay_products; + + return gammas; +} + diff --git a/tests/test_GatePositroniumDecayModel.cpp b/tests/test_GatePositroniumDecayModel.cpp index 1bb2b4a53..acb409448 100644 --- a/tests/test_GatePositroniumDecayModel.cpp +++ b/tests/test_GatePositroniumDecayModel.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include #include "GateRunManager.hh" #include "GatePhysicsList.hh" From bce7ba0746b17aa764952a677b2d746b20ae2ac6 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Sun, 26 Oct 2025 17:37:33 +0100 Subject: [PATCH 030/144] Extend GatePositronium test --- tests/test_GatePositronium.cpp | 57 +++++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/tests/test_GatePositronium.cpp b/tests/test_GatePositronium.cpp index b1f4ff4ce..712943b29 100644 --- a/tests/test_GatePositronium.cpp +++ b/tests/test_GatePositronium.cpp @@ -22,7 +22,6 @@ void initializeGateRunManager(GateRunManager* runManager) bool run_tests() { - bool res = true; GatePositronium pPs("pPs", 0.1 , 2); if (pPs.GetLifeTime() != 0.1) { return false; @@ -33,12 +32,11 @@ bool run_tests() if (pPs.GetAnnihilationGammasNumber() != 2) { return false; } - return res; + return true; } bool run_tests2() { - bool res = true; GatePositronium oPs("oPs", 1000, 3); if (oPs.GetLifeTime() != 1000) { return false; @@ -49,7 +47,57 @@ bool run_tests2() if (oPs.GetAnnihilationGammasNumber() != 3) { return false; } - return res; + return true; +} + +/// todo: add test to check what happens if pPs with 3 ? +bool run_tests3() +{ + std::vector vect; + vect.push_back(std::move(GatePositronium("oPs", 1000, 3))); + vect.push_back(std::move(GatePositronium("pPs", 0.1, 2))); + vect.push_back(std::move(GatePositronium("oPs", 2000, 2))); + vect.push_back(std::move(GatePositronium("oPs", 5000, 2))); + if(vect[0].GetName()!="oPs") { + return false; + } + if(vect[1].GetName()!="pPs") { + return false; + } + if(vect[2].GetName()!="oPs") { + return false; + } + if(vect[3].GetName()!="oPs") { + return false; + } + + if(vect[0].GetLifeTime()!= 1000) { + return false; + } + if(vect[1].GetLifeTime()!= 0.1) { + return false; + } + if(vect[2].GetLifeTime()!=2000) { + return false; + } + if(vect[3].GetLifeTime()!=5000) { + return false; + } + + if(vect[0].GetAnnihilationGammasNumber()!= 3) { + return false; + } + if(vect[1].GetAnnihilationGammasNumber()!= 2) { + return false; + } + if(vect[2].GetAnnihilationGammasNumber()!= 2) { + return false; + } + if(vect[3].GetAnnihilationGammasNumber()!= 2) { + return false; + } + + return true; } @@ -62,6 +110,7 @@ int main() bool res = true; res = res & run_tests(); res = res & run_tests2(); + res = res & run_tests3(); if (res) { std::cout << "All tests have passed" << std::endl; From 24b4b90fc15421b57e537477b276bae65be2ce45 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Mon, 27 Oct 2025 14:38:16 +0100 Subject: [PATCH 031/144] Add explicit cast to GAtePositroniumDecayChannel in GatePositrionum It allows to check number of photons from the decay by calling the function from GatePositroniumDecayChannel. Also, remove the field that corresponded to this number of photons in the GatePositrionium class. Also, remove this argument from the Positronium constructor, and fix the calls. This version should be more consistent. --- source/physics/include/GatePositronium.hh | 8 ++++---- .../include/GatePositroniumDecayChannel.hh | 2 +- .../physics/src/GateMiniPositroniumDecayModel.cc | 4 ++-- source/physics/src/GatePositronium.cc | 6 +++--- tests/test_GatePositronium.cpp | 16 ++++++++-------- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/source/physics/include/GatePositronium.hh b/source/physics/include/GatePositronium.hh index 0ade5d3f4..375d04d5b 100644 --- a/source/physics/include/GatePositronium.hh +++ b/source/physics/include/GatePositronium.hh @@ -7,13 +7,13 @@ #ifndef GatePositronium_hh #define GatePositronium_hh -#include "G4VDecayChannel.hh" +//#include "G4VDecayChannel.hh" #include "G4DecayProducts.hh" +#include class GatePositronium { public: - GatePositronium(const G4String& name, G4double life_time, - G4int annihilation_gammas_number); + GatePositronium(const G4String& name, G4double life_time); ~GatePositronium() = default; GatePositronium(const GatePositronium&) = delete; @@ -31,6 +31,6 @@ private: G4String fName; G4double fLifeTime = 0.0; //[ns] G4int fAnnihilationGammasNumber = 0; - G4VDecayChannel *pDecayChannel = nullptr; // Todo check who owns pDecayChannel? + GatePositroniumDecayChannel *pDecayChannel = nullptr; // Todo check who owns pDecayChannel? }; #endif diff --git a/source/physics/include/GatePositroniumDecayChannel.hh b/source/physics/include/GatePositroniumDecayChannel.hh index 4222a9ec5..6b1b8d0f9 100644 --- a/source/physics/include/GatePositroniumDecayChannel.hh +++ b/source/physics/include/GatePositroniumDecayChannel.hh @@ -27,7 +27,7 @@ class GatePositroniumDecayChannel : public G4GeneralPhaseSpaceDecay virtual ~GatePositroniumDecayChannel() = default; /** Return gammas from positronium decay **/ - virtual G4DecayProducts* DecayIt(G4double) override; + virtual G4DecayProducts* DecayIt(G4double m=0.0) override; protected: /** Return gammas from para-positronium decay diff --git a/source/physics/src/GateMiniPositroniumDecayModel.cc b/source/physics/src/GateMiniPositroniumDecayModel.cc index b1966c0cb..77dddc2d1 100644 --- a/source/physics/src/GateMiniPositroniumDecayModel.cc +++ b/source/physics/src/GateMiniPositroniumDecayModel.cc @@ -29,9 +29,9 @@ MiniPositroniumDecayModel::MiniPositroniumDecayModel(const PositroniumDecayModel for (int i = 0; i < num_of_decay_channels; i++) { if (fModelParams.fDecayKind[i] == PositroniumDecayKind::k2Gamma) { - fPositroniumDecayChannel.push_back(std::move(GatePositronium("pPs", fModelParams.fLifetimes[i]* ns, 2))); + fPositroniumDecayChannel.push_back(std::move(GatePositronium("pPs", fModelParams.fLifetimes[i]* ns))); } else { - fPositroniumDecayChannel.push_back(std::move(GatePositronium("oPs", fModelParams.fLifetimes[i]* ns, 3))); + fPositroniumDecayChannel.push_back(std::move(GatePositronium("oPs", fModelParams.fLifetimes[i]* ns))); } } } diff --git a/source/physics/src/GatePositronium.cc b/source/physics/src/GatePositronium.cc index f74927c16..1f977cd16 100644 --- a/source/physics/src/GatePositronium.cc +++ b/source/physics/src/GatePositronium.cc @@ -12,17 +12,17 @@ #include "GatePositronium.hh" -GatePositronium::GatePositronium(const G4String& name, G4double life_time, G4int annihilation_gammas_number ): fName(name), fLifeTime(life_time), fAnnihilationGammasNumber(annihilation_gammas_number) +GatePositronium::GatePositronium(const G4String& name, G4double life_time): fName(name), fLifeTime(life_time) { G4ParticleDefinition *positronium_def = G4ParticleTable::GetParticleTable()->FindParticle(name); G4DecayTable *positronium_decay_table = positronium_def->GetDecayTable(); - pDecayChannel = positronium_decay_table->GetDecayChannel(0); + pDecayChannel = static_cast(positronium_decay_table->GetDecayChannel(0)); } G4double GatePositronium::GetLifeTime() const { return fLifeTime; } const G4String& GatePositronium::GetName() const { return fName; } -G4int GatePositronium::GetAnnihilationGammasNumber() const { return fAnnihilationGammasNumber; } +G4int GatePositronium::GetAnnihilationGammasNumber() const { return pDecayChannel->GetNumberOfDaughters(); } G4DecayProducts* GatePositronium::GetDecayProducts() const { return pDecayChannel->DecayIt(); } diff --git a/tests/test_GatePositronium.cpp b/tests/test_GatePositronium.cpp index 712943b29..bdc802ec9 100644 --- a/tests/test_GatePositronium.cpp +++ b/tests/test_GatePositronium.cpp @@ -22,7 +22,7 @@ void initializeGateRunManager(GateRunManager* runManager) bool run_tests() { - GatePositronium pPs("pPs", 0.1 , 2); + GatePositronium pPs("pPs", 0.1); if (pPs.GetLifeTime() != 0.1) { return false; } @@ -37,7 +37,7 @@ bool run_tests() bool run_tests2() { - GatePositronium oPs("oPs", 1000, 3); + GatePositronium oPs("oPs", 1000); if (oPs.GetLifeTime() != 1000) { return false; } @@ -54,10 +54,10 @@ bool run_tests2() bool run_tests3() { std::vector vect; - vect.push_back(std::move(GatePositronium("oPs", 1000, 3))); - vect.push_back(std::move(GatePositronium("pPs", 0.1, 2))); - vect.push_back(std::move(GatePositronium("oPs", 2000, 2))); - vect.push_back(std::move(GatePositronium("oPs", 5000, 2))); + vect.push_back(std::move(GatePositronium("oPs", 1000))); + vect.push_back(std::move(GatePositronium("pPs", 0.1))); + vect.push_back(std::move(GatePositronium("oPs", 2000))); + vect.push_back(std::move(GatePositronium("oPs", 5000))); if(vect[0].GetName()!="oPs") { return false; } @@ -90,10 +90,10 @@ bool run_tests3() if(vect[1].GetAnnihilationGammasNumber()!= 2) { return false; } - if(vect[2].GetAnnihilationGammasNumber()!= 2) { + if(vect[2].GetAnnihilationGammasNumber()!= 3) { return false; } - if(vect[3].GetAnnihilationGammasNumber()!= 2) { + if(vect[3].GetAnnihilationGammasNumber()!= 3) { return false; } From b83c7e163816457509279268f77c3a27370a7554 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Mon, 27 Oct 2025 14:56:08 +0100 Subject: [PATCH 032/144] Small code fixes and formatting e.g. remove const from G4Double arguments --- .../include/GatePositroniumDecayChannel.hh | 4 ++-- .../src/GatePositroniumDecayChannel.cc | 22 ++++++++++--------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/source/physics/include/GatePositroniumDecayChannel.hh b/source/physics/include/GatePositroniumDecayChannel.hh index 6b1b8d0f9..3af067371 100644 --- a/source/physics/include/GatePositroniumDecayChannel.hh +++ b/source/physics/include/GatePositroniumDecayChannel.hh @@ -41,10 +41,10 @@ class GatePositroniumDecayChannel : public G4GeneralPhaseSpaceDecay * Chapter: 89. Annihilation of positronium * Equation: 89.14 **/ - G4double GetOrthoPsM( const G4double w1, const G4double w2, const G4double w3 ) const; + G4double GetOrthoPsM(G4double w1, G4double w2, G4double w3) const; /** Calculate polarization orthogonal to momentum direction **/ - G4ThreeVector GetPolarization( const G4ThreeVector& momentum ) const; + G4ThreeVector GetPolarization(const G4ThreeVector& momentum) const; /** Generate perpendiculator vector ( to calculate orthogonal polarization ) **/ G4ThreeVector GetPerpendicularVector(const G4ThreeVector& v) const; diff --git a/source/physics/src/GatePositroniumDecayChannel.cc b/source/physics/src/GatePositroniumDecayChannel.cc index a220ba4a4..29eb8f0af 100644 --- a/source/physics/src/GatePositroniumDecayChannel.cc +++ b/source/physics/src/GatePositroniumDecayChannel.cc @@ -115,21 +115,23 @@ G4DecayProducts* GatePositroniumDecayChannel::DecayOrthoPositronium() return decay_products; } -G4double GatePositroniumDecayChannel::GetOrthoPsM( const G4double w1, const G4double w2, const G4double w3 ) const +G4double GatePositroniumDecayChannel::GetOrthoPsM(G4double w1, G4double w2, G4double w3) const { - return pow( ( kElectronMass - w1 ) / ( w2 * w3 ), 2 ) + pow( ( kElectronMass - w2 ) / ( w1 * w3 ), 2 ) + pow( ( kElectronMass - w3 ) / ( w1 * w2 ), 2 ); + return pow((kElectronMass - w1) / (w2 * w3), 2) + + pow((kElectronMass - w2) / (w1 * w3), 2) + + pow((kElectronMass - w3) / (w1 * w2), 2); } -G4ThreeVector GatePositroniumDecayChannel::GetPolarization( const G4ThreeVector& momentum ) const +G4ThreeVector GatePositroniumDecayChannel::GetPolarization(const G4ThreeVector& momentum) const { - auto d0 = momentum.unit(); - auto a0 = GetPerpendicularVector( d0 ).unit(); - auto b0 = d0.cross( a0 ).unit(); - G4double angle_radians = G4UniformRand() * M_PI; - auto polarization = std::cos( angle_radians ) * a0 + std::sin( angle_radians ) * b0; - return polarization.unit(); + auto d0 = momentum.unit(); + auto a0 = GetPerpendicularVector(d0).unit(); + auto b0 = d0.cross(a0).unit(); + G4double angle_radians = G4UniformRand() * M_PI; + auto polarization = std::cos(angle_radians) * a0 + std::sin(angle_radians) * b0; + return polarization.unit(); } - + G4ThreeVector GatePositroniumDecayChannel::GetPerpendicularVector(const G4ThreeVector& v) const { G4double dx = v.x(); From 8cbb27e9f316c96e5aab6032ad76953280990019 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Mon, 27 Oct 2025 15:06:23 +0100 Subject: [PATCH 033/144] Add a function generatePositroniumDecayParams() It extracts the logic to read the input commands from macros and create set of params that is used next to define the decay positronium model. --- source/physics/include/GateExtendedVSource.hh | 3 +- source/physics/src/GateExtendedVSource.cc | 88 ++++++++----------- 2 files changed, 40 insertions(+), 51 deletions(-) diff --git a/source/physics/include/GateExtendedVSource.hh b/source/physics/include/GateExtendedVSource.hh index f594d0fd6..114794aec 100644 --- a/source/physics/include/GateExtendedVSource.hh +++ b/source/physics/include/GateExtendedVSource.hh @@ -100,6 +100,8 @@ public: static inline const G4String kParaPositroniumName = "pPs"; static inline const G4String kOrthoPositroniumName = "oPs"; + PositroniumDecayModelParams generatePositroniumDecayParams() const; + public: std::optional> fPositroniumFractions; @@ -107,7 +109,6 @@ public: std::optional> fIsPromptPhoton; std::optional> fPromptPhotonEnergies; std::optional> fDecayKinds; - }; diff --git a/source/physics/src/GateExtendedVSource.cc b/source/physics/src/GateExtendedVSource.cc index 48d6f7f55..71a664a08 100644 --- a/source/physics/src/GateExtendedVSource.cc +++ b/source/physics/src/GateExtendedVSource.cc @@ -10,6 +10,7 @@ #include "GateExtendedVSource.hh" #include "GatePositroniumDecayModel.hh" +#include "GateMiniPositroniumDecayModel.hh" GateExtendedVSource::GateExtendedVSource(const G4String &name) @@ -33,7 +34,7 @@ void GateExtendedVSource::SetModel(const G4String &model_name) } else { fBehaveLikeVSource = true; G4cout << "GateExtendedVSource::SetModel : Unknown gamma source model. " - "Enable: sg, pPs, oPs, Ps. Switching to GateVSource behavour." + "Enable: sg, pPs, oPs, Ps, mPs. Switching to GateVSource behavour." << G4endl; } } @@ -73,6 +74,41 @@ void GateExtendedVSource::SetPositroniumFraction( const G4String& positronium_ki fParaPositroniumFraction = pPs_fraction; } +PositroniumDecayModelParams GateExtendedVSource::generatePositroniumDecayParams() const +{ + PositroniumDecayModelParams params; + if(fPositroniumFractions.has_value()) { + params.fFractions=fPositroniumFractions.value(); + } else { + GateError("GateExtendedVSource::generatePositroniumDecayParams: Positronium decay fractions are not set"); + } + + if(fPositroniumLifetimes.has_value()) { + params.fLifetimes=fPositroniumLifetimes.value(); + } else { + GateError("GateExtendedVSource::generatePositroniumDecayParams: Positronium lifetimes are not set"); + } + + if(fDecayKinds.has_value()) { + params.fDecayKind=fDecayKinds.value(); + } else { + GateError("GateExtendedVSource::generatePositroniumDecayParams: Positronium decay kinds are not set"); + } + + if(fIsPromptPhoton.has_value()) { + params.fIsPromptPhoton=fIsPromptPhoton.value(); + } else { + GateError("GateExtendedVSource::generatePositroniumDecayParams: Positronium is prompt photon flags are not set"); + } + + if(fPromptPhotonEnergies.has_value()) { + params.fPromptPhotonEnergy=fPromptPhotonEnergies.value(); + } else { + GateError("GateExtendedVSource::generatePositroniumDecayParams: Prompt photon energies are not set"); + } + return params; +} + void GateExtendedVSource::PrepareModel() { SetModel(GetType()); @@ -115,55 +151,8 @@ void GateExtendedVSource::PrepareModel() pModel = std::make_unique(); } else { if(fModelKind == GateExtendedVSource::ModelKind::MiniPositronium) { - /// WK: todo extract a dedicated function that creates proper params std::cout << "initializing MiniPositronium model" << std::endl; - PositroniumDecayModelParams params; - if(fPositroniumFractions.has_value()) { - std::cout <<"we get some values from fParaPositroniumFractions" << std::endl; - params.fFractions=fPositroniumFractions.value(); - } else { - params.fFractions={0.4, 0.3, 0.2, 0.1}; - } - - if(fPositroniumLifetimes.has_value()) { - std::cout <<"we get some values from fPositroniumLifetimes" << std::endl; - params.fLifetimes=fPositroniumLifetimes.value(); - } else { - params.fLifetimes={0.1244 ,6, 2, 3}; - } - - if(fDecayKinds.has_value()) { - std::cout <<"we get some values from fDecayKinds" << std::endl; - params.fDecayKind=fDecayKinds.value(); - } else { - params.fDecayKind={PositroniumDecayKind::k2Gamma, PositroniumDecayKind::k3Gamma, PositroniumDecayKind::k2Gamma, PositroniumDecayKind::k2Gamma}; - } - - if(fIsPromptPhoton.has_value()) { - std::cout <<"we get some values from fIsPromptPhoton" << std::endl; - params.fIsPromptPhoton=fIsPromptPhoton.value(); - } else { - params.fIsPromptPhoton={true,true, true, true}; - } - - if(fPromptPhotonEnergies.has_value()) { - std::cout <<"we get some values from fPromptPhotonEnergies" << std::endl; - params.fPromptPhotonEnergy=fPromptPhotonEnergies.value(); - } else { - params.fPromptPhotonEnergy={1.274 * MeV,1.274 * MeV,1.274 * MeV, 1.274 * MeV}; - } - - - //params.fFractions={0.6, 0.4}; - //params.fLifetimes={5, 2}; - //params.fDecayKind={PositroniumDecayKind::k2Gamma, PositroniumDecayKind::k2Gamma}; - //params.fIsPromptPhoton={false,false}; - //params.fFractions={1}; - //params.fLifetimes={5}; - //params.fPromptPhotonEnergy={1.274 * MeV}; - ////params.fIsPromptPhoton={false,false, false, false}; - //params.fIsPromptPhoton={true}; - //params.fDecayKind={PositroniumDecayKind::k2Gamma}; + auto params = generatePositroniumDecayParams(); pModel = std::make_unique(params); std::cout << "initializing MiniPositronium model 2" << std::endl; } else { @@ -197,4 +186,3 @@ G4int GateExtendedVSource::GeneratePrimaries(G4Event* event) ChangeParticlePositionRelativeToAttachedVolume(particle_position); return pModel->GeneratePrimaryVertices(event, particle_time, particle_position); } - From 0d02eea612528c4987c1a06dad3bc3f6e2a53620 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Mon, 27 Oct 2025 15:08:35 +0100 Subject: [PATCH 034/144] Add missing include --- source/physics/include/GatePositronium.hh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/physics/include/GatePositronium.hh b/source/physics/include/GatePositronium.hh index 375d04d5b..8d5ee69ca 100644 --- a/source/physics/include/GatePositronium.hh +++ b/source/physics/include/GatePositronium.hh @@ -7,9 +7,8 @@ #ifndef GatePositronium_hh #define GatePositronium_hh -//#include "G4VDecayChannel.hh" #include "G4DecayProducts.hh" -#include +#include "GatePositroniumDecayChannel.hh" class GatePositronium { public: From 695ae37a26caaa6990e4b8d911ea11ba219f4fa1 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Mon, 27 Oct 2025 15:13:50 +0100 Subject: [PATCH 035/144] Remove commented coded Also, small formatting fixes. Remove obsolete metadata. --- .../physics/include/GateGammaEmissionModel.hh | 1 - .../physics/include/GateOrthoPositronium.hh | 1 - source/physics/include/GateParaPositronium.hh | 1 - .../include/GatePositroniumDecayModel.hh | 32 --------- .../physics/src/GatePositroniumDecayModel.cc | 72 ------------------- 5 files changed, 107 deletions(-) diff --git a/source/physics/include/GateGammaEmissionModel.hh b/source/physics/include/GateGammaEmissionModel.hh index b25136071..dfb218c77 100644 --- a/source/physics/include/GateGammaEmissionModel.hh +++ b/source/physics/include/GateGammaEmissionModel.hh @@ -14,7 +14,6 @@ /** Author: Mateusz Bała * Email: bala.mateusz@gmail.com - * Organization: J-PET (http://koza.if.uj.edu.pl/pet/) * About class: Basic class for other model of gammas emission. Provides basic tools for calculation and can generate single gamma. **/ class GateGammaEmissionModel diff --git a/source/physics/include/GateOrthoPositronium.hh b/source/physics/include/GateOrthoPositronium.hh index e55a05162..f1d7ac915 100644 --- a/source/physics/include/GateOrthoPositronium.hh +++ b/source/physics/include/GateOrthoPositronium.hh @@ -11,7 +11,6 @@ /** Author: Mateusz Bała * Email: bala.mateusz@gmail.com - * Organization: J-PET (http://koza.if.uj.edu.pl/pet/) * About class: Generate ortho-positronium definition and set decay chanel for oPs. **/ class GateOrthoPositronium : public G4ParticleDefinition diff --git a/source/physics/include/GateParaPositronium.hh b/source/physics/include/GateParaPositronium.hh index f2f78a546..bce3eeb95 100644 --- a/source/physics/include/GateParaPositronium.hh +++ b/source/physics/include/GateParaPositronium.hh @@ -11,7 +11,6 @@ /** Author: Mateusz Bała * Email: bala.mateusz@gmail.com - * Organization: J-PET (http://koza.if.uj.edu.pl/pet/) * About class: Generate para-positronium definition and set decay chanel for pPs. **/ class GateParaPositronium : public G4ParticleDefinition diff --git a/source/physics/include/GatePositroniumDecayModel.hh b/source/physics/include/GatePositroniumDecayModel.hh index c2a69af94..fc5e714c6 100644 --- a/source/physics/include/GatePositroniumDecayModel.hh +++ b/source/physics/include/GatePositroniumDecayModel.hh @@ -16,7 +16,6 @@ #include "GateEmittedGammaInformation.hh" #include "GateGammaEmissionModel.hh" -#include "GatePositroniumDecayModelParams.hh" /** Author: Mateusz Bała @@ -124,35 +123,4 @@ class GatePositroniumDecayModel : public GateGammaEmissionModel G4bool fUsePositroniumFractions = false; }; - -class MiniPositroniumDecayModel:public GateGammaEmissionModel -{ - public: - static int getPositroniumDecayIndex(const std::vector& fractions); - explicit MiniPositroniumDecayModel(const PositroniumDecayModelParams& modelParams):fModelParams(modelParams) - { - auto num_of_decay_channels = fModelParams.fDecayKind.size(); - for (int i = 0; i < num_of_decay_channels; i++) { - if (fModelParams.fDecayKind[i] == PositroniumDecayKind::k2Gamma) - { - fPositroniumDecayChannel.push_back(std::move(GatePositroniumDecayModel::Positronium("pPs", fModelParams.fLifetimes[i]* ns, 2))); - } else { - fPositroniumDecayChannel.push_back(std::move(GatePositroniumDecayModel::Positronium("oPs", fModelParams.fLifetimes[i]* ns, 3))); - } - } - } - - protected: - virtual G4int GeneratePrimaryVertices(G4Event* event, G4double& particle_time, G4ThreeVector& particle_position) override; - G4PrimaryVertex* GetPrimaryVertexFromDeexcitation(G4double particle_time, const G4ThreeVector& particle_position, int decayIndex); - G4PrimaryVertex *GetPrimaryVertexFromPositroniumAnnihilation(G4double particle_time, const G4ThreeVector &particle_position, int decayIndex); - G4PrimaryParticle* GetGammaFromDeexcitation(int decayIndex); - std::vector GetGammasFromPositroniumAnnihilation(int decayIndex); - -private: - PositroniumDecayModelParams fModelParams; - std::vector fPositroniumDecayChannel; -}; - - #endif diff --git a/source/physics/src/GatePositroniumDecayModel.cc b/source/physics/src/GatePositroniumDecayModel.cc index b99f3be62..755615e5d 100644 --- a/source/physics/src/GatePositroniumDecayModel.cc +++ b/source/physics/src/GatePositroniumDecayModel.cc @@ -179,75 +179,3 @@ std::vector GatePositroniumDecayModel::GetGammasFromPositron return gammas; } - -int MiniPositroniumDecayModel::getPositroniumDecayIndex(const std::vector& fractions) { - auto r = G4UniformRand(); - float curr_frac_cumulative = 0.0; - for (int i = 0; i < fractions.size(); ++i) { - curr_frac_cumulative = curr_frac_cumulative + fractions[i]; - if(r<= curr_frac_cumulative) return i; - } - return -1; -} - -G4PrimaryVertex* MiniPositroniumDecayModel::GetPrimaryVertexFromPositroniumAnnihilation(G4double particle_time, const G4ThreeVector& particle_position, int decayIndex) -{ - - G4double shifted_particle_time = particle_time + G4RandExponential::shoot(fModelParams.fLifetimes[decayIndex]); - - G4PrimaryVertex* vertex = new G4PrimaryVertex( particle_position, shifted_particle_time ); - std::vector gammas = GetGammasFromPositroniumAnnihilation(decayIndex); - std::for_each( gammas.begin(), gammas.end(), [&]( G4PrimaryParticle* gamma ) { vertex->SetPrimary( gamma ); } ); - return vertex; -} - -G4int MiniPositroniumDecayModel::GeneratePrimaryVertices(G4Event* event, G4double& particle_time, G4ThreeVector& particle_position) -{ - auto decayIndex = MiniPositroniumDecayModel::getPositroniumDecayIndex(fModelParams.fFractions); - - G4int number_of_vertices = 1; - if(fModelParams.fIsPromptPhoton[decayIndex]) - { - ++number_of_vertices; - event->AddPrimaryVertex(GetPrimaryVertexFromDeexcitation(particle_time, particle_position, decayIndex)); - } - event->AddPrimaryVertex(GetPrimaryVertexFromPositroniumAnnihilation(particle_time, particle_position, decayIndex)); - return number_of_vertices; -} - -G4PrimaryParticle* MiniPositroniumDecayModel::GetGammaFromDeexcitation(int decayIndex) -{ - G4PrimaryParticle* gamma = GetSingleGamma(fModelParams.fPromptPhotonEnergy[decayIndex]); - gamma->SetUserInformation( GetPrimaryParticleInformation( gamma, GateEmittedGammaInformation::GammaKind::Prompt ) ); - return gamma; -} - -G4PrimaryVertex* MiniPositroniumDecayModel::GetPrimaryVertexFromDeexcitation(G4double particle_time, const G4ThreeVector& particle_position, int decayIndex) -{ - G4PrimaryVertex* vertex = new G4PrimaryVertex(particle_position, particle_time); - vertex->SetPrimary(GetGammaFromDeexcitation(decayIndex)); - return vertex; -} - -std::vector MiniPositroniumDecayModel::GetGammasFromPositroniumAnnihilation(int decayIndex) -{ - int annihilation_gammas_number = fPositroniumDecayChannel[decayIndex].GetAnnihilationGammasNumber(); - std::vector gammas(annihilation_gammas_number); - - G4DecayProducts* decay_products = fPositroniumDecayChannel[decayIndex].GetDecayProducts(); - for ( G4int i = 0; i < annihilation_gammas_number; ++i ) - { - G4PrimaryParticle* gamma = new G4PrimaryParticle( pGammaDefinition ); - - G4DynamicParticle* dynamic_gamma = (*decay_products)[i]; - G4LorentzVector lv = dynamic_gamma->Get4Momentum(); - gamma->Set4Momentum( lv.px(), lv.py(), lv.pz(), lv.e() ); - gamma->SetPolarization( dynamic_gamma->GetPolarization() ); - gamma->SetUserInformation( GetPrimaryParticleInformation( gamma, GateEmittedGammaInformation::GammaKind::Annihilation ) ); - gammas[i] = gamma; - } - delete decay_products; - - return gammas; -} - From 5d34771a34ced6c3b86547d043b224185541c277 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Mon, 27 Oct 2025 15:54:01 +0100 Subject: [PATCH 036/144] Fix reading bool argument as true and false Before only 1 or 0 was accepted --- source/physics/src/GateExtendedVSource.cc | 2 -- source/physics/src/GateExtendedVSourceMessenger.cc | 6 ++++-- source/physics/src/GateMiniPositroniumDecayModel.cc | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/source/physics/src/GateExtendedVSource.cc b/source/physics/src/GateExtendedVSource.cc index 71a664a08..6e2b04e3e 100644 --- a/source/physics/src/GateExtendedVSource.cc +++ b/source/physics/src/GateExtendedVSource.cc @@ -151,10 +151,8 @@ void GateExtendedVSource::PrepareModel() pModel = std::make_unique(); } else { if(fModelKind == GateExtendedVSource::ModelKind::MiniPositronium) { - std::cout << "initializing MiniPositronium model" << std::endl; auto params = generatePositroniumDecayParams(); pModel = std::make_unique(params); - std::cout << "initializing MiniPositronium model 2" << std::endl; } else { GateError("GateExtendedVSource::PrepareModel - unknown model."); } diff --git a/source/physics/src/GateExtendedVSourceMessenger.cc b/source/physics/src/GateExtendedVSourceMessenger.cc index 34d271924..3331eef11 100644 --- a/source/physics/src/GateExtendedVSourceMessenger.cc +++ b/source/physics/src/GateExtendedVSourceMessenger.cc @@ -89,6 +89,8 @@ void GateExtendedVSourceMessenger::InitCommands() upCmdSetPositroniumFractions.reset(GetStringCmd( "setPositroniumFractions", "\"f1, f2, f3 .., fn\" - where fi in [0.0, 1.0] and sum of all fi ==1" ) ); upCmdSetPositroniumLifetimes.reset(GetStringCmd( "setPositroniumLifetimes", "\"t1, t2, t3 .., tn\" - where" ) ); upCmdSetDecayKinds.reset(GetStringCmd( "setDecayKinds", "\"k1, k2, k3 .., kn\" - where ki " ) ); + upCmdSetIsPromptPhoton.reset(GetStringCmd( "setIsPromptPhoton", "\"f1, f2, f3 .., fn\" - where fi are true or false " ) ); + upCmdSetPromptPhotonEnergies.reset(GetStringCmd( "setPromptPhotonEnergies", "\"e1, e2, e3 .., fn\" - where ei are energies " ) ); } void GateExtendedVSourceMessenger::SetNewValue( G4UIcommand* command, G4String new_value ) @@ -170,12 +172,12 @@ void GateExtendedVSourceMessenger::SetNewValue( G4UIcommand* command, G4String n } pSource->fPositroniumLifetimes=lifetimes; } - else if(command == upCmdSetIsPromptPhoton.get()) + else if(command == upCmdSetIsPromptPhoton.get()) { std::vector isPromptPhoton; std::stringstream ss(new_value); bool flag; - while (ss >> flag) { + while (ss >> std::boolalpha >> flag) { isPromptPhoton.push_back(flag); } pSource->fIsPromptPhoton=isPromptPhoton; diff --git a/source/physics/src/GateMiniPositroniumDecayModel.cc b/source/physics/src/GateMiniPositroniumDecayModel.cc index 77dddc2d1..0ac8bf975 100644 --- a/source/physics/src/GateMiniPositroniumDecayModel.cc +++ b/source/physics/src/GateMiniPositroniumDecayModel.cc @@ -31,7 +31,7 @@ MiniPositroniumDecayModel::MiniPositroniumDecayModel(const PositroniumDecayModel { fPositroniumDecayChannel.push_back(std::move(GatePositronium("pPs", fModelParams.fLifetimes[i]* ns))); } else { - fPositroniumDecayChannel.push_back(std::move(GatePositronium("oPs", fModelParams.fLifetimes[i]* ns))); + fPositroniumDecayChannel.push_back(std::move(GatePositronium("pPs", fModelParams.fLifetimes[i]* ns))); } } } From b8e3f79525da36b7e68c629c1a97a6bcf6929954 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Tue, 4 Nov 2025 09:20:27 +0100 Subject: [PATCH 037/144] Add separated class GatePositroniumDecayParamsGenerator To handle preparing parameters from macros. Now an object of this class is a member of the Messanger. Also, add stub of the test of ExtendedSource, and modify other classes. --- source/physics/include/GateExtendedVSource.hh | 17 +---- .../include/GateExtendedVSourceMessenger.hh | 12 ++- .../GatePositroniumDecayParamsGenerator.hh | 42 +++++++++++ source/physics/src/GateExtendedVSource.cc | 75 ++++++++++--------- .../src/GateExtendedVSourceMessenger.cc | 29 ++++--- .../GatePositroniumDecayParamsGenerator.cc | 59 +++++++++++++++ tests/test_GateExtendedVSource.cpp | 56 ++++++++++++++ 7 files changed, 222 insertions(+), 68 deletions(-) create mode 100644 source/physics/include/GatePositroniumDecayParamsGenerator.hh create mode 100644 source/physics/src/GatePositroniumDecayParamsGenerator.cc create mode 100644 tests/test_GateExtendedVSource.cpp diff --git a/source/physics/include/GateExtendedVSource.hh b/source/physics/include/GateExtendedVSource.hh index 114794aec..7f7b0e4ff 100644 --- a/source/physics/include/GateExtendedVSource.hh +++ b/source/physics/include/GateExtendedVSource.hh @@ -9,11 +9,10 @@ #include #include - #include "GateVSource.hh" #include "GateExtendedVSourceMessenger.hh" #include "GateGammaEmissionModel.hh" -#include "GatePositroniumDecayModelParams.hh" +//#include "GatePositroniumDecayModelParams.hh" /** Author: Mateusz Bała * Email: bala.mateusz@gmail.com @@ -36,6 +35,8 @@ public: explicit GateExtendedVSource(const G4String& name); virtual ~GateExtendedVSource() = default; + //PositroniumDecayModelParams generatePositroniumDecayParams() const; + /** Generate gammas for event **/ virtual G4int GeneratePrimaries( G4Event* event ) override; @@ -99,18 +100,6 @@ public: //Constants for Set(...) methods static inline const G4String kParaPositroniumName = "pPs"; static inline const G4String kOrthoPositroniumName = "oPs"; - - PositroniumDecayModelParams generatePositroniumDecayParams() const; - - public: - - std::optional> fPositroniumFractions; - std::optional> fPositroniumLifetimes; - std::optional> fIsPromptPhoton; - std::optional> fPromptPhotonEnergies; - std::optional> fDecayKinds; - - }; #endif diff --git a/source/physics/include/GateExtendedVSourceMessenger.hh b/source/physics/include/GateExtendedVSourceMessenger.hh index fb81fe84e..69478fd16 100644 --- a/source/physics/include/GateExtendedVSourceMessenger.hh +++ b/source/physics/include/GateExtendedVSourceMessenger.hh @@ -17,6 +17,8 @@ #include "G4UIcmdWithAString.hh" #include "G4UIcmdWith3VectorAndUnit.hh" +#include "GatePositroniumDecayParamsGenerator.hh" + class GateExtendedVSource; /** Author: Mateusz Bała @@ -27,10 +29,12 @@ class GateExtendedVSource; class GateExtendedVSourceMessenger: public GateVSourceMessenger { public: - explicit GateExtendedVSourceMessenger(GateExtendedVSource *source); - ~GateExtendedVSourceMessenger()=default; + explicit GateExtendedVSourceMessenger(GateExtendedVSource *source); + ~GateExtendedVSourceMessenger()=default; - void SetNewValue(G4UIcommand *command, G4String newValue) override; + void SetNewValue(G4UIcommand *command, G4String newValue) override; + + PositroniumDecayModelParams generatePositroniumDecayParams() const; protected: void InitCommands(); @@ -63,6 +67,8 @@ class GateExtendedVSourceMessenger: public GateVSourceMessenger std::unique_ptr upCmdSetIsPromptPhoton; std::unique_ptr upCmdSetPromptPhotonEnergies; std::unique_ptr upCmdSetDecayKinds; + + GatePositroniumDecayParamsGenerator fParamGenerator; }; diff --git a/source/physics/include/GatePositroniumDecayParamsGenerator.hh b/source/physics/include/GatePositroniumDecayParamsGenerator.hh new file mode 100644 index 000000000..15143cd8b --- /dev/null +++ b/source/physics/include/GatePositroniumDecayParamsGenerator.hh @@ -0,0 +1,42 @@ + +/** ---------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + ----------------------*/ +#ifndef GatePositroniumDecayParamsGenerator_hh +#define GatePositroniumDecayParamsGenerator_hh + +#include +#include + +#include "GatePositroniumDecayModelParams.hh" + +/*! class GatePositroniumDecayParamsGenerator + * brief Brief class description + * + * Detailed description + */ +class GatePositroniumDecayParamsGenerator +{ +public: + GatePositroniumDecayParamsGenerator()= default; + virtual ~GatePositroniumDecayParamsGenerator()=default; + + void SetEnableDeexcitation(const std::vector& IsPromptPhoton); + void SetPromptGammaEnergies(const std::vector& energies); + void SetPostroniumLifetimes(const std::vector& fPositroniumLifetimes); + void SetDecayKinds(const std::vector& decayKinds); + void SetPositroniumFraction(const std::vector& positroniumFractions); + + PositroniumDecayModelParams generatePositroniumDecayParams() const; + +private: + std::optional> fPositroniumFractions; + std::optional> fPositroniumLifetimes; + std::optional> fIsPromptPhoton; + std::optional> fPromptPhotonEnergies; + std::optional> fDecayKinds; +}; +#endif diff --git a/source/physics/src/GateExtendedVSource.cc b/source/physics/src/GateExtendedVSource.cc index 6e2b04e3e..e97cc2118 100644 --- a/source/physics/src/GateExtendedVSource.cc +++ b/source/physics/src/GateExtendedVSource.cc @@ -16,7 +16,8 @@ GateExtendedVSource::GateExtendedVSource(const G4String &name) : GateVSource(name), pMessenger(std::make_unique(this)) -{} +{ +} void GateExtendedVSource::SetModel(const G4String &model_name) { @@ -74,40 +75,40 @@ void GateExtendedVSource::SetPositroniumFraction( const G4String& positronium_ki fParaPositroniumFraction = pPs_fraction; } -PositroniumDecayModelParams GateExtendedVSource::generatePositroniumDecayParams() const -{ - PositroniumDecayModelParams params; - if(fPositroniumFractions.has_value()) { - params.fFractions=fPositroniumFractions.value(); - } else { - GateError("GateExtendedVSource::generatePositroniumDecayParams: Positronium decay fractions are not set"); - } - - if(fPositroniumLifetimes.has_value()) { - params.fLifetimes=fPositroniumLifetimes.value(); - } else { - GateError("GateExtendedVSource::generatePositroniumDecayParams: Positronium lifetimes are not set"); - } - - if(fDecayKinds.has_value()) { - params.fDecayKind=fDecayKinds.value(); - } else { - GateError("GateExtendedVSource::generatePositroniumDecayParams: Positronium decay kinds are not set"); - } - - if(fIsPromptPhoton.has_value()) { - params.fIsPromptPhoton=fIsPromptPhoton.value(); - } else { - GateError("GateExtendedVSource::generatePositroniumDecayParams: Positronium is prompt photon flags are not set"); - } - - if(fPromptPhotonEnergies.has_value()) { - params.fPromptPhotonEnergy=fPromptPhotonEnergies.value(); - } else { - GateError("GateExtendedVSource::generatePositroniumDecayParams: Prompt photon energies are not set"); - } - return params; -} +//PositroniumDecayModelParams GateExtendedVSource::generatePositroniumDecayParams() const +//{ + //PositroniumDecayModelParams params; + //if(fPositroniumFractions.has_value()) { + //params.fFractions=fPositroniumFractions.value(); + //} else { + //GateError("GateExtendedVSource::generatePositroniumDecayParams: Positronium decay fractions are not set"); + //} + + //if(fPositroniumLifetimes.has_value()) { + //params.fLifetimes=fPositroniumLifetimes.value(); + //} else { + //GateError("GateExtendedVSource::generatePositroniumDecayParams: Positronium lifetimes are not set"); + //} + + //if(fDecayKinds.has_value()) { + //params.fDecayKind=fDecayKinds.value(); + //} else { + //GateError("GateExtendedVSource::generatePositroniumDecayParams: Positronium decay kinds are not set"); + //} + + //if(fIsPromptPhoton.has_value()) { + //params.fIsPromptPhoton=fIsPromptPhoton.value(); + //} else { + //GateError("GateExtendedVSource::generatePositroniumDecayParams: Positronium is prompt photon flags are not set"); + //} + + //if(fPromptPhotonEnergies.has_value()) { + //params.fPromptPhotonEnergy=fPromptPhotonEnergies.value(); + //} else { + //GateError("GateExtendedVSource::generatePositroniumDecayParams: Prompt photon energies are not set"); + //} + //return params; +//} void GateExtendedVSource::PrepareModel() { @@ -151,7 +152,7 @@ void GateExtendedVSource::PrepareModel() pModel = std::make_unique(); } else { if(fModelKind == GateExtendedVSource::ModelKind::MiniPositronium) { - auto params = generatePositroniumDecayParams(); + auto params = pMessenger->generatePositroniumDecayParams(); pModel = std::make_unique(params); } else { GateError("GateExtendedVSource::PrepareModel - unknown model."); @@ -182,5 +183,7 @@ G4int GateExtendedVSource::GeneratePrimaries(G4Event* event) G4double particle_time = GetTime(); G4ThreeVector particle_position = GetPosDist()->GenerateOne(); ChangeParticlePositionRelativeToAttachedVolume(particle_position); + pModel->GeneratePrimaryVertices(event, particle_time, particle_position); return pModel->GeneratePrimaryVertices(event, particle_time, particle_position); } + diff --git a/source/physics/src/GateExtendedVSourceMessenger.cc b/source/physics/src/GateExtendedVSourceMessenger.cc index 3331eef11..937a01ff7 100644 --- a/source/physics/src/GateExtendedVSourceMessenger.cc +++ b/source/physics/src/GateExtendedVSourceMessenger.cc @@ -150,18 +150,9 @@ void GateExtendedVSourceMessenger::SetNewValue( G4UIcommand* command, G4String n while (ss >> num) { fractions.push_back(num); } - pSource->fPositroniumFractions=fractions; + fParamGenerator.SetPositroniumFraction(fractions); + //pSource->fPositroniumFractions=fractions; } - else if(command == upCmdSetPositroniumFractions.get()) - { - std::vector fractions; - std::stringstream ss(new_value); - G4double num; - while (ss >> num) { - fractions.push_back(num); - } - pSource->fPositroniumFractions=fractions; - } else if(command == upCmdSetPositroniumLifetimes.get()) { std::vector lifetimes; @@ -170,7 +161,8 @@ void GateExtendedVSourceMessenger::SetNewValue( G4UIcommand* command, G4String n while (ss >> num) { lifetimes.push_back(num); } - pSource->fPositroniumLifetimes=lifetimes; + //pSource->fPositroniumLifetimes=lifetimes; + fParamGenerator.SetPostroniumLifetimes(lifetimes); } else if(command == upCmdSetIsPromptPhoton.get()) { @@ -180,7 +172,8 @@ void GateExtendedVSourceMessenger::SetNewValue( G4UIcommand* command, G4String n while (ss >> std::boolalpha >> flag) { isPromptPhoton.push_back(flag); } - pSource->fIsPromptPhoton=isPromptPhoton; + //pSource->fIsPromptPhoton=isPromptPhoton; + fParamGenerator.SetEnableDeexcitation(isPromptPhoton); } else if(command == upCmdSetPromptPhotonEnergies.get()) { std::vector promptPhotonEnergies; @@ -189,7 +182,8 @@ void GateExtendedVSourceMessenger::SetNewValue( G4UIcommand* command, G4String n while (ss >> energy) { promptPhotonEnergies.push_back(energy); } - pSource->fPromptPhotonEnergies=promptPhotonEnergies; + //pSource->fPromptPhotonEnergies=promptPhotonEnergies; + fParamGenerator.SetPromptGammaEnergies(promptPhotonEnergies); } else if(command == upCmdSetDecayKinds.get()) { std::vector decayKinds; @@ -202,7 +196,8 @@ void GateExtendedVSourceMessenger::SetNewValue( G4UIcommand* command, G4String n decayKinds.push_back(k3Gamma); } } - pSource->fDecayKinds=decayKinds; + fParamGenerator.SetDecayKinds(decayKinds); + //pSource->fDecayKinds=decayKinds; } else { @@ -211,5 +206,9 @@ void GateExtendedVSourceMessenger::SetNewValue( G4UIcommand* command, G4String n } +PositroniumDecayModelParams GateExtendedVSourceMessenger::generatePositroniumDecayParams() const +{ + return fParamGenerator.generatePositroniumDecayParams(); +} diff --git a/source/physics/src/GatePositroniumDecayParamsGenerator.cc b/source/physics/src/GatePositroniumDecayParamsGenerator.cc new file mode 100644 index 000000000..e575b1635 --- /dev/null +++ b/source/physics/src/GatePositroniumDecayParamsGenerator.cc @@ -0,0 +1,59 @@ + +#include "GateMessageManager.hh" +#include "GatePositroniumDecayParamsGenerator.hh" + +void GatePositroniumDecayParamsGenerator::SetEnableDeexcitation(const std::vector& isPromptPhoton) +{ + fIsPromptPhoton = isPromptPhoton; +} +void GatePositroniumDecayParamsGenerator::SetDecayKinds(const std::vector& decayKinds) +{ + fDecayKinds = decayKinds; +} +void GatePositroniumDecayParamsGenerator::SetPostroniumLifetimes(const std::vector& positroniumLifetimes) +{ + fPositroniumLifetimes = positroniumLifetimes; +} +void GatePositroniumDecayParamsGenerator::SetPromptGammaEnergies(const std::vector& energies) +{ + fPromptPhotonEnergies = energies; +} +void GatePositroniumDecayParamsGenerator::SetPositroniumFraction(const std::vector& positroniumFractions) +{ + fPositroniumFractions = positroniumFractions; +} + +PositroniumDecayModelParams GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams() const +{ + PositroniumDecayModelParams params; + if(fPositroniumFractions.has_value()) { + params.fFractions=fPositroniumFractions.value(); + } else { + GateError("GateExtendedVSource::generatePositroniumDecayParams: Positronium decay fractions are not set"); + } + + if(fPositroniumLifetimes.has_value()) { + params.fLifetimes=fPositroniumLifetimes.value(); + } else { + GateError("GateExtendedVSource::generatePositroniumDecayParams: Positronium lifetimes are not set"); + } + + if(fDecayKinds.has_value()) { + params.fDecayKind=fDecayKinds.value(); + } else { + GateError("GateExtendedVSource::generatePositroniumDecayParams: Positronium decay kinds are not set"); + } + + if(fIsPromptPhoton.has_value()) { + params.fIsPromptPhoton=fIsPromptPhoton.value(); + } else { + GateError("GateExtendedVSource::generatePositroniumDecayParams: Positronium is prompt photon flags are not set"); + } + + if(fPromptPhotonEnergies.has_value()) { + params.fPromptPhotonEnergy=fPromptPhotonEnergies.value(); + } else { + GateError("GateExtendedVSource::generatePositroniumDecayParams: Prompt photon energies are not set"); + } + return params; +} diff --git a/tests/test_GateExtendedVSource.cpp b/tests/test_GateExtendedVSource.cpp new file mode 100644 index 000000000..9285fa9b9 --- /dev/null +++ b/tests/test_GateExtendedVSource.cpp @@ -0,0 +1,56 @@ +#include +#include + +#include + +#include + +#include "GateRunManager.hh" +#include "GatePhysicsList.hh" +#include "GateDetectorConstruction.hh" + +void initializeGateRunManager(GateRunManager* runManager) +{ + // Set the DetectorConstruction + GateDetectorConstruction* gateDC = new GateDetectorConstruction(); + runManager->SetUserInitialization( gateDC ); + // Set the PhysicsList + runManager->SetUserInitialization( GatePhysicsList::GetInstance() ); + // Initialize G4 kernel + runManager->InitializeAll(); +} + +bool run_tests() +{ + GateExtendedVSource source("source1"); + if(source.GetName() !="source1") { + return false; + } + source.SetType("mPs"); + if(source.GetType() !="mPs") { + return false; + } + + G4Event* pEvent = nullptr; + source.GeneratePrimaries(pEvent); + return true; +} + + +int main() +{ + // Initialization + std::unique_ptr runManager(new GateRunManager); + initializeGateRunManager(runManager.get()); + + bool res = true; + res = res & run_tests(); + + if (res) { + std::cout << "All tests have passed" << std::endl; + return 0; + } else { + std::cerr << "Some tests failed" << std::endl; + return -1; + } +} From 9019cb4d74dd9abecd6b144fcbe86d06a17148be Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Tue, 4 Nov 2025 18:39:47 +0100 Subject: [PATCH 038/144] ParaPositronium and OrthoPositronium handling by Mini model Also, remove unnecessary methods from different places. --- source/physics/include/GateExtendedVSource.hh | 26 +--- .../include/GateExtendedVSourceMessenger.hh | 2 +- .../GatePositroniumDecayParamsGenerator.hh | 6 +- source/physics/src/GateExtendedVSource.cc | 111 +++--------------- .../src/GateExtendedVSourceMessenger.cc | 40 +------ .../GatePositroniumDecayParamsGenerator.cc | 81 +++++++++---- 6 files changed, 83 insertions(+), 183 deletions(-) diff --git a/source/physics/include/GateExtendedVSource.hh b/source/physics/include/GateExtendedVSource.hh index 7f7b0e4ff..c2c949213 100644 --- a/source/physics/include/GateExtendedVSource.hh +++ b/source/physics/include/GateExtendedVSource.hh @@ -24,7 +24,7 @@ class GateExtendedVSource : public GateVSource public: enum class ModelKind { - NotDefined, //by default - in this case this clas will behave like GateVSource + NotDefined, //by default - in this case this class will behave like GateVSource SingleGamma, // generate single gamma ParaPositronium, //generate gammas from para-positronium decay OrthoPositronium, //generate gammas from ortho-positronium decay @@ -41,7 +41,6 @@ public: **/ virtual G4int GeneratePrimaries( G4Event* event ) override; - void SetEnableDeexcitation(G4bool enable_deexcitation); /** Set fixed direction of single gamma ( or prompt gamma ) **/ void SetFixedEmissionDirection(const G4ThreeVector &fixed_emission_direction); @@ -52,22 +51,6 @@ public: **/ void SetEmissionEnergy(G4double energy); void SetSeed(G4long seed); - /** Set positronium lifetime - which is included as constant for exponential distribution ( G4RandExponential ) - * @param: positronium_name - for example: pPs, oPs - * @param: life_time - in ns - * Lifetime value will have inpact of vertex time set for annihilation gammas - **/ - void SetPostroniumLifetime(const G4String &positronium_name, G4double life_time); - /** Set prompt gamma energy ( deexcictation energy ). - * If user set enable emission of prompt gamma without set prompt energy then il wii be default value used ( deexcitation of Na22 ) - **/ - void SetPromptGammaEnergy(G4double energy); - /** Set propability of gammas emission from different positronium. - * @param: positronium_kind - positronium name: pPs, oPs - * @param: fraction - number in range from 0.0 to 1.0 - * You have to call this method for only one kind of positronium - for the second one propability will be calculated as: 1.0 - fraction. - **/ - void SetPositroniumFraction(const G4String& positronium_kind, G4double fraction); protected: /** Set model used for this source. If is not defined then this class will behave like GateVSource. @@ -84,16 +67,11 @@ public: std::unique_ptr pMessenger; //User settings: ModelKind fModelKind = ModelKind::NotDefined; - std::optional fEnableDeexcitation; std::optional fFixedEmissionDirection; std::optional fEnableFixedEmissionDirection; std::optional fEmissionEnergy; std::optional fSeed; - std::optional fParaPostroniumLifetime; - std::optional fOrthoPostroniumLifetime; - std::optional fPromptGammaEnergy; - std::optional fParaPositroniumFraction; - // + //Set by PrepareModel() and used in GeneratePrimaries() G4bool fBehaveLikeVSource = false; diff --git a/source/physics/include/GateExtendedVSourceMessenger.hh b/source/physics/include/GateExtendedVSourceMessenger.hh index 69478fd16..f625e7172 100644 --- a/source/physics/include/GateExtendedVSourceMessenger.hh +++ b/source/physics/include/GateExtendedVSourceMessenger.hh @@ -34,7 +34,7 @@ class GateExtendedVSourceMessenger: public GateVSourceMessenger void SetNewValue(G4UIcommand *command, G4String newValue) override; - PositroniumDecayModelParams generatePositroniumDecayParams() const; + PositroniumDecayModelParams generatePositroniumDecayParams(GatePositroniumDecayParamsGenerator::DecayModel model= GatePositroniumDecayParamsGenerator::kPositronium) const; protected: void InitCommands(); diff --git a/source/physics/include/GatePositroniumDecayParamsGenerator.hh b/source/physics/include/GatePositroniumDecayParamsGenerator.hh index 15143cd8b..7dcd7e5ef 100644 --- a/source/physics/include/GatePositroniumDecayParamsGenerator.hh +++ b/source/physics/include/GatePositroniumDecayParamsGenerator.hh @@ -18,9 +18,13 @@ * * Detailed description */ + + class GatePositroniumDecayParamsGenerator { public: + enum DecayModel {kParaPositronium, kOrthoPositronium, kPositronium}; + GatePositroniumDecayParamsGenerator()= default; virtual ~GatePositroniumDecayParamsGenerator()=default; @@ -30,7 +34,7 @@ public: void SetDecayKinds(const std::vector& decayKinds); void SetPositroniumFraction(const std::vector& positroniumFractions); - PositroniumDecayModelParams generatePositroniumDecayParams() const; + PositroniumDecayModelParams generatePositroniumDecayParams(DecayModel model= kPositronium) const; private: std::optional> fPositroniumFractions; diff --git a/source/physics/src/GateExtendedVSource.cc b/source/physics/src/GateExtendedVSource.cc index e97cc2118..0c77af23e 100644 --- a/source/physics/src/GateExtendedVSource.cc +++ b/source/physics/src/GateExtendedVSource.cc @@ -40,7 +40,6 @@ void GateExtendedVSource::SetModel(const G4String &model_name) } } -void GateExtendedVSource::SetEnableDeexcitation(G4bool enable_deexcitation) { fEnableDeexcitation = enable_deexcitation; } void GateExtendedVSource::SetFixedEmissionDirection(const G4ThreeVector& fixed_emission_direction) { fFixedEmissionDirection = fixed_emission_direction; } @@ -50,65 +49,6 @@ void GateExtendedVSource::SetEmissionEnergy(G4double energy) { fEmissionEnergy = void GateExtendedVSource::SetSeed(G4long seed) { fSeed = seed; } -void GateExtendedVSource::SetPostroniumLifetime(const G4String& positronium_name, G4double life_time) -{ - if ( positronium_name == kParaPositroniumName ) { fParaPostroniumLifetime = life_time; } - else if ( positronium_name == kOrthoPositroniumName ) { fOrthoPostroniumLifetime = life_time; } - else { GateError( "GateExtendedVSource::SetPostroniumLifetime : incorrect positronium name - try: pPs or oPs" ); } -} - -void GateExtendedVSource::SetPromptGammaEnergy(G4double energy) { fPromptGammaEnergy = energy; } - -void GateExtendedVSource::SetPositroniumFraction( const G4String& positronium_kind, G4double fraction ) -{ - if ( fraction > 1.0 || fraction < 0.0 ) - { - GateError( "GateExtendedVSource::SetPositroniumFraction : incorrect fraction value - required: 0.0 <= fraction <= 1.0 " ); - } - - G4double pPs_fraction = 0.0; - - if ( positronium_kind == kParaPositroniumName ) { pPs_fraction = fraction; } - else if ( positronium_kind == kOrthoPositroniumName ) { pPs_fraction = 1.0 - fraction; } - else { GateError( "GateExtendedVSource::SetPositroniumFraction : incorrect positronium kind - enable are: pPs, oPs" ); } - - fParaPositroniumFraction = pPs_fraction; -} - -//PositroniumDecayModelParams GateExtendedVSource::generatePositroniumDecayParams() const -//{ - //PositroniumDecayModelParams params; - //if(fPositroniumFractions.has_value()) { - //params.fFractions=fPositroniumFractions.value(); - //} else { - //GateError("GateExtendedVSource::generatePositroniumDecayParams: Positronium decay fractions are not set"); - //} - - //if(fPositroniumLifetimes.has_value()) { - //params.fLifetimes=fPositroniumLifetimes.value(); - //} else { - //GateError("GateExtendedVSource::generatePositroniumDecayParams: Positronium lifetimes are not set"); - //} - - //if(fDecayKinds.has_value()) { - //params.fDecayKind=fDecayKinds.value(); - //} else { - //GateError("GateExtendedVSource::generatePositroniumDecayParams: Positronium decay kinds are not set"); - //} - - //if(fIsPromptPhoton.has_value()) { - //params.fIsPromptPhoton=fIsPromptPhoton.value(); - //} else { - //GateError("GateExtendedVSource::generatePositroniumDecayParams: Positronium is prompt photon flags are not set"); - //} - - //if(fPromptPhotonEnergies.has_value()) { - //params.fPromptPhotonEnergy=fPromptPhotonEnergies.value(); - //} else { - //GateError("GateExtendedVSource::generatePositroniumDecayParams: Prompt photon energies are not set"); - //} - //return params; -//} void GateExtendedVSource::PrepareModel() { @@ -118,44 +58,25 @@ void GateExtendedVSource::PrepareModel() return; } - if (fModelKind == GateExtendedVSource::ModelKind::ParaPositronium || - fModelKind == GateExtendedVSource::ModelKind::OrthoPositronium || - fModelKind == GateExtendedVSource::ModelKind::Positronium) { - auto model = std::make_unique(); - - if (fModelKind == GateExtendedVSource::ModelKind::OrthoPositronium) { - model->SetPositroniumKind( - GatePositroniumDecayModel::PositroniumKind::oPs); - } - if (fModelKind == GateExtendedVSource::ModelKind::Positronium && - fParaPositroniumFraction.has_value()) { - model->SetParaPositroniumFraction(fParaPositroniumFraction.value()); - } - - if (fEnableDeexcitation.has_value() && fEnableDeexcitation.value()) { - model->SetDecayModel(GatePositroniumDecayModel::DecayModel::WithPrompt); - } - if (fParaPostroniumLifetime.has_value()) { - model->SetPostroniumLifetime(kParaPositroniumName, - fParaPostroniumLifetime.value()); - } - if (fOrthoPostroniumLifetime.has_value()) { - model->SetPostroniumLifetime(kOrthoPositroniumName, - fOrthoPostroniumLifetime.value()); - } - if (fPromptGammaEnergy.has_value()) { - model->SetPromptGammaEnergy(fPromptGammaEnergy.value()); - } - pModel = std::move(model); + if (fModelKind == GateExtendedVSource::ModelKind::SingleGamma) { + pModel = std::make_unique(); } else { - if (fModelKind == GateExtendedVSource::ModelKind::SingleGamma) { - pModel = std::make_unique(); - } else { - if(fModelKind == GateExtendedVSource::ModelKind::MiniPositronium) { - auto params = pMessenger->generatePositroniumDecayParams(); + if ((fModelKind == GateExtendedVSource::ModelKind::MiniPositronium) || + (fModelKind == GateExtendedVSource::ModelKind::Positronium)) { + auto params = pMessenger->generatePositroniumDecayParams(); + pModel = std::make_unique(params); + } else { + if (fModelKind == GateExtendedVSource::ModelKind::ParaPositronium) { + auto params = pMessenger->generatePositroniumDecayParams(GatePositroniumDecayParamsGenerator::kParaPositronium); pModel = std::make_unique(params); + } else { - GateError("GateExtendedVSource::PrepareModel - unknown model."); + if (fModelKind == GateExtendedVSource::ModelKind::OrthoPositronium) { + auto params = pMessenger->generatePositroniumDecayParams(GatePositroniumDecayParamsGenerator::kOrthoPositronium); + pModel = std::make_unique(params); + } else { + GateError("GateExtendedVSource::PrepareModel - unknown model."); + } } } } diff --git a/source/physics/src/GateExtendedVSourceMessenger.cc b/source/physics/src/GateExtendedVSourceMessenger.cc index 937a01ff7..27b51f963 100644 --- a/source/physics/src/GateExtendedVSourceMessenger.cc +++ b/source/physics/src/GateExtendedVSourceMessenger.cc @@ -95,11 +95,7 @@ void GateExtendedVSourceMessenger::InitCommands() void GateExtendedVSourceMessenger::SetNewValue( G4UIcommand* command, G4String new_value ) { - if ( command == upCmdSetEnableDeexcitation.get() ) - { - pSource->SetEnableDeexcitation( upCmdSetEnableDeexcitation->GetNewBoolValue( new_value ) ); - } - else if ( command == upCmdSetFixedEmissionDirection.get() ) + if ( command == upCmdSetFixedEmissionDirection.get() ) { pSource->SetFixedEmissionDirection( upCmdSetFixedEmissionDirection->GetNew3VectorValue( new_value ) ); } @@ -115,33 +111,6 @@ void GateExtendedVSourceMessenger::SetNewValue( G4UIcommand* command, G4String n { pSource->SetSeed( static_cast( upCmdSetSeed->GetNewIntValue( new_value ) ) ); } - else if ( command == upCmdSetPositroniumLifetime.get() ) - { - G4String positronium_name; - G4String units; - G4double value = 0.0; - - std::stringstream ss(new_value); - ss >> positronium_name >> value >> units; - - G4String new_lifetime_value = std::to_string( value ) + " " + units; - pSource->SetPostroniumLifetime(positronium_name, upCmdSetLifetime->GetNewDoubleValue(new_lifetime_value )); - - } - else if ( command == upCmdSetPromptGammaEnergy.get() ) - { - pSource->SetPromptGammaEnergy( upCmdSetPromptGammaEnergy->GetNewDoubleValue( new_value ) ); - } - else if ( command == upCmdSetPositroniumFraction.get() ) - { - G4double fraction = 0.0; - G4String positronium_kind; - - std::stringstream ss(new_value); - ss >> positronium_kind >> fraction; - - pSource->SetPositroniumFraction(positronium_kind, fraction); - } else if(command == upCmdSetPositroniumFractions.get()) { std::vector fractions; @@ -151,7 +120,6 @@ void GateExtendedVSourceMessenger::SetNewValue( G4UIcommand* command, G4String n fractions.push_back(num); } fParamGenerator.SetPositroniumFraction(fractions); - //pSource->fPositroniumFractions=fractions; } else if(command == upCmdSetPositroniumLifetimes.get()) { @@ -161,7 +129,6 @@ void GateExtendedVSourceMessenger::SetNewValue( G4UIcommand* command, G4String n while (ss >> num) { lifetimes.push_back(num); } - //pSource->fPositroniumLifetimes=lifetimes; fParamGenerator.SetPostroniumLifetimes(lifetimes); } else if(command == upCmdSetIsPromptPhoton.get()) @@ -172,7 +139,6 @@ void GateExtendedVSourceMessenger::SetNewValue( G4UIcommand* command, G4String n while (ss >> std::boolalpha >> flag) { isPromptPhoton.push_back(flag); } - //pSource->fIsPromptPhoton=isPromptPhoton; fParamGenerator.SetEnableDeexcitation(isPromptPhoton); } else if(command == upCmdSetPromptPhotonEnergies.get()) { @@ -182,7 +148,6 @@ void GateExtendedVSourceMessenger::SetNewValue( G4UIcommand* command, G4String n while (ss >> energy) { promptPhotonEnergies.push_back(energy); } - //pSource->fPromptPhotonEnergies=promptPhotonEnergies; fParamGenerator.SetPromptGammaEnergies(promptPhotonEnergies); } else if(command == upCmdSetDecayKinds.get()) { @@ -197,7 +162,6 @@ void GateExtendedVSourceMessenger::SetNewValue( G4UIcommand* command, G4String n } } fParamGenerator.SetDecayKinds(decayKinds); - //pSource->fDecayKinds=decayKinds; } else { @@ -206,7 +170,7 @@ void GateExtendedVSourceMessenger::SetNewValue( G4UIcommand* command, G4String n } -PositroniumDecayModelParams GateExtendedVSourceMessenger::generatePositroniumDecayParams() const +PositroniumDecayModelParams GateExtendedVSourceMessenger::generatePositroniumDecayParams(GatePositroniumDecayParamsGenerator::DecayModel model) const { return fParamGenerator.generatePositroniumDecayParams(); } diff --git a/source/physics/src/GatePositroniumDecayParamsGenerator.cc b/source/physics/src/GatePositroniumDecayParamsGenerator.cc index e575b1635..6de1271cc 100644 --- a/source/physics/src/GatePositroniumDecayParamsGenerator.cc +++ b/source/physics/src/GatePositroniumDecayParamsGenerator.cc @@ -1,6 +1,7 @@ #include "GateMessageManager.hh" #include "GatePositroniumDecayParamsGenerator.hh" +#include void GatePositroniumDecayParamsGenerator::SetEnableDeexcitation(const std::vector& isPromptPhoton) { @@ -23,37 +24,69 @@ void GatePositroniumDecayParamsGenerator::SetPositroniumFraction(const std::vect fPositroniumFractions = positroniumFractions; } -PositroniumDecayModelParams GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams() const +PositroniumDecayModelParams GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams(const GatePositroniumDecayParamsGenerator::DecayModel model) const { PositroniumDecayModelParams params; - if(fPositroniumFractions.has_value()) { - params.fFractions=fPositroniumFractions.value(); - } else { - GateError("GateExtendedVSource::generatePositroniumDecayParams: Positronium decay fractions are not set"); + if (model == GatePositroniumDecayParamsGenerator::kParaPositronium) { + params.fFractions= {1}; + params.fLifetimes= {0.1244}; // [ns] + params.fDecayKind= {k2Gamma}; + if(fIsPromptPhoton.has_value() && fPromptPhotonEnergies.has_value()) { + params.fIsPromptPhoton=fIsPromptPhoton.value(); + assert(params.fIsPromptPhoton.size()==1); + params.fPromptPhotonEnergy=fPromptPhotonEnergies.value(); + assert(params.fPromptPhotonEnergy.size()==1); + } else { + params.fIsPromptPhoton={false}; + params.fPromptPhotonEnergy ={0.0}; + } } - - if(fPositroniumLifetimes.has_value()) { - params.fLifetimes=fPositroniumLifetimes.value(); - } else { - GateError("GateExtendedVSource::generatePositroniumDecayParams: Positronium lifetimes are not set"); + if (model == GatePositroniumDecayParamsGenerator::kOrthoPositronium) { + params.fFractions= {1}; + params.fLifetimes= {138.6}; // [ns] + params.fDecayKind= {k3Gamma}; + if(fIsPromptPhoton.has_value() && fPromptPhotonEnergies.has_value()) { + params.fIsPromptPhoton=fIsPromptPhoton.value(); + assert(params.fIsPromptPhoton.size()==1); + params.fPromptPhotonEnergy=fPromptPhotonEnergies.value(); + assert(params.fPromptPhotonEnergy.size()==1); + } else { + params.fIsPromptPhoton={false}; + params.fPromptPhotonEnergy ={0.0}; + } } - if(fDecayKinds.has_value()) { - params.fDecayKind=fDecayKinds.value(); - } else { - GateError("GateExtendedVSource::generatePositroniumDecayParams: Positronium decay kinds are not set"); - } + if (model == GatePositroniumDecayParamsGenerator::kPositronium) { + if(fPositroniumFractions.has_value()) { + params.fFractions=fPositroniumFractions.value(); + } else { + GateError("GateExtendedVSource::generatePositroniumDecayParams: Positronium decay fractions are not set"); + } - if(fIsPromptPhoton.has_value()) { - params.fIsPromptPhoton=fIsPromptPhoton.value(); - } else { - GateError("GateExtendedVSource::generatePositroniumDecayParams: Positronium is prompt photon flags are not set"); - } + if(fPositroniumLifetimes.has_value()) { + params.fLifetimes=fPositroniumLifetimes.value(); + } else { + GateError("GateExtendedVSource::generatePositroniumDecayParams: Positronium lifetimes are not set"); + } - if(fPromptPhotonEnergies.has_value()) { - params.fPromptPhotonEnergy=fPromptPhotonEnergies.value(); - } else { - GateError("GateExtendedVSource::generatePositroniumDecayParams: Prompt photon energies are not set"); + if(fDecayKinds.has_value()) { + params.fDecayKind=fDecayKinds.value(); + } else { + GateError("GateExtendedVSource::generatePositroniumDecayParams: Positronium decay kinds are not set"); + } + + if(fIsPromptPhoton.has_value()) { + params.fIsPromptPhoton=fIsPromptPhoton.value(); + } else { + GateError("GateExtendedVSource::generatePositroniumDecayParams: Positronium is prompt photon flags are not set"); + } + + if(fPromptPhotonEnergies.has_value()) { + params.fPromptPhotonEnergy=fPromptPhotonEnergies.value(); + } else { + GateError("GateExtendedVSource::generatePositroniumDecayParams: Prompt photon energies are not set"); + } } + return params; } From a5f544488647e02d47bab5e421030aae675f988c Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Tue, 4 Nov 2025 18:41:59 +0100 Subject: [PATCH 039/144] Remove commented include --- source/physics/include/GateExtendedVSource.hh | 1 - 1 file changed, 1 deletion(-) diff --git a/source/physics/include/GateExtendedVSource.hh b/source/physics/include/GateExtendedVSource.hh index c2c949213..53b54e8b8 100644 --- a/source/physics/include/GateExtendedVSource.hh +++ b/source/physics/include/GateExtendedVSource.hh @@ -12,7 +12,6 @@ #include "GateVSource.hh" #include "GateExtendedVSourceMessenger.hh" #include "GateGammaEmissionModel.hh" -//#include "GatePositroniumDecayModelParams.hh" /** Author: Mateusz Bała * Email: bala.mateusz@gmail.com From 3356f905f76c3b7fa73c8453b6042a3e2cc11de9 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Tue, 4 Nov 2025 18:43:16 +0100 Subject: [PATCH 040/144] Remove commented code --- source/physics/include/GateExtendedVSource.hh | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/physics/include/GateExtendedVSource.hh b/source/physics/include/GateExtendedVSource.hh index 53b54e8b8..6e354c43b 100644 --- a/source/physics/include/GateExtendedVSource.hh +++ b/source/physics/include/GateExtendedVSource.hh @@ -34,8 +34,6 @@ public: explicit GateExtendedVSource(const G4String& name); virtual ~GateExtendedVSource() = default; - //PositroniumDecayModelParams generatePositroniumDecayParams() const; - /** Generate gammas for event **/ virtual G4int GeneratePrimaries( G4Event* event ) override; From 66cc1eb54933a1ff3537b80b2c65435b26c88f96 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Tue, 4 Nov 2025 18:45:32 +0100 Subject: [PATCH 041/144] Remove repeated line --- source/physics/src/GateExtendedVSource.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/source/physics/src/GateExtendedVSource.cc b/source/physics/src/GateExtendedVSource.cc index 0c77af23e..74f38d626 100644 --- a/source/physics/src/GateExtendedVSource.cc +++ b/source/physics/src/GateExtendedVSource.cc @@ -104,7 +104,6 @@ G4int GateExtendedVSource::GeneratePrimaries(G4Event* event) G4double particle_time = GetTime(); G4ThreeVector particle_position = GetPosDist()->GenerateOne(); ChangeParticlePositionRelativeToAttachedVolume(particle_position); - pModel->GeneratePrimaryVertices(event, particle_time, particle_position); return pModel->GeneratePrimaryVertices(event, particle_time, particle_position); } From 39763f8f60102f3657bf3990ae2f1d953689a71e Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Tue, 4 Nov 2025 18:46:42 +0100 Subject: [PATCH 042/144] Fix missing argument passing in function generating params --- source/physics/src/GateExtendedVSourceMessenger.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/physics/src/GateExtendedVSourceMessenger.cc b/source/physics/src/GateExtendedVSourceMessenger.cc index 27b51f963..0d5a20099 100644 --- a/source/physics/src/GateExtendedVSourceMessenger.cc +++ b/source/physics/src/GateExtendedVSourceMessenger.cc @@ -172,7 +172,7 @@ void GateExtendedVSourceMessenger::SetNewValue( G4UIcommand* command, G4String n PositroniumDecayModelParams GateExtendedVSourceMessenger::generatePositroniumDecayParams(GatePositroniumDecayParamsGenerator::DecayModel model) const { - return fParamGenerator.generatePositroniumDecayParams(); + return fParamGenerator.generatePositroniumDecayParams(model); } From b296291aaf582a9ceae5c58fad50f5f196fda433 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Tue, 4 Nov 2025 19:00:58 +0100 Subject: [PATCH 043/144] Remove include to GatePositroniumDecayModel --- source/physics/src/GateExtendedVSource.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/source/physics/src/GateExtendedVSource.cc b/source/physics/src/GateExtendedVSource.cc index 74f38d626..d5b793fe9 100644 --- a/source/physics/src/GateExtendedVSource.cc +++ b/source/physics/src/GateExtendedVSource.cc @@ -9,7 +9,6 @@ #include "G4Event.hh" #include "GateExtendedVSource.hh" -#include "GatePositroniumDecayModel.hh" #include "GateMiniPositroniumDecayModel.hh" From 16991aa45bbd076963e1f0274b2e0119d2778c54 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Tue, 4 Nov 2025 20:46:07 +0100 Subject: [PATCH 044/144] Add test_GatePositroniumDecayParamsGenerator Those tests are generated by AI. Should be cross-checked. --- ...st_GatePositroniumDecayParamsGenerator.cpp | 148 ++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 tests/test_GatePositroniumDecayParamsGenerator.cpp diff --git a/tests/test_GatePositroniumDecayParamsGenerator.cpp b/tests/test_GatePositroniumDecayParamsGenerator.cpp new file mode 100644 index 000000000..60144eef2 --- /dev/null +++ b/tests/test_GatePositroniumDecayParamsGenerator.cpp @@ -0,0 +1,148 @@ +#include +#include +#include + +#include "GatePositroniumDecayParamsGenerator.hh" +#include "GatePositroniumDecayModelParams.hh" +#include "GateRunManager.hh" +#include "GatePhysicsList.hh" +#include "GateDetectorConstruction.hh" + +#define CHECK(cond, msg) \ + do { \ + if (!(cond)) { \ + std::cerr << "Test failure: " << msg \ + << " (in " << __FUNCTION__ << ", line " << __LINE__ << ")\n"; \ + return false; \ + } \ + } while(0) + +void initializeGateRunManager(GateRunManager* runManager) +{ + GateDetectorConstruction* gateDC = new GateDetectorConstruction(); + runManager->SetUserInitialization(gateDC); + runManager->SetUserInitialization(GatePhysicsList::GetInstance()); + runManager->InitializeAll(); +} + +bool test_para_default() +{ + std::cout << "test_para_default" << std::endl; + GatePositroniumDecayParamsGenerator gen; + auto p = gen.generatePositroniumDecayParams(GatePositroniumDecayParamsGenerator::kParaPositronium); + + CHECK(p.fFractions[0] == 1, "para fraction != 1"); + CHECK(p.fLifetimes[0] == 0.1244f, "para lifetime wrong"); + CHECK(p.fDecayKind[0] == k2Gamma, "para decay kind wrong"); + CHECK(p.fIsPromptPhoton[0] == false, "para prompt flag wrong"); + CHECK(p.fPromptPhotonEnergy[0] == 0.0f, "para prompt energy wrong"); + return true; +} + +bool test_ortho_default() +{ + std::cout << "test_ortho_default" << std::endl; + GatePositroniumDecayParamsGenerator gen; + auto p = gen.generatePositroniumDecayParams(GatePositroniumDecayParamsGenerator::kOrthoPositronium); + + CHECK(p.fFractions[0] == 1, "ortho fraction != 1"); + CHECK(p.fLifetimes[0] == 138.6f, "ortho lifetime wrong"); + CHECK(p.fDecayKind[0] == k3Gamma, "ortho decay kind wrong"); + CHECK(p.fIsPromptPhoton[0] == false, "ortho prompt flag wrong"); + CHECK(p.fPromptPhotonEnergy[0] == 0.0f, "ortho prompt energy wrong"); + return true; +} + +bool test_para_prompt_gamma() +{ + std::cout << "test_para_prompt_gamma" << std::endl; + GatePositroniumDecayParamsGenerator gen; + gen.SetEnableDeexcitation({true}); + gen.SetPromptGammaEnergies({0.511f}); + + auto p = gen.generatePositroniumDecayParams(GatePositroniumDecayParamsGenerator::kParaPositronium); + + CHECK(p.fIsPromptPhoton[0] == true, "para prompt photon flag wrong"); + CHECK(p.fPromptPhotonEnergy[0] == 0.511f, "para prompt photon energy wrong"); + return true; +} + +bool test_positronium_custom() +{ + std::cout << "test_positronium_custom" << std::endl; + GatePositroniumDecayParamsGenerator gen; + + gen.SetPositroniumFraction({0.3f, 0.7f}); + gen.SetPostroniumLifetimes({0.12f, 140.0f}); + gen.SetDecayKinds({k2Gamma, k3Gamma}); + gen.SetEnableDeexcitation({false, true}); + gen.SetPromptGammaEnergies({0.0f, 0.511f}); + + auto p = gen.generatePositroniumDecayParams(GatePositroniumDecayParamsGenerator::kPositronium); + + CHECK(p.fFractions[1] == 0.7f, "PS fraction mismatch"); + CHECK(p.fLifetimes[1] == 140.0f, "PS lifetime mismatch"); + CHECK(p.fDecayKind[1] == k3Gamma, "PS decay kind mismatch"); + CHECK(p.fIsPromptPhoton[1] == true, "PS prompt flag mismatch"); + CHECK(p.fPromptPhotonEnergy[1] == 0.511f, "PS prompt energy mismatch"); + return true; +} + +bool test_missing_params_should_fail() +{ + std::cout << "test_missing_params_should_fail" << std::endl; + GatePositroniumDecayParamsGenerator gen; + + bool caught = false; + try { + auto p = gen.generatePositroniumDecayParams(GatePositroniumDecayParamsGenerator::kPositronium); + } catch (...) { + caught = true; + } + CHECK(caught, "Missing positronium params did NOT throw"); + return true; +} + +bool test_vector_size_mismatch() +{ + std::cout << "test_vector_size_mismatch" << std::endl; + GatePositroniumDecayParamsGenerator gen; + + gen.SetPositroniumFraction({0.5f, 0.5f}); + gen.SetPostroniumLifetimes({0.12f}); // mismatch! + gen.SetDecayKinds({k2Gamma, k3Gamma}); + gen.SetEnableDeexcitation({false, false}); + gen.SetPromptGammaEnergies({0.0f, 0.0f}); + + bool caught = false; + try { + auto p = gen.generatePositroniumDecayParams(); + } catch (...) { + caught = true; + } + CHECK(caught, "Mismatch in vector sizes did NOT throw"); + return true; +} + +int main() +{ + std::unique_ptr runManager(new GateRunManager); + initializeGateRunManager(runManager.get()); + + bool res = true; + res &= test_para_default(); + res &= test_ortho_default(); + res &= test_para_prompt_gamma(); + res &= test_positronium_custom(); + res &= test_missing_params_should_fail(); + res &= test_vector_size_mismatch(); + + if (res) { + std::cout << " All PositroniumDecayParamsGenerator tests passed\n"; + return 0; + } else { + std::cerr << " Some PositroniumDecayParamsGenerator tests failed\n"; + return -1; + } +} + From 24629b3940a3cbe23bf50e2724e3b3a89654b20a Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Tue, 4 Nov 2025 20:56:18 +0100 Subject: [PATCH 045/144] Fix typo in SetPositroniumLifetimes function --- source/physics/include/GatePositroniumDecayParamsGenerator.hh | 2 +- source/physics/src/GateExtendedVSourceMessenger.cc | 3 +-- source/physics/src/GatePositroniumDecayParamsGenerator.cc | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/source/physics/include/GatePositroniumDecayParamsGenerator.hh b/source/physics/include/GatePositroniumDecayParamsGenerator.hh index 7dcd7e5ef..cc8668589 100644 --- a/source/physics/include/GatePositroniumDecayParamsGenerator.hh +++ b/source/physics/include/GatePositroniumDecayParamsGenerator.hh @@ -30,7 +30,7 @@ public: void SetEnableDeexcitation(const std::vector& IsPromptPhoton); void SetPromptGammaEnergies(const std::vector& energies); - void SetPostroniumLifetimes(const std::vector& fPositroniumLifetimes); + void SetPositroniumLifetimes(const std::vector& fPositroniumLifetimes); void SetDecayKinds(const std::vector& decayKinds); void SetPositroniumFraction(const std::vector& positroniumFractions); diff --git a/source/physics/src/GateExtendedVSourceMessenger.cc b/source/physics/src/GateExtendedVSourceMessenger.cc index 0d5a20099..f678969f2 100644 --- a/source/physics/src/GateExtendedVSourceMessenger.cc +++ b/source/physics/src/GateExtendedVSourceMessenger.cc @@ -129,7 +129,7 @@ void GateExtendedVSourceMessenger::SetNewValue( G4UIcommand* command, G4String n while (ss >> num) { lifetimes.push_back(num); } - fParamGenerator.SetPostroniumLifetimes(lifetimes); + fParamGenerator.SetPositroniumLifetimes(lifetimes); } else if(command == upCmdSetIsPromptPhoton.get()) { @@ -169,7 +169,6 @@ void GateExtendedVSourceMessenger::SetNewValue( G4UIcommand* command, G4String n } } - PositroniumDecayModelParams GateExtendedVSourceMessenger::generatePositroniumDecayParams(GatePositroniumDecayParamsGenerator::DecayModel model) const { return fParamGenerator.generatePositroniumDecayParams(model); diff --git a/source/physics/src/GatePositroniumDecayParamsGenerator.cc b/source/physics/src/GatePositroniumDecayParamsGenerator.cc index 6de1271cc..f15f92389 100644 --- a/source/physics/src/GatePositroniumDecayParamsGenerator.cc +++ b/source/physics/src/GatePositroniumDecayParamsGenerator.cc @@ -11,7 +11,7 @@ void GatePositroniumDecayParamsGenerator::SetDecayKinds(const std::vector& positroniumLifetimes) +void GatePositroniumDecayParamsGenerator::SetPositroniumLifetimes(const std::vector& positroniumLifetimes) { fPositroniumLifetimes = positroniumLifetimes; } From 3bd2b3bada90f4a6df61d2908ee34b4a1a352bb0 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Tue, 4 Nov 2025 20:58:27 +0100 Subject: [PATCH 046/144] Fix orthopositronium default lifetime to 142 ns --- .../GatePositroniumDecayParamsGenerator.cc | 2 +- ...st_GatePositroniumDecayParamsGenerator.cpp | 23 ++++++++++++------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/source/physics/src/GatePositroniumDecayParamsGenerator.cc b/source/physics/src/GatePositroniumDecayParamsGenerator.cc index f15f92389..9f99885ad 100644 --- a/source/physics/src/GatePositroniumDecayParamsGenerator.cc +++ b/source/physics/src/GatePositroniumDecayParamsGenerator.cc @@ -43,7 +43,7 @@ PositroniumDecayModelParams GatePositroniumDecayParamsGenerator::generatePositro } if (model == GatePositroniumDecayParamsGenerator::kOrthoPositronium) { params.fFractions= {1}; - params.fLifetimes= {138.6}; // [ns] + params.fLifetimes= {142}; // [ns] params.fDecayKind= {k3Gamma}; if(fIsPromptPhoton.has_value() && fPromptPhotonEnergies.has_value()) { params.fIsPromptPhoton=fIsPromptPhoton.value(); diff --git a/tests/test_GatePositroniumDecayParamsGenerator.cpp b/tests/test_GatePositroniumDecayParamsGenerator.cpp index 60144eef2..2e381dd26 100644 --- a/tests/test_GatePositroniumDecayParamsGenerator.cpp +++ b/tests/test_GatePositroniumDecayParamsGenerator.cpp @@ -46,7 +46,7 @@ bool test_ortho_default() auto p = gen.generatePositroniumDecayParams(GatePositroniumDecayParamsGenerator::kOrthoPositronium); CHECK(p.fFractions[0] == 1, "ortho fraction != 1"); - CHECK(p.fLifetimes[0] == 138.6f, "ortho lifetime wrong"); + CHECK(p.fLifetimes[0] == 142.0f, "ortho lifetime wrong"); CHECK(p.fDecayKind[0] == k3Gamma, "ortho decay kind wrong"); CHECK(p.fIsPromptPhoton[0] == false, "ortho prompt flag wrong"); CHECK(p.fPromptPhotonEnergy[0] == 0.0f, "ortho prompt energy wrong"); @@ -55,15 +55,15 @@ bool test_ortho_default() bool test_para_prompt_gamma() { - std::cout << "test_para_prompt_gamma" << std::endl; + std::cout << "test_prompt_gamma" << std::endl; GatePositroniumDecayParamsGenerator gen; gen.SetEnableDeexcitation({true}); - gen.SetPromptGammaEnergies({0.511f}); + gen.SetPromptGammaEnergies({1.2f}); auto p = gen.generatePositroniumDecayParams(GatePositroniumDecayParamsGenerator::kParaPositronium); CHECK(p.fIsPromptPhoton[0] == true, "para prompt photon flag wrong"); - CHECK(p.fPromptPhotonEnergy[0] == 0.511f, "para prompt photon energy wrong"); + CHECK(p.fPromptPhotonEnergy[0] == 1.2f, "prompt photon energy wrong"); return true; } @@ -73,18 +73,24 @@ bool test_positronium_custom() GatePositroniumDecayParamsGenerator gen; gen.SetPositroniumFraction({0.3f, 0.7f}); - gen.SetPostroniumLifetimes({0.12f, 140.0f}); + gen.SetPositroniumLifetimes({0.12f, 140.0f}); gen.SetDecayKinds({k2Gamma, k3Gamma}); gen.SetEnableDeexcitation({false, true}); - gen.SetPromptGammaEnergies({0.0f, 0.511f}); + gen.SetPromptGammaEnergies({0.0f, 1.2f}); auto p = gen.generatePositroniumDecayParams(GatePositroniumDecayParamsGenerator::kPositronium); + CHECK(p.fFractions[0] == 0.3f, "PS fraction mismatch"); + CHECK(p.fLifetimes[0] == 0.12f, "PS lifetime mismatch"); + CHECK(p.fDecayKind[0] == k2Gamma, "PS decay kind mismatch"); + CHECK(p.fIsPromptPhoton[0] == false, "PS prompt flag mismatch"); + CHECK(p.fPromptPhotonEnergy[0] == 0.0f, "PS prompt energy mismatch"); + CHECK(p.fFractions[1] == 0.7f, "PS fraction mismatch"); CHECK(p.fLifetimes[1] == 140.0f, "PS lifetime mismatch"); CHECK(p.fDecayKind[1] == k3Gamma, "PS decay kind mismatch"); CHECK(p.fIsPromptPhoton[1] == true, "PS prompt flag mismatch"); - CHECK(p.fPromptPhotonEnergy[1] == 0.511f, "PS prompt energy mismatch"); + CHECK(p.fPromptPhotonEnergy[1] == 1.2f, "PS prompt energy mismatch"); return true; } @@ -109,7 +115,7 @@ bool test_vector_size_mismatch() GatePositroniumDecayParamsGenerator gen; gen.SetPositroniumFraction({0.5f, 0.5f}); - gen.SetPostroniumLifetimes({0.12f}); // mismatch! + gen.SetPositroniumLifetimes({0.12f}); // mismatch! gen.SetDecayKinds({k2Gamma, k3Gamma}); gen.SetEnableDeexcitation({false, false}); gen.SetPromptGammaEnergies({0.0f, 0.0f}); @@ -118,6 +124,7 @@ bool test_vector_size_mismatch() try { auto p = gen.generatePositroniumDecayParams(); } catch (...) { + std::cout << "we caught the exception" << std::endl; caught = true; } CHECK(caught, "Mismatch in vector sizes did NOT throw"); From 33fa86e1e4554791741165146feda7d9a3f6966f Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Tue, 4 Nov 2025 21:29:05 +0100 Subject: [PATCH 047/144] Add comment that the failure is not caught, cause GateError returns exit(-1) --- tests/test_GatePositroniumDecayParamsGenerator.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/tests/test_GatePositroniumDecayParamsGenerator.cpp b/tests/test_GatePositroniumDecayParamsGenerator.cpp index 2e381dd26..e8ee594ac 100644 --- a/tests/test_GatePositroniumDecayParamsGenerator.cpp +++ b/tests/test_GatePositroniumDecayParamsGenerator.cpp @@ -98,17 +98,14 @@ bool test_missing_params_should_fail() { std::cout << "test_missing_params_should_fail" << std::endl; GatePositroniumDecayParamsGenerator gen; - - bool caught = false; - try { - auto p = gen.generatePositroniumDecayParams(GatePositroniumDecayParamsGenerator::kPositronium); - } catch (...) { - caught = true; - } - CHECK(caught, "Missing positronium params did NOT throw"); + std::cout << "test_missing_params_should_fail" << std::endl; + std::cout << "Expecting program termination..." << std::endl; + //auto p = gen.generatePositroniumDecayParams(GatePositroniumDecayParamsGenerator::kPositronium); + // This test cannot run — GateError calls exit(-1) return true; } + bool test_vector_size_mismatch() { std::cout << "test_vector_size_mismatch" << std::endl; From 3bcd946cdb26f8989fc17d862b27f98944aa64a3 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Tue, 4 Nov 2025 21:53:51 +0100 Subject: [PATCH 048/144] Improve and unify tests of GatePositronium Also, extract the CHECK macro to a dedicated include header. --- tests/TestingTools.h | 15 ++++ tests/test_GatePositronium.cpp | 80 ++++++++++++------- ...st_GatePositroniumDecayParamsGenerator.cpp | 10 +-- 3 files changed, 69 insertions(+), 36 deletions(-) create mode 100644 tests/TestingTools.h diff --git a/tests/TestingTools.h b/tests/TestingTools.h new file mode 100644 index 000000000..7556ac205 --- /dev/null +++ b/tests/TestingTools.h @@ -0,0 +1,15 @@ +#ifndef TestingTools_h +#define TestingTools_h + + +// ---- Test helper ---- +#define CHECK(cond, msg) \ + do { \ + if (!(cond)) { \ + std::cerr << "Test failure: " << msg \ + << " (in " << __FUNCTION__ << ", line " << __LINE__ << ")" << "\n"; \ + return false; \ + } \ + } while(0) + +#endif diff --git a/tests/test_GatePositronium.cpp b/tests/test_GatePositronium.cpp index bdc802ec9..9e8f4a7c1 100644 --- a/tests/test_GatePositronium.cpp +++ b/tests/test_GatePositronium.cpp @@ -9,6 +9,9 @@ #include "GatePhysicsList.hh" #include "GateDetectorConstruction.hh" +#include "TestingTools.h" + + void initializeGateRunManager(GateRunManager* runManager) { // Set the DetectorConstruction @@ -20,38 +23,45 @@ void initializeGateRunManager(GateRunManager* runManager) runManager->InitializeAll(); } -bool run_tests() +bool test_pPs_properties() { - GatePositronium pPs("pPs", 0.1); - if (pPs.GetLifeTime() != 0.1) { - return false; - } - if (pPs.GetName() != "pPs") { - return false; - } - if (pPs.GetAnnihilationGammasNumber() != 2) { - return false; - } + std::cout << "test_pPs_properties\n"; + GatePositronium pPs("pPs", 0.1 * ns); + + CHECK(pPs.GetName() == "pPs", "Name mismatch"); + CHECK(std::abs(pPs.GetLifeTime() - 0.1 * ns) < 1e-12, "Lifetime mismatch"); + CHECK(pPs.GetAnnihilationGammasNumber() == 2, "pPs should decay to 2 gammas"); + return true; } -bool run_tests2() +bool test_oPs_properties() { - GatePositronium oPs("oPs", 1000); - if (oPs.GetLifeTime() != 1000) { - return false; - } - if (oPs.GetName() != "oPs") { - return false; - } - if (oPs.GetAnnihilationGammasNumber() != 3) { - return false; - } + std::cout << "test_oPs_properties\n"; + GatePositronium oPs("oPs", 142.0 * ns); + + CHECK(oPs.GetName() == "oPs", "Name mismatch"); + CHECK(oPs.GetAnnihilationGammasNumber() == 3, "oPs should decay to 3 gammas"); + + return true; +} + +bool test_move_semantics() +{ + std::cout << "test_move_semantics\n"; + + GatePositronium oPs("oPs", 1000); + GatePositronium moved = std::move(oPs); + + CHECK(moved.GetName() == "oPs", "move: name lost"); + CHECK(moved.GetAnnihilationGammasNumber() == 3, "move: wrong gamma count"); + return true; } + /// todo: add test to check what happens if pPs with 3 ? -bool run_tests3() +bool test_in_vector() { std::vector vect; vect.push_back(std::move(GatePositronium("oPs", 1000))); @@ -100,6 +110,18 @@ bool run_tests3() return true; } +bool test_decay_products() +{ + std::cout << "test_decay_products\n"; + GatePositronium pPs("pPs", 0.1 * ns); + + auto* products = pPs.GetDecayProducts(); + CHECK(products != nullptr, "DecayProducts should not be null"); + CHECK(products->entries() == 2, "pPs should produce 2 daughters"); + + return true; +} + int main() { @@ -107,12 +129,14 @@ int main() std::unique_ptr runManager(new GateRunManager); initializeGateRunManager(runManager.get()); - bool res = true; - res = res & run_tests(); - res = res & run_tests2(); - res = res & run_tests3(); + bool ok = true; + ok = ok & test_pPs_properties(); + ok = ok & test_oPs_properties(); + ok = ok & test_move_semantics(); + ok = ok & test_in_vector(); + ok = ok & test_decay_products(); - if (res) { + if (ok) { std::cout << "All tests have passed" << std::endl; return 0; } else { diff --git a/tests/test_GatePositroniumDecayParamsGenerator.cpp b/tests/test_GatePositroniumDecayParamsGenerator.cpp index e8ee594ac..21ea0b067 100644 --- a/tests/test_GatePositroniumDecayParamsGenerator.cpp +++ b/tests/test_GatePositroniumDecayParamsGenerator.cpp @@ -8,14 +8,8 @@ #include "GatePhysicsList.hh" #include "GateDetectorConstruction.hh" -#define CHECK(cond, msg) \ - do { \ - if (!(cond)) { \ - std::cerr << "Test failure: " << msg \ - << " (in " << __FUNCTION__ << ", line " << __LINE__ << ")\n"; \ - return false; \ - } \ - } while(0) +#include "TestingTools.h" + void initializeGateRunManager(GateRunManager* runManager) { From 359abaa983164bb126c090e0e252b5890ed9a21a Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Tue, 4 Nov 2025 21:59:37 +0100 Subject: [PATCH 049/144] Remove unused field --- source/physics/include/GatePositronium.hh | 1 - 1 file changed, 1 deletion(-) diff --git a/source/physics/include/GatePositronium.hh b/source/physics/include/GatePositronium.hh index 8d5ee69ca..c91b73ddc 100644 --- a/source/physics/include/GatePositronium.hh +++ b/source/physics/include/GatePositronium.hh @@ -29,7 +29,6 @@ public: private: G4String fName; G4double fLifeTime = 0.0; //[ns] - G4int fAnnihilationGammasNumber = 0; GatePositroniumDecayChannel *pDecayChannel = nullptr; // Todo check who owns pDecayChannel? }; #endif From f9e7cc94424676023ab15eecc1dbe63b7c72b000 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 14 Nov 2025 12:08:47 +0100 Subject: [PATCH 050/144] Rename SetEnableDeexcitation to SetEnablePromptGamma --- .../physics/include/GatePositroniumDecayParamsGenerator.hh | 2 +- source/physics/src/GateExtendedVSourceMessenger.cc | 2 +- source/physics/src/GatePositroniumDecayParamsGenerator.cc | 2 +- tests/test_GatePositroniumDecayParamsGenerator.cpp | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/source/physics/include/GatePositroniumDecayParamsGenerator.hh b/source/physics/include/GatePositroniumDecayParamsGenerator.hh index cc8668589..96ae58d2b 100644 --- a/source/physics/include/GatePositroniumDecayParamsGenerator.hh +++ b/source/physics/include/GatePositroniumDecayParamsGenerator.hh @@ -28,7 +28,7 @@ public: GatePositroniumDecayParamsGenerator()= default; virtual ~GatePositroniumDecayParamsGenerator()=default; - void SetEnableDeexcitation(const std::vector& IsPromptPhoton); + void SetEnablePromptGamma(const std::vector& IsPromptPhoton); void SetPromptGammaEnergies(const std::vector& energies); void SetPositroniumLifetimes(const std::vector& fPositroniumLifetimes); void SetDecayKinds(const std::vector& decayKinds); diff --git a/source/physics/src/GateExtendedVSourceMessenger.cc b/source/physics/src/GateExtendedVSourceMessenger.cc index f678969f2..7d15867ae 100644 --- a/source/physics/src/GateExtendedVSourceMessenger.cc +++ b/source/physics/src/GateExtendedVSourceMessenger.cc @@ -139,7 +139,7 @@ void GateExtendedVSourceMessenger::SetNewValue( G4UIcommand* command, G4String n while (ss >> std::boolalpha >> flag) { isPromptPhoton.push_back(flag); } - fParamGenerator.SetEnableDeexcitation(isPromptPhoton); + fParamGenerator.SetEnablePromptGamma(isPromptPhoton); } else if(command == upCmdSetPromptPhotonEnergies.get()) { std::vector promptPhotonEnergies; diff --git a/source/physics/src/GatePositroniumDecayParamsGenerator.cc b/source/physics/src/GatePositroniumDecayParamsGenerator.cc index 9f99885ad..cd7450c12 100644 --- a/source/physics/src/GatePositroniumDecayParamsGenerator.cc +++ b/source/physics/src/GatePositroniumDecayParamsGenerator.cc @@ -3,7 +3,7 @@ #include "GatePositroniumDecayParamsGenerator.hh" #include -void GatePositroniumDecayParamsGenerator::SetEnableDeexcitation(const std::vector& isPromptPhoton) +void GatePositroniumDecayParamsGenerator::SetEnablePromptGamma(const std::vector& isPromptPhoton) { fIsPromptPhoton = isPromptPhoton; } diff --git a/tests/test_GatePositroniumDecayParamsGenerator.cpp b/tests/test_GatePositroniumDecayParamsGenerator.cpp index 21ea0b067..6c88fbcd6 100644 --- a/tests/test_GatePositroniumDecayParamsGenerator.cpp +++ b/tests/test_GatePositroniumDecayParamsGenerator.cpp @@ -51,7 +51,7 @@ bool test_para_prompt_gamma() { std::cout << "test_prompt_gamma" << std::endl; GatePositroniumDecayParamsGenerator gen; - gen.SetEnableDeexcitation({true}); + gen.SetEnablePromptGamma({true}); gen.SetPromptGammaEnergies({1.2f}); auto p = gen.generatePositroniumDecayParams(GatePositroniumDecayParamsGenerator::kParaPositronium); @@ -69,7 +69,7 @@ bool test_positronium_custom() gen.SetPositroniumFraction({0.3f, 0.7f}); gen.SetPositroniumLifetimes({0.12f, 140.0f}); gen.SetDecayKinds({k2Gamma, k3Gamma}); - gen.SetEnableDeexcitation({false, true}); + gen.SetEnablePromptGamma({false, true}); gen.SetPromptGammaEnergies({0.0f, 1.2f}); auto p = gen.generatePositroniumDecayParams(GatePositroniumDecayParamsGenerator::kPositronium); @@ -108,7 +108,7 @@ bool test_vector_size_mismatch() gen.SetPositroniumFraction({0.5f, 0.5f}); gen.SetPositroniumLifetimes({0.12f}); // mismatch! gen.SetDecayKinds({k2Gamma, k3Gamma}); - gen.SetEnableDeexcitation({false, false}); + gen.SetEnablePromptGamma({false, false}); gen.SetPromptGammaEnergies({0.0f, 0.0f}); bool caught = false; From 9c006671248421af1039e14655d5a6d42aaceab4 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 14 Nov 2025 12:26:12 +0100 Subject: [PATCH 051/144] Change fPromptPhoton to PromptGamma for consistency --- .../GatePositroniumDecayModelParams.hh | 4 +- .../GatePositroniumDecayParamsGenerator.hh | 4 +- .../src/GateMiniPositroniumDecayModel.cc | 4 +- .../GatePositroniumDecayParamsGenerator.cc | 46 +++++++++---------- ...st_GatePositroniumDecayParamsGenerator.cpp | 20 ++++---- 5 files changed, 39 insertions(+), 39 deletions(-) diff --git a/source/physics/include/GatePositroniumDecayModelParams.hh b/source/physics/include/GatePositroniumDecayModelParams.hh index 7fe4ea1fa..047a64fd9 100644 --- a/source/physics/include/GatePositroniumDecayModelParams.hh +++ b/source/physics/include/GatePositroniumDecayModelParams.hh @@ -15,8 +15,8 @@ struct PositroniumDecayModelParams { std::vector fFractions; std::vector fLifetimes; - std::vector fIsPromptPhoton; - std::vector fPromptPhotonEnergy; + std::vector fIsPromptGamma; + std::vector fPromptGammaEnergy; std::vector fDecayKind; }; diff --git a/source/physics/include/GatePositroniumDecayParamsGenerator.hh b/source/physics/include/GatePositroniumDecayParamsGenerator.hh index 96ae58d2b..928931c91 100644 --- a/source/physics/include/GatePositroniumDecayParamsGenerator.hh +++ b/source/physics/include/GatePositroniumDecayParamsGenerator.hh @@ -39,8 +39,8 @@ public: private: std::optional> fPositroniumFractions; std::optional> fPositroniumLifetimes; - std::optional> fIsPromptPhoton; - std::optional> fPromptPhotonEnergies; + std::optional> fIsPromptGamma; + std::optional> fPromptGammaEnergies; std::optional> fDecayKinds; }; #endif diff --git a/source/physics/src/GateMiniPositroniumDecayModel.cc b/source/physics/src/GateMiniPositroniumDecayModel.cc index 0ac8bf975..35d34ff16 100644 --- a/source/physics/src/GateMiniPositroniumDecayModel.cc +++ b/source/physics/src/GateMiniPositroniumDecayModel.cc @@ -52,7 +52,7 @@ G4int MiniPositroniumDecayModel::GeneratePrimaryVertices(G4Event* event, G4doubl auto decayIndex = MiniPositroniumDecayModel::getPositroniumDecayIndex(fModelParams.fFractions); G4int number_of_vertices = 1; - if(fModelParams.fIsPromptPhoton[decayIndex]) + if(fModelParams.fIsPromptGamma[decayIndex]) { ++number_of_vertices; event->AddPrimaryVertex(GetPrimaryVertexFromDeexcitation(particle_time, particle_position, decayIndex)); @@ -63,7 +63,7 @@ G4int MiniPositroniumDecayModel::GeneratePrimaryVertices(G4Event* event, G4doubl G4PrimaryParticle* MiniPositroniumDecayModel::GetGammaFromDeexcitation(int decayIndex) { - G4PrimaryParticle* gamma = GetSingleGamma(fModelParams.fPromptPhotonEnergy[decayIndex]); + G4PrimaryParticle* gamma = GetSingleGamma(fModelParams.fPromptGammaEnergy[decayIndex]); gamma->SetUserInformation( GetPrimaryParticleInformation( gamma, GateEmittedGammaInformation::GammaKind::Prompt ) ); return gamma; } diff --git a/source/physics/src/GatePositroniumDecayParamsGenerator.cc b/source/physics/src/GatePositroniumDecayParamsGenerator.cc index cd7450c12..c05eba699 100644 --- a/source/physics/src/GatePositroniumDecayParamsGenerator.cc +++ b/source/physics/src/GatePositroniumDecayParamsGenerator.cc @@ -1,11 +1,11 @@ +#include -#include "GateMessageManager.hh" +#include "GateMessageManager.hh" #include "GatePositroniumDecayParamsGenerator.hh" -#include -void GatePositroniumDecayParamsGenerator::SetEnablePromptGamma(const std::vector& isPromptPhoton) +void GatePositroniumDecayParamsGenerator::SetEnablePromptGamma(const std::vector& isPromptGamma) { - fIsPromptPhoton = isPromptPhoton; + fIsPromptGamma = isPromptGamma; } void GatePositroniumDecayParamsGenerator::SetDecayKinds(const std::vector& decayKinds) { @@ -17,7 +17,7 @@ void GatePositroniumDecayParamsGenerator::SetPositroniumLifetimes(const std::vec } void GatePositroniumDecayParamsGenerator::SetPromptGammaEnergies(const std::vector& energies) { - fPromptPhotonEnergies = energies; + fPromptGammaEnergies = energies; } void GatePositroniumDecayParamsGenerator::SetPositroniumFraction(const std::vector& positroniumFractions) { @@ -31,28 +31,28 @@ PositroniumDecayModelParams GatePositroniumDecayParamsGenerator::generatePositro params.fFractions= {1}; params.fLifetimes= {0.1244}; // [ns] params.fDecayKind= {k2Gamma}; - if(fIsPromptPhoton.has_value() && fPromptPhotonEnergies.has_value()) { - params.fIsPromptPhoton=fIsPromptPhoton.value(); - assert(params.fIsPromptPhoton.size()==1); - params.fPromptPhotonEnergy=fPromptPhotonEnergies.value(); - assert(params.fPromptPhotonEnergy.size()==1); + if(fIsPromptGamma.has_value() && fPromptGammaEnergies.has_value()) { + params.fIsPromptGamma=fIsPromptGamma.value(); + assert(params.fIsPromptGamma.size()==1); + params.fPromptGammaEnergy=fPromptGammaEnergies.value(); + assert(params.fPromptGammaEnergy.size()==1); } else { - params.fIsPromptPhoton={false}; - params.fPromptPhotonEnergy ={0.0}; + params.fIsPromptGamma={false}; + params.fPromptGammaEnergy ={0.0}; } } if (model == GatePositroniumDecayParamsGenerator::kOrthoPositronium) { params.fFractions= {1}; params.fLifetimes= {142}; // [ns] params.fDecayKind= {k3Gamma}; - if(fIsPromptPhoton.has_value() && fPromptPhotonEnergies.has_value()) { - params.fIsPromptPhoton=fIsPromptPhoton.value(); - assert(params.fIsPromptPhoton.size()==1); - params.fPromptPhotonEnergy=fPromptPhotonEnergies.value(); - assert(params.fPromptPhotonEnergy.size()==1); + if(fIsPromptGamma.has_value() && fPromptGammaEnergies.has_value()) { + params.fIsPromptGamma=fIsPromptGamma.value(); + assert(params.fIsPromptGamma.size()==1); + params.fPromptGammaEnergy=fPromptGammaEnergies.value(); + assert(params.fPromptGammaEnergy.size()==1); } else { - params.fIsPromptPhoton={false}; - params.fPromptPhotonEnergy ={0.0}; + params.fIsPromptGamma={false}; + params.fPromptGammaEnergy ={0.0}; } } @@ -75,14 +75,14 @@ PositroniumDecayModelParams GatePositroniumDecayParamsGenerator::generatePositro GateError("GateExtendedVSource::generatePositroniumDecayParams: Positronium decay kinds are not set"); } - if(fIsPromptPhoton.has_value()) { - params.fIsPromptPhoton=fIsPromptPhoton.value(); + if(fIsPromptGamma.has_value()) { + params.fIsPromptGamma=fIsPromptGamma.value(); } else { GateError("GateExtendedVSource::generatePositroniumDecayParams: Positronium is prompt photon flags are not set"); } - if(fPromptPhotonEnergies.has_value()) { - params.fPromptPhotonEnergy=fPromptPhotonEnergies.value(); + if(fPromptGammaEnergies.has_value()) { + params.fPromptGammaEnergy=fPromptGammaEnergies.value(); } else { GateError("GateExtendedVSource::generatePositroniumDecayParams: Prompt photon energies are not set"); } diff --git a/tests/test_GatePositroniumDecayParamsGenerator.cpp b/tests/test_GatePositroniumDecayParamsGenerator.cpp index 6c88fbcd6..3d13f5aec 100644 --- a/tests/test_GatePositroniumDecayParamsGenerator.cpp +++ b/tests/test_GatePositroniumDecayParamsGenerator.cpp @@ -28,8 +28,8 @@ bool test_para_default() CHECK(p.fFractions[0] == 1, "para fraction != 1"); CHECK(p.fLifetimes[0] == 0.1244f, "para lifetime wrong"); CHECK(p.fDecayKind[0] == k2Gamma, "para decay kind wrong"); - CHECK(p.fIsPromptPhoton[0] == false, "para prompt flag wrong"); - CHECK(p.fPromptPhotonEnergy[0] == 0.0f, "para prompt energy wrong"); + CHECK(p.fIsPromptGamma[0] == false, "para prompt flag wrong"); + CHECK(p.fPromptGammaEnergy[0] == 0.0f, "para prompt energy wrong"); return true; } @@ -42,8 +42,8 @@ bool test_ortho_default() CHECK(p.fFractions[0] == 1, "ortho fraction != 1"); CHECK(p.fLifetimes[0] == 142.0f, "ortho lifetime wrong"); CHECK(p.fDecayKind[0] == k3Gamma, "ortho decay kind wrong"); - CHECK(p.fIsPromptPhoton[0] == false, "ortho prompt flag wrong"); - CHECK(p.fPromptPhotonEnergy[0] == 0.0f, "ortho prompt energy wrong"); + CHECK(p.fIsPromptGamma[0] == false, "ortho prompt flag wrong"); + CHECK(p.fPromptGammaEnergy[0] == 0.0f, "ortho prompt energy wrong"); return true; } @@ -56,8 +56,8 @@ bool test_para_prompt_gamma() auto p = gen.generatePositroniumDecayParams(GatePositroniumDecayParamsGenerator::kParaPositronium); - CHECK(p.fIsPromptPhoton[0] == true, "para prompt photon flag wrong"); - CHECK(p.fPromptPhotonEnergy[0] == 1.2f, "prompt photon energy wrong"); + CHECK(p.fIsPromptGamma[0] == true, "para prompt photon flag wrong"); + CHECK(p.fPromptGammaEnergy[0] == 1.2f, "prompt photon energy wrong"); return true; } @@ -77,14 +77,14 @@ bool test_positronium_custom() CHECK(p.fFractions[0] == 0.3f, "PS fraction mismatch"); CHECK(p.fLifetimes[0] == 0.12f, "PS lifetime mismatch"); CHECK(p.fDecayKind[0] == k2Gamma, "PS decay kind mismatch"); - CHECK(p.fIsPromptPhoton[0] == false, "PS prompt flag mismatch"); - CHECK(p.fPromptPhotonEnergy[0] == 0.0f, "PS prompt energy mismatch"); + CHECK(p.fIsPromptGamma[0] == false, "PS prompt flag mismatch"); + CHECK(p.fPromptGammaEnergy[0] == 0.0f, "PS prompt energy mismatch"); CHECK(p.fFractions[1] == 0.7f, "PS fraction mismatch"); CHECK(p.fLifetimes[1] == 140.0f, "PS lifetime mismatch"); CHECK(p.fDecayKind[1] == k3Gamma, "PS decay kind mismatch"); - CHECK(p.fIsPromptPhoton[1] == true, "PS prompt flag mismatch"); - CHECK(p.fPromptPhotonEnergy[1] == 1.2f, "PS prompt energy mismatch"); + CHECK(p.fIsPromptGamma[1] == true, "PS prompt flag mismatch"); + CHECK(p.fPromptGammaEnergy[1] == 1.2f, "PS prompt energy mismatch"); return true; } From 21843699020e1a5145aae0b7fb30919bb82f0aae Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 14 Nov 2025 13:25:27 +0100 Subject: [PATCH 052/144] Add checks if the number of parameters provides are equal Also, provided commented tests of those cases. The tests are commented because GateError returns exit(-1) and effectively do not permit the proper test running. --- .../GatePositroniumDecayParamsGenerator.cc | 22 +++++++-- ...st_GatePositroniumDecayParamsGenerator.cpp | 48 +++++++++++-------- 2 files changed, 45 insertions(+), 25 deletions(-) diff --git a/source/physics/src/GatePositroniumDecayParamsGenerator.cc b/source/physics/src/GatePositroniumDecayParamsGenerator.cc index c05eba699..d8a6d4dd8 100644 --- a/source/physics/src/GatePositroniumDecayParamsGenerator.cc +++ b/source/physics/src/GatePositroniumDecayParamsGenerator.cc @@ -60,33 +60,45 @@ PositroniumDecayModelParams GatePositroniumDecayParamsGenerator::generatePositro if(fPositroniumFractions.has_value()) { params.fFractions=fPositroniumFractions.value(); } else { - GateError("GateExtendedVSource::generatePositroniumDecayParams: Positronium decay fractions are not set"); + GateError("GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams: Positronium decay fractions are not set"); } if(fPositroniumLifetimes.has_value()) { params.fLifetimes=fPositroniumLifetimes.value(); } else { - GateError("GateExtendedVSource::generatePositroniumDecayParams: Positronium lifetimes are not set"); + GateError("GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams: Positronium lifetimes are not set"); } if(fDecayKinds.has_value()) { params.fDecayKind=fDecayKinds.value(); } else { - GateError("GateExtendedVSource::generatePositroniumDecayParams: Positronium decay kinds are not set"); + GateError("GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams: Positronium decay kinds are not set"); } if(fIsPromptGamma.has_value()) { params.fIsPromptGamma=fIsPromptGamma.value(); } else { - GateError("GateExtendedVSource::generatePositroniumDecayParams: Positronium is prompt photon flags are not set"); + GateError("GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams: Positronium is prompt gamma flags are not set"); } if(fPromptGammaEnergies.has_value()) { params.fPromptGammaEnergy=fPromptGammaEnergies.value(); } else { - GateError("GateExtendedVSource::generatePositroniumDecayParams: Prompt photon energies are not set"); + GateError("GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams: Prompt gamma energies are not set"); } } + auto ref_param_number = params.fDecayKind.size(); + bool size_mismatch = (ref_param_number != params.fFractions.size()) || + (ref_param_number != params.fIsPromptGamma.size()) || + (ref_param_number != params.fLifetimes.size()) || + (ref_param_number != params.fPromptGammaEnergy.size()); + if (size_mismatch) { + GateError( + "GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams: " + "number of provided parameters in Fractions, PromptGamma, Lifetimes, " + "Gamma Energies are not the same"); + } + return params; } diff --git a/tests/test_GatePositroniumDecayParamsGenerator.cpp b/tests/test_GatePositroniumDecayParamsGenerator.cpp index 3d13f5aec..bf0272d31 100644 --- a/tests/test_GatePositroniumDecayParamsGenerator.cpp +++ b/tests/test_GatePositroniumDecayParamsGenerator.cpp @@ -89,36 +89,44 @@ bool test_positronium_custom() } bool test_missing_params_should_fail() +// This test cannot run — GateError calls exit(-1) { - std::cout << "test_missing_params_should_fail" << std::endl; GatePositroniumDecayParamsGenerator gen; std::cout << "test_missing_params_should_fail" << std::endl; - std::cout << "Expecting program termination..." << std::endl; - //auto p = gen.generatePositroniumDecayParams(GatePositroniumDecayParamsGenerator::kPositronium); - // This test cannot run — GateError calls exit(-1) + std::cout << "but we cannot run it because GateError() calls exit(-1)" << std::endl; + //bool caught = false; + //try { + //auto p = gen.generatePositroniumDecayParams(GatePositroniumDecayParamsGenerator::kPositronium); + //} catch (...) { + //std::cout << "we caught the exception" << std::endl; + //caught = true; + //} + //CHECK(caught, "Missing parameters did NOT throw"); return true; } bool test_vector_size_mismatch() +// This test cannot run — GateError calls exit(-1) { std::cout << "test_vector_size_mismatch" << std::endl; - GatePositroniumDecayParamsGenerator gen; - - gen.SetPositroniumFraction({0.5f, 0.5f}); - gen.SetPositroniumLifetimes({0.12f}); // mismatch! - gen.SetDecayKinds({k2Gamma, k3Gamma}); - gen.SetEnablePromptGamma({false, false}); - gen.SetPromptGammaEnergies({0.0f, 0.0f}); - - bool caught = false; - try { - auto p = gen.generatePositroniumDecayParams(); - } catch (...) { - std::cout << "we caught the exception" << std::endl; - caught = true; - } - CHECK(caught, "Mismatch in vector sizes did NOT throw"); + std::cout << "but we cannot run it because GateError() calls exit(-1)" << std::endl; + + //GatePositroniumDecayParamsGenerator gen; + //gen.SetPositroniumFraction({0.5f, 0.5f}); + //gen.SetPositroniumLifetimes({0.12f}); // mismatch! + //gen.SetDecayKinds({k2Gamma, k3Gamma}); + //gen.SetEnablePromptGamma({false, false}); + //gen.SetPromptGammaEnergies({0.0f, 0.0f}); + + //bool caught = false; + //try { + //auto p = gen.generatePositroniumDecayParams(); + //} catch (...) { + //std::cout << "we caught the exception" << std::endl; + //caught = true; + //} + //CHECK(caught, "Mismatch in vector sizes did NOT throw"); return true; } From fdcd801bb1ca9d1662ba726379b20e9332957279 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 14 Nov 2025 13:39:46 +0100 Subject: [PATCH 053/144] Remove setting seed It just overwrites the main setSeed command. --- source/physics/include/GateExtendedVSource.hh | 2 -- source/physics/include/GateExtendedVSourceMessenger.hh | 1 - source/physics/include/GateGammaEmissionModel.hh | 7 ------- source/physics/src/GateExtendedVSource.cc | 6 ------ source/physics/src/GateExtendedVSourceMessenger.cc | 5 ----- source/physics/src/GateGammaEmissionModel.cc | 8 -------- 6 files changed, 29 deletions(-) diff --git a/source/physics/include/GateExtendedVSource.hh b/source/physics/include/GateExtendedVSource.hh index 6e354c43b..7dc8738f6 100644 --- a/source/physics/include/GateExtendedVSource.hh +++ b/source/physics/include/GateExtendedVSource.hh @@ -47,7 +47,6 @@ public: /** Set single gamma kinetic energy **/ void SetEmissionEnergy(G4double energy); - void SetSeed(G4long seed); protected: /** Set model used for this source. If is not defined then this class will behave like GateVSource. @@ -67,7 +66,6 @@ public: std::optional fFixedEmissionDirection; std::optional fEnableFixedEmissionDirection; std::optional fEmissionEnergy; - std::optional fSeed; //Set by PrepareModel() and used in GeneratePrimaries() G4bool fBehaveLikeVSource = false; diff --git a/source/physics/include/GateExtendedVSourceMessenger.hh b/source/physics/include/GateExtendedVSourceMessenger.hh index f625e7172..d26471e31 100644 --- a/source/physics/include/GateExtendedVSourceMessenger.hh +++ b/source/physics/include/GateExtendedVSourceMessenger.hh @@ -53,7 +53,6 @@ class GateExtendedVSourceMessenger: public GateVSourceMessenger std::unique_ptr upCmdSetFixedEmissionDirection; std::unique_ptr upCmdSetEnableFixedEmissionDirection; std::unique_ptr upCmdSetEmissionEnergy; - std::unique_ptr upCmdSetSeed; std::unique_ptr upCmdSetPositroniumLifetime; std::unique_ptr upCmdSetPromptGammaEnergy; std::unique_ptr upCmdSetPositroniumFraction; diff --git a/source/physics/include/GateGammaEmissionModel.hh b/source/physics/include/GateGammaEmissionModel.hh index dfb218c77..72c52f94b 100644 --- a/source/physics/include/GateGammaEmissionModel.hh +++ b/source/physics/include/GateGammaEmissionModel.hh @@ -37,13 +37,6 @@ class GateGammaEmissionModel **/ G4double GetEmissionEnergy() const; - /** Set seed for generators from "Randomize.hh" - **/ - void SetSeed( G4long seed ); - /** Get seed for generators from "Randomize.hh" - **/ - G4long GetSeed() const; - /** Generate single vertex with single gamma **/ virtual G4int GeneratePrimaryVertices(G4Event* event, G4double& particle_time, G4ThreeVector& particle_position); diff --git a/source/physics/src/GateExtendedVSource.cc b/source/physics/src/GateExtendedVSource.cc index d5b793fe9..8e338e570 100644 --- a/source/physics/src/GateExtendedVSource.cc +++ b/source/physics/src/GateExtendedVSource.cc @@ -46,9 +46,6 @@ void GateExtendedVSource::SetEnableFixedEmissionDirection(G4bool enable_fixed_em void GateExtendedVSource::SetEmissionEnergy(G4double energy) { fEmissionEnergy =energy; } -void GateExtendedVSource::SetSeed(G4long seed) { fSeed = seed; } - - void GateExtendedVSource::PrepareModel() { SetModel(GetType()); @@ -90,9 +87,6 @@ void GateExtendedVSource::PrepareModel() if (fEmissionEnergy.has_value()) { pModel->SetEmissionEnergy(fEmissionEnergy.value()); } - if (fSeed.has_value()) { - pModel->SetSeed(fSeed.value()); - } } G4int GateExtendedVSource::GeneratePrimaries(G4Event* event) diff --git a/source/physics/src/GateExtendedVSourceMessenger.cc b/source/physics/src/GateExtendedVSourceMessenger.cc index 7d15867ae..a638fe88e 100644 --- a/source/physics/src/GateExtendedVSourceMessenger.cc +++ b/source/physics/src/GateExtendedVSourceMessenger.cc @@ -79,7 +79,6 @@ void GateExtendedVSourceMessenger::InitCommands() upCmdSetFixedEmissionDirection.reset( GetVectorCmd( "setFixedEmissionDirection", "Set fixed direction for single and prompt gamma." ) ); upCmdSetEnableFixedEmissionDirection.reset( GetBoolCmd( "setEnableFixedEmissionDirection", "Set fixed direction enable/disable." ) ); upCmdSetEmissionEnergy.reset( GetDoubleCmdWithUnit( "setEmissionEnergy", "Set energy for single gamma.", "keV", "keV MeV GeV" ) ); - upCmdSetSeed.reset( GetIntCmd("setSeed", "Set seed for random generator" ) ); upCmdSetPositroniumLifetime.reset( GetStringCmd( "setPositroniumLifetime", "Set life-time of positronium." ) ); upCmdSetLifetime.reset( GetDoubleCmdWithUnit( "setLifetime", "Set life-time of positronium - disable for user.", "ns", "ps ns" ) ); upCmdSetPromptGammaEnergy.reset( GetDoubleCmdWithUnit( "setPromptGammaEnergy", "Set energy for prompt gamma.", "keV", "keV MeV GeV" ) ); @@ -107,10 +106,6 @@ void GateExtendedVSourceMessenger::SetNewValue( G4UIcommand* command, G4String n { pSource->SetEmissionEnergy( upCmdSetEmissionEnergy->GetNewDoubleValue( new_value ) ); } - else if ( command == upCmdSetSeed.get() ) - { - pSource->SetSeed( static_cast( upCmdSetSeed->GetNewIntValue( new_value ) ) ); - } else if(command == upCmdSetPositroniumFractions.get()) { std::vector fractions; diff --git a/source/physics/src/GateGammaEmissionModel.cc b/source/physics/src/GateGammaEmissionModel.cc index 88a7bd4e2..2e27a6e15 100644 --- a/source/physics/src/GateGammaEmissionModel.cc +++ b/source/physics/src/GateGammaEmissionModel.cc @@ -70,14 +70,6 @@ void GateGammaEmissionModel::SetEmissionEnergy( const G4double& energy ) G4double GateGammaEmissionModel::GetEmissionEnergy() const { return fEmissionEnergy; } -void GateGammaEmissionModel::SetSeed( G4long seed ) -{ - if ( seed < 0 ) { NoticeError( G4String( __FUNCTION__ ), "seed should be positive value." ); } - G4Random::setTheSeed( seed ); -} - -G4long GateGammaEmissionModel::GetSeed() const { return G4Random::getTheSeed (); } - G4ThreeVector GateGammaEmissionModel::GetUniformOnSphere() const { //Based on TRandom::Sphere From b3478941e3ef187d9fd43ff73b374699e9dc54a9 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 14 Nov 2025 13:55:07 +0100 Subject: [PATCH 054/144] Remove Single Gamma model And all the related commands. --- source/physics/include/GateExtendedVSource.hh | 5 --- .../include/GateExtendedVSourceMessenger.hh | 3 -- source/physics/src/GateExtendedVSource.cc | 45 +++++-------------- .../src/GateExtendedVSourceMessenger.cc | 17 +------ 4 files changed, 13 insertions(+), 57 deletions(-) diff --git a/source/physics/include/GateExtendedVSource.hh b/source/physics/include/GateExtendedVSource.hh index 7dc8738f6..3e41736e5 100644 --- a/source/physics/include/GateExtendedVSource.hh +++ b/source/physics/include/GateExtendedVSource.hh @@ -7,7 +7,6 @@ #ifndef GateExtendedVSource_hh #define GateExtendedVSource_hh -#include #include #include "GateVSource.hh" #include "GateExtendedVSourceMessenger.hh" @@ -24,7 +23,6 @@ public: enum class ModelKind { NotDefined, //by default - in this case this class will behave like GateVSource - SingleGamma, // generate single gamma ParaPositronium, //generate gammas from para-positronium decay OrthoPositronium, //generate gammas from ortho-positronium decay Positronium, //generate gammas from mixed model ( from pPs and oPs decay with setted ratio ) @@ -63,9 +61,6 @@ public: std::unique_ptr pMessenger; //User settings: ModelKind fModelKind = ModelKind::NotDefined; - std::optional fFixedEmissionDirection; - std::optional fEnableFixedEmissionDirection; - std::optional fEmissionEnergy; //Set by PrepareModel() and used in GeneratePrimaries() G4bool fBehaveLikeVSource = false; diff --git a/source/physics/include/GateExtendedVSourceMessenger.hh b/source/physics/include/GateExtendedVSourceMessenger.hh index d26471e31..9dfabe7b9 100644 --- a/source/physics/include/GateExtendedVSourceMessenger.hh +++ b/source/physics/include/GateExtendedVSourceMessenger.hh @@ -50,9 +50,6 @@ class GateExtendedVSourceMessenger: public GateVSourceMessenger GateExtendedVSource* pSource = nullptr; std::unique_ptr upCmdSetEnableDeexcitation; - std::unique_ptr upCmdSetFixedEmissionDirection; - std::unique_ptr upCmdSetEnableFixedEmissionDirection; - std::unique_ptr upCmdSetEmissionEnergy; std::unique_ptr upCmdSetPositroniumLifetime; std::unique_ptr upCmdSetPromptGammaEnergy; std::unique_ptr upCmdSetPositroniumFraction; diff --git a/source/physics/src/GateExtendedVSource.cc b/source/physics/src/GateExtendedVSource.cc index 8e338e570..950016c20 100644 --- a/source/physics/src/GateExtendedVSource.cc +++ b/source/physics/src/GateExtendedVSource.cc @@ -21,7 +21,6 @@ GateExtendedVSource::GateExtendedVSource(const G4String &name) void GateExtendedVSource::SetModel(const G4String &model_name) { static const std::map models{ - {"sg", GateExtendedVSource::ModelKind::SingleGamma}, {"pPs", GateExtendedVSource::ModelKind::ParaPositronium}, {"oPs", GateExtendedVSource::ModelKind::OrthoPositronium}, {"Ps", GateExtendedVSource::ModelKind::Positronium}, @@ -39,13 +38,6 @@ void GateExtendedVSource::SetModel(const G4String &model_name) } } - -void GateExtendedVSource::SetFixedEmissionDirection(const G4ThreeVector& fixed_emission_direction) { fFixedEmissionDirection = fixed_emission_direction; } - -void GateExtendedVSource::SetEnableFixedEmissionDirection(G4bool enable_fixed_emission_direction) { fEnableFixedEmissionDirection = enable_fixed_emission_direction; } - -void GateExtendedVSource::SetEmissionEnergy(G4double energy) { fEmissionEnergy =energy; } - void GateExtendedVSource::PrepareModel() { SetModel(GetType()); @@ -54,39 +46,26 @@ void GateExtendedVSource::PrepareModel() return; } - if (fModelKind == GateExtendedVSource::ModelKind::SingleGamma) { - pModel = std::make_unique(); + if ((fModelKind == GateExtendedVSource::ModelKind::MiniPositronium) || + (fModelKind == GateExtendedVSource::ModelKind::Positronium)) { + auto params = pMessenger->generatePositroniumDecayParams(); + pModel = std::make_unique(params); } else { - if ((fModelKind == GateExtendedVSource::ModelKind::MiniPositronium) || - (fModelKind == GateExtendedVSource::ModelKind::Positronium)) { - auto params = pMessenger->generatePositroniumDecayParams(); + if (fModelKind == GateExtendedVSource::ModelKind::ParaPositronium) { + auto params = pMessenger->generatePositroniumDecayParams( + GatePositroniumDecayParamsGenerator::kParaPositronium); pModel = std::make_unique(params); + } else { - if (fModelKind == GateExtendedVSource::ModelKind::ParaPositronium) { - auto params = pMessenger->generatePositroniumDecayParams(GatePositroniumDecayParamsGenerator::kParaPositronium); + if (fModelKind == GateExtendedVSource::ModelKind::OrthoPositronium) { + auto params = pMessenger->generatePositroniumDecayParams( + GatePositroniumDecayParamsGenerator::kOrthoPositronium); pModel = std::make_unique(params); - } else { - if (fModelKind == GateExtendedVSource::ModelKind::OrthoPositronium) { - auto params = pMessenger->generatePositroniumDecayParams(GatePositroniumDecayParamsGenerator::kOrthoPositronium); - pModel = std::make_unique(params); - } else { - GateError("GateExtendedVSource::PrepareModel - unknown model."); - } + GateError("GateExtendedVSource::PrepareModel - unknown model."); } } } - - if (fFixedEmissionDirection.has_value()) { - pModel->SetFixedEmissionDirection(fFixedEmissionDirection.value()); - } - if (fEnableFixedEmissionDirection.has_value()) { - pModel->SetEnableFixedEmissionDirection( - fEnableFixedEmissionDirection.value()); - } - if (fEmissionEnergy.has_value()) { - pModel->SetEmissionEnergy(fEmissionEnergy.value()); - } } G4int GateExtendedVSource::GeneratePrimaries(G4Event* event) diff --git a/source/physics/src/GateExtendedVSourceMessenger.cc b/source/physics/src/GateExtendedVSourceMessenger.cc index a638fe88e..355dde9b0 100644 --- a/source/physics/src/GateExtendedVSourceMessenger.cc +++ b/source/physics/src/GateExtendedVSourceMessenger.cc @@ -76,9 +76,6 @@ G4UIcmdWith3VectorAndUnit* GateExtendedVSourceMessenger::GetVectorCmdWithUnit( c void GateExtendedVSourceMessenger::InitCommands() { upCmdSetEnableDeexcitation.reset( GetBoolCmd( "setEnableDeexcitation", "Generate prompt gamma from positron source which precedes positronium formation and decay." ) ); - upCmdSetFixedEmissionDirection.reset( GetVectorCmd( "setFixedEmissionDirection", "Set fixed direction for single and prompt gamma." ) ); - upCmdSetEnableFixedEmissionDirection.reset( GetBoolCmd( "setEnableFixedEmissionDirection", "Set fixed direction enable/disable." ) ); - upCmdSetEmissionEnergy.reset( GetDoubleCmdWithUnit( "setEmissionEnergy", "Set energy for single gamma.", "keV", "keV MeV GeV" ) ); upCmdSetPositroniumLifetime.reset( GetStringCmd( "setPositroniumLifetime", "Set life-time of positronium." ) ); upCmdSetLifetime.reset( GetDoubleCmdWithUnit( "setLifetime", "Set life-time of positronium - disable for user.", "ns", "ps ns" ) ); upCmdSetPromptGammaEnergy.reset( GetDoubleCmdWithUnit( "setPromptGammaEnergy", "Set energy for prompt gamma.", "keV", "keV MeV GeV" ) ); @@ -94,19 +91,7 @@ void GateExtendedVSourceMessenger::InitCommands() void GateExtendedVSourceMessenger::SetNewValue( G4UIcommand* command, G4String new_value ) { - if ( command == upCmdSetFixedEmissionDirection.get() ) - { - pSource->SetFixedEmissionDirection( upCmdSetFixedEmissionDirection->GetNew3VectorValue( new_value ) ); - } - else if ( command == upCmdSetEnableFixedEmissionDirection.get() ) - { - pSource->SetEnableFixedEmissionDirection( upCmdSetEnableFixedEmissionDirection->GetNewBoolValue( new_value ) ); - } - else if ( command == upCmdSetEmissionEnergy.get() ) - { - pSource->SetEmissionEnergy( upCmdSetEmissionEnergy->GetNewDoubleValue( new_value ) ); - } - else if(command == upCmdSetPositroniumFractions.get()) + if(command == upCmdSetPositroniumFractions.get()) { std::vector fractions; std::stringstream ss(new_value); From 590bda2fa0aecf81cbe0b3ad560439398e4787da Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 14 Nov 2025 14:11:54 +0100 Subject: [PATCH 055/144] Remove miniPositronium name of the model and leave only Ps model --- source/physics/include/GateExtendedVSource.hh | 7 ++----- source/physics/include/GateExtendedVSourceMessenger.hh | 9 --------- source/physics/src/GateExtendedVSource.cc | 8 +++----- source/physics/src/GateExtendedVSourceMessenger.cc | 7 ------- tests/test_GateExtendedVSource.cpp | 4 ++-- 5 files changed, 7 insertions(+), 28 deletions(-) diff --git a/source/physics/include/GateExtendedVSource.hh b/source/physics/include/GateExtendedVSource.hh index 3e41736e5..a478c3324 100644 --- a/source/physics/include/GateExtendedVSource.hh +++ b/source/physics/include/GateExtendedVSource.hh @@ -8,6 +8,7 @@ #define GateExtendedVSource_hh #include + #include "GateVSource.hh" #include "GateExtendedVSourceMessenger.hh" #include "GateGammaEmissionModel.hh" @@ -25,9 +26,7 @@ public: NotDefined, //by default - in this case this class will behave like GateVSource ParaPositronium, //generate gammas from para-positronium decay OrthoPositronium, //generate gammas from ortho-positronium decay - Positronium, //generate gammas from mixed model ( from pPs and oPs decay with setted ratio ) - MiniPositronium //generate gammas from extended mixed model - }; + Positronium}; // generate gammas from mixed model explicit GateExtendedVSource(const G4String& name); virtual ~GateExtendedVSource() = default; @@ -55,9 +54,7 @@ public: void PrepareModel(); protected: - //Gamma emission model std::unique_ptr pModel; - //Source messanger std::unique_ptr pMessenger; //User settings: ModelKind fModelKind = ModelKind::NotDefined; diff --git a/source/physics/include/GateExtendedVSourceMessenger.hh b/source/physics/include/GateExtendedVSourceMessenger.hh index 9dfabe7b9..fea8bbd00 100644 --- a/source/physics/include/GateExtendedVSourceMessenger.hh +++ b/source/physics/include/GateExtendedVSourceMessenger.hh @@ -49,15 +49,6 @@ class GateExtendedVSourceMessenger: public GateVSourceMessenger protected: GateExtendedVSource* pSource = nullptr; - std::unique_ptr upCmdSetEnableDeexcitation; - std::unique_ptr upCmdSetPositroniumLifetime; - std::unique_ptr upCmdSetPromptGammaEnergy; - std::unique_ptr upCmdSetPositroniumFraction; - - //Supporting commands - disable for user - std::unique_ptr upCmdSetLifetime; - - //New commands std::unique_ptr upCmdSetPositroniumFractions; std::unique_ptr upCmdSetPositroniumLifetimes; std::unique_ptr upCmdSetIsPromptPhoton; diff --git a/source/physics/src/GateExtendedVSource.cc b/source/physics/src/GateExtendedVSource.cc index 950016c20..18e5dfa3d 100644 --- a/source/physics/src/GateExtendedVSource.cc +++ b/source/physics/src/GateExtendedVSource.cc @@ -23,8 +23,7 @@ void GateExtendedVSource::SetModel(const G4String &model_name) static const std::map models{ {"pPs", GateExtendedVSource::ModelKind::ParaPositronium}, {"oPs", GateExtendedVSource::ModelKind::OrthoPositronium}, - {"Ps", GateExtendedVSource::ModelKind::Positronium}, - {"mPs", GateExtendedVSource::ModelKind::MiniPositronium}}; + {"Ps", GateExtendedVSource::ModelKind::Positronium}}; auto it = models.find(model_name); if (it != models.end()) @@ -33,7 +32,7 @@ void GateExtendedVSource::SetModel(const G4String &model_name) } else { fBehaveLikeVSource = true; G4cout << "GateExtendedVSource::SetModel : Unknown gamma source model. " - "Enable: sg, pPs, oPs, Ps, mPs. Switching to GateVSource behavour." + "Enable: pPs, oPs, Ps. Switching to GateVSource behavour." << G4endl; } } @@ -46,8 +45,7 @@ void GateExtendedVSource::PrepareModel() return; } - if ((fModelKind == GateExtendedVSource::ModelKind::MiniPositronium) || - (fModelKind == GateExtendedVSource::ModelKind::Positronium)) { + if (fModelKind == GateExtendedVSource::ModelKind::Positronium) { auto params = pMessenger->generatePositroniumDecayParams(); pModel = std::make_unique(params); } else { diff --git a/source/physics/src/GateExtendedVSourceMessenger.cc b/source/physics/src/GateExtendedVSourceMessenger.cc index 355dde9b0..762453ce0 100644 --- a/source/physics/src/GateExtendedVSourceMessenger.cc +++ b/source/physics/src/GateExtendedVSourceMessenger.cc @@ -75,13 +75,6 @@ G4UIcmdWith3VectorAndUnit* GateExtendedVSourceMessenger::GetVectorCmdWithUnit( c void GateExtendedVSourceMessenger::InitCommands() { - upCmdSetEnableDeexcitation.reset( GetBoolCmd( "setEnableDeexcitation", "Generate prompt gamma from positron source which precedes positronium formation and decay." ) ); - upCmdSetPositroniumLifetime.reset( GetStringCmd( "setPositroniumLifetime", "Set life-time of positronium." ) ); - upCmdSetLifetime.reset( GetDoubleCmdWithUnit( "setLifetime", "Set life-time of positronium - disable for user.", "ns", "ps ns" ) ); - upCmdSetPromptGammaEnergy.reset( GetDoubleCmdWithUnit( "setPromptGammaEnergy", "Set energy for prompt gamma.", "keV", "keV MeV GeV" ) ); - upCmdSetPositroniumFraction.reset( GetStringCmd( "setPositroniumFraction", "\"positronium_kind fraction\" - where positronium_kind = {pPs, oPs} and fraction in [0.0, 1.0]" ) ); - - upCmdSetPositroniumFractions.reset(GetStringCmd( "setPositroniumFractions", "\"f1, f2, f3 .., fn\" - where fi in [0.0, 1.0] and sum of all fi ==1" ) ); upCmdSetPositroniumLifetimes.reset(GetStringCmd( "setPositroniumLifetimes", "\"t1, t2, t3 .., tn\" - where" ) ); upCmdSetDecayKinds.reset(GetStringCmd( "setDecayKinds", "\"k1, k2, k3 .., kn\" - where ki " ) ); diff --git a/tests/test_GateExtendedVSource.cpp b/tests/test_GateExtendedVSource.cpp index 9285fa9b9..980f3122d 100644 --- a/tests/test_GateExtendedVSource.cpp +++ b/tests/test_GateExtendedVSource.cpp @@ -26,8 +26,8 @@ bool run_tests() if(source.GetName() !="source1") { return false; } - source.SetType("mPs"); - if(source.GetType() !="mPs") { + source.SetType("Ps"); + if(source.GetType() !="Ps") { return false; } From 4292cf6387eeab081ecf0950a2fb472566a295df Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 14 Nov 2025 14:15:04 +0100 Subject: [PATCH 056/144] Remove unused fields and function declarations --- source/physics/include/GateExtendedVSource.hh | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/source/physics/include/GateExtendedVSource.hh b/source/physics/include/GateExtendedVSource.hh index a478c3324..3db2c851c 100644 --- a/source/physics/include/GateExtendedVSource.hh +++ b/source/physics/include/GateExtendedVSource.hh @@ -35,16 +35,6 @@ public: **/ virtual G4int GeneratePrimaries( G4Event* event ) override; - /** Set fixed direction of single gamma ( or prompt gamma ) - **/ - void SetFixedEmissionDirection(const G4ThreeVector &fixed_emission_direction); - /** Set enable/disable emission of single gamma with fixed direction - **/ - void SetEnableFixedEmissionDirection(G4bool enable_fixed_emission_direction); - /** Set single gamma kinetic energy - **/ - void SetEmissionEnergy(G4double energy); - protected: /** Set model used for this source. If is not defined then this class will behave like GateVSource. **/ @@ -61,10 +51,6 @@ public: //Set by PrepareModel() and used in GeneratePrimaries() G4bool fBehaveLikeVSource = false; - - //Constants for Set(...) methods - static inline const G4String kParaPositroniumName = "pPs"; - static inline const G4String kOrthoPositroniumName = "oPs"; }; #endif From 9808a4429d0f74c2b346da8230e5d78b990b116d Mon Sep 17 00:00:00 2001 From: kdulski Date: Fri, 28 Nov 2025 18:39:34 +0100 Subject: [PATCH 057/144] Implementing positronium helper into param generator / changing setting isPrompt into PromptProbability --- CMakeLists.txt | 5 + .../include/GateExtendedVSourceMessenger.hh | 4 +- .../GatePositroniumDecayModelParams.hh | 5 +- .../GatePositroniumDecayParamsGenerator.hh | 7 +- .../physics/include/GatePositroniumHelper.hh | 31 +++++ .../src/GateExtendedVSourceMessenger.cc | 41 +++++- .../GatePositroniumDecayParamsGenerator.cc | 47 ++++--- source/physics/src/GatePositroniumHelper.cc | 119 ++++++++++++++++++ ...st_GatePositroniumDecayParamsGenerator.cpp | 10 +- tests/test_GatePositroniumHelper.cpp | 82 ++++++++++++ 10 files changed, 322 insertions(+), 29 deletions(-) create mode 100644 source/physics/include/GatePositroniumHelper.hh create mode 100644 source/physics/src/GatePositroniumHelper.cc create mode 100644 tests/test_GatePositroniumHelper.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index f5fa8281c..7ce892681 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,6 +39,11 @@ IF(BUILD_TESTING) ADD_TEST(NAME test_GatePositronium COMMAND test_GatePositronium) + ADD_EXECUTABLE(test_GatePositroniumHelper tests/test_GatePositroniumHelper.cpp) + TARGET_LINK_LIBRARIES(test_GatePositroniumHelper GateLib) + + ADD_TEST(NAME test_GatePositroniumHelper COMMAND test_GatePositroniumHelper) + ENDIF(BUILD_TESTING) #========================================================= diff --git a/source/physics/include/GateExtendedVSourceMessenger.hh b/source/physics/include/GateExtendedVSourceMessenger.hh index fea8bbd00..b84a3f179 100644 --- a/source/physics/include/GateExtendedVSourceMessenger.hh +++ b/source/physics/include/GateExtendedVSourceMessenger.hh @@ -17,6 +17,7 @@ #include "G4UIcmdWithAString.hh" #include "G4UIcmdWith3VectorAndUnit.hh" +#include "GatePositroniumHelper.hh" #include "GatePositroniumDecayParamsGenerator.hh" class GateExtendedVSource; @@ -51,9 +52,10 @@ class GateExtendedVSourceMessenger: public GateVSourceMessenger std::unique_ptr upCmdSetPositroniumFractions; std::unique_ptr upCmdSetPositroniumLifetimes; - std::unique_ptr upCmdSetIsPromptPhoton; + std::unique_ptr upCmdSetPromptPhotonProbabilites; std::unique_ptr upCmdSetPromptPhotonEnergies; std::unique_ptr upCmdSetDecayKinds; + std::unique_ptr upCmdSetPositronInteractions; GatePositroniumDecayParamsGenerator fParamGenerator; diff --git a/source/physics/include/GatePositroniumDecayModelParams.hh b/source/physics/include/GatePositroniumDecayModelParams.hh index 047a64fd9..e0d33f6ba 100644 --- a/source/physics/include/GatePositroniumDecayModelParams.hh +++ b/source/physics/include/GatePositroniumDecayModelParams.hh @@ -9,15 +9,18 @@ #include +#include + enum PositroniumDecayKind { k2Gamma, k3Gamma}; struct PositroniumDecayModelParams { std::vector fFractions; std::vector fLifetimes; - std::vector fIsPromptGamma; + std::vector fPromptGammaProbabilities; std::vector fPromptGammaEnergy; std::vector fDecayKind; + std::vector fPositronInteractions; }; #endif diff --git a/source/physics/include/GatePositroniumDecayParamsGenerator.hh b/source/physics/include/GatePositroniumDecayParamsGenerator.hh index 928931c91..bd1e33e24 100644 --- a/source/physics/include/GatePositroniumDecayParamsGenerator.hh +++ b/source/physics/include/GatePositroniumDecayParamsGenerator.hh @@ -11,6 +11,7 @@ #include #include +#include "GatePositroniumHelper.hh" #include "GatePositroniumDecayModelParams.hh" /*! class GatePositroniumDecayParamsGenerator @@ -28,10 +29,11 @@ public: GatePositroniumDecayParamsGenerator()= default; virtual ~GatePositroniumDecayParamsGenerator()=default; - void SetEnablePromptGamma(const std::vector& IsPromptPhoton); + void SetPromptGammaProbabilities(const std::vector& promptGammaProb); void SetPromptGammaEnergies(const std::vector& energies); void SetPositroniumLifetimes(const std::vector& fPositroniumLifetimes); void SetDecayKinds(const std::vector& decayKinds); + void SetPositronInteractions(const std::vector& positronInteractions); void SetPositroniumFraction(const std::vector& positroniumFractions); PositroniumDecayModelParams generatePositroniumDecayParams(DecayModel model= kPositronium) const; @@ -39,8 +41,9 @@ public: private: std::optional> fPositroniumFractions; std::optional> fPositroniumLifetimes; - std::optional> fIsPromptGamma; + std::optional> fPromptGammaProbabilities; std::optional> fPromptGammaEnergies; std::optional> fDecayKinds; + std::optional> fPositronInteractions; }; #endif diff --git a/source/physics/include/GatePositroniumHelper.hh b/source/physics/include/GatePositroniumHelper.hh new file mode 100644 index 000000000..744c26d64 --- /dev/null +++ b/source/physics/include/GatePositroniumHelper.hh @@ -0,0 +1,31 @@ +#ifndef GatePositroniumHelper_h +#define GatePositroniumHelper_h 1 + +#include "GatePositroniumDecayModelParams.hh" +#include "GatePositroniumDecayModel.hh" +#include "globals.hh" + +enum PositronElectronInteraction {kpPs, kDirect, koPs}; + +class GatePositroniumHelper +{ +public: + GatePositroniumHelper() {}; + virtual ~GatePositroniumHelper() {}; + + PositroniumDecayModelParams CalculateFractionsFromLifetimes(PositroniumDecayModelParams params); + float CalcPPsFractionFromOPs(std::vector fractions, std::vector decays); + std::pair CalcFractionsFromLifetime(float intensity, float lifetime, PositronElectronInteraction inter); + float CalcFractionFromOPsLifetime(float intensity, float lifetime, PositroniumDecayKind decay); + float CalcFractionFromDirectLifetime(float intensity, PositroniumDecayKind decay); + std::vector NormalizeFractions (std::vector fractions); + +private: +//For now stored as a internal parameter. To check if there is already defined value somewhere + float fpPsLifetime = 0.125; //ps + float fPPsToOPsFrac = 1.0/3.0; + float fOPsMeanLifetime = 142.; //ns + float fHyperfineCoef = 372; +}; + +#endif diff --git a/source/physics/src/GateExtendedVSourceMessenger.cc b/source/physics/src/GateExtendedVSourceMessenger.cc index 762453ce0..71652c9b1 100644 --- a/source/physics/src/GateExtendedVSourceMessenger.cc +++ b/source/physics/src/GateExtendedVSourceMessenger.cc @@ -77,8 +77,10 @@ void GateExtendedVSourceMessenger::InitCommands() { upCmdSetPositroniumFractions.reset(GetStringCmd( "setPositroniumFractions", "\"f1, f2, f3 .., fn\" - where fi in [0.0, 1.0] and sum of all fi ==1" ) ); upCmdSetPositroniumLifetimes.reset(GetStringCmd( "setPositroniumLifetimes", "\"t1, t2, t3 .., tn\" - where" ) ); - upCmdSetDecayKinds.reset(GetStringCmd( "setDecayKinds", "\"k1, k2, k3 .., kn\" - where ki " ) ); + upCmdSetDecayKinds.reset(GetStringCmd( "setDecayKinds", "\"k1, k2, k3 .., kn\" - where ki is k2gamma or k3gamma" ) ); + upCmdSetPositronInteractions.reset(GetStringCmd( "setPositronInteractions", "\"k1, k2, k3 .., kn\" - where ki is kpPs, kdirect or koPs" ) ); upCmdSetIsPromptPhoton.reset(GetStringCmd( "setIsPromptPhoton", "\"f1, f2, f3 .., fn\" - where fi are true or false " ) ); + upCmdSetPromptPhotonProbabilites.reset(GetStringCmd( "setPromptPhotonProbabilites", "\"f1, f2, f3 .., fn\" - where fi in [0.0, 1.0] " ) ); upCmdSetPromptPhotonEnergies.reset(GetStringCmd( "setPromptPhotonEnergies", "\"e1, e2, e3 .., fn\" - where ei are energies " ) ); } @@ -113,7 +115,21 @@ void GateExtendedVSourceMessenger::SetNewValue( G4UIcommand* command, G4String n isPromptPhoton.push_back(flag); } fParamGenerator.SetEnablePromptGamma(isPromptPhoton); - } + } + else if(command == upCmdSetPromptPhotonProbabilites.get()) + { + std::vector promptPhotonProb; + std::stringstream ss(new_value); + float prob; + while (ss >> prob) { + if (prob < 0) + prob = 0; + else if (prob > 1) + prob = 1; + promptPhotonProb.push_back(prob); + } + fParamGenerator.SetPromptGammaProbabilities(promptPhotonProb); + } else if(command == upCmdSetPromptPhotonEnergies.get()) { std::vector promptPhotonEnergies; std::stringstream ss(new_value); @@ -129,13 +145,28 @@ void GateExtendedVSourceMessenger::SetNewValue( G4UIcommand* command, G4String n std::string kind; while (ss >> kind) { if (kind == "k2Gamma") { - decayKinds.push_back(k2Gamma); - } else { + decayKinds.push_back(k2Gamma); + } else { decayKinds.push_back(k3Gamma); - } + } } fParamGenerator.SetDecayKinds(decayKinds); } + else if(command == upCmdSetPositronInteractions.get()) { + std::vector positronInteractions; + std::stringstream ss(new_value); + std::string inter; + while (ss >> inter) { + if (inter == "kpPs") { + positronInteractions.push_back(PositronElectronInteraction::kpPs); + } else if (inter == "kdirect") { + positronInteractions.push_back(PositronElectronInteraction::kdirect); + } else { + positronInteractions.push_back(PositronElectronInteraction::koPs); + } + } + fParamGenerator.SetPositronInteractions(positronInteractions); + } else { GateVSourceMessenger::SetNewValue(command, new_value); diff --git a/source/physics/src/GatePositroniumDecayParamsGenerator.cc b/source/physics/src/GatePositroniumDecayParamsGenerator.cc index d8a6d4dd8..152d11ccb 100644 --- a/source/physics/src/GatePositroniumDecayParamsGenerator.cc +++ b/source/physics/src/GatePositroniumDecayParamsGenerator.cc @@ -1,16 +1,21 @@ #include #include "GateMessageManager.hh" +#include "GatePositroniumHelper.hh" #include "GatePositroniumDecayParamsGenerator.hh" -void GatePositroniumDecayParamsGenerator::SetEnablePromptGamma(const std::vector& isPromptGamma) +void GatePositroniumDecayParamsGenerator::SetPromptGammaProbabilities(const std::vector& promptGammaProb) { - fIsPromptGamma = isPromptGamma; + fPromptGammaProbabilities = promptGammaProb; } void GatePositroniumDecayParamsGenerator::SetDecayKinds(const std::vector& decayKinds) { fDecayKinds = decayKinds; } +void GatePositroniumDecayParamsGenerator::SetPositronInteractions(const std::vector& positronInteractions) +{ + fPositronInteractions = positronInteractions; +} void GatePositroniumDecayParamsGenerator::SetPositroniumLifetimes(const std::vector& positroniumLifetimes) { fPositroniumLifetimes = positroniumLifetimes; @@ -31,13 +36,14 @@ PositroniumDecayModelParams GatePositroniumDecayParamsGenerator::generatePositro params.fFractions= {1}; params.fLifetimes= {0.1244}; // [ns] params.fDecayKind= {k2Gamma}; - if(fIsPromptGamma.has_value() && fPromptGammaEnergies.has_value()) { - params.fIsPromptGamma=fIsPromptGamma.value(); - assert(params.fIsPromptGamma.size()==1); + params.fPositronInteractions= {kpPs}; + if(fPromptGammaProbabilities.has_value() && fPromptGammaEnergies.has_value()) { + params.fPromptGammaProbabilities=fPromptGammaProbabilities.value(); + assert(params.fPromptGammaProbabilities.size()==1); params.fPromptGammaEnergy=fPromptGammaEnergies.value(); assert(params.fPromptGammaEnergy.size()==1); } else { - params.fIsPromptGamma={false}; + params.fPromptGammaProbabilities={0.0}; params.fPromptGammaEnergy ={0.0}; } } @@ -45,20 +51,21 @@ PositroniumDecayModelParams GatePositroniumDecayParamsGenerator::generatePositro params.fFractions= {1}; params.fLifetimes= {142}; // [ns] params.fDecayKind= {k3Gamma}; - if(fIsPromptGamma.has_value() && fPromptGammaEnergies.has_value()) { - params.fIsPromptGamma=fIsPromptGamma.value(); - assert(params.fIsPromptGamma.size()==1); + params.fPositronInteractions= {koPs}; + if(fPromptGammaProbabilities.has_value() && fPromptGammaEnergies.has_value()) { + params.fPromptGammaProbabilities=fPromptGammaProbabilities.value(); + assert(params.fPromptGammaProbabilities.size()==1); params.fPromptGammaEnergy=fPromptGammaEnergies.value(); assert(params.fPromptGammaEnergy.size()==1); } else { - params.fIsPromptGamma={false}; + params.fPromptGammaProbabilities={0.0}; params.fPromptGammaEnergy ={0.0}; } } if (model == GatePositroniumDecayParamsGenerator::kPositronium) { if(fPositroniumFractions.has_value()) { - params.fFractions=fPositroniumFractions.value(); + params.fFractions=GatePositroniumHelper::NormalizeFractions(fPositroniumFractions.value()); } else { GateError("GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams: Positronium decay fractions are not set"); } @@ -75,10 +82,16 @@ PositroniumDecayModelParams GatePositroniumDecayParamsGenerator::generatePositro GateError("GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams: Positronium decay kinds are not set"); } - if(fIsPromptGamma.has_value()) { - params.fIsPromptGamma=fIsPromptGamma.value(); + if(fPositronInteractions.has_value()) { + params.fPositronInteractions=fPositronInteractions.value(); } else { - GateError("GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams: Positronium is prompt gamma flags are not set"); + GateError("GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams: Positronium interactions are not set"); + } + + if(fPromptGammaProbabilities.has_value()) { + params.fPromptGammaProbabilities=fPromptGammaProbabilities.value(); + } else { + GateError("GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams: Positronium prompt gamma probabilities are not set"); } if(fPromptGammaEnergies.has_value()) { @@ -86,11 +99,15 @@ PositroniumDecayModelParams GatePositroniumDecayParamsGenerator::generatePositro } else { GateError("GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams: Prompt gamma energies are not set"); } + + if (params.fDecayKind.empty() && !params.fPositronInteractions.empty() && !params.fLifetimes.empty() && !params.fFractions.empty()) { + params = GatePositroniumHelper::CalculateFractionsFromLifetimes(params); + } } auto ref_param_number = params.fDecayKind.size(); bool size_mismatch = (ref_param_number != params.fFractions.size()) || - (ref_param_number != params.fIsPromptGamma.size()) || + (ref_param_number != params.fPromptGammaProbabilities.size()) || (ref_param_number != params.fLifetimes.size()) || (ref_param_number != params.fPromptGammaEnergy.size()); if (size_mismatch) { diff --git a/source/physics/src/GatePositroniumHelper.cc b/source/physics/src/GatePositroniumHelper.cc new file mode 100644 index 000000000..f9d2ca987 --- /dev/null +++ b/source/physics/src/GatePositroniumHelper.cc @@ -0,0 +1,119 @@ +#include +#include +#include + +#include "GateMessageManager.hh" +#include "GatePositroniumHelper.hh" +#include "globals.hh" + +PositroniumDecayModelParams GatePositroniumHelper::CalculateFractionsFromLifetimes(PositroniumDecayModelParams params) +{ + PositroniumDecayModelParams paramsOut; + 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.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 (!paramsOut.fPositronInteractions.size()) { + GateError("GatePositroniumHelper::CalculateFractionsFromLifetimes: Could not calculate fraction from lifetimes"); + return params; + } +// setting pPs + float pPsIntens = CalcPPsFractionFromOPs(paramsOut.fFractions, paramsOut.fPositronInteractions) + if (pPsIntens > 0) { + paramsOut.fFractions.push_back(pPsIntens); + paramsOut.fLifetimes.push_back(fpPsLifetime); + paramsOut.fPromptGammaProbabilities.push_back(paramsOut.fPromptGammaProbabilities.at(0)); + paramsOut.fPromptGammaEnergy.push_back(paramsOut.fPromptGammaEnergy.at(0)); + paramsOut.fDecayKind.push_back(PositroniumDecayKind::k2Gamma); + paramsOut.fPositronInteractions.push_back(PositronElectronInteraction::koPs); + } + + paramsOut.fFractions = NormalizeFractions(paramsOut.fFractions); + return paramsOut; +} + +float GatePositroniumHelper::CalcPPsFractionFromOPs(std::vector fractions, std::vector decays) { + float sum = 0.; + auto itFrac = fractions.begin(); + auto itDec = decays.begin(); + while (itFrac != fractions.end() && itDec != decays.end()) { + if (*itDec == PositronElectronInteraction::koPs) + sum += *itFrac; + ++itFrac; + ++itDec; + } + return sum*fPPsToOPsFrac; +} + +std::pair GatePositroniumHelper::CalcFractionsFromLifetime(float intensity, float lifetime, PositronElectronInteraction inter) { + float intens2G = 1., intens3G = 0.; + switch (inter) { + case PositronElectronInteraction::kDirect: + intens2G = intensity*(fHyperfineCoef - 1.)/fHyperfineCoef; + intens3G = intensity/fHyperfineCoef; + break; + case PositronElectronInteraction::koPs: + intens2G = intensity*(fOPsMeanLifetime - lifetime)/fOPsMeanLifetime; + intens3G = intensity*lifetime/fOPsMeanLifetime; + break; + } + 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 = fOPsMeanLifetime - lifetime; + break; + case PositroniumDecayKind::k3Gamma: + nominator = lifetime; + break; +// If there will be more decays one needs to modify it + } + return intensity*nominator/fOPsMeanLifetime; + } else + return nominator; +} + +float GatePositroniumHelper::CalcFractionFromDirectLifetime(float intensity, PositroniumDecayKind decay) { + float nominator = 0.; + if (intensity > 0) { + switch (decay) { + case PositroniumDecayKind::k2Gamma: + nominator = fHyperfineCoef - 1.; + break; + case PositroniumDecayKind::k3Gamma: + nominator = 1.; + break; + } + return intensity*nominator/fHyperfineCoef; + } else + return nominator; +} + +std::vector GatePositroniumHelper::NormalizeFractions(std::vector fractions) { + std::vector normalizedVector; + double sum = std::accumulate(fractions.begin(), fractions.end(), 0.0); + if (sum > 0) + std::transform(fractions.begin(), fractions.end(), normalizedVector.begin(), [sum](float element){ return element/sum; }); + else { +//ThrowException + normalizedVector = fractions; + } + return normalizedVector; +} diff --git a/tests/test_GatePositroniumDecayParamsGenerator.cpp b/tests/test_GatePositroniumDecayParamsGenerator.cpp index bf0272d31..26ea48f5d 100644 --- a/tests/test_GatePositroniumDecayParamsGenerator.cpp +++ b/tests/test_GatePositroniumDecayParamsGenerator.cpp @@ -28,7 +28,7 @@ bool test_para_default() CHECK(p.fFractions[0] == 1, "para fraction != 1"); CHECK(p.fLifetimes[0] == 0.1244f, "para lifetime wrong"); CHECK(p.fDecayKind[0] == k2Gamma, "para decay kind wrong"); - CHECK(p.fIsPromptGamma[0] == false, "para prompt flag wrong"); + CHECK(p.fPromptGammaProbabilities[0] == 0.0, "para prompt probability wrong"); CHECK(p.fPromptGammaEnergy[0] == 0.0f, "para prompt energy wrong"); return true; } @@ -42,7 +42,7 @@ bool test_ortho_default() CHECK(p.fFractions[0] == 1, "ortho fraction != 1"); CHECK(p.fLifetimes[0] == 142.0f, "ortho lifetime wrong"); CHECK(p.fDecayKind[0] == k3Gamma, "ortho decay kind wrong"); - CHECK(p.fIsPromptGamma[0] == false, "ortho prompt flag wrong"); + CHECK(p.fPromptGammaProbabilities[0] == 0.0, "ortho prompt probability wrong"); CHECK(p.fPromptGammaEnergy[0] == 0.0f, "ortho prompt energy wrong"); return true; } @@ -56,7 +56,7 @@ bool test_para_prompt_gamma() auto p = gen.generatePositroniumDecayParams(GatePositroniumDecayParamsGenerator::kParaPositronium); - CHECK(p.fIsPromptGamma[0] == true, "para prompt photon flag wrong"); + CHECK(p.fPromptGammaProbabilities[0] == 1.0, "para prompt photon probability wrong"); CHECK(p.fPromptGammaEnergy[0] == 1.2f, "prompt photon energy wrong"); return true; } @@ -77,13 +77,13 @@ bool test_positronium_custom() CHECK(p.fFractions[0] == 0.3f, "PS fraction mismatch"); CHECK(p.fLifetimes[0] == 0.12f, "PS lifetime mismatch"); CHECK(p.fDecayKind[0] == k2Gamma, "PS decay kind mismatch"); - CHECK(p.fIsPromptGamma[0] == false, "PS prompt flag mismatch"); + CHECK(p.fPromptGammaProbabilities[0] == 0.0, "PS prompt probability mismatch"); CHECK(p.fPromptGammaEnergy[0] == 0.0f, "PS prompt energy mismatch"); CHECK(p.fFractions[1] == 0.7f, "PS fraction mismatch"); CHECK(p.fLifetimes[1] == 140.0f, "PS lifetime mismatch"); CHECK(p.fDecayKind[1] == k3Gamma, "PS decay kind mismatch"); - CHECK(p.fIsPromptGamma[1] == true, "PS prompt flag mismatch"); + CHECK(p.fPromptGammaProbabilities[1] == 1.0, "PS prompt probability mismatch"); CHECK(p.fPromptGammaEnergy[1] == 1.2f, "PS prompt energy mismatch"); return true; } diff --git a/tests/test_GatePositroniumHelper.cpp b/tests/test_GatePositroniumHelper.cpp new file mode 100644 index 000000000..8caa38d26 --- /dev/null +++ b/tests/test_GatePositroniumHelper.cpp @@ -0,0 +1,82 @@ +#include +#include + +#include + +#include +#include +#include + +#include "GateRunManager.hh" +#include "GatePhysicsList.hh" +#include "GateDetectorConstruction.hh" + +void initializeGateRunManager(GateRunManager* runManager) +{ + // Set the DetectorConstruction + GateDetectorConstruction* gateDC = new GateDetectorConstruction(); + runManager->SetUserInitialization( gateDC ); + // Set the PhysicsList + runManager->SetUserInitialization( GatePhysicsList::GetInstance() ); + // Initialize G4 kernel + runManager->InitializeAll(); +} + +bool run_tests() +{ + bool res = true; + std::unique_ptr runManager(new GateRunManager); + initializeGateRunManager(runManager.get()); + + GatePositroniumHelper helper; + + std::vector fractions = {2, 12, 4, 2}; + std::vector lifetimes = {0.125, 0.4, 2.0, 50.0}; + std::vector decays = {kpPs, kDirect, koPs, koPs} + std::vector normFractions = helper.NormalizeFractions(fractions); + std::vector goodFractions = {0.1, 0.6, 0.2, 0.1}; + for (int i=0; i Date: Mon, 1 Dec 2025 12:20:03 +0100 Subject: [PATCH 058/144] Fixing compile errors --- .../include/GatePositroniumDecayModelParams.hh | 4 +++- source/physics/include/GatePositroniumHelper.hh | 4 +--- source/physics/src/GateExtendedVSourceMessenger.cc | 13 +------------ source/physics/src/GateMiniPositroniumDecayModel.cc | 2 +- .../src/GatePositroniumDecayParamsGenerator.cc | 5 +++-- source/physics/src/GatePositroniumHelper.cc | 2 +- 6 files changed, 10 insertions(+), 20 deletions(-) diff --git a/source/physics/include/GatePositroniumDecayModelParams.hh b/source/physics/include/GatePositroniumDecayModelParams.hh index e0d33f6ba..0c53b9afe 100644 --- a/source/physics/include/GatePositroniumDecayModelParams.hh +++ b/source/physics/include/GatePositroniumDecayModelParams.hh @@ -9,10 +9,12 @@ #include -#include +//#include "GatePositroniumHelper.hh" enum PositroniumDecayKind { k2Gamma, k3Gamma}; +enum PositronElectronInteraction {kpPs, kDirect, koPs}; + struct PositroniumDecayModelParams { std::vector fFractions; diff --git a/source/physics/include/GatePositroniumHelper.hh b/source/physics/include/GatePositroniumHelper.hh index 744c26d64..d50a7b19c 100644 --- a/source/physics/include/GatePositroniumHelper.hh +++ b/source/physics/include/GatePositroniumHelper.hh @@ -5,15 +5,13 @@ #include "GatePositroniumDecayModel.hh" #include "globals.hh" -enum PositronElectronInteraction {kpPs, kDirect, koPs}; - class GatePositroniumHelper { public: GatePositroniumHelper() {}; virtual ~GatePositroniumHelper() {}; - PositroniumDecayModelParams CalculateFractionsFromLifetimes(PositroniumDecayModelParams params); + struct PositroniumDecayModelParams CalculateFractionsFromLifetimes(struct PositroniumDecayModelParams params); float CalcPPsFractionFromOPs(std::vector fractions, std::vector decays); std::pair CalcFractionsFromLifetime(float intensity, float lifetime, PositronElectronInteraction inter); float CalcFractionFromOPsLifetime(float intensity, float lifetime, PositroniumDecayKind decay); diff --git a/source/physics/src/GateExtendedVSourceMessenger.cc b/source/physics/src/GateExtendedVSourceMessenger.cc index 71652c9b1..95f897a34 100644 --- a/source/physics/src/GateExtendedVSourceMessenger.cc +++ b/source/physics/src/GateExtendedVSourceMessenger.cc @@ -79,7 +79,6 @@ void GateExtendedVSourceMessenger::InitCommands() upCmdSetPositroniumLifetimes.reset(GetStringCmd( "setPositroniumLifetimes", "\"t1, t2, t3 .., tn\" - where" ) ); upCmdSetDecayKinds.reset(GetStringCmd( "setDecayKinds", "\"k1, k2, k3 .., kn\" - where ki is k2gamma or k3gamma" ) ); upCmdSetPositronInteractions.reset(GetStringCmd( "setPositronInteractions", "\"k1, k2, k3 .., kn\" - where ki is kpPs, kdirect or koPs" ) ); - upCmdSetIsPromptPhoton.reset(GetStringCmd( "setIsPromptPhoton", "\"f1, f2, f3 .., fn\" - where fi are true or false " ) ); upCmdSetPromptPhotonProbabilites.reset(GetStringCmd( "setPromptPhotonProbabilites", "\"f1, f2, f3 .., fn\" - where fi in [0.0, 1.0] " ) ); upCmdSetPromptPhotonEnergies.reset(GetStringCmd( "setPromptPhotonEnergies", "\"e1, e2, e3 .., fn\" - where ei are energies " ) ); } @@ -106,16 +105,6 @@ void GateExtendedVSourceMessenger::SetNewValue( G4UIcommand* command, G4String n } fParamGenerator.SetPositroniumLifetimes(lifetimes); } - else if(command == upCmdSetIsPromptPhoton.get()) - { - std::vector isPromptPhoton; - std::stringstream ss(new_value); - bool flag; - while (ss >> std::boolalpha >> flag) { - isPromptPhoton.push_back(flag); - } - fParamGenerator.SetEnablePromptGamma(isPromptPhoton); - } else if(command == upCmdSetPromptPhotonProbabilites.get()) { std::vector promptPhotonProb; @@ -160,7 +149,7 @@ void GateExtendedVSourceMessenger::SetNewValue( G4UIcommand* command, G4String n if (inter == "kpPs") { positronInteractions.push_back(PositronElectronInteraction::kpPs); } else if (inter == "kdirect") { - positronInteractions.push_back(PositronElectronInteraction::kdirect); + positronInteractions.push_back(PositronElectronInteraction::kDirect); } else { positronInteractions.push_back(PositronElectronInteraction::koPs); } diff --git a/source/physics/src/GateMiniPositroniumDecayModel.cc b/source/physics/src/GateMiniPositroniumDecayModel.cc index 35d34ff16..f70781208 100644 --- a/source/physics/src/GateMiniPositroniumDecayModel.cc +++ b/source/physics/src/GateMiniPositroniumDecayModel.cc @@ -52,7 +52,7 @@ G4int MiniPositroniumDecayModel::GeneratePrimaryVertices(G4Event* event, G4doubl auto decayIndex = MiniPositroniumDecayModel::getPositroniumDecayIndex(fModelParams.fFractions); G4int number_of_vertices = 1; - if(fModelParams.fIsPromptGamma[decayIndex]) + if(fModelParams.fPromptGammaProbabilities[decayIndex] > G4UniformRand()) { ++number_of_vertices; event->AddPrimaryVertex(GetPrimaryVertexFromDeexcitation(particle_time, particle_position, decayIndex)); diff --git a/source/physics/src/GatePositroniumDecayParamsGenerator.cc b/source/physics/src/GatePositroniumDecayParamsGenerator.cc index 152d11ccb..94ce6a3e2 100644 --- a/source/physics/src/GatePositroniumDecayParamsGenerator.cc +++ b/source/physics/src/GatePositroniumDecayParamsGenerator.cc @@ -64,8 +64,9 @@ PositroniumDecayModelParams GatePositroniumDecayParamsGenerator::generatePositro } if (model == GatePositroniumDecayParamsGenerator::kPositronium) { + GatePositroniumHelper positronHelper; if(fPositroniumFractions.has_value()) { - params.fFractions=GatePositroniumHelper::NormalizeFractions(fPositroniumFractions.value()); + params.fFractions=positronHelper.NormalizeFractions(fPositroniumFractions.value()); } else { GateError("GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams: Positronium decay fractions are not set"); } @@ -101,7 +102,7 @@ PositroniumDecayModelParams GatePositroniumDecayParamsGenerator::generatePositro } if (params.fDecayKind.empty() && !params.fPositronInteractions.empty() && !params.fLifetimes.empty() && !params.fFractions.empty()) { - params = GatePositroniumHelper::CalculateFractionsFromLifetimes(params); + params = positronHelper.CalculateFractionsFromLifetimes(params); } } diff --git a/source/physics/src/GatePositroniumHelper.cc b/source/physics/src/GatePositroniumHelper.cc index f9d2ca987..c77bd04e9 100644 --- a/source/physics/src/GatePositroniumHelper.cc +++ b/source/physics/src/GatePositroniumHelper.cc @@ -31,7 +31,7 @@ PositroniumDecayModelParams GatePositroniumHelper::CalculateFractionsFromLifetim return params; } // setting pPs - float pPsIntens = CalcPPsFractionFromOPs(paramsOut.fFractions, paramsOut.fPositronInteractions) + float pPsIntens = CalcPPsFractionFromOPs(paramsOut.fFractions, paramsOut.fPositronInteractions); if (pPsIntens > 0) { paramsOut.fFractions.push_back(pPsIntens); paramsOut.fLifetimes.push_back(fpPsLifetime); From 0884d42894a4a17a7a6f761b3fd9dffd2eecb300 Mon Sep 17 00:00:00 2001 From: kdulski Date: Mon, 15 Dec 2025 16:34:59 +0100 Subject: [PATCH 059/144] Small fixes for safe use --- .../src/GatePositroniumDecayParamsGenerator.cc | 17 ++++++++--------- source/physics/src/GatePositroniumHelper.cc | 7 +++++-- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/source/physics/src/GatePositroniumDecayParamsGenerator.cc b/source/physics/src/GatePositroniumDecayParamsGenerator.cc index 94ce6a3e2..af1c2c86b 100644 --- a/source/physics/src/GatePositroniumDecayParamsGenerator.cc +++ b/source/physics/src/GatePositroniumDecayParamsGenerator.cc @@ -77,16 +77,10 @@ PositroniumDecayModelParams GatePositroniumDecayParamsGenerator::generatePositro GateError("GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams: Positronium lifetimes are not set"); } - if(fDecayKinds.has_value()) { - params.fDecayKind=fDecayKinds.value(); - } else { - GateError("GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams: Positronium decay kinds are not set"); - } - if(fPositronInteractions.has_value()) { params.fPositronInteractions=fPositronInteractions.value(); - } else { - GateError("GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams: Positronium interactions are not set"); + } else if (!fDecayKinds.has_value()) { + GateError("GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams: Positronium interactions are not set and decay kinds are also empty"); } if(fPromptGammaProbabilities.has_value()) { @@ -101,8 +95,12 @@ PositroniumDecayModelParams GatePositroniumDecayParamsGenerator::generatePositro GateError("GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams: Prompt gamma energies are not set"); } - if (params.fDecayKind.empty() && !params.fPositronInteractions.empty() && !params.fLifetimes.empty() && !params.fFractions.empty()) { + if(fDecayKinds.has_value()) { + params.fDecayKind=fDecayKinds.value(); + } else if (!params.fPositronInteractions.empty() && !params.fLifetimes.empty() && !params.fFractions.empty()) { params = positronHelper.CalculateFractionsFromLifetimes(params); + } else { + GateError("GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams: Positronium decay kinds are not set and one or more sets that can calculate them (positronInteractions, lifetimes or fractions) is/are empty"); } } @@ -112,6 +110,7 @@ PositroniumDecayModelParams GatePositroniumDecayParamsGenerator::generatePositro (ref_param_number != params.fLifetimes.size()) || (ref_param_number != params.fPromptGammaEnergy.size()); if (size_mismatch) { + std::cout << ref_param_number << " " << params.fFractions.size() << " " << params.fPromptGammaProbabilities.size() << " " << params.fLifetimes.size() << " " << params.fPromptGammaEnergy.size() << std::endl; GateError( "GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams: " "number of provided parameters in Fractions, PromptGamma, Lifetimes, " diff --git a/source/physics/src/GatePositroniumHelper.cc b/source/physics/src/GatePositroniumHelper.cc index c77bd04e9..9bce43d7c 100644 --- a/source/physics/src/GatePositroniumHelper.cc +++ b/source/physics/src/GatePositroniumHelper.cc @@ -9,6 +9,7 @@ PositroniumDecayModelParams GatePositroniumHelper::CalculateFractionsFromLifetimes(PositroniumDecayModelParams params) { PositroniumDecayModelParams paramsOut; + bool pPsExist = false; for (unsigned i=0; i intens = CalcFractionsFromLifetime(params.fFractions.at(i), params.fLifetimes.at(i), params.fPositronInteractions.at(i)); @@ -30,6 +31,7 @@ PositroniumDecayModelParams GatePositroniumHelper::CalculateFractionsFromLifetim GateError("GatePositroniumHelper::CalculateFractionsFromLifetimes: Could not calculate fraction from lifetimes"); return params; } + // setting pPs float pPsIntens = CalcPPsFractionFromOPs(paramsOut.fFractions, paramsOut.fPositronInteractions); if (pPsIntens > 0) { @@ -38,10 +40,11 @@ PositroniumDecayModelParams GatePositroniumHelper::CalculateFractionsFromLifetim paramsOut.fPromptGammaProbabilities.push_back(paramsOut.fPromptGammaProbabilities.at(0)); paramsOut.fPromptGammaEnergy.push_back(paramsOut.fPromptGammaEnergy.at(0)); paramsOut.fDecayKind.push_back(PositroniumDecayKind::k2Gamma); - paramsOut.fPositronInteractions.push_back(PositronElectronInteraction::koPs); + paramsOut.fPositronInteractions.push_back(PositronElectronInteraction::kpPs); } paramsOut.fFractions = NormalizeFractions(paramsOut.fFractions); + return paramsOut; } @@ -107,7 +110,7 @@ float GatePositroniumHelper::CalcFractionFromDirectLifetime(float intensity, Pos } std::vector GatePositroniumHelper::NormalizeFractions(std::vector fractions) { - std::vector normalizedVector; + std::vector normalizedVector = fractions; double sum = std::accumulate(fractions.begin(), fractions.end(), 0.0); if (sum > 0) std::transform(fractions.begin(), fractions.end(), normalizedVector.begin(), [sum](float element){ return element/sum; }); From b209c5b970939f024d82cd8540dde4b60dcd3ac3 Mon Sep 17 00:00:00 2001 From: kdulski Date: Fri, 16 Jan 2026 10:48:49 +0100 Subject: [PATCH 060/144] Applying corrections --- source/physics/include/GatePositroniumHelper.hh | 8 ++++---- source/physics/src/GateExtendedVSourceMessenger.cc | 8 +++----- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/source/physics/include/GatePositroniumHelper.hh b/source/physics/include/GatePositroniumHelper.hh index d50a7b19c..b5fa4ff77 100644 --- a/source/physics/include/GatePositroniumHelper.hh +++ b/source/physics/include/GatePositroniumHelper.hh @@ -20,10 +20,10 @@ public: private: //For now stored as a internal parameter. To check if there is already defined value somewhere - float fpPsLifetime = 0.125; //ps - float fPPsToOPsFrac = 1.0/3.0; - float fOPsMeanLifetime = 142.; //ns - float fHyperfineCoef = 372; + float fParaPsLifetime = 0.125; //ps + float fParaToOrthoPsFraction = 1.0/3.0; + float fOrthoPsMeanLifetime = 142.; //ns + float fHyperfineCoefficient = 372; }; #endif diff --git a/source/physics/src/GateExtendedVSourceMessenger.cc b/source/physics/src/GateExtendedVSourceMessenger.cc index 95f897a34..7cdf366a2 100644 --- a/source/physics/src/GateExtendedVSourceMessenger.cc +++ b/source/physics/src/GateExtendedVSourceMessenger.cc @@ -78,7 +78,7 @@ void GateExtendedVSourceMessenger::InitCommands() upCmdSetPositroniumFractions.reset(GetStringCmd( "setPositroniumFractions", "\"f1, f2, f3 .., fn\" - where fi in [0.0, 1.0] and sum of all fi ==1" ) ); upCmdSetPositroniumLifetimes.reset(GetStringCmd( "setPositroniumLifetimes", "\"t1, t2, t3 .., tn\" - where" ) ); upCmdSetDecayKinds.reset(GetStringCmd( "setDecayKinds", "\"k1, k2, k3 .., kn\" - where ki is k2gamma or k3gamma" ) ); - upCmdSetPositronInteractions.reset(GetStringCmd( "setPositronInteractions", "\"k1, k2, k3 .., kn\" - where ki is kpPs, kdirect or koPs" ) ); + upCmdSetPositronInteractions.reset(GetStringCmd( "setPositronInteractions", "\"k1, k2, k3 .., kn\" - where ki is kpPs, kdirect or koPs, of a given element in vector of components. Used to properly recalculate intensities of the components from the theory" ) ); upCmdSetPromptPhotonProbabilites.reset(GetStringCmd( "setPromptPhotonProbabilites", "\"f1, f2, f3 .., fn\" - where fi in [0.0, 1.0] " ) ); upCmdSetPromptPhotonEnergies.reset(GetStringCmd( "setPromptPhotonEnergies", "\"e1, e2, e3 .., fn\" - where ei are energies " ) ); } @@ -111,10 +111,8 @@ void GateExtendedVSourceMessenger::SetNewValue( G4UIcommand* command, G4String n std::stringstream ss(new_value); float prob; while (ss >> prob) { - if (prob < 0) - prob = 0; - else if (prob > 1) - prob = 1; + prob = prob < 0 ? 0 : prob; + prob = prob > 1 ? 1 : prob; promptPhotonProb.push_back(prob); } fParamGenerator.SetPromptGammaProbabilities(promptPhotonProb); From 4def586b56d5ed755ec8ffc384cbfcb95b479ceb Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Wed, 21 Jan 2026 12:02:31 +0100 Subject: [PATCH 061/144] Remove unused include headers --- source/physics/src/GatePositroniumHelper.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/source/physics/src/GatePositroniumHelper.cc b/source/physics/src/GatePositroniumHelper.cc index 9bce43d7c..271cd24d1 100644 --- a/source/physics/src/GatePositroniumHelper.cc +++ b/source/physics/src/GatePositroniumHelper.cc @@ -4,7 +4,6 @@ #include "GateMessageManager.hh" #include "GatePositroniumHelper.hh" -#include "globals.hh" PositroniumDecayModelParams GatePositroniumHelper::CalculateFractionsFromLifetimes(PositroniumDecayModelParams params) { From d258cea81febe8b22f1a240ece16fbef5f405fb5 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Wed, 21 Jan 2026 12:18:54 +0100 Subject: [PATCH 062/144] Fix naming of the variables. Still tests don't compile!!! --- source/physics/src/GatePositroniumHelper.cc | 20 ++++++++++---------- tests/test_GatePositroniumHelper.cpp | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/source/physics/src/GatePositroniumHelper.cc b/source/physics/src/GatePositroniumHelper.cc index 271cd24d1..ca0dc7b83 100644 --- a/source/physics/src/GatePositroniumHelper.cc +++ b/source/physics/src/GatePositroniumHelper.cc @@ -35,7 +35,7 @@ PositroniumDecayModelParams GatePositroniumHelper::CalculateFractionsFromLifetim float pPsIntens = CalcPPsFractionFromOPs(paramsOut.fFractions, paramsOut.fPositronInteractions); if (pPsIntens > 0) { paramsOut.fFractions.push_back(pPsIntens); - paramsOut.fLifetimes.push_back(fpPsLifetime); + paramsOut.fLifetimes.push_back(fParaPsLifetime); paramsOut.fPromptGammaProbabilities.push_back(paramsOut.fPromptGammaProbabilities.at(0)); paramsOut.fPromptGammaEnergy.push_back(paramsOut.fPromptGammaEnergy.at(0)); paramsOut.fDecayKind.push_back(PositroniumDecayKind::k2Gamma); @@ -57,19 +57,19 @@ float GatePositroniumHelper::CalcPPsFractionFromOPs(std::vector fractions ++itFrac; ++itDec; } - return sum*fPPsToOPsFrac; + return sum*fParaToOrthoPsFraction ; } std::pair GatePositroniumHelper::CalcFractionsFromLifetime(float intensity, float lifetime, PositronElectronInteraction inter) { float intens2G = 1., intens3G = 0.; switch (inter) { case PositronElectronInteraction::kDirect: - intens2G = intensity*(fHyperfineCoef - 1.)/fHyperfineCoef; - intens3G = intensity/fHyperfineCoef; + intens2G = intensity*(fHyperfineCoefficient - 1.)/fHyperfineCoefficient; + intens3G = intensity/fHyperfineCoefficient; break; case PositronElectronInteraction::koPs: - intens2G = intensity*(fOPsMeanLifetime - lifetime)/fOPsMeanLifetime; - intens3G = intensity*lifetime/fOPsMeanLifetime; + intens2G = intensity*(fOrthoPsMeanLifetime - lifetime)/fOrthoPsMeanLifetime; + intens3G = intensity*lifetime/fOrthoPsMeanLifetime; break; } return std::make_pair(intens2G, intens3G); @@ -80,14 +80,14 @@ float GatePositroniumHelper::CalcFractionFromOPsLifetime(float intensity, float if (intensity > 0 && lifetime > 0) { switch (decay) { case PositroniumDecayKind::k2Gamma: - nominator = fOPsMeanLifetime - lifetime; + nominator = fOrthoPsMeanLifetime - lifetime; break; case PositroniumDecayKind::k3Gamma: nominator = lifetime; break; // If there will be more decays one needs to modify it } - return intensity*nominator/fOPsMeanLifetime; + return intensity*nominator/fOrthoPsMeanLifetime ; } else return nominator; } @@ -97,13 +97,13 @@ float GatePositroniumHelper::CalcFractionFromDirectLifetime(float intensity, Pos if (intensity > 0) { switch (decay) { case PositroniumDecayKind::k2Gamma: - nominator = fHyperfineCoef - 1.; + nominator = fHyperfineCoefficient - 1.; break; case PositroniumDecayKind::k3Gamma: nominator = 1.; break; } - return intensity*nominator/fHyperfineCoef; + return intensity*nominator/fHyperfineCoefficient ; } else return nominator; } diff --git a/tests/test_GatePositroniumHelper.cpp b/tests/test_GatePositroniumHelper.cpp index 8caa38d26..b04d097fe 100644 --- a/tests/test_GatePositroniumHelper.cpp +++ b/tests/test_GatePositroniumHelper.cpp @@ -32,7 +32,7 @@ bool run_tests() std::vector fractions = {2, 12, 4, 2}; std::vector lifetimes = {0.125, 0.4, 2.0, 50.0}; - std::vector decays = {kpPs, kDirect, koPs, koPs} + std::vector decays = {kpPs, kDirect, koPs, koPs}; std::vector normFractions = helper.NormalizeFractions(fractions); std::vector goodFractions = {0.1, 0.6, 0.2, 0.1}; for (int i=0; i Date: Wed, 21 Jan 2026 12:26:41 +0100 Subject: [PATCH 063/144] Change to constexpt from float --- source/physics/include/GatePositroniumHelper.hh | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/source/physics/include/GatePositroniumHelper.hh b/source/physics/include/GatePositroniumHelper.hh index b5fa4ff77..4cface60e 100644 --- a/source/physics/include/GatePositroniumHelper.hh +++ b/source/physics/include/GatePositroniumHelper.hh @@ -2,8 +2,6 @@ #define GatePositroniumHelper_h 1 #include "GatePositroniumDecayModelParams.hh" -#include "GatePositroniumDecayModel.hh" -#include "globals.hh" class GatePositroniumHelper { @@ -20,10 +18,10 @@ public: private: //For now stored as a internal parameter. To check if there is already defined value somewhere - float fParaPsLifetime = 0.125; //ps - float fParaToOrthoPsFraction = 1.0/3.0; - float fOrthoPsMeanLifetime = 142.; //ns - float fHyperfineCoefficient = 372; + static constexpr float fParaPsLifetime = 0.125; //ps + static constexpr float fParaToOrthoPsFraction = 1.0/3.0; + static constexpr float fOrthoPsMeanLifetime = 142.; //ns + static constexpr float fHyperfineCoefficient = 372; }; #endif From 7abed5c4af6ae60b70d0903901e23ba1d5c42427 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Wed, 21 Jan 2026 12:48:35 +0100 Subject: [PATCH 064/144] Extract constants to namespace --- .../include/GatePositroniumConstants.hh | 19 +++++++++++++++ .../physics/include/GatePositroniumHelper.hh | 7 ------ source/physics/src/GatePositroniumHelper.cc | 23 +++++++++++-------- 3 files changed, 32 insertions(+), 17 deletions(-) create mode 100644 source/physics/include/GatePositroniumConstants.hh diff --git a/source/physics/include/GatePositroniumConstants.hh b/source/physics/include/GatePositroniumConstants.hh new file mode 100644 index 000000000..fa6760602 --- /dev/null +++ b/source/physics/include/GatePositroniumConstants.hh @@ -0,0 +1,19 @@ +/** ---------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + ----------------------*/ +#ifndef GatePositroniumConstants_hh +#define GatePositroniumConstants_hh + +namespace gate_positronium_constants +{ + constexpr double kParaPsLifetime_ps = 0.125; + constexpr double kParaToOrthoPsFraction = 1.0/3.0; + constexpr double kOrthoPsMeanLifetime_ns = 142.; + constexpr double kHyperfineCoefficient = 372; +} + +#endif + diff --git a/source/physics/include/GatePositroniumHelper.hh b/source/physics/include/GatePositroniumHelper.hh index 4cface60e..b90c3dd64 100644 --- a/source/physics/include/GatePositroniumHelper.hh +++ b/source/physics/include/GatePositroniumHelper.hh @@ -15,13 +15,6 @@ public: float CalcFractionFromOPsLifetime(float intensity, float lifetime, PositroniumDecayKind decay); float CalcFractionFromDirectLifetime(float intensity, PositroniumDecayKind decay); std::vector NormalizeFractions (std::vector fractions); - -private: -//For now stored as a internal parameter. To check if there is already defined value somewhere - static constexpr float fParaPsLifetime = 0.125; //ps - static constexpr float fParaToOrthoPsFraction = 1.0/3.0; - static constexpr float fOrthoPsMeanLifetime = 142.; //ns - static constexpr float fHyperfineCoefficient = 372; }; #endif diff --git a/source/physics/src/GatePositroniumHelper.cc b/source/physics/src/GatePositroniumHelper.cc index ca0dc7b83..63457ea92 100644 --- a/source/physics/src/GatePositroniumHelper.cc +++ b/source/physics/src/GatePositroniumHelper.cc @@ -4,6 +4,9 @@ #include "GateMessageManager.hh" #include "GatePositroniumHelper.hh" +#include "GatePositroniumConstants.hh" + +using namespace gate_positronium_constants; PositroniumDecayModelParams GatePositroniumHelper::CalculateFractionsFromLifetimes(PositroniumDecayModelParams params) { @@ -35,7 +38,7 @@ PositroniumDecayModelParams GatePositroniumHelper::CalculateFractionsFromLifetim float pPsIntens = CalcPPsFractionFromOPs(paramsOut.fFractions, paramsOut.fPositronInteractions); if (pPsIntens > 0) { paramsOut.fFractions.push_back(pPsIntens); - paramsOut.fLifetimes.push_back(fParaPsLifetime); + paramsOut.fLifetimes.push_back(kParaPsLifetime_ps); paramsOut.fPromptGammaProbabilities.push_back(paramsOut.fPromptGammaProbabilities.at(0)); paramsOut.fPromptGammaEnergy.push_back(paramsOut.fPromptGammaEnergy.at(0)); paramsOut.fDecayKind.push_back(PositroniumDecayKind::k2Gamma); @@ -57,19 +60,19 @@ float GatePositroniumHelper::CalcPPsFractionFromOPs(std::vector fractions ++itFrac; ++itDec; } - return sum*fParaToOrthoPsFraction ; + return sum*kParaToOrthoPsFraction ; } std::pair GatePositroniumHelper::CalcFractionsFromLifetime(float intensity, float lifetime, PositronElectronInteraction inter) { float intens2G = 1., intens3G = 0.; switch (inter) { case PositronElectronInteraction::kDirect: - intens2G = intensity*(fHyperfineCoefficient - 1.)/fHyperfineCoefficient; - intens3G = intensity/fHyperfineCoefficient; + intens2G = intensity*(kHyperfineCoefficient - 1.)/kHyperfineCoefficient; + intens3G = intensity/kHyperfineCoefficient; break; case PositronElectronInteraction::koPs: - intens2G = intensity*(fOrthoPsMeanLifetime - lifetime)/fOrthoPsMeanLifetime; - intens3G = intensity*lifetime/fOrthoPsMeanLifetime; + intens2G = intensity*(kOrthoPsMeanLifetime_ns - lifetime)/kOrthoPsMeanLifetime_ns; + intens3G = intensity*lifetime/kOrthoPsMeanLifetime_ns; break; } return std::make_pair(intens2G, intens3G); @@ -80,14 +83,14 @@ float GatePositroniumHelper::CalcFractionFromOPsLifetime(float intensity, float if (intensity > 0 && lifetime > 0) { switch (decay) { case PositroniumDecayKind::k2Gamma: - nominator = fOrthoPsMeanLifetime - lifetime; + 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/fOrthoPsMeanLifetime ; + return intensity*nominator/kOrthoPsMeanLifetime_ns ; } else return nominator; } @@ -97,13 +100,13 @@ float GatePositroniumHelper::CalcFractionFromDirectLifetime(float intensity, Pos if (intensity > 0) { switch (decay) { case PositroniumDecayKind::k2Gamma: - nominator = fHyperfineCoefficient - 1.; + nominator = kHyperfineCoefficient - 1.; break; case PositroniumDecayKind::k3Gamma: nominator = 1.; break; } - return intensity*nominator/fHyperfineCoefficient ; + return intensity*nominator/kHyperfineCoefficient ; } else return nominator; } From c96dea5dac412b56304f6339eb9eccfa01790a29 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Wed, 21 Jan 2026 12:49:02 +0100 Subject: [PATCH 065/144] Fix tests --- tests/test_GatePositroniumHelper.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_GatePositroniumHelper.cpp b/tests/test_GatePositroniumHelper.cpp index b04d097fe..2f55daa58 100644 --- a/tests/test_GatePositroniumHelper.cpp +++ b/tests/test_GatePositroniumHelper.cpp @@ -50,8 +50,8 @@ bool run_tests() std::cerr << "Calculation of direct annihilation intensity error" << std::endl; } - float intensityOPs2G = helper.CalcFractionFromOPsLifetime(goodFractions.at(2), PositroniumDecayKind::k2Gamma); - float intensityOPs3G = helper.CalcFractionFromOPsLifetime(goodFractions.at(2), PositroniumDecayKind::k3Gamma); + float intensityOPs2G = helper.CalcFractionFromOPsLifetime(goodFractions.at(2), lifetimes.at(2), PositroniumDecayKind::k2Gamma); + float intensityOPs3G = helper.CalcFractionFromOPsLifetime(goodFractions.at(2), lifetimes.at(2), PositroniumDecayKind::k3Gamma); float goodIntensOPs3G = lifetimes.at(2)*goodFractions.at(2)/142.; if (intensityOPs3G != goodIntensOPs3G || intensityOPs2G != goodFractions.at(2) - goodIntensOPs3G) { res = false; From cf77b917b979e721e89208894348ca355587ed34 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Thu, 15 Jan 2026 13:34:48 +0100 Subject: [PATCH 066/144] Add GatePositroniumSource which basically should replace GatExtendedVSource. Also, add GatePositroniumSourceMessenger. Also, rename GateMiniPositroniumDecayModel to GatePositroniumDecayModel --- .../include/GateMiniPositroniumDecayModel.hh | 41 ---- .../include/GatePositroniumDecayModel.hh | 127 ++---------- .../physics/include/GatePositroniumSource.hh | 51 +++++ .../include/GatePositroniumSourceMessenger.hh | 60 ++++++ source/physics/src/GateExtendedVSource.cc | 8 +- .../src/GateMiniPositroniumDecayModel.cc | 99 ---------- .../physics/src/GatePositroniumDecayModel.cc | 180 +++++------------- source/physics/src/GatePositroniumSource.cc | 79 ++++++++ .../src/GatePositroniumSourceMessenger.cc | 150 +++++++++++++++ tests/test_GatePositroniumDecayModel.cpp | 12 +- 10 files changed, 420 insertions(+), 387 deletions(-) delete mode 100644 source/physics/include/GateMiniPositroniumDecayModel.hh create mode 100644 source/physics/include/GatePositroniumSource.hh create mode 100644 source/physics/include/GatePositroniumSourceMessenger.hh delete mode 100644 source/physics/src/GateMiniPositroniumDecayModel.cc create mode 100644 source/physics/src/GatePositroniumSource.cc create mode 100644 source/physics/src/GatePositroniumSourceMessenger.cc diff --git a/source/physics/include/GateMiniPositroniumDecayModel.hh b/source/physics/include/GateMiniPositroniumDecayModel.hh deleted file mode 100644 index a515aaf14..000000000 --- a/source/physics/include/GateMiniPositroniumDecayModel.hh +++ /dev/null @@ -1,41 +0,0 @@ -/** ---------------------- - Copyright (C): OpenGATE Collaboration - This software is distributed under the terms - of the GNU Lesser General Public Licence (LGPL) - See LICENSE.md for further details - ----------------------*/ -#ifndef GateMiniPositroniumDecayModel_hh -#define GateMiniPositroniumDecayModel_hh - -#include - -#include "G4PrimaryParticle.hh" -#include "G4PrimaryVertex.hh" - -#include "GateGammaEmissionModel.hh" -#include "GatePositroniumDecayModelParams.hh" -#include "GatePositronium.hh" - -// Todo change the name to GatePositroniumDecayModel -// Todo2: add docs -class MiniPositroniumDecayModel:public GateGammaEmissionModel -{ - public: - static int getPositroniumDecayIndex(const std::vector& fractions); - - public: - explicit MiniPositroniumDecayModel(const PositroniumDecayModelParams& modelParams); - - protected: - virtual G4int GeneratePrimaryVertices(G4Event* event, G4double& particle_time, G4ThreeVector& particle_position) override; - G4PrimaryVertex* GetPrimaryVertexFromDeexcitation(G4double particle_time, const G4ThreeVector& particle_position, int decayIndex); - G4PrimaryVertex *GetPrimaryVertexFromPositroniumAnnihilation(G4double particle_time, const G4ThreeVector &particle_position, int decayIndex); - G4PrimaryParticle* GetGammaFromDeexcitation(int decayIndex); - std::vector GetGammasFromPositroniumAnnihilation(int decayIndex); - -private: - PositroniumDecayModelParams fModelParams; - std::vector fPositroniumDecayChannel; -}; - -#endif diff --git a/source/physics/include/GatePositroniumDecayModel.hh b/source/physics/include/GatePositroniumDecayModel.hh index fc5e714c6..18b92a5b5 100644 --- a/source/physics/include/GatePositroniumDecayModel.hh +++ b/source/physics/include/GatePositroniumDecayModel.hh @@ -4,123 +4,38 @@ of the GNU Lesser General Public Licence (LGPL) See LICENSE.md for further details ----------------------*/ -#ifndef PositroniumDecayModel_hh -#define PositroniumDecayModel_hh +#ifndef GatePositroniumDecayModel_hh +#define GatePositroniumDecayModel_hh #include #include "G4PrimaryParticle.hh" -#include "G4SystemOfUnits.hh" -#include "G4VDecayChannel.hh" #include "G4PrimaryVertex.hh" -#include "GateEmittedGammaInformation.hh" #include "GateGammaEmissionModel.hh" +#include "GatePositroniumDecayModelParams.hh" +#include "GatePositronium.hh" - -/** Author: Mateusz Bała - * Email: bala.mateusz@gmail.com - * About class: Class generate gammas from positronium decay with including deexcitation gamma ( prompt gamma ) if is required. - **/ -class GatePositroniumDecayModel : public GateGammaEmissionModel +// Todo change the name to GatePositroniumDecayModel +// Todo2: add docs +class PositroniumDecayModel:public GateGammaEmissionModel { - public: - - /** About class: representation of positronium for main class. Provides access to positronium decay channel. - **/ - class Positronium - { - public: - Positronium( G4String name, G4double life_time, G4int annihilation_gammas_number ); - void SetLifeTime( const G4double& life_time ); - G4double GetLifeTime() const; - G4String GetName() const; - G4int GetAnnihilationGammasNumber() const; - G4DecayProducts* GetDecayProducts(); - private: - G4String fName = ""; - G4double fLifeTime = 0.0;//[ns] - G4int fAnnihilationGammasNumber = 0; - G4VDecayChannel* pDecayChannel = nullptr; - }; - - /** Positronium kind tells which positronium we will use. - * Depends on used Ps gammas number and time will be different. - **/ - enum PositroniumKind { pPs, oPs }; - /** Decay model descibes decay of positronium. - * For example for PositroniumKind::pPs we have decays: - * 1) Standard: pPs -> 2 gamma - * 2) WithPrompt: pPs* -> 2 gamma + prompt_gamma - * Only prompt gamma has nod modified time, other gammas always has modified time. - **/ - enum DecayModel { Standard, WithPrompt }; - - GatePositroniumDecayModel(); - virtual ~GatePositroniumDecayModel(); - - void SetPositroniumKind( PositroniumKind positronium_kind ); - PositroniumKind GetPositroniumKind() const; - - void SetDecayModel( DecayModel decay_model ); - DecayModel GetDecayModel() const; + public: + static int getPositroniumDecayIndex(const std::vector& fractions); - void SetPostroniumLifetime( G4String positronium_name, G4double life_time ); //[ns] + public: + explicit PositroniumDecayModel(const PositroniumDecayModelParams& modelParams); - void SetPromptGammaEnergy( G4double prompt_energy ); //[keV] - G4double GetPromptGammaEnergy() const; //[keV] - - /** Set probability of gammas emission from para-positronium decay ( and ortho-positronium too ) - * @param: fraction - number in range from 0.0 to 1.0 ( 0.0 - generate only gammas from oPs decay, 1.0 - generate only gammas from pPs decay ) - **/ - void SetParaPositroniumFraction( G4double fraction ); - - /** Generate vertices and fill them with gammas. Vertices number depends on used DecayModel: - * - one ( if DecayModel::Standard ) - * - two ( if DecayModel::WithPrompt ) - **/ + protected: virtual G4int GeneratePrimaryVertices(G4Event* event, G4double& particle_time, G4ThreeVector& particle_position) override; - - protected: - /** Provides additional information for user about gamma - **/ - virtual GateEmittedGammaInformation* GetPrimaryParticleInformation( const G4PrimaryParticle* pp, const GateEmittedGammaInformation::GammaKind& gamma_kind ) const override; - /** Depends on used model and setted fractions it chooses positronium which decay will be used to generate gammas - **/ - void PreparePositroniumParametrization(); - /** Generate vertex for deexcitation gamma ( prompt gamma ) - position and time is the same as generted by source - **/ - G4PrimaryVertex* GetPrimaryVertexFromDeexcitation(const G4double& particle_time, const G4ThreeVector& particle_position ); - /** Generate vertex for annihilation gammas - position is the same as generted by source, but time is shifted by positronium lifetime ( T0 + f(lifetime)) - **/ - G4PrimaryVertex* GetPrimaryVertexFromPositroniumAnnihilation( const G4double& particle_time, const G4ThreeVector& particle_position ); - /** Generate deexcitation ( prompt ) gamma - **/ - G4PrimaryParticle* GetGammaFromDeexcitation(); - /** Generate annihilation gammas - **/ - std::vector GetGammasFromPositroniumAnnihilation(); - - protected: - //Positronium model - for para-positronium - Positronium fParaPs = Positronium( "pPs", 0.1244 * ns, 2 ); - //Positronium model - for ortho-positronium - Positronium fOrthoPs = Positronium( "oPs", 138.6 * ns, 3 ); - //Positronium model - for current event - Positronium* pInfoPs = nullptr; - //Default deexcitation gamma energy - if user didn't set prompt gamma energy this value will be used - static constexpr G4double kSodium22DeexcitationGammaEnergy = 1.022 * MeV; - - //Which positronium use for gammas generation - PositroniumKind fPositroniumKind = PositroniumKind::pPs; - //Which decay model use for gammas generation - DecayModel fDecayModel = DecayModel::Standard; - //Dexcitation gamma energy - G4double fPromptGammaEnergy = kSodium22DeexcitationGammaEnergy;//[MeV] - //Propability of emiiting gammas from para-positronium ( number in range from 0.0 to 1.0 ) - G4double fParaPositroniumFraction = 1.0; - //It is required to generate mixed positronium decays ( pPs and oPs witch propability controled by varaible fParaPositroniumFraction ) - G4bool fUsePositroniumFractions = false; - + G4PrimaryVertex* GetPrimaryVertexFromDeexcitation(G4double particle_time, const G4ThreeVector& particle_position, int decayIndex); + G4PrimaryVertex *GetPrimaryVertexFromPositroniumAnnihilation(G4double particle_time, const G4ThreeVector &particle_position, int decayIndex); + G4PrimaryParticle* GetGammaFromDeexcitation(int decayIndex); + std::vector GetGammasFromPositroniumAnnihilation(int decayIndex); + +private: + PositroniumDecayModelParams fModelParams; + std::vector fPositroniumDecayChannel; }; + #endif diff --git a/source/physics/include/GatePositroniumSource.hh b/source/physics/include/GatePositroniumSource.hh new file mode 100644 index 000000000..496e895ca --- /dev/null +++ b/source/physics/include/GatePositroniumSource.hh @@ -0,0 +1,51 @@ +/** ---------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + ----------------------*/ +#ifndef GatePositroniumSource_hh +#define GatePositroniumSource_hh + +#include + +#include "GateVSource.hh" +#include "GatePositroniumSourceMessenger.hh" +#include "GateGammaEmissionModel.hh" + +class GatePositroniumSource : public GateVSource +{ +public: + + enum class ModelKind { + NotDefined, //by default - in this case this class will behave like GateVSource + ParaPositronium, //generate gammas from para-positronium decay + OrthoPositronium, //generate gammas from ortho-positronium decay + Positronium}; // generate gammas from mixed model + + explicit GatePositroniumSource(const G4String& name); + virtual ~GatePositroniumSource() = default; + + /** Generate gammas for event + **/ + virtual G4int GeneratePrimaries( G4Event* event ) override; + + protected: + /** Set model used for this source. If is not defined then this class will behave like GateVSource. + **/ + void SetModel(const G4String &model_name); + /** Prepare model to work - set all settings from user to model + **/ + void PrepareModel(); + + protected: + std::unique_ptr pModel; + std::unique_ptr pMessenger; + //User settings: + ModelKind fModelKind = ModelKind::NotDefined; + + //Set by PrepareModel() and used in GeneratePrimaries() + G4bool fBehaveLikeVSource = false; +}; + +#endif diff --git a/source/physics/include/GatePositroniumSourceMessenger.hh b/source/physics/include/GatePositroniumSourceMessenger.hh new file mode 100644 index 000000000..0790c5811 --- /dev/null +++ b/source/physics/include/GatePositroniumSourceMessenger.hh @@ -0,0 +1,60 @@ +/** ---------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + ----------------------*/ +#ifndef GatePositroniumSourceMessenger_hh +#define GatePositroniumSourceMessenger_hh + +#include + +#include "GateVSourceMessenger.hh" +#include "G4UIcmdWithAnInteger.hh" +#include "G4UIcmdWithADoubleAndUnit.hh" +#include "G4UIcmdWithABool.hh" +#include "G4UIcmdWith3Vector.hh" +#include "G4UIcmdWithAString.hh" +#include "G4UIcmdWith3VectorAndUnit.hh" + +#include "GatePositroniumDecayParamsGenerator.hh" + +class GatePositroniumSource; + +/** + * About class: Messenger for GatePositroniumSource class + **/ +class GatePositroniumSourceMessenger: public GateVSourceMessenger +{ + public: + explicit GatePositroniumSourceMessenger(GatePositroniumSource *source); + ~GatePositroniumSourceMessenger()=default; + + void SetNewValue(G4UIcommand *command, G4String newValue) override; + + PositroniumDecayModelParams generatePositroniumDecayParams(GatePositroniumDecayParamsGenerator::DecayModel model= GatePositroniumDecayParamsGenerator::kPositronium) const; + + protected: + void InitCommands(); + + G4UIcmdWithABool* GetBoolCmd(const G4String& cmd_name, const G4String& cmd_guidance); + G4UIcmdWithADoubleAndUnit* GetDoubleCmdWithUnit(const G4String& cmd_name, const G4String& cmd_guidance, const G4String& default_unit, const G4String& unit_candidates); + G4UIcmdWith3Vector* GetVectorCmd(const G4String& cmd_name, const G4String& cmd_guidance); + G4UIcmdWithAnInteger* GetIntCmd(const G4String& cmd_name, const G4String& cmd_guidance); + G4UIcmdWithAString* GetStringCmd(const G4String& cmd_name, const G4String& cmd_guidance); + G4UIcmdWith3VectorAndUnit* GetVectorCmdWithUnit(const G4String& cmd_name, const G4String& cmd_guidance, const G4String& default_unit, const G4String& unit_candidates); + + protected: + GatePositroniumSource* pSource = nullptr; + + std::unique_ptr upCmdSetPositroniumFractions; + std::unique_ptr upCmdSetPositroniumLifetimes; + std::unique_ptr upCmdSetIsPromptPhoton; + std::unique_ptr upCmdSetPromptPhotonEnergies; + std::unique_ptr upCmdSetDecayKinds; + + GatePositroniumDecayParamsGenerator fParamGenerator; + +}; + +#endif diff --git a/source/physics/src/GateExtendedVSource.cc b/source/physics/src/GateExtendedVSource.cc index 18e5dfa3d..1f8b4c36c 100644 --- a/source/physics/src/GateExtendedVSource.cc +++ b/source/physics/src/GateExtendedVSource.cc @@ -9,7 +9,7 @@ #include "G4Event.hh" #include "GateExtendedVSource.hh" -#include "GateMiniPositroniumDecayModel.hh" +#include "GatePositroniumDecayModel.hh" GateExtendedVSource::GateExtendedVSource(const G4String &name) @@ -47,18 +47,18 @@ void GateExtendedVSource::PrepareModel() if (fModelKind == GateExtendedVSource::ModelKind::Positronium) { auto params = pMessenger->generatePositroniumDecayParams(); - pModel = std::make_unique(params); + pModel = std::make_unique(params); } else { if (fModelKind == GateExtendedVSource::ModelKind::ParaPositronium) { auto params = pMessenger->generatePositroniumDecayParams( GatePositroniumDecayParamsGenerator::kParaPositronium); - pModel = std::make_unique(params); + pModel = std::make_unique(params); } else { if (fModelKind == GateExtendedVSource::ModelKind::OrthoPositronium) { auto params = pMessenger->generatePositroniumDecayParams( GatePositroniumDecayParamsGenerator::kOrthoPositronium); - pModel = std::make_unique(params); + pModel = std::make_unique(params); } else { GateError("GateExtendedVSource::PrepareModel - unknown model."); } diff --git a/source/physics/src/GateMiniPositroniumDecayModel.cc b/source/physics/src/GateMiniPositroniumDecayModel.cc deleted file mode 100644 index f70781208..000000000 --- a/source/physics/src/GateMiniPositroniumDecayModel.cc +++ /dev/null @@ -1,99 +0,0 @@ -/** ---------------------- - Copyright (C): OpenGATE Collaboration - This software is distributed under the terms - of the GNU Lesser General Public Licence (LGPL) - See LICENSE.md for further details - ----------------------*/ -#include -#include - -#include "Randomize.hh" -#include "G4DecayProducts.hh" -#include "G4LorentzVector.hh" - -#include "GateMiniPositroniumDecayModel.hh" - -int MiniPositroniumDecayModel::getPositroniumDecayIndex(const std::vector& fractions) { - auto r = G4UniformRand(); - float curr_frac_cumulative = 0.0; - for (int i = 0; i < fractions.size(); ++i) { - curr_frac_cumulative = curr_frac_cumulative + fractions[i]; - if(r<= curr_frac_cumulative) return i; - } - return -1; -} - -MiniPositroniumDecayModel::MiniPositroniumDecayModel(const PositroniumDecayModelParams& modelParams):fModelParams(modelParams) -{ - auto num_of_decay_channels = fModelParams.fDecayKind.size(); - for (int i = 0; i < num_of_decay_channels; i++) { - if (fModelParams.fDecayKind[i] == PositroniumDecayKind::k2Gamma) - { - fPositroniumDecayChannel.push_back(std::move(GatePositronium("pPs", fModelParams.fLifetimes[i]* ns))); - } else { - fPositroniumDecayChannel.push_back(std::move(GatePositronium("pPs", fModelParams.fLifetimes[i]* ns))); - } - } -} - -G4PrimaryVertex* MiniPositroniumDecayModel::GetPrimaryVertexFromPositroniumAnnihilation(G4double particle_time, const G4ThreeVector& particle_position, int decayIndex) -{ - - G4double shifted_particle_time = particle_time + G4RandExponential::shoot(fModelParams.fLifetimes[decayIndex]); - - G4PrimaryVertex* vertex = new G4PrimaryVertex( particle_position, shifted_particle_time ); - std::vector gammas = GetGammasFromPositroniumAnnihilation(decayIndex); - std::for_each( gammas.begin(), gammas.end(), [&]( G4PrimaryParticle* gamma ) { vertex->SetPrimary( gamma ); } ); - return vertex; -} - -G4int MiniPositroniumDecayModel::GeneratePrimaryVertices(G4Event* event, G4double& particle_time, G4ThreeVector& particle_position) -{ - auto decayIndex = MiniPositroniumDecayModel::getPositroniumDecayIndex(fModelParams.fFractions); - - G4int number_of_vertices = 1; - if(fModelParams.fPromptGammaProbabilities[decayIndex] > G4UniformRand()) - { - ++number_of_vertices; - event->AddPrimaryVertex(GetPrimaryVertexFromDeexcitation(particle_time, particle_position, decayIndex)); - } - event->AddPrimaryVertex(GetPrimaryVertexFromPositroniumAnnihilation(particle_time, particle_position, decayIndex)); - return number_of_vertices; -} - -G4PrimaryParticle* MiniPositroniumDecayModel::GetGammaFromDeexcitation(int decayIndex) -{ - G4PrimaryParticle* gamma = GetSingleGamma(fModelParams.fPromptGammaEnergy[decayIndex]); - gamma->SetUserInformation( GetPrimaryParticleInformation( gamma, GateEmittedGammaInformation::GammaKind::Prompt ) ); - return gamma; -} - -G4PrimaryVertex* MiniPositroniumDecayModel::GetPrimaryVertexFromDeexcitation(G4double particle_time, const G4ThreeVector& particle_position, int decayIndex) -{ - G4PrimaryVertex* vertex = new G4PrimaryVertex(particle_position, particle_time); - vertex->SetPrimary(GetGammaFromDeexcitation(decayIndex)); - return vertex; -} - -std::vector MiniPositroniumDecayModel::GetGammasFromPositroniumAnnihilation(int decayIndex) -{ - int annihilation_gammas_number = fPositroniumDecayChannel[decayIndex].GetAnnihilationGammasNumber(); - std::vector gammas(annihilation_gammas_number); - - G4DecayProducts* decay_products = fPositroniumDecayChannel[decayIndex].GetDecayProducts(); - for ( G4int i = 0; i < annihilation_gammas_number; ++i ) - { - G4PrimaryParticle* gamma = new G4PrimaryParticle( pGammaDefinition ); - - G4DynamicParticle* dynamic_gamma = (*decay_products)[i]; - G4LorentzVector lv = dynamic_gamma->Get4Momentum(); - gamma->Set4Momentum( lv.px(), lv.py(), lv.pz(), lv.e() ); - gamma->SetPolarization( dynamic_gamma->GetPolarization() ); - gamma->SetUserInformation( GetPrimaryParticleInformation( gamma, GateEmittedGammaInformation::GammaKind::Annihilation ) ); - gammas[i] = gamma; - } - delete decay_products; - - return gammas; -} - diff --git a/source/physics/src/GatePositroniumDecayModel.cc b/source/physics/src/GatePositroniumDecayModel.cc index 755615e5d..071e86e94 100644 --- a/source/physics/src/GatePositroniumDecayModel.cc +++ b/source/physics/src/GatePositroniumDecayModel.cc @@ -10,161 +10,78 @@ #include "Randomize.hh" #include "G4DecayProducts.hh" #include "G4LorentzVector.hh" -#include "G4ParticleTable.hh" -#include "G4DecayTable.hh" -#include "G4ParticleTable.hh" -#include "G4DecayTable.hh" -#include "G4ParticleDefinition.hh" #include "GatePositroniumDecayModel.hh" -GatePositroniumDecayModel::Positronium::Positronium(const G4String name, G4double life_time, G4int annihilation_gammas_number ) : fName( name ), fLifeTime( life_time ), fAnnihilationGammasNumber( annihilation_gammas_number ) -{ - G4ParticleDefinition* positronium_def = G4ParticleTable::GetParticleTable()->FindParticle( name ); - G4DecayTable* positronium_decay_table = positronium_def->GetDecayTable(); - pDecayChannel = positronium_decay_table->GetDecayChannel(0); -} - -void GatePositroniumDecayModel::Positronium::SetLifeTime( const G4double& life_time ) { fLifeTime = life_time; } - -G4double GatePositroniumDecayModel::Positronium::GetLifeTime() const { return fLifeTime; } - -G4String GatePositroniumDecayModel::Positronium::GetName() const { return fName; } - -G4int GatePositroniumDecayModel::Positronium::GetAnnihilationGammasNumber() const { return fAnnihilationGammasNumber; } - -G4DecayProducts* GatePositroniumDecayModel::Positronium::GetDecayProducts() { return pDecayChannel->DecayIt(); } - -GatePositroniumDecayModel::GatePositroniumDecayModel() -{ - SetModelName( "GatePositroniumDecayModel" ); -} - -GatePositroniumDecayModel::~GatePositroniumDecayModel() {} - -void GatePositroniumDecayModel::SetPositroniumKind( GatePositroniumDecayModel::PositroniumKind positronium_kind ) { fPositroniumKind = positronium_kind; } - -GatePositroniumDecayModel::PositroniumKind GatePositroniumDecayModel::GetPositroniumKind() const { return fPositroniumKind; } - -void GatePositroniumDecayModel::SetDecayModel( GatePositroniumDecayModel::DecayModel decay_model ) { fDecayModel = decay_model; } - -GatePositroniumDecayModel::DecayModel GatePositroniumDecayModel::GetDecayModel() const { return fDecayModel; } - -void GatePositroniumDecayModel::SetPostroniumLifetime( G4String positronium_name, G4double life_time ) -{ - if ( !( life_time > 0.0 ) ) { NoticeError( G4String( __FUNCTION__ ), "positronium life-time should be positive value." ); } - - if ( positronium_name == fParaPs.GetName() ) { fParaPs.SetLifeTime( life_time ); } - else if ( positronium_name == fOrthoPs.GetName() ) { fOrthoPs.SetLifeTime( life_time ); } - else { NoticeError( G4String( __FUNCTION__ ), "Unknown positronium name." ); } -} - -void GatePositroniumDecayModel::SetPromptGammaEnergy( G4double prompt_energy ) -{ - if ( !( prompt_energy > 0.0 ) ) { NoticeError( G4String( __FUNCTION__ ), "prompt gamma energy should be positive value." ); } - fPromptGammaEnergy = prompt_energy; -} - -G4double GatePositroniumDecayModel::GetPromptGammaEnergy() const { return fPromptGammaEnergy; } - -void GatePositroniumDecayModel::SetParaPositroniumFraction( G4double fraction ) -{ - fUsePositroniumFractions = true; - fParaPositroniumFraction = fraction; -} - -void GatePositroniumDecayModel::PreparePositroniumParametrization() -{ - if ( pInfoPs != nullptr ) - { - if ( !fUsePositroniumFractions ) { return; } - - //Let's draw a positronium decay for current event - - if ( fParaPositroniumFraction >= G4UniformRand() ) { fPositroniumKind = PositroniumKind::pPs; } - else { fPositroniumKind = PositroniumKind::oPs; } +int PositroniumDecayModel::getPositroniumDecayIndex(const std::vector& fractions) { + auto r = G4UniformRand(); + float curr_frac_cumulative = 0.0; + for (int i = 0; i < fractions.size(); ++i) { + curr_frac_cumulative = curr_frac_cumulative + fractions[i]; + if(r<= curr_frac_cumulative) return i; } - - switch ( fPositroniumKind ) - { - case PositroniumKind::pPs: - pInfoPs = &fParaPs; - break; - case PositroniumKind::oPs: - pInfoPs = &fOrthoPs; - break; - default: - NoticeError( G4String( __FUNCTION__ ), "improper chosen positronium kind." ); - break; - }; + return -1; } -GateEmittedGammaInformation* GatePositroniumDecayModel::GetPrimaryParticleInformation( const G4PrimaryParticle* pp, const GateEmittedGammaInformation::GammaKind& gamma_kind ) const +PositroniumDecayModel::PositroniumDecayModel(const PositroniumDecayModelParams& modelParams):fModelParams(modelParams) { - GateEmittedGammaInformation* egi = new GateEmittedGammaInformation(); - - GateEmittedGammaInformation::SourceKind source_kind = GateEmittedGammaInformation::SourceKind::ParaPositronium; - GateEmittedGammaInformation::DecayModel decay_model = GateEmittedGammaInformation::DecayModel::Standard; - - if ( fPositroniumKind == PositroniumKind::oPs ) { source_kind = GateEmittedGammaInformation::SourceKind::OrthoPositronium; } - if ( fDecayModel == DecayModel::WithPrompt ) { decay_model = GateEmittedGammaInformation::DecayModel::Deexcitation; } - - egi->SetSourceKind( source_kind ); - egi->SetDecayModel( decay_model ); - egi->SetGammaKind( gamma_kind ); - egi->SetInitialPolarization( pp->GetPolarization() ); - - if ( gamma_kind == GateEmittedGammaInformation::GammaKind::Annihilation ){ egi->SetTimeShift( pInfoPs->GetLifeTime() ); } - - return egi; -} - -G4int GatePositroniumDecayModel::GeneratePrimaryVertices(G4Event* event, G4double& particle_time, G4ThreeVector& particle_position ) -{ - PreparePositroniumParametrization(); - G4int vertexes_number = 1; - - if ( fDecayModel == DecayModel::WithPrompt ) - { - ++vertexes_number; - event->AddPrimaryVertex( GetPrimaryVertexFromDeexcitation(particle_time, particle_position) ); - } - - event->AddPrimaryVertex( GetPrimaryVertexFromPositroniumAnnihilation(particle_time, particle_position) ); - - return vertexes_number; + auto num_of_decay_channels = fModelParams.fDecayKind.size(); + for (int i = 0; i < num_of_decay_channels; i++) { + if (fModelParams.fDecayKind[i] == PositroniumDecayKind::k2Gamma) + { + fPositroniumDecayChannel.push_back(std::move(GatePositronium("pPs", fModelParams.fLifetimes[i]* ns))); + } else { + fPositroniumDecayChannel.push_back(std::move(GatePositronium("pPs", fModelParams.fLifetimes[i]* ns))); + } + } } -G4PrimaryVertex* GatePositroniumDecayModel::GetPrimaryVertexFromDeexcitation(const G4double& particle_time, const G4ThreeVector& particle_position ) +G4PrimaryVertex* PositroniumDecayModel::GetPrimaryVertexFromPositroniumAnnihilation(G4double particle_time, const G4ThreeVector& particle_position, int decayIndex) { - G4PrimaryVertex* vertex = new G4PrimaryVertex(particle_position, particle_time); - vertex->SetPrimary( GetGammaFromDeexcitation() ); - return vertex; -} -G4PrimaryVertex* GatePositroniumDecayModel::GetPrimaryVertexFromPositroniumAnnihilation( const G4double& particle_time, const G4ThreeVector& particle_position ) -{ - G4double shifted_particle_time = particle_time + G4RandExponential::shoot( pInfoPs->GetLifeTime() ); + G4double shifted_particle_time = particle_time + G4RandExponential::shoot(fModelParams.fLifetimes[decayIndex]); G4PrimaryVertex* vertex = new G4PrimaryVertex( particle_position, shifted_particle_time ); - std::vector gammas = GetGammasFromPositroniumAnnihilation(); + std::vector gammas = GetGammasFromPositroniumAnnihilation(decayIndex); std::for_each( gammas.begin(), gammas.end(), [&]( G4PrimaryParticle* gamma ) { vertex->SetPrimary( gamma ); } ); return vertex; } -G4PrimaryParticle* GatePositroniumDecayModel::GetGammaFromDeexcitation() +G4int PositroniumDecayModel::GeneratePrimaryVertices(G4Event* event, G4double& particle_time, G4ThreeVector& particle_position) +{ + auto decayIndex = PositroniumDecayModel::getPositroniumDecayIndex(fModelParams.fFractions); + + G4int number_of_vertices = 1; + if(fModelParams.fIsPromptGamma[decayIndex]) + { + ++number_of_vertices; + event->AddPrimaryVertex(GetPrimaryVertexFromDeexcitation(particle_time, particle_position, decayIndex)); + } + event->AddPrimaryVertex(GetPrimaryVertexFromPositroniumAnnihilation(particle_time, particle_position, decayIndex)); + return number_of_vertices; +} + +G4PrimaryParticle* PositroniumDecayModel::GetGammaFromDeexcitation(int decayIndex) { - G4PrimaryParticle* gamma = GetSingleGamma( fPromptGammaEnergy ); + G4PrimaryParticle* gamma = GetSingleGamma(fModelParams.fPromptGammaEnergy[decayIndex]); gamma->SetUserInformation( GetPrimaryParticleInformation( gamma, GateEmittedGammaInformation::GammaKind::Prompt ) ); return gamma; } -std::vector GatePositroniumDecayModel::GetGammasFromPositroniumAnnihilation() +G4PrimaryVertex* PositroniumDecayModel::GetPrimaryVertexFromDeexcitation(G4double particle_time, const G4ThreeVector& particle_position, int decayIndex) +{ + G4PrimaryVertex* vertex = new G4PrimaryVertex(particle_position, particle_time); + vertex->SetPrimary(GetGammaFromDeexcitation(decayIndex)); + return vertex; +} + +std::vector PositroniumDecayModel::GetGammasFromPositroniumAnnihilation(int decayIndex) { - std::vector gammas( pInfoPs->GetAnnihilationGammasNumber() ) ; + int annihilation_gammas_number = fPositroniumDecayChannel[decayIndex].GetAnnihilationGammasNumber(); + std::vector gammas(annihilation_gammas_number); - G4DecayProducts* decay_products = pInfoPs->GetDecayProducts(); - for ( G4int i = 0; i < pInfoPs->GetAnnihilationGammasNumber(); ++i ) + G4DecayProducts* decay_products = fPositroniumDecayChannel[decayIndex].GetDecayProducts(); + for ( G4int i = 0; i < annihilation_gammas_number; ++i ) { G4PrimaryParticle* gamma = new G4PrimaryParticle( pGammaDefinition ); @@ -179,3 +96,4 @@ std::vector GatePositroniumDecayModel::GetGammasFromPositron return gammas; } + diff --git a/source/physics/src/GatePositroniumSource.cc b/source/physics/src/GatePositroniumSource.cc new file mode 100644 index 000000000..cffadb71e --- /dev/null +++ b/source/physics/src/GatePositroniumSource.cc @@ -0,0 +1,79 @@ +/** ---------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + ----------------------*/ +#include + +#include "G4Event.hh" + +#include "GatePositroniumSource.hh" +#include "GatePositroniumDecayModel.hh" + + +GatePositroniumSource::GatePositroniumSource(const G4String &name) + : GateVSource(name), + pMessenger(std::make_unique(this)) +{ +} + +void GatePositroniumSource::SetModel(const G4String &model_name) +{ + static const std::map models{ + {"pPs", GatePositroniumSource::ModelKind::ParaPositronium}, + {"oPs", GatePositroniumSource::ModelKind::OrthoPositronium}, + {"Ps", GatePositroniumSource::ModelKind::Positronium}}; + + auto it = models.find(model_name); + if (it != models.end()) + { + fModelKind = it->second; + } else { + fBehaveLikeVSource = true; + G4cout << "GatePositroniumSource::SetModel : Unknown gamma source model. " + "Enable: pPs, oPs, Ps. Switching to GateVSource behavour." + << G4endl; + } +} + +void GatePositroniumSource::PrepareModel() +{ + SetModel(GetType()); + + if (fBehaveLikeVSource) { + return; + } + + if (fModelKind == GatePositroniumSource::ModelKind::Positronium) { + auto params = pMessenger->generatePositroniumDecayParams(); + pModel = std::make_unique(params); + } else { + if (fModelKind == GatePositroniumSource::ModelKind::ParaPositronium) { + auto params = pMessenger->generatePositroniumDecayParams( + GatePositroniumDecayParamsGenerator::kParaPositronium); + pModel = std::make_unique(params); + + } else { + if (fModelKind == GatePositroniumSource::ModelKind::OrthoPositronium) { + auto params = pMessenger->generatePositroniumDecayParams( + GatePositroniumDecayParamsGenerator::kOrthoPositronium); + pModel = std::make_unique(params); + } else { + GateError("GatePositroniumSource::PrepareModel - unknown model."); + } + } + } +} + +G4int GatePositroniumSource::GeneratePrimaries(G4Event* event) +{ + if (!fBehaveLikeVSource && !pModel) { PrepareModel(); } + if (fBehaveLikeVSource) { return GateVSource::GeneratePrimaries(event); } + + G4double particle_time = GetTime(); + G4ThreeVector particle_position = GetPosDist()->GenerateOne(); + ChangeParticlePositionRelativeToAttachedVolume(particle_position); + return pModel->GeneratePrimaryVertices(event, particle_time, particle_position); +} + diff --git a/source/physics/src/GatePositroniumSourceMessenger.cc b/source/physics/src/GatePositroniumSourceMessenger.cc new file mode 100644 index 000000000..acfc253af --- /dev/null +++ b/source/physics/src/GatePositroniumSourceMessenger.cc @@ -0,0 +1,150 @@ +/** ---------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + ----------------------*/ + +#include + +#include "GatePositroniumSourceMessenger.hh" +#include "GatePositroniumSource.hh" + +GatePositroniumSourceMessenger::GatePositroniumSourceMessenger(GatePositroniumSource *source): GateVSourceMessenger(source), pSource(source) +{ + InitCommands(); +} + +G4UIcmdWithABool* GatePositroniumSourceMessenger::GetBoolCmd(const G4String& cmd_name, const G4String& cmd_guidance ) +{ + G4String cmd_path = GetDirectoryName() + cmd_name; + G4UIcmdWithABool* cmd = new G4UIcmdWithABool( cmd_path, this ); + cmd->SetGuidance( cmd_guidance ); + cmd->SetParameterName( cmd_name, false ); + return cmd; +} + +G4UIcmdWithADoubleAndUnit* GatePositroniumSourceMessenger::GetDoubleCmdWithUnit( const G4String& cmd_name, const G4String& cmd_guidance, const G4String& default_unit, const G4String& unit_candidates ) +{ + G4String cmd_path = GetDirectoryName() + cmd_name; + G4UIcmdWithADoubleAndUnit* cmd = new G4UIcmdWithADoubleAndUnit( cmd_path , this ); + cmd->SetGuidance( cmd_guidance ); + cmd->SetParameterName( cmd_name, false ); + cmd->SetDefaultUnit( default_unit.c_str() ); + cmd->SetUnitCandidates( unit_candidates.c_str() ); + return cmd; +} + +G4UIcmdWith3Vector* GatePositroniumSourceMessenger::GetVectorCmd( const G4String& cmd_name, const G4String& cmd_guidance ) +{ + G4String cmd_path = GetDirectoryName() + cmd_name; + G4UIcmdWith3Vector* cmd = new G4UIcmdWith3Vector( cmd_path, this ); + cmd->SetGuidance( cmd_guidance ); + cmd->SetParameterName( G4String( cmd_name + "_x" ), G4String( cmd_name + "y" ), G4String( cmd_name + "z" ), false ); + return cmd; +} + +G4UIcmdWithAnInteger* GatePositroniumSourceMessenger::GetIntCmd( const G4String& cmd_name, const G4String& cmd_guidance ) +{ + G4String cmd_path = GetDirectoryName() + cmd_name; + G4UIcmdWithAnInteger* cmd = new G4UIcmdWithAnInteger( cmd_path, this ); + cmd->SetGuidance( cmd_guidance ); + cmd->SetParameterName( cmd_name, false ); + return cmd; +} + +G4UIcmdWithAString* GatePositroniumSourceMessenger::GetStringCmd(const G4String& cmd_name, const G4String& cmd_guidance ) +{ + G4String cmd_path = GetDirectoryName() + cmd_name; + G4UIcmdWithAString* cmd = new G4UIcmdWithAString( cmd_path, this ); + cmd->SetGuidance( cmd_guidance ); + cmd->SetParameterName( cmd_name, false ); + return cmd; +} + +G4UIcmdWith3VectorAndUnit* GatePositroniumSourceMessenger::GetVectorCmdWithUnit( const G4String& cmd_name, const G4String& cmd_guidance, const G4String& default_unit, const G4String& unit_candidates ) +{ + G4String cmd_path = GetDirectoryName() + cmd_name; + G4UIcmdWith3VectorAndUnit* cmd = new G4UIcmdWith3VectorAndUnit( cmd_path , this ); + cmd->SetGuidance( cmd_guidance ); + cmd->SetParameterName( G4String( cmd_name + "_x" ), G4String( cmd_name + "y" ), G4String( cmd_name + "z" ), false ); + cmd->SetDefaultUnit( default_unit.c_str() ); + cmd->SetUnitCandidates( unit_candidates.c_str() ); + return cmd; +} + +void GatePositroniumSourceMessenger::InitCommands() +{ + upCmdSetPositroniumFractions.reset(GetStringCmd( "setPositroniumFractions", "\"f1, f2, f3 .., fn\" - where fi in [0.0, 1.0] and sum of all fi ==1" ) ); + upCmdSetPositroniumLifetimes.reset(GetStringCmd( "setPositroniumLifetimes", "\"t1, t2, t3 .., tn\" - where" ) ); + upCmdSetDecayKinds.reset(GetStringCmd( "setDecayKinds", "\"k1, k2, k3 .., kn\" - where ki " ) ); + upCmdSetIsPromptPhoton.reset(GetStringCmd( "setIsPromptPhoton", "\"f1, f2, f3 .., fn\" - where fi are true or false " ) ); + upCmdSetPromptPhotonEnergies.reset(GetStringCmd( "setPromptPhotonEnergies", "\"e1, e2, e3 .., fn\" - where ei are energies " ) ); +} + +void GatePositroniumSourceMessenger::SetNewValue( G4UIcommand* command, G4String new_value ) +{ + if(command == upCmdSetPositroniumFractions.get()) + { + std::vector fractions; + std::stringstream ss(new_value); + G4double num; + while (ss >> num) { + fractions.push_back(num); + } + fParamGenerator.SetPositroniumFraction(fractions); + } + else if(command == upCmdSetPositroniumLifetimes.get()) + { + std::vector lifetimes; + std::stringstream ss(new_value); + G4double num; + while (ss >> num) { + lifetimes.push_back(num); + } + fParamGenerator.SetPositroniumLifetimes(lifetimes); + } + else if(command == upCmdSetIsPromptPhoton.get()) + { + std::vector isPromptPhoton; + std::stringstream ss(new_value); + bool flag; + while (ss >> std::boolalpha >> flag) { + isPromptPhoton.push_back(flag); + } + fParamGenerator.SetEnablePromptGamma(isPromptPhoton); + } + else if(command == upCmdSetPromptPhotonEnergies.get()) { + std::vector promptPhotonEnergies; + std::stringstream ss(new_value); + float energy; + while (ss >> energy) { + promptPhotonEnergies.push_back(energy); + } + fParamGenerator.SetPromptGammaEnergies(promptPhotonEnergies); + } + else if(command == upCmdSetDecayKinds.get()) { + std::vector decayKinds; + std::stringstream ss(new_value); + std::string kind; + while (ss >> kind) { + if (kind == "k2Gamma") { + decayKinds.push_back(k2Gamma); + } else { + decayKinds.push_back(k3Gamma); + } + } + fParamGenerator.SetDecayKinds(decayKinds); + } + else + { + GateVSourceMessenger::SetNewValue(command, new_value); + } +} + +PositroniumDecayModelParams GatePositroniumSourceMessenger::generatePositroniumDecayParams(GatePositroniumDecayParamsGenerator::DecayModel model) const +{ + return fParamGenerator.generatePositroniumDecayParams(model); +} + + diff --git a/tests/test_GatePositroniumDecayModel.cpp b/tests/test_GatePositroniumDecayModel.cpp index acb409448..4b7e5c93e 100644 --- a/tests/test_GatePositroniumDecayModel.cpp +++ b/tests/test_GatePositroniumDecayModel.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include #include "GateRunManager.hh" #include "GatePhysicsList.hh" @@ -29,7 +29,7 @@ bool run_tests2() params.fFractions={0.3,0.7}; params.fLifetimes={0.1244 ,138.6}; params.fDecayKind={PositroniumDecayKind::k2Gamma, PositroniumDecayKind::k3Gamma}; - MiniPositroniumDecayModel model(params); + PositroniumDecayModel model(params); return true; } @@ -39,13 +39,13 @@ bool run_tests() { bool res = true; PositroniumDecayModelParams params; - MiniPositroniumDecayModel model(params); - int index = MiniPositroniumDecayModel::getPositroniumDecayIndex({1}); + PositroniumDecayModel model(params); + int index = PositroniumDecayModel::getPositroniumDecayIndex({1}); if (index != 0) { res = false; std::cerr << "getPositroniumDecayDecayIndex({1})!=0" << std::endl; } - index = MiniPositroniumDecayModel::getPositroniumDecayIndex({}); + index = PositroniumDecayModel::getPositroniumDecayIndex({}); if (index != -1) { res = false; std::cerr << "getPositroniumDecayDecayIndex({})!=-1" << std::endl; @@ -56,7 +56,7 @@ bool run_tests() std::vector fractions={0.5,0.4,0.1}; std::vector indices = {0,0,0}; for (int i = 0; i < num_of_trials; i++) { - index = MiniPositroniumDecayModel::getPositroniumDecayIndex(fractions); + index = PositroniumDecayModel::getPositroniumDecayIndex(fractions); indices[index] = indices[index] +1; } std::vector estimated_fractions = {0.,0., 0.}; From 00671fa8ee095381fd4947514444ba456ae0edbc Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Wed, 21 Jan 2026 21:46:50 +0100 Subject: [PATCH 067/144] Remove commented code --- source/physics/include/GatePositroniumDecayModelParams.hh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/source/physics/include/GatePositroniumDecayModelParams.hh b/source/physics/include/GatePositroniumDecayModelParams.hh index 0c53b9afe..c853508d9 100644 --- a/source/physics/include/GatePositroniumDecayModelParams.hh +++ b/source/physics/include/GatePositroniumDecayModelParams.hh @@ -9,9 +9,7 @@ #include -//#include "GatePositroniumHelper.hh" - -enum PositroniumDecayKind { k2Gamma, k3Gamma}; +enum PositroniumDecayKind {k2Gamma, k3Gamma}; enum PositronElectronInteraction {kpPs, kDirect, koPs}; From c27793e218ec09a557a32152eec7309258fba332 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Wed, 21 Jan 2026 21:50:56 +0100 Subject: [PATCH 068/144] Small fixes in ParamsGenerator --- source/physics/include/GatePositroniumDecayParamsGenerator.hh | 2 -- source/physics/src/GatePositroniumDecayParamsGenerator.cc | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/source/physics/include/GatePositroniumDecayParamsGenerator.hh b/source/physics/include/GatePositroniumDecayParamsGenerator.hh index bd1e33e24..f0cd00034 100644 --- a/source/physics/include/GatePositroniumDecayParamsGenerator.hh +++ b/source/physics/include/GatePositroniumDecayParamsGenerator.hh @@ -1,4 +1,3 @@ - /** ---------------------- Copyright (C): OpenGATE Collaboration This software is distributed under the terms @@ -11,7 +10,6 @@ #include #include -#include "GatePositroniumHelper.hh" #include "GatePositroniumDecayModelParams.hh" /*! class GatePositroniumDecayParamsGenerator diff --git a/source/physics/src/GatePositroniumDecayParamsGenerator.cc b/source/physics/src/GatePositroniumDecayParamsGenerator.cc index af1c2c86b..e8e2b6858 100644 --- a/source/physics/src/GatePositroniumDecayParamsGenerator.cc +++ b/source/physics/src/GatePositroniumDecayParamsGenerator.cc @@ -26,7 +26,7 @@ void GatePositroniumDecayParamsGenerator::SetPromptGammaEnergies(const std::vect } void GatePositroniumDecayParamsGenerator::SetPositroniumFraction(const std::vector& positroniumFractions) { - fPositroniumFractions = positroniumFractions; + fPositroniumFractions = positroniumFractions; } PositroniumDecayModelParams GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams(const GatePositroniumDecayParamsGenerator::DecayModel model) const From 8aa09cf6b9d1b475c878a7eb458612639351cf10 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Wed, 21 Jan 2026 21:58:26 +0100 Subject: [PATCH 069/144] Add handling prompt probabilities to GatePositroniumSourceMessenger --- .../include/GatePositroniumSourceMessenger.hh | 3 ++- .../src/GatePositroniumSourceMessenger.cc | 25 +++++++++++-------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/source/physics/include/GatePositroniumSourceMessenger.hh b/source/physics/include/GatePositroniumSourceMessenger.hh index 0790c5811..6c8744c87 100644 --- a/source/physics/include/GatePositroniumSourceMessenger.hh +++ b/source/physics/include/GatePositroniumSourceMessenger.hh @@ -49,9 +49,10 @@ class GatePositroniumSourceMessenger: public GateVSourceMessenger std::unique_ptr upCmdSetPositroniumFractions; std::unique_ptr upCmdSetPositroniumLifetimes; - std::unique_ptr upCmdSetIsPromptPhoton; + std::unique_ptr upCmdSetPromptPhotonProbabilites; std::unique_ptr upCmdSetPromptPhotonEnergies; std::unique_ptr upCmdSetDecayKinds; + std::unique_ptr upCmdSetPositronInteractions; GatePositroniumDecayParamsGenerator fParamGenerator; diff --git a/source/physics/src/GatePositroniumSourceMessenger.cc b/source/physics/src/GatePositroniumSourceMessenger.cc index acfc253af..ace4772c0 100644 --- a/source/physics/src/GatePositroniumSourceMessenger.cc +++ b/source/physics/src/GatePositroniumSourceMessenger.cc @@ -77,8 +77,9 @@ void GatePositroniumSourceMessenger::InitCommands() { upCmdSetPositroniumFractions.reset(GetStringCmd( "setPositroniumFractions", "\"f1, f2, f3 .., fn\" - where fi in [0.0, 1.0] and sum of all fi ==1" ) ); upCmdSetPositroniumLifetimes.reset(GetStringCmd( "setPositroniumLifetimes", "\"t1, t2, t3 .., tn\" - where" ) ); - upCmdSetDecayKinds.reset(GetStringCmd( "setDecayKinds", "\"k1, k2, k3 .., kn\" - where ki " ) ); - upCmdSetIsPromptPhoton.reset(GetStringCmd( "setIsPromptPhoton", "\"f1, f2, f3 .., fn\" - where fi are true or false " ) ); + upCmdSetDecayKinds.reset(GetStringCmd( "setDecayKinds", "\"k1, k2, k3 .., kn\" - where ki is k2gamma or k3gamma" ) ); + upCmdSetPositronInteractions.reset(GetStringCmd( "setPositronInteractions", "\"k1, k2, k3 .., kn\" - where ki is kpPs, kdirect or koPs, of a given element in vector of components. Used to properly recalculate intensities of the components from the theory" ) ); + upCmdSetPromptPhotonProbabilites.reset(GetStringCmd( "setPromptPhotonProbabilites", "\"f1, f2, f3 .., fn\" - where fi in [0.0, 1.0] " ) ); upCmdSetPromptPhotonEnergies.reset(GetStringCmd( "setPromptPhotonEnergies", "\"e1, e2, e3 .., fn\" - where ei are energies " ) ); } @@ -104,16 +105,18 @@ void GatePositroniumSourceMessenger::SetNewValue( G4UIcommand* command, G4String } fParamGenerator.SetPositroniumLifetimes(lifetimes); } - else if(command == upCmdSetIsPromptPhoton.get()) + else if(command == upCmdSetPromptPhotonProbabilites.get()) { - std::vector isPromptPhoton; - std::stringstream ss(new_value); - bool flag; - while (ss >> std::boolalpha >> flag) { - isPromptPhoton.push_back(flag); - } - fParamGenerator.SetEnablePromptGamma(isPromptPhoton); - } + std::vector promptPhotonProb; + std::stringstream ss(new_value); + float prob; + while (ss >> prob) { + prob = prob < 0 ? 0 : prob; + prob = prob > 1 ? 1 : prob; + promptPhotonProb.push_back(prob); + } + fParamGenerator.SetPromptGammaProbabilities(promptPhotonProb); + } else if(command == upCmdSetPromptPhotonEnergies.get()) { std::vector promptPhotonEnergies; std::stringstream ss(new_value); From 0d547a22c1d4d519457e60368d339b2a8ea8a7f2 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Wed, 21 Jan 2026 22:02:28 +0100 Subject: [PATCH 070/144] Add using prompt probabilities in DecayModel --- source/physics/src/GatePositroniumDecayModel.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/physics/src/GatePositroniumDecayModel.cc b/source/physics/src/GatePositroniumDecayModel.cc index 071e86e94..efd26d725 100644 --- a/source/physics/src/GatePositroniumDecayModel.cc +++ b/source/physics/src/GatePositroniumDecayModel.cc @@ -52,7 +52,7 @@ G4int PositroniumDecayModel::GeneratePrimaryVertices(G4Event* event, G4double& p auto decayIndex = PositroniumDecayModel::getPositroniumDecayIndex(fModelParams.fFractions); G4int number_of_vertices = 1; - if(fModelParams.fIsPromptGamma[decayIndex]) + if(fModelParams.fPromptGammaProbabilities[decayIndex] > G4UniformRand()) { ++number_of_vertices; event->AddPrimaryVertex(GetPrimaryVertexFromDeexcitation(particle_time, particle_position, decayIndex)); From 0fa879ae3051cf78cb437d48abed1557cb809678 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Wed, 21 Jan 2026 22:23:42 +0100 Subject: [PATCH 071/144] Rename PositroniumDecayModel to GatePositroniumDecayModel --- .../physics/include/GatePositroniumDecayModel.hh | 5 ++--- source/physics/src/GateExtendedVSource.cc | 6 +++--- source/physics/src/GatePositroniumDecayModel.cc | 16 ++++++++-------- source/physics/src/GatePositroniumSource.cc | 6 +++--- tests/test_GatePositroniumDecayModel.cpp | 10 +++++----- 5 files changed, 21 insertions(+), 22 deletions(-) diff --git a/source/physics/include/GatePositroniumDecayModel.hh b/source/physics/include/GatePositroniumDecayModel.hh index 18b92a5b5..eab18e969 100644 --- a/source/physics/include/GatePositroniumDecayModel.hh +++ b/source/physics/include/GatePositroniumDecayModel.hh @@ -16,15 +16,14 @@ #include "GatePositroniumDecayModelParams.hh" #include "GatePositronium.hh" -// Todo change the name to GatePositroniumDecayModel // Todo2: add docs -class PositroniumDecayModel:public GateGammaEmissionModel +class GatePositroniumDecayModel:public GateGammaEmissionModel { public: static int getPositroniumDecayIndex(const std::vector& fractions); public: - explicit PositroniumDecayModel(const PositroniumDecayModelParams& modelParams); + explicit GatePositroniumDecayModel(const PositroniumDecayModelParams& modelParams); protected: virtual G4int GeneratePrimaryVertices(G4Event* event, G4double& particle_time, G4ThreeVector& particle_position) override; diff --git a/source/physics/src/GateExtendedVSource.cc b/source/physics/src/GateExtendedVSource.cc index 1f8b4c36c..a54308aa1 100644 --- a/source/physics/src/GateExtendedVSource.cc +++ b/source/physics/src/GateExtendedVSource.cc @@ -47,18 +47,18 @@ void GateExtendedVSource::PrepareModel() if (fModelKind == GateExtendedVSource::ModelKind::Positronium) { auto params = pMessenger->generatePositroniumDecayParams(); - pModel = std::make_unique(params); + pModel = std::make_unique(params); } else { if (fModelKind == GateExtendedVSource::ModelKind::ParaPositronium) { auto params = pMessenger->generatePositroniumDecayParams( GatePositroniumDecayParamsGenerator::kParaPositronium); - pModel = std::make_unique(params); + pModel = std::make_unique(params); } else { if (fModelKind == GateExtendedVSource::ModelKind::OrthoPositronium) { auto params = pMessenger->generatePositroniumDecayParams( GatePositroniumDecayParamsGenerator::kOrthoPositronium); - pModel = std::make_unique(params); + pModel = std::make_unique(params); } else { GateError("GateExtendedVSource::PrepareModel - unknown model."); } diff --git a/source/physics/src/GatePositroniumDecayModel.cc b/source/physics/src/GatePositroniumDecayModel.cc index efd26d725..ae3468fc7 100644 --- a/source/physics/src/GatePositroniumDecayModel.cc +++ b/source/physics/src/GatePositroniumDecayModel.cc @@ -13,7 +13,7 @@ #include "GatePositroniumDecayModel.hh" -int PositroniumDecayModel::getPositroniumDecayIndex(const std::vector& fractions) { +int GatePositroniumDecayModel::getPositroniumDecayIndex(const std::vector& fractions) { auto r = G4UniformRand(); float curr_frac_cumulative = 0.0; for (int i = 0; i < fractions.size(); ++i) { @@ -23,7 +23,7 @@ int PositroniumDecayModel::getPositroniumDecayIndex(const std::vector& fr return -1; } -PositroniumDecayModel::PositroniumDecayModel(const PositroniumDecayModelParams& modelParams):fModelParams(modelParams) +GatePositroniumDecayModel::GatePositroniumDecayModel(const PositroniumDecayModelParams& modelParams):fModelParams(modelParams) { auto num_of_decay_channels = fModelParams.fDecayKind.size(); for (int i = 0; i < num_of_decay_channels; i++) { @@ -36,7 +36,7 @@ PositroniumDecayModel::PositroniumDecayModel(const PositroniumDecayModelParams& } } -G4PrimaryVertex* PositroniumDecayModel::GetPrimaryVertexFromPositroniumAnnihilation(G4double particle_time, const G4ThreeVector& particle_position, int decayIndex) +G4PrimaryVertex* GatePositroniumDecayModel::GetPrimaryVertexFromPositroniumAnnihilation(G4double particle_time, const G4ThreeVector& particle_position, int decayIndex) { G4double shifted_particle_time = particle_time + G4RandExponential::shoot(fModelParams.fLifetimes[decayIndex]); @@ -47,9 +47,9 @@ G4PrimaryVertex* PositroniumDecayModel::GetPrimaryVertexFromPositroniumAnnihilat return vertex; } -G4int PositroniumDecayModel::GeneratePrimaryVertices(G4Event* event, G4double& particle_time, G4ThreeVector& particle_position) +G4int GatePositroniumDecayModel::GeneratePrimaryVertices(G4Event* event, G4double& particle_time, G4ThreeVector& particle_position) { - auto decayIndex = PositroniumDecayModel::getPositroniumDecayIndex(fModelParams.fFractions); + auto decayIndex = GatePositroniumDecayModel::getPositroniumDecayIndex(fModelParams.fFractions); G4int number_of_vertices = 1; if(fModelParams.fPromptGammaProbabilities[decayIndex] > G4UniformRand()) @@ -61,21 +61,21 @@ G4int PositroniumDecayModel::GeneratePrimaryVertices(G4Event* event, G4double& p return number_of_vertices; } -G4PrimaryParticle* PositroniumDecayModel::GetGammaFromDeexcitation(int decayIndex) +G4PrimaryParticle* GatePositroniumDecayModel::GetGammaFromDeexcitation(int decayIndex) { G4PrimaryParticle* gamma = GetSingleGamma(fModelParams.fPromptGammaEnergy[decayIndex]); gamma->SetUserInformation( GetPrimaryParticleInformation( gamma, GateEmittedGammaInformation::GammaKind::Prompt ) ); return gamma; } -G4PrimaryVertex* PositroniumDecayModel::GetPrimaryVertexFromDeexcitation(G4double particle_time, const G4ThreeVector& particle_position, int decayIndex) +G4PrimaryVertex* GatePositroniumDecayModel::GetPrimaryVertexFromDeexcitation(G4double particle_time, const G4ThreeVector& particle_position, int decayIndex) { G4PrimaryVertex* vertex = new G4PrimaryVertex(particle_position, particle_time); vertex->SetPrimary(GetGammaFromDeexcitation(decayIndex)); return vertex; } -std::vector PositroniumDecayModel::GetGammasFromPositroniumAnnihilation(int decayIndex) +std::vector GatePositroniumDecayModel::GetGammasFromPositroniumAnnihilation(int decayIndex) { int annihilation_gammas_number = fPositroniumDecayChannel[decayIndex].GetAnnihilationGammasNumber(); std::vector gammas(annihilation_gammas_number); diff --git a/source/physics/src/GatePositroniumSource.cc b/source/physics/src/GatePositroniumSource.cc index cffadb71e..ef5abadab 100644 --- a/source/physics/src/GatePositroniumSource.cc +++ b/source/physics/src/GatePositroniumSource.cc @@ -47,18 +47,18 @@ void GatePositroniumSource::PrepareModel() if (fModelKind == GatePositroniumSource::ModelKind::Positronium) { auto params = pMessenger->generatePositroniumDecayParams(); - pModel = std::make_unique(params); + pModel = std::make_unique(params); } else { if (fModelKind == GatePositroniumSource::ModelKind::ParaPositronium) { auto params = pMessenger->generatePositroniumDecayParams( GatePositroniumDecayParamsGenerator::kParaPositronium); - pModel = std::make_unique(params); + pModel = std::make_unique(params); } else { if (fModelKind == GatePositroniumSource::ModelKind::OrthoPositronium) { auto params = pMessenger->generatePositroniumDecayParams( GatePositroniumDecayParamsGenerator::kOrthoPositronium); - pModel = std::make_unique(params); + pModel = std::make_unique(params); } else { GateError("GatePositroniumSource::PrepareModel - unknown model."); } diff --git a/tests/test_GatePositroniumDecayModel.cpp b/tests/test_GatePositroniumDecayModel.cpp index 4b7e5c93e..773a9c620 100644 --- a/tests/test_GatePositroniumDecayModel.cpp +++ b/tests/test_GatePositroniumDecayModel.cpp @@ -29,7 +29,7 @@ bool run_tests2() params.fFractions={0.3,0.7}; params.fLifetimes={0.1244 ,138.6}; params.fDecayKind={PositroniumDecayKind::k2Gamma, PositroniumDecayKind::k3Gamma}; - PositroniumDecayModel model(params); + GatePositroniumDecayModel model(params); return true; } @@ -39,13 +39,13 @@ bool run_tests() { bool res = true; PositroniumDecayModelParams params; - PositroniumDecayModel model(params); - int index = PositroniumDecayModel::getPositroniumDecayIndex({1}); + GatePositroniumDecayModel model(params); + int index = GatePositroniumDecayModel::getPositroniumDecayIndex({1}); if (index != 0) { res = false; std::cerr << "getPositroniumDecayDecayIndex({1})!=0" << std::endl; } - index = PositroniumDecayModel::getPositroniumDecayIndex({}); + index = GatePositroniumDecayModel::getPositroniumDecayIndex({}); if (index != -1) { res = false; std::cerr << "getPositroniumDecayDecayIndex({})!=-1" << std::endl; @@ -56,7 +56,7 @@ bool run_tests() std::vector fractions={0.5,0.4,0.1}; std::vector indices = {0,0,0}; for (int i = 0; i < num_of_trials; i++) { - index = PositroniumDecayModel::getPositroniumDecayIndex(fractions); + index = GatePositroniumDecayModel::getPositroniumDecayIndex(fractions); indices[index] = indices[index] +1; } std::vector estimated_fractions = {0.,0., 0.}; From b4a3dbd384e85b5c210bda9d71bd164e4a318b04 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Wed, 21 Jan 2026 22:52:21 +0100 Subject: [PATCH 072/144] Fix name of the variable It was expressed in ns but it had a suffix of _ps. --- source/physics/include/GatePositroniumConstants.hh | 2 +- source/physics/src/GatePositroniumHelper.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/physics/include/GatePositroniumConstants.hh b/source/physics/include/GatePositroniumConstants.hh index fa6760602..db49ac297 100644 --- a/source/physics/include/GatePositroniumConstants.hh +++ b/source/physics/include/GatePositroniumConstants.hh @@ -9,7 +9,7 @@ namespace gate_positronium_constants { - constexpr double kParaPsLifetime_ps = 0.125; + constexpr double kParaPsLifetime_ns = 0.125; constexpr double kParaToOrthoPsFraction = 1.0/3.0; constexpr double kOrthoPsMeanLifetime_ns = 142.; constexpr double kHyperfineCoefficient = 372; diff --git a/source/physics/src/GatePositroniumHelper.cc b/source/physics/src/GatePositroniumHelper.cc index 63457ea92..d02d0d896 100644 --- a/source/physics/src/GatePositroniumHelper.cc +++ b/source/physics/src/GatePositroniumHelper.cc @@ -38,7 +38,7 @@ PositroniumDecayModelParams GatePositroniumHelper::CalculateFractionsFromLifetim float pPsIntens = CalcPPsFractionFromOPs(paramsOut.fFractions, paramsOut.fPositronInteractions); if (pPsIntens > 0) { paramsOut.fFractions.push_back(pPsIntens); - paramsOut.fLifetimes.push_back(kParaPsLifetime_ps); + paramsOut.fLifetimes.push_back(kParaPsLifetime_ns); paramsOut.fPromptGammaProbabilities.push_back(paramsOut.fPromptGammaProbabilities.at(0)); paramsOut.fPromptGammaEnergy.push_back(paramsOut.fPromptGammaEnergy.at(0)); paramsOut.fDecayKind.push_back(PositroniumDecayKind::k2Gamma); From 077e0f5cb5c3d91cce351bb2634d212a18de4bea Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Wed, 21 Jan 2026 22:57:02 +0100 Subject: [PATCH 073/144] Change the decay time of pPs to 0.1244 ns in constants Also, replace magic numbers by constants. --- source/physics/include/GatePositroniumConstants.hh | 2 +- source/physics/src/GatePositroniumDecayParamsGenerator.cc | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/source/physics/include/GatePositroniumConstants.hh b/source/physics/include/GatePositroniumConstants.hh index db49ac297..4b5ece479 100644 --- a/source/physics/include/GatePositroniumConstants.hh +++ b/source/physics/include/GatePositroniumConstants.hh @@ -9,7 +9,7 @@ namespace gate_positronium_constants { - constexpr double kParaPsLifetime_ns = 0.125; + constexpr double kParaPsLifetime_ns = 0.1244; constexpr double kParaToOrthoPsFraction = 1.0/3.0; constexpr double kOrthoPsMeanLifetime_ns = 142.; constexpr double kHyperfineCoefficient = 372; diff --git a/source/physics/src/GatePositroniumDecayParamsGenerator.cc b/source/physics/src/GatePositroniumDecayParamsGenerator.cc index e8e2b6858..0fd8495fe 100644 --- a/source/physics/src/GatePositroniumDecayParamsGenerator.cc +++ b/source/physics/src/GatePositroniumDecayParamsGenerator.cc @@ -3,6 +3,9 @@ #include "GateMessageManager.hh" #include "GatePositroniumHelper.hh" #include "GatePositroniumDecayParamsGenerator.hh" +#include "GatePositroniumConstants.hh" + +using namespace gate_positronium_constants; void GatePositroniumDecayParamsGenerator::SetPromptGammaProbabilities(const std::vector& promptGammaProb) { @@ -34,7 +37,7 @@ PositroniumDecayModelParams GatePositroniumDecayParamsGenerator::generatePositro PositroniumDecayModelParams params; if (model == GatePositroniumDecayParamsGenerator::kParaPositronium) { params.fFractions= {1}; - params.fLifetimes= {0.1244}; // [ns] + params.fLifetimes= {kParaPsLifetime_ns}; // [ns] params.fDecayKind= {k2Gamma}; params.fPositronInteractions= {kpPs}; if(fPromptGammaProbabilities.has_value() && fPromptGammaEnergies.has_value()) { @@ -49,7 +52,7 @@ PositroniumDecayModelParams GatePositroniumDecayParamsGenerator::generatePositro } if (model == GatePositroniumDecayParamsGenerator::kOrthoPositronium) { params.fFractions= {1}; - params.fLifetimes= {142}; // [ns] + params.fLifetimes= {kOrthoPsMeanLifetime_ns}; // [ns] params.fDecayKind= {k3Gamma}; params.fPositronInteractions= {koPs}; if(fPromptGammaProbabilities.has_value() && fPromptGammaEnergies.has_value()) { From 3dfd7ee50392d0723e2aac107f88fe28f4786d77 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Wed, 21 Jan 2026 23:04:20 +0100 Subject: [PATCH 074/144] Fix error in the GatePositroniumDecayModel constructor If decayKind was set to other than k2Gamma, then the type was always set to pPs. --- source/physics/src/GatePositroniumDecayModel.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/physics/src/GatePositroniumDecayModel.cc b/source/physics/src/GatePositroniumDecayModel.cc index ae3468fc7..8eafeffc1 100644 --- a/source/physics/src/GatePositroniumDecayModel.cc +++ b/source/physics/src/GatePositroniumDecayModel.cc @@ -31,7 +31,7 @@ GatePositroniumDecayModel::GatePositroniumDecayModel(const PositroniumDecayModel { fPositroniumDecayChannel.push_back(std::move(GatePositronium("pPs", fModelParams.fLifetimes[i]* ns))); } else { - fPositroniumDecayChannel.push_back(std::move(GatePositronium("pPs", fModelParams.fLifetimes[i]* ns))); + fPositroniumDecayChannel.push_back(std::move(GatePositronium("oPs", fModelParams.fLifetimes[i]* ns))); } } } From dda5632b224cfadf436f81ba894a70bed8979790 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Thu, 22 Jan 2026 09:33:22 +0100 Subject: [PATCH 075/144] Fix and enable test for test_GatePositroniumDecayParamsGenerator --- CMakeLists.txt | 5 +++++ tests/test_GatePositroniumDecayParamsGenerator.cpp | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7ce892681..2026bc8d4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,6 +44,11 @@ IF(BUILD_TESTING) ADD_TEST(NAME test_GatePositroniumHelper COMMAND test_GatePositroniumHelper) + ADD_EXECUTABLE(test_GatePositroniumDecyParamsGenerator tests/test_GatePositroniumDecayParamsGenerator.cpp) + TARGET_LINK_LIBRARIES(test_GatePositroniumDecyParamsGenerator GateLib) + + ADD_TEST(NAME test_GatePositroniumDecyParamsGenerator COMMAND test_GatePositroniumDecayParamsGenerator) + ENDIF(BUILD_TESTING) #========================================================= diff --git a/tests/test_GatePositroniumDecayParamsGenerator.cpp b/tests/test_GatePositroniumDecayParamsGenerator.cpp index 26ea48f5d..206ff74db 100644 --- a/tests/test_GatePositroniumDecayParamsGenerator.cpp +++ b/tests/test_GatePositroniumDecayParamsGenerator.cpp @@ -51,7 +51,7 @@ bool test_para_prompt_gamma() { std::cout << "test_prompt_gamma" << std::endl; GatePositroniumDecayParamsGenerator gen; - gen.SetEnablePromptGamma({true}); + gen.SetPromptGammaProbabilities({1.0}); gen.SetPromptGammaEnergies({1.2f}); auto p = gen.generatePositroniumDecayParams(GatePositroniumDecayParamsGenerator::kParaPositronium); @@ -69,7 +69,7 @@ bool test_positronium_custom() gen.SetPositroniumFraction({0.3f, 0.7f}); gen.SetPositroniumLifetimes({0.12f, 140.0f}); gen.SetDecayKinds({k2Gamma, k3Gamma}); - gen.SetEnablePromptGamma({false, true}); + gen.SetPromptGammaProbabilities({0, 1.0}); gen.SetPromptGammaEnergies({0.0f, 1.2f}); auto p = gen.generatePositroniumDecayParams(GatePositroniumDecayParamsGenerator::kPositronium); From e5163c14e37fbdf81d4ba2c88492ee979cf44507 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Thu, 22 Jan 2026 09:57:09 +0100 Subject: [PATCH 076/144] Update an outdated comment --- tests/test_GatePositroniumDecayParamsGenerator.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/test_GatePositroniumDecayParamsGenerator.cpp b/tests/test_GatePositroniumDecayParamsGenerator.cpp index 206ff74db..bef1600df 100644 --- a/tests/test_GatePositroniumDecayParamsGenerator.cpp +++ b/tests/test_GatePositroniumDecayParamsGenerator.cpp @@ -10,7 +10,6 @@ #include "TestingTools.h" - void initializeGateRunManager(GateRunManager* runManager) { GateDetectorConstruction* gateDC = new GateDetectorConstruction(); @@ -116,7 +115,7 @@ bool test_vector_size_mismatch() //gen.SetPositroniumFraction({0.5f, 0.5f}); //gen.SetPositroniumLifetimes({0.12f}); // mismatch! //gen.SetDecayKinds({k2Gamma, k3Gamma}); - //gen.SetEnablePromptGamma({false, false}); + //gen.SetPromptGammaProbabilities({0, 0}); //gen.SetPromptGammaEnergies({0.0f, 0.0f}); //bool caught = false; From d938317c439b7303225f14bc20eb8f94e385255d Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 23 Jan 2026 13:28:32 +0100 Subject: [PATCH 077/144] Add test of GatePositronium that number of daughters are 3 --- tests/test_GatePositronium.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/tests/test_GatePositronium.cpp b/tests/test_GatePositronium.cpp index 9e8f4a7c1..d96d06184 100644 --- a/tests/test_GatePositronium.cpp +++ b/tests/test_GatePositronium.cpp @@ -110,9 +110,9 @@ bool test_in_vector() return true; } -bool test_decay_products() +bool test_decay_products_pPs() { - std::cout << "test_decay_products\n"; + std::cout << "test_decay_products_pPs\n"; GatePositronium pPs("pPs", 0.1 * ns); auto* products = pPs.GetDecayProducts(); @@ -122,6 +122,16 @@ bool test_decay_products() return true; } +bool test_decay_products_oPs() +{ + std::cout << "test_decay_products_oPs\n"; + GatePositronium pPs("oPs", 0.1 * ns); + + auto* products = pPs.GetDecayProducts(); + CHECK(products != nullptr, "DecayProducts should not be null"); + CHECK(products->entries() == 3, "oPs should produce 3 daughters"); + return true; +} int main() { @@ -134,7 +144,8 @@ int main() ok = ok & test_oPs_properties(); ok = ok & test_move_semantics(); ok = ok & test_in_vector(); - ok = ok & test_decay_products(); + ok = ok & test_decay_products_pPs(); + ok = ok & test_decay_products_oPs(); if (ok) { std::cout << "All tests have passed" << std::endl; From 8c4bc87bcf7a956164d653dbf30b533cfce7bec1 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 23 Jan 2026 14:25:40 +0100 Subject: [PATCH 078/144] Change name of namespace to be conformed with GATE --- source/physics/include/GatePositroniumConstants.hh | 2 +- source/physics/src/GatePositroniumDecayParamsGenerator.cc | 2 +- source/physics/src/GatePositroniumHelper.cc | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/physics/include/GatePositroniumConstants.hh b/source/physics/include/GatePositroniumConstants.hh index 4b5ece479..d1e823a58 100644 --- a/source/physics/include/GatePositroniumConstants.hh +++ b/source/physics/include/GatePositroniumConstants.hh @@ -7,7 +7,7 @@ #ifndef GatePositroniumConstants_hh #define GatePositroniumConstants_hh -namespace gate_positronium_constants +namespace GatePositroniumConstants { constexpr double kParaPsLifetime_ns = 0.1244; constexpr double kParaToOrthoPsFraction = 1.0/3.0; diff --git a/source/physics/src/GatePositroniumDecayParamsGenerator.cc b/source/physics/src/GatePositroniumDecayParamsGenerator.cc index 0fd8495fe..136b33abe 100644 --- a/source/physics/src/GatePositroniumDecayParamsGenerator.cc +++ b/source/physics/src/GatePositroniumDecayParamsGenerator.cc @@ -5,7 +5,7 @@ #include "GatePositroniumDecayParamsGenerator.hh" #include "GatePositroniumConstants.hh" -using namespace gate_positronium_constants; +using namespace GatePositroniumConstants; void GatePositroniumDecayParamsGenerator::SetPromptGammaProbabilities(const std::vector& promptGammaProb) { diff --git a/source/physics/src/GatePositroniumHelper.cc b/source/physics/src/GatePositroniumHelper.cc index d02d0d896..fdd7faaaf 100644 --- a/source/physics/src/GatePositroniumHelper.cc +++ b/source/physics/src/GatePositroniumHelper.cc @@ -6,7 +6,7 @@ #include "GatePositroniumHelper.hh" #include "GatePositroniumConstants.hh" -using namespace gate_positronium_constants; +using namespace GatePositroniumConstants; PositroniumDecayModelParams GatePositroniumHelper::CalculateFractionsFromLifetimes(PositroniumDecayModelParams params) { From 140e488f288c3f36ddf8862e8697241b1cf69313 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 23 Jan 2026 14:48:28 +0100 Subject: [PATCH 079/144] Add registration of new positronium source into GateMgr --- source/physics/src/GateSourceMgr.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source/physics/src/GateSourceMgr.cc b/source/physics/src/GateSourceMgr.cc index db8f192ce..85299dee0 100644 --- a/source/physics/src/GateSourceMgr.cc +++ b/source/physics/src/GateSourceMgr.cc @@ -30,6 +30,7 @@ #include "GateSourceOfPromptGamma.hh" #include "GateSourcePhaseSpace.hh" #include "GateExtendedVSource.hh" +#include "GatePositroniumSource.hh" //---------------------------------------------------------------------------------------- GateSourceMgr* GateSourceMgr::mInstance = 0; @@ -237,6 +238,10 @@ G4int GateSourceMgr::AddSource( std::vector sourceVec ) source = new GateExtendedVSource( sourceName ); source->SetSourceID( m_sourceProgressiveNumber ); } + else if (sourceGeomType == "PositroniumSource"){ + source = new GatePositroniumSource( sourceName ); + source->SetSourceID( m_sourceProgressiveNumber ); + } else { GateError("Unknown source type '" << sourceGeomType << "'. Known types are voxel, linacbeam, gps.\n"); From 7d36eb2683105c07f2fa93cc42849e04bbe821a4 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Tue, 27 Jan 2026 19:10:49 +0100 Subject: [PATCH 080/144] Remove comment about docs --- source/physics/include/GatePositroniumDecayModel.hh | 1 - 1 file changed, 1 deletion(-) diff --git a/source/physics/include/GatePositroniumDecayModel.hh b/source/physics/include/GatePositroniumDecayModel.hh index eab18e969..6506c2989 100644 --- a/source/physics/include/GatePositroniumDecayModel.hh +++ b/source/physics/include/GatePositroniumDecayModel.hh @@ -16,7 +16,6 @@ #include "GatePositroniumDecayModelParams.hh" #include "GatePositronium.hh" -// Todo2: add docs class GatePositroniumDecayModel:public GateGammaEmissionModel { public: From 092432d29315a263f8f7312a489ba0a12bde66f6 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Tue, 27 Jan 2026 19:11:40 +0100 Subject: [PATCH 081/144] Remove unnecessary public keyword --- source/physics/include/GatePositroniumDecayModel.hh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/physics/include/GatePositroniumDecayModel.hh b/source/physics/include/GatePositroniumDecayModel.hh index 6506c2989..a99238595 100644 --- a/source/physics/include/GatePositroniumDecayModel.hh +++ b/source/physics/include/GatePositroniumDecayModel.hh @@ -19,9 +19,8 @@ class GatePositroniumDecayModel:public GateGammaEmissionModel { public: - static int getPositroniumDecayIndex(const std::vector& fractions); + static int getPositroniumDecayIndex(const std::vector& fractions); - public: explicit GatePositroniumDecayModel(const PositroniumDecayModelParams& modelParams); protected: From 8582cdc083bd20d1fa166ecb78fa47988636a817 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Tue, 27 Jan 2026 19:12:51 +0100 Subject: [PATCH 082/144] Fix formatting --- source/physics/include/GatePositroniumDecayModel.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/physics/include/GatePositroniumDecayModel.hh b/source/physics/include/GatePositroniumDecayModel.hh index a99238595..cca7b8813 100644 --- a/source/physics/include/GatePositroniumDecayModel.hh +++ b/source/physics/include/GatePositroniumDecayModel.hh @@ -26,7 +26,7 @@ class GatePositroniumDecayModel:public GateGammaEmissionModel protected: virtual G4int GeneratePrimaryVertices(G4Event* event, G4double& particle_time, G4ThreeVector& particle_position) override; G4PrimaryVertex* GetPrimaryVertexFromDeexcitation(G4double particle_time, const G4ThreeVector& particle_position, int decayIndex); - G4PrimaryVertex *GetPrimaryVertexFromPositroniumAnnihilation(G4double particle_time, const G4ThreeVector &particle_position, int decayIndex); + G4PrimaryVertex* GetPrimaryVertexFromPositroniumAnnihilation(G4double particle_time, const G4ThreeVector &particle_position, int decayIndex); G4PrimaryParticle* GetGammaFromDeexcitation(int decayIndex); std::vector GetGammasFromPositroniumAnnihilation(int decayIndex); From f0b71e95765abf89d7b7013e3b616423e35367b9 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Tue, 27 Jan 2026 19:13:53 +0100 Subject: [PATCH 083/144] Fix formatting 2 --- source/physics/include/GatePositroniumDecayModel.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/physics/include/GatePositroniumDecayModel.hh b/source/physics/include/GatePositroniumDecayModel.hh index cca7b8813..0a9aeac30 100644 --- a/source/physics/include/GatePositroniumDecayModel.hh +++ b/source/physics/include/GatePositroniumDecayModel.hh @@ -30,7 +30,7 @@ class GatePositroniumDecayModel:public GateGammaEmissionModel G4PrimaryParticle* GetGammaFromDeexcitation(int decayIndex); std::vector GetGammasFromPositroniumAnnihilation(int decayIndex); -private: + private: PositroniumDecayModelParams fModelParams; std::vector fPositroniumDecayChannel; }; From 882c1a4602383ece90f6ba14d18193c2d9bd2cc0 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Tue, 27 Jan 2026 19:21:57 +0100 Subject: [PATCH 084/144] Rename kpPs to kParaPs and koPs to kOrthoPs --- source/physics/include/GatePositroniumDecayModelParams.hh | 2 +- source/physics/src/GateExtendedVSourceMessenger.cc | 4 ++-- source/physics/src/GatePositroniumDecayParamsGenerator.cc | 4 ++-- source/physics/src/GatePositroniumHelper.cc | 8 ++++---- tests/test_GatePositroniumHelper.cpp | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/source/physics/include/GatePositroniumDecayModelParams.hh b/source/physics/include/GatePositroniumDecayModelParams.hh index c853508d9..f9a495388 100644 --- a/source/physics/include/GatePositroniumDecayModelParams.hh +++ b/source/physics/include/GatePositroniumDecayModelParams.hh @@ -11,7 +11,7 @@ enum PositroniumDecayKind {k2Gamma, k3Gamma}; -enum PositronElectronInteraction {kpPs, kDirect, koPs}; +enum PositronElectronInteraction {kParaPs, kDirect, kOrthoPs}; struct PositroniumDecayModelParams { diff --git a/source/physics/src/GateExtendedVSourceMessenger.cc b/source/physics/src/GateExtendedVSourceMessenger.cc index 7cdf366a2..723e10265 100644 --- a/source/physics/src/GateExtendedVSourceMessenger.cc +++ b/source/physics/src/GateExtendedVSourceMessenger.cc @@ -145,11 +145,11 @@ void GateExtendedVSourceMessenger::SetNewValue( G4UIcommand* command, G4String n std::string inter; while (ss >> inter) { if (inter == "kpPs") { - positronInteractions.push_back(PositronElectronInteraction::kpPs); + positronInteractions.push_back(PositronElectronInteraction::kParaPs); } else if (inter == "kdirect") { positronInteractions.push_back(PositronElectronInteraction::kDirect); } else { - positronInteractions.push_back(PositronElectronInteraction::koPs); + positronInteractions.push_back(PositronElectronInteraction::kOrthoPs); } } fParamGenerator.SetPositronInteractions(positronInteractions); diff --git a/source/physics/src/GatePositroniumDecayParamsGenerator.cc b/source/physics/src/GatePositroniumDecayParamsGenerator.cc index 136b33abe..6e2b52d2a 100644 --- a/source/physics/src/GatePositroniumDecayParamsGenerator.cc +++ b/source/physics/src/GatePositroniumDecayParamsGenerator.cc @@ -39,7 +39,7 @@ PositroniumDecayModelParams GatePositroniumDecayParamsGenerator::generatePositro params.fFractions= {1}; params.fLifetimes= {kParaPsLifetime_ns}; // [ns] params.fDecayKind= {k2Gamma}; - params.fPositronInteractions= {kpPs}; + params.fPositronInteractions= {kParaPs}; if(fPromptGammaProbabilities.has_value() && fPromptGammaEnergies.has_value()) { params.fPromptGammaProbabilities=fPromptGammaProbabilities.value(); assert(params.fPromptGammaProbabilities.size()==1); @@ -54,7 +54,7 @@ PositroniumDecayModelParams GatePositroniumDecayParamsGenerator::generatePositro params.fFractions= {1}; params.fLifetimes= {kOrthoPsMeanLifetime_ns}; // [ns] params.fDecayKind= {k3Gamma}; - params.fPositronInteractions= {koPs}; + params.fPositronInteractions={kOrthoPs}; if(fPromptGammaProbabilities.has_value() && fPromptGammaEnergies.has_value()) { params.fPromptGammaProbabilities=fPromptGammaProbabilities.value(); assert(params.fPromptGammaProbabilities.size()==1); diff --git a/source/physics/src/GatePositroniumHelper.cc b/source/physics/src/GatePositroniumHelper.cc index fdd7faaaf..746747554 100644 --- a/source/physics/src/GatePositroniumHelper.cc +++ b/source/physics/src/GatePositroniumHelper.cc @@ -13,7 +13,7 @@ PositroniumDecayModelParams GatePositroniumHelper::CalculateFractionsFromLifetim PositroniumDecayModelParams paramsOut; bool pPsExist = false; 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 @@ -42,7 +42,7 @@ PositroniumDecayModelParams GatePositroniumHelper::CalculateFractionsFromLifetim paramsOut.fPromptGammaProbabilities.push_back(paramsOut.fPromptGammaProbabilities.at(0)); paramsOut.fPromptGammaEnergy.push_back(paramsOut.fPromptGammaEnergy.at(0)); paramsOut.fDecayKind.push_back(PositroniumDecayKind::k2Gamma); - paramsOut.fPositronInteractions.push_back(PositronElectronInteraction::kpPs); + paramsOut.fPositronInteractions.push_back(PositronElectronInteraction::kParaPs); } paramsOut.fFractions = NormalizeFractions(paramsOut.fFractions); @@ -55,7 +55,7 @@ float GatePositroniumHelper::CalcPPsFractionFromOPs(std::vector fractions auto itFrac = fractions.begin(); auto itDec = decays.begin(); while (itFrac != fractions.end() && itDec != decays.end()) { - if (*itDec == PositronElectronInteraction::koPs) + if (*itDec == PositronElectronInteraction::kOrthoPs) sum += *itFrac; ++itFrac; ++itDec; @@ -70,7 +70,7 @@ std::pair GatePositroniumHelper::CalcFractionsFromLifetime(float i intens2G = intensity*(kHyperfineCoefficient - 1.)/kHyperfineCoefficient; intens3G = intensity/kHyperfineCoefficient; break; - case PositronElectronInteraction::koPs: + case PositronElectronInteraction::kOrthoPs: intens2G = intensity*(kOrthoPsMeanLifetime_ns - lifetime)/kOrthoPsMeanLifetime_ns; intens3G = intensity*lifetime/kOrthoPsMeanLifetime_ns; break; diff --git a/tests/test_GatePositroniumHelper.cpp b/tests/test_GatePositroniumHelper.cpp index 2f55daa58..77b0e0c9d 100644 --- a/tests/test_GatePositroniumHelper.cpp +++ b/tests/test_GatePositroniumHelper.cpp @@ -32,7 +32,7 @@ bool run_tests() std::vector fractions = {2, 12, 4, 2}; std::vector lifetimes = {0.125, 0.4, 2.0, 50.0}; - std::vector decays = {kpPs, kDirect, koPs, koPs}; + std::vector decays = {kParaPs, kDirect, kOrthoPs, kOrthoPs}; std::vector normFractions = helper.NormalizeFractions(fractions); std::vector goodFractions = {0.1, 0.6, 0.2, 0.1}; for (int i=0; i Date: Tue, 27 Jan 2026 20:03:28 +0100 Subject: [PATCH 085/144] Fix formatting --- source/physics/include/GatePositroniumDecayParamsGenerator.hh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/physics/include/GatePositroniumDecayParamsGenerator.hh b/source/physics/include/GatePositroniumDecayParamsGenerator.hh index f0cd00034..e65d570e1 100644 --- a/source/physics/include/GatePositroniumDecayParamsGenerator.hh +++ b/source/physics/include/GatePositroniumDecayParamsGenerator.hh @@ -24,7 +24,7 @@ class GatePositroniumDecayParamsGenerator public: enum DecayModel {kParaPositronium, kOrthoPositronium, kPositronium}; - GatePositroniumDecayParamsGenerator()= default; + GatePositroniumDecayParamsGenerator()=default; virtual ~GatePositroniumDecayParamsGenerator()=default; void SetPromptGammaProbabilities(const std::vector& promptGammaProb); @@ -34,7 +34,7 @@ public: void SetPositronInteractions(const std::vector& positronInteractions); void SetPositroniumFraction(const std::vector& positroniumFractions); - PositroniumDecayModelParams generatePositroniumDecayParams(DecayModel model= kPositronium) const; + PositroniumDecayModelParams generatePositroniumDecayParams(DecayModel model=kPositronium) const; private: std::optional> fPositroniumFractions; From 18ef5d7040f1ba0b266288b785ad47709c5f1eab Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Tue, 27 Jan 2026 20:24:06 +0100 Subject: [PATCH 086/144] Remove behaveLikeVSource variable Now, if no proper source type is provided, then the error is called. --- source/physics/include/GatePositroniumSource.hh | 3 --- source/physics/src/GatePositroniumSource.cc | 14 +++----------- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/source/physics/include/GatePositroniumSource.hh b/source/physics/include/GatePositroniumSource.hh index 496e895ca..53f97c9ff 100644 --- a/source/physics/include/GatePositroniumSource.hh +++ b/source/physics/include/GatePositroniumSource.hh @@ -43,9 +43,6 @@ public: std::unique_ptr pMessenger; //User settings: ModelKind fModelKind = ModelKind::NotDefined; - - //Set by PrepareModel() and used in GeneratePrimaries() - G4bool fBehaveLikeVSource = false; }; #endif diff --git a/source/physics/src/GatePositroniumSource.cc b/source/physics/src/GatePositroniumSource.cc index ef5abadab..3c1edcd36 100644 --- a/source/physics/src/GatePositroniumSource.cc +++ b/source/physics/src/GatePositroniumSource.cc @@ -30,10 +30,7 @@ void GatePositroniumSource::SetModel(const G4String &model_name) { fModelKind = it->second; } else { - fBehaveLikeVSource = true; - G4cout << "GatePositroniumSource::SetModel : Unknown gamma source model. " - "Enable: pPs, oPs, Ps. Switching to GateVSource behavour." - << G4endl; + GateError("GatePositroniumSource::SetModel: Unknown gamma source model."); } } @@ -41,10 +38,6 @@ void GatePositroniumSource::PrepareModel() { SetModel(GetType()); - if (fBehaveLikeVSource) { - return; - } - if (fModelKind == GatePositroniumSource::ModelKind::Positronium) { auto params = pMessenger->generatePositroniumDecayParams(); pModel = std::make_unique(params); @@ -68,9 +61,8 @@ void GatePositroniumSource::PrepareModel() G4int GatePositroniumSource::GeneratePrimaries(G4Event* event) { - if (!fBehaveLikeVSource && !pModel) { PrepareModel(); } - if (fBehaveLikeVSource) { return GateVSource::GeneratePrimaries(event); } - + if (!pModel) { PrepareModel(); } + G4double particle_time = GetTime(); G4ThreeVector particle_position = GetPosDist()->GenerateOne(); ChangeParticlePositionRelativeToAttachedVolume(particle_position); From 1424e874dbaa166c54533282aad0b04d8e4c3f59 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Tue, 27 Jan 2026 20:30:42 +0100 Subject: [PATCH 087/144] Remove obsolete comments --- .../physics/include/GatePositroniumSource.hh | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/source/physics/include/GatePositroniumSource.hh b/source/physics/include/GatePositroniumSource.hh index 53f97c9ff..50bdd7c74 100644 --- a/source/physics/include/GatePositroniumSource.hh +++ b/source/physics/include/GatePositroniumSource.hh @@ -17,31 +17,25 @@ class GatePositroniumSource : public GateVSource { public: - enum class ModelKind { - NotDefined, //by default - in this case this class will behave like GateVSource - ParaPositronium, //generate gammas from para-positronium decay - OrthoPositronium, //generate gammas from ortho-positronium decay - Positronium}; // generate gammas from mixed model + enum class ModelKind { + NotDefined, + ParaPositronium, + OrthoPositronium, + Positronium + }; explicit GatePositroniumSource(const G4String& name); virtual ~GatePositroniumSource() = default; - /** Generate gammas for event - **/ virtual G4int GeneratePrimaries( G4Event* event ) override; protected: - /** Set model used for this source. If is not defined then this class will behave like GateVSource. - **/ void SetModel(const G4String &model_name); - /** Prepare model to work - set all settings from user to model - **/ void PrepareModel(); protected: - std::unique_ptr pModel; - std::unique_ptr pMessenger; - //User settings: + std::unique_ptr pModel; + std::unique_ptr pMessenger; ModelKind fModelKind = ModelKind::NotDefined; }; From 76261bed23e032fccd7754d62e5e18de50673cab Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Tue, 27 Jan 2026 20:38:51 +0100 Subject: [PATCH 088/144] Add missing _ in strings. --- source/physics/src/GatePositroniumSourceMessenger.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/physics/src/GatePositroniumSourceMessenger.cc b/source/physics/src/GatePositroniumSourceMessenger.cc index ace4772c0..2efab3816 100644 --- a/source/physics/src/GatePositroniumSourceMessenger.cc +++ b/source/physics/src/GatePositroniumSourceMessenger.cc @@ -40,7 +40,7 @@ G4UIcmdWith3Vector* GatePositroniumSourceMessenger::GetVectorCmd( const G4String G4String cmd_path = GetDirectoryName() + cmd_name; G4UIcmdWith3Vector* cmd = new G4UIcmdWith3Vector( cmd_path, this ); cmd->SetGuidance( cmd_guidance ); - cmd->SetParameterName( G4String( cmd_name + "_x" ), G4String( cmd_name + "y" ), G4String( cmd_name + "z" ), false ); + cmd->SetParameterName( G4String( cmd_name + "_x" ), G4String( cmd_name + "_y" ), G4String( cmd_name + "_z" ), false ); return cmd; } From 1b0ce8157c8ad9f55decb4c2829401d37423ddad Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Tue, 27 Jan 2026 20:40:41 +0100 Subject: [PATCH 089/144] Add missing explanation to macro command --- source/physics/src/GatePositroniumSourceMessenger.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/physics/src/GatePositroniumSourceMessenger.cc b/source/physics/src/GatePositroniumSourceMessenger.cc index 2efab3816..734257836 100644 --- a/source/physics/src/GatePositroniumSourceMessenger.cc +++ b/source/physics/src/GatePositroniumSourceMessenger.cc @@ -76,7 +76,7 @@ G4UIcmdWith3VectorAndUnit* GatePositroniumSourceMessenger::GetVectorCmdWithUnit( void GatePositroniumSourceMessenger::InitCommands() { upCmdSetPositroniumFractions.reset(GetStringCmd( "setPositroniumFractions", "\"f1, f2, f3 .., fn\" - where fi in [0.0, 1.0] and sum of all fi ==1" ) ); - upCmdSetPositroniumLifetimes.reset(GetStringCmd( "setPositroniumLifetimes", "\"t1, t2, t3 .., tn\" - where" ) ); + upCmdSetPositroniumLifetimes.reset(GetStringCmd( "setPositroniumLifetimes", "\"t1, t2, t3 .., tn\" - where ti corresponds to lifetime constants of the components" ) ); upCmdSetDecayKinds.reset(GetStringCmd( "setDecayKinds", "\"k1, k2, k3 .., kn\" - where ki is k2gamma or k3gamma" ) ); upCmdSetPositronInteractions.reset(GetStringCmd( "setPositronInteractions", "\"k1, k2, k3 .., kn\" - where ki is kpPs, kdirect or koPs, of a given element in vector of components. Used to properly recalculate intensities of the components from the theory" ) ); upCmdSetPromptPhotonProbabilites.reset(GetStringCmd( "setPromptPhotonProbabilites", "\"f1, f2, f3 .., fn\" - where fi in [0.0, 1.0] " ) ); From 14273786bd6883660e5b3b89322c11a40dc4d5ef Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Tue, 27 Jan 2026 20:47:57 +0100 Subject: [PATCH 090/144] Explicit check for k3Gamma string Also, if neither k2Gamma nor k3Gamma is present, GateError is called. --- source/physics/src/GatePositroniumSourceMessenger.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/source/physics/src/GatePositroniumSourceMessenger.cc b/source/physics/src/GatePositroniumSourceMessenger.cc index 734257836..3ecebeaf0 100644 --- a/source/physics/src/GatePositroniumSourceMessenger.cc +++ b/source/physics/src/GatePositroniumSourceMessenger.cc @@ -77,7 +77,7 @@ void GatePositroniumSourceMessenger::InitCommands() { upCmdSetPositroniumFractions.reset(GetStringCmd( "setPositroniumFractions", "\"f1, f2, f3 .., fn\" - where fi in [0.0, 1.0] and sum of all fi ==1" ) ); upCmdSetPositroniumLifetimes.reset(GetStringCmd( "setPositroniumLifetimes", "\"t1, t2, t3 .., tn\" - where ti corresponds to lifetime constants of the components" ) ); - upCmdSetDecayKinds.reset(GetStringCmd( "setDecayKinds", "\"k1, k2, k3 .., kn\" - where ki is k2gamma or k3gamma" ) ); + upCmdSetDecayKinds.reset(GetStringCmd( "setDecayKinds", "\"k1, k2, k3 .., kn\" - where ki is k2Gamma or k3Gamma" ) ); upCmdSetPositronInteractions.reset(GetStringCmd( "setPositronInteractions", "\"k1, k2, k3 .., kn\" - where ki is kpPs, kdirect or koPs, of a given element in vector of components. Used to properly recalculate intensities of the components from the theory" ) ); upCmdSetPromptPhotonProbabilites.reset(GetStringCmd( "setPromptPhotonProbabilites", "\"f1, f2, f3 .., fn\" - where fi in [0.0, 1.0] " ) ); upCmdSetPromptPhotonEnergies.reset(GetStringCmd( "setPromptPhotonEnergies", "\"e1, e2, e3 .., fn\" - where ei are energies " ) ); @@ -134,7 +134,11 @@ void GatePositroniumSourceMessenger::SetNewValue( G4UIcommand* command, G4String if (kind == "k2Gamma") { decayKinds.push_back(k2Gamma); } else { - decayKinds.push_back(k3Gamma); + if (kind == "k3Gamma") { + decayKinds.push_back(k3Gamma); + } else { + GateError("GatePositroniumSourceMessenger::SetNewValue: unknown decay kind read from macro."); + } } } fParamGenerator.SetDecayKinds(decayKinds); From 917382bb8898282e93e67a1302bc1a5d19baa3f0 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Tue, 27 Jan 2026 20:51:38 +0100 Subject: [PATCH 091/144] Add another _ in string --- source/physics/src/GatePositroniumSourceMessenger.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/physics/src/GatePositroniumSourceMessenger.cc b/source/physics/src/GatePositroniumSourceMessenger.cc index 3ecebeaf0..c4138c2b4 100644 --- a/source/physics/src/GatePositroniumSourceMessenger.cc +++ b/source/physics/src/GatePositroniumSourceMessenger.cc @@ -67,7 +67,7 @@ G4UIcmdWith3VectorAndUnit* GatePositroniumSourceMessenger::GetVectorCmdWithUnit( G4String cmd_path = GetDirectoryName() + cmd_name; G4UIcmdWith3VectorAndUnit* cmd = new G4UIcmdWith3VectorAndUnit( cmd_path , this ); cmd->SetGuidance( cmd_guidance ); - cmd->SetParameterName( G4String( cmd_name + "_x" ), G4String( cmd_name + "y" ), G4String( cmd_name + "z" ), false ); + cmd->SetParameterName( G4String( cmd_name + "_x" ), G4String( cmd_name + "_y" ), G4String( cmd_name + "_z" ), false ); cmd->SetDefaultUnit( default_unit.c_str() ); cmd->SetUnitCandidates( unit_candidates.c_str() ); return cmd; From d46f79c73e6c058f3ea3679e5036c23a6036dc73 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Tue, 27 Jan 2026 22:15:35 +0100 Subject: [PATCH 092/144] Add handling of command SetPositronInteractions It was removed accidentally while fixing the code. Also, change the arguments in macro to be consistent with the code naming e.g. kParaPs, kDirect, kOrthoPs. --- .../src/GatePositroniumSourceMessenger.cc | 122 ++++++++++-------- 1 file changed, 66 insertions(+), 56 deletions(-) diff --git a/source/physics/src/GatePositroniumSourceMessenger.cc b/source/physics/src/GatePositroniumSourceMessenger.cc index c4138c2b4..908b60246 100644 --- a/source/physics/src/GatePositroniumSourceMessenger.cc +++ b/source/physics/src/GatePositroniumSourceMessenger.cc @@ -78,75 +78,85 @@ void GatePositroniumSourceMessenger::InitCommands() upCmdSetPositroniumFractions.reset(GetStringCmd( "setPositroniumFractions", "\"f1, f2, f3 .., fn\" - where fi in [0.0, 1.0] and sum of all fi ==1" ) ); upCmdSetPositroniumLifetimes.reset(GetStringCmd( "setPositroniumLifetimes", "\"t1, t2, t3 .., tn\" - where ti corresponds to lifetime constants of the components" ) ); upCmdSetDecayKinds.reset(GetStringCmd( "setDecayKinds", "\"k1, k2, k3 .., kn\" - where ki is k2Gamma or k3Gamma" ) ); - upCmdSetPositronInteractions.reset(GetStringCmd( "setPositronInteractions", "\"k1, k2, k3 .., kn\" - where ki is kpPs, kdirect or koPs, of a given element in vector of components. Used to properly recalculate intensities of the components from the theory" ) ); + upCmdSetPositronInteractions.reset(GetStringCmd( "setPositronInteractions", "\"k1, k2, k3 .., kn\" - where ki is kParaPs, kDirect or kOrthoPs, of a given element in vector of components. Used to properly recalculate intensities of the components from the theory" ) ); upCmdSetPromptPhotonProbabilites.reset(GetStringCmd( "setPromptPhotonProbabilites", "\"f1, f2, f3 .., fn\" - where fi in [0.0, 1.0] " ) ); upCmdSetPromptPhotonEnergies.reset(GetStringCmd( "setPromptPhotonEnergies", "\"e1, e2, e3 .., fn\" - where ei are energies " ) ); } -void GatePositroniumSourceMessenger::SetNewValue( G4UIcommand* command, G4String new_value ) +void GatePositroniumSourceMessenger::SetNewValue(G4UIcommand *command, G4String new_value) { - if(command == upCmdSetPositroniumFractions.get()) - { - std::vector fractions; - std::stringstream ss(new_value); - G4double num; - while (ss >> num) { - fractions.push_back(num); - } - fParamGenerator.SetPositroniumFraction(fractions); - } - else if(command == upCmdSetPositroniumLifetimes.get()) - { - std::vector lifetimes; - std::stringstream ss(new_value); - G4double num; - while (ss >> num) { - lifetimes.push_back(num); - } - fParamGenerator.SetPositroniumLifetimes(lifetimes); - } - else if(command == upCmdSetPromptPhotonProbabilites.get()) - { - std::vector promptPhotonProb; - std::stringstream ss(new_value); - float prob; - while (ss >> prob) { - prob = prob < 0 ? 0 : prob; - prob = prob > 1 ? 1 : prob; - promptPhotonProb.push_back(prob); - } - fParamGenerator.SetPromptGammaProbabilities(promptPhotonProb); - } - else if(command == upCmdSetPromptPhotonEnergies.get()) { - std::vector promptPhotonEnergies; - std::stringstream ss(new_value); - float energy; - while (ss >> energy) { - promptPhotonEnergies.push_back(energy); - } - fParamGenerator.SetPromptGammaEnergies(promptPhotonEnergies); - } - else if(command == upCmdSetDecayKinds.get()) { - std::vector decayKinds; - std::stringstream ss(new_value); - std::string kind; - while (ss >> kind) { - if (kind == "k2Gamma") { + if (command == upCmdSetPositroniumFractions.get()) { + std::vector fractions; + std::stringstream ss(new_value); + G4double num; + while (ss >> num) { + fractions.push_back(num); + } + fParamGenerator.SetPositroniumFraction(fractions); + } else if (command == upCmdSetPositroniumLifetimes.get()) { + std::vector lifetimes; + std::stringstream ss(new_value); + G4double num; + while (ss >> num) { + lifetimes.push_back(num); + } + fParamGenerator.SetPositroniumLifetimes(lifetimes); + } else if (command == upCmdSetPromptPhotonProbabilites.get()) { + std::vector promptPhotonProb; + std::stringstream ss(new_value); + float prob; + while (ss >> prob) { + prob = prob < 0 ? 0 : prob; + prob = prob > 1 ? 1 : prob; + promptPhotonProb.push_back(prob); + } + fParamGenerator.SetPromptGammaProbabilities(promptPhotonProb); + } else if (command == upCmdSetPromptPhotonEnergies.get()) { + std::vector promptPhotonEnergies; + std::stringstream ss(new_value); + float energy; + while (ss >> energy) { + promptPhotonEnergies.push_back(energy); + } + fParamGenerator.SetPromptGammaEnergies(promptPhotonEnergies); + } else if (command == upCmdSetDecayKinds.get()) { + std::vector decayKinds; + std::stringstream ss(new_value); + std::string kind; + while (ss >> kind) { + if (kind == "k2Gamma") { decayKinds.push_back(k2Gamma); } else { if (kind == "k3Gamma") { decayKinds.push_back(k3Gamma); } else { - GateError("GatePositroniumSourceMessenger::SetNewValue: unknown decay kind read from macro."); + GateError("GatePositroniumSourceMessenger::SetNewValue: unknown " + "decay kind read from macro."); } } + } + fParamGenerator.SetDecayKinds(decayKinds); + } else if (command == upCmdSetPositronInteractions.get()) { + std::vector positronInteractions; + std::stringstream ss(new_value); + std::string inter; + while (ss >> inter) { + if (inter == "kParaPs") { + positronInteractions.push_back(PositronElectronInteraction::kParaPs); + } else if (inter == "kDirect") { + positronInteractions.push_back(PositronElectronInteraction::kDirect); + } else if (inter == "kOrthoPs") { + positronInteractions.push_back(PositronElectronInteraction::kOrthoPs); + } else { + + GateError("GatePositroniumSourceMessenger::SetNewValue: unknown " + "interaction type read from macro."); + } + } + fParamGenerator.SetPositronInteractions(positronInteractions); + } else { + GateVSourceMessenger::SetNewValue(command, new_value); } - fParamGenerator.SetDecayKinds(decayKinds); - } - else - { - GateVSourceMessenger::SetNewValue(command, new_value); - } } PositroniumDecayModelParams GatePositroniumSourceMessenger::generatePositroniumDecayParams(GatePositroniumDecayParamsGenerator::DecayModel model) const From 3084b97335eff52d78b7e09f1db4f29e61b7f413 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 23 Jan 2026 16:14:19 +0100 Subject: [PATCH 093/144] Add legacy version of GatePositroniumDecayModel --- .../legacy/GatePositroniumDecayModel.hh | 136 ++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 source/physics/include/legacy/GatePositroniumDecayModel.hh diff --git a/source/physics/include/legacy/GatePositroniumDecayModel.hh b/source/physics/include/legacy/GatePositroniumDecayModel.hh new file mode 100644 index 000000000..4b80f3cb1 --- /dev/null +++ b/source/physics/include/legacy/GatePositroniumDecayModel.hh @@ -0,0 +1,136 @@ +/** ---------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + ----------------------*/ +#ifndef Legacy_PositroniumDecayModel_hh +#define Legacy_PositroniumDecayModel_hh + +#include +#include "G4DecayTable.hh" +#include "G4ParticleTable.hh" +#include "G4PrimaryParticle.hh" +#include "G4GeneralPhaseSpaceDecay.hh" +#include "G4DecayTable.hh" +#include "G4ParticleDefinition.hh" +#include "GateEmittedGammaInformation.hh" +#include "GateGammaEmissionModel.hh" + +#include "G4PhysicalConstants.hh" +#include "G4SystemOfUnits.hh" + +#include "G4VDecayChannel.hh" +#include "G4ParticleDefinition.hh" +#include "G4PrimaryVertex.hh" + +namespace GateLegacy { + +/** Author: Mateusz Bała + * Email: bala.mateusz@gmail.com + * Organization: J-PET (http://koza.if.uj.edu.pl/pet/) + * About class: Class generate gammas from positronium decay with including deexcitation gamma ( prompt gamma ) if is required. + **/ +class GatePositroniumDecayModel : public GateGammaEmissionModel +{ + public: + + /** About class: representation of positronium for main class. Provides access to positronium decay channel. + **/ + class Positronium + { + public: + Positronium( G4String name, G4double life_time, G4int annihilation_gammas_number ); + void SetLifeTime( const G4double& life_time ); + G4double GetLifeTime() const; + G4String GetName() const; + G4int GetAnnihilationGammasNumber() const; + G4DecayProducts* GetDecayProducts(); + private: + G4String fName = ""; + G4double fLifeTime = 0.0;//[ns] + G4int fAnnihilationGammasNumber = 0; + G4VDecayChannel* pDecayChannel = nullptr; + }; + + /** Positronium kind tells which positronium we will use. + * Depends on used Ps gammas number and time will be different. + **/ + enum PositroniumKind { pPs, oPs }; + /** Decay model descibes decay of positronium. + * For example for PositroniumKind::pPs we have decays: + * 1) Standard: pPs -> 2 gamma + * 2) WithPrompt: pPs* -> 2 gamma + prompt_gamma + * Only prompt gamma has nod modified time, other gammas always has modified time. + **/ + enum DecayModel { Standard, WithPrompt }; + + GatePositroniumDecayModel(); + virtual ~GatePositroniumDecayModel(); + + void SetPositroniumKind( PositroniumKind positronium_kind ); + PositroniumKind GetPositroniumKind() const; + + void SetDecayModel( DecayModel decay_model ); + DecayModel GetDecayModel() const; + + void SetPostroniumLifetime( G4String positronium_name, G4double life_time ); //[ns] + + void SetPromptGammaEnergy( G4double prompt_energy ); //[keV] + G4double GetPromptGammaEnergy() const; //[keV] + + /** Set probability of gammas emission from para-positronium decay ( and ortho-positronium too ) + * @param: fraction - number in range from 0.0 to 1.0 ( 0.0 - generate only gammas from oPs decay, 1.0 - generate only gammas from pPs decay ) + **/ + void SetParaPositroniumFraction( G4double fraction ); + + /** Generate vertices and fill them with gammas. Vertices number depends on used DecayModel: + * - one ( if DecayModel::Standard ) + * - two ( if DecayModel::WithPrompt ) + **/ + virtual G4int GeneratePrimaryVertices(G4Event* event, G4double& particle_time, G4ThreeVector& particle_position) override; + + protected: + /** Provides additional information for user about gamma + **/ + virtual GateEmittedGammaInformation* GetPrimaryParticleInformation( const G4PrimaryParticle* pp, const GateEmittedGammaInformation::GammaKind& gamma_kind ) const override; + /** Depends on used model and setted fractions it chooses positronium which decay will be used to generate gammas + **/ + void PreparePositroniumParametrization(); + /** Generate vertex for deexcitation gamma ( prompt gamma ) - position and time is the same as generted by source + **/ + G4PrimaryVertex* GetPrimaryVertexFromDeexcitation(const G4double& particle_time, const G4ThreeVector& particle_position ); + /** Generate vertex for annihilation gammas - position is the same as generted by source, but time is shifted by positronium lifetime ( T0 + f(lifetime)) + **/ + G4PrimaryVertex* GetPrimaryVertexFromPositroniumAnnihilation( const G4double& particle_time, const G4ThreeVector& particle_position ); + /** Generate deexcitation ( prompt ) gamma + **/ + G4PrimaryParticle* GetGammaFromDeexcitation(); + /** Generate annihilation gammas + **/ + std::vector GetGammasFromPositroniumAnnihilation(); + + protected: + //Positronium model - for para-positronium + Positronium fParaPs = Positronium( "pPs", 0.1244 * ns, 2 ); + //Positronium model - for ortho-positronium + Positronium fOrthoPs = Positronium( "oPs", 138.6 * ns, 3 ); + //Positronium model - for current event + Positronium* pInfoPs = nullptr; + //Default deexcitation gamma energy - if user didn't set prompt gamma energy this value will be used + const G4double kSodium22DeexcitationGammaEnergy = 1.022 * MeV;//[MeV] + + //Which positronium use for gammas generation + PositroniumKind fPositroniumKind = PositroniumKind::pPs; + //Which decay model use for gammas generation + DecayModel fDecayModel = DecayModel::Standard; + //Dexcitation gamma energy + G4double fPromptGammaEnergy = kSodium22DeexcitationGammaEnergy;//[MeV] + //Propability of emiiting gammas from para-positronium ( number in range from 0.0 to 1.0 ) + G4double fParaPositroniumFraction = 1.0; + //It is required to generate mixed positronium decays ( pPs and oPs witch propability controled by varaible fParaPositroniumFraction ) + G4bool fUsePositroniumFractions = false; + +}; +} +#endif From fcaa5234c4faa61429dfd0815668cf46cda62768 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 23 Jan 2026 16:22:37 +0100 Subject: [PATCH 094/144] Add legacy ExtendedVSource --- .../include/legacy/GateExtendedVSource.hh | 123 ++++++++++++++++++ .../legacy/GateExtendedVSourceMessenger.hh | 63 +++++++++ 2 files changed, 186 insertions(+) create mode 100644 source/physics/include/legacy/GateExtendedVSource.hh create mode 100644 source/physics/include/legacy/GateExtendedVSourceMessenger.hh diff --git a/source/physics/include/legacy/GateExtendedVSource.hh b/source/physics/include/legacy/GateExtendedVSource.hh new file mode 100644 index 000000000..6cbd71d17 --- /dev/null +++ b/source/physics/include/legacy/GateExtendedVSource.hh @@ -0,0 +1,123 @@ +/** ---------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + ----------------------*/ +#ifndef Legacy_GateExtendedVSource_hh +#define Legacy_GateExtendedVSource_hh + +#include "GateVSource.hh" +#include "legacy/GateExtendedVSourceMessenger.hh" +#include "GateGammaEmissionModel.hh" + +namespace GateLegacy{ + +/** About class: this is helper class to control if setting is in use + **/ +template +class ModelSetting +{ + public: + void Set( T value ) + { + fValue = value; + fIsSetted = true; + } + T Get() const { return fValue; } + bool IsSetted() const { return fIsSetted; } + private: + T fValue; + bool fIsSetted = false; +}; + +/** Author: Mateusz Bała + * Email: bala.mateusz@gmail.com + * Organization: J-PET (http://koza.if.uj.edu.pl/pet/) + * About class: Extended version of GateVSource. It focuses on generating gammas from positronium decay. + **/ +class GateExtendedVSource : public GateVSource +{ +public: + + enum class ModelKind { + NotDefined, //by default - in this case this clas will behave like GateVSource + SingleGamma, // generate single gamma + ParaPositronium, //generate gammas from para-positronium decay + OrthoPositronium, //generate gammas from ortho-positronium decay + Positronium //generate gammas from mixed model ( from pPs and oPs decay with setted ratio ) + }; + + GateExtendedVSource( G4String name ); + virtual ~GateExtendedVSource(); + + /** Generate gammas for event + **/ + virtual G4int GeneratePrimaries( G4Event* event ) override; + + /** Set enable emission of additional gamma - from deexcitation ( prompt gamma ) + **/ + void SetEnableDeexcitation( const G4bool& enable_deexcitation ); + /** Set fixed direction of single gamma ( or prompt gamma ) + **/ + void SetFixedEmissionDirection( const G4ThreeVector& fixed_emission_direction ); + /** Set enable/disable emission of single gamma with fixed direction + **/ + void SetEnableFixedEmissionDirection( const G4bool& enable_fixed_emission_direction ); + /** Set single gamma kinetic energy + **/ + void SetEmissionEnergy( const G4double& energy ); + /** Set seed for Randomize.hh generatores + **/ + void SetSeed( const G4long& seed ); + /** Set positronium lifetime - which is included as constant for exponential distribution ( G4RandExponential ) + * @param: positronium_name - for example: pPs, oPs + * @param: life_time - in ns + * Lifetime value will have inpact of vertex time set for annihilation gammas + **/ + void SetPostroniumLifetime( const G4String& positronium_name ,const G4double& life_time ); + /** Set prompt gamma energy ( deexcictation energy ). + * If user set enable emission of prompt gamma without set prompt energy then il wii be default value used ( deexcitation of Na22 ) + **/ + void SetPromptGammaEnergy( const G4double& energy ); + /** Set propability of gammas emission from different positronium. + * @param: positronium_kind - positronium name: pPs, oPs + * @param: fraction - number in range from 0.0 to 1.0 + * You have to call this method for only one kind of positronium - for the second one propability will be calculated as: 1.0 - fraction. + **/ + void SetPositroniumFraction( const G4String& positronium_kind, const G4double& fraction ); + + protected: + /** Set model used for this source. If is not defined then this class will behave like GateVSource. + **/ + void SetModel( const G4String& model_name ); + /** Prepare model to work - set all settings from user to model + **/ + void PrepareModel(); + + protected: + //Gamma emission model + GateGammaEmissionModel* pModel = nullptr; + //Source messanger + GateExtendedVSourceMessenger* pMessenger = nullptr; + //User settings: + ModelKind fModelKind = ModelKind::NotDefined; + ModelSetting fEnableDeexcitation; + ModelSetting fFixedEmissionDirection; + ModelSetting fEnableFixedEmissionDirection; + ModelSetting fEmissionEnergy; + ModelSetting fSeed; + ModelSetting fParaPostroniumLifetime; + ModelSetting fOrthoPostroniumLifetime; + ModelSetting fPromptGammaEnergy; + ModelSetting fParaPositroniumFraction; + //Constants for Set(...) methods + const G4String kParaPositroniumName = "pPs"; + const G4String kOrthoPositroniumName = "oPs"; + //Set by PrepareModel() and used in GeneratePrimaries() + G4bool fBehaveLikeVSource = false; + +}; +} + +#endif diff --git a/source/physics/include/legacy/GateExtendedVSourceMessenger.hh b/source/physics/include/legacy/GateExtendedVSourceMessenger.hh new file mode 100644 index 000000000..b5adc57e9 --- /dev/null +++ b/source/physics/include/legacy/GateExtendedVSourceMessenger.hh @@ -0,0 +1,63 @@ +/** ---------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + ----------------------*/ +#ifndef Legacy_GateExtendedVSourceMessenger_hh +#define Legacy_GateExtendedVSourceMessenger_hh + +#include "GateVSourceMessenger.hh" +#include "G4UImessenger.hh" +#include "G4UIcmdWithAnInteger.hh" +#include "G4UIcmdWithADoubleAndUnit.hh" +#include "G4UIcmdWithABool.hh" +#include "G4UIcmdWith3Vector.hh" +#include "G4UIcmdWithAString.hh" +#include "G4UIcmdWith3VectorAndUnit.hh" +#include + + +namespace GateLegacy +{ +class GateExtendedVSource; + +/** Author: Mateusz Bała + * Email: bala.mateusz@gmail.com + * Organization: J-PET (http://koza.if.uj.edu.pl/pet/) + * About class: Messenger for GateExtendedVSource class + **/ +class GateExtendedVSourceMessenger: public GateVSourceMessenger +{ + public: + GateExtendedVSourceMessenger( GateExtendedVSource* source ); + ~GateExtendedVSourceMessenger(); + + void SetNewValue( G4UIcommand* command, G4String newValue ); + + protected: + void InitCommands(); + G4UIcmdWithABool* GetBoolCmd( const G4String cmd_name, const G4String cmd_guidance ); + G4UIcmdWithADoubleAndUnit* GetDoubleCmdWithUnit( const G4String cmd_name, const G4String cmd_guidance, const G4String default_unit, const G4String unit_candidates ); + G4UIcmdWith3Vector* GetVectorCmd( const G4String cmd_name, const G4String cmd_guidance ); + G4UIcmdWithAnInteger* GetIntCmd( const G4String cmd_name, const G4String cmd_guidance ); + G4UIcmdWithAString* GetStringCmd(const G4String cmd_name, const G4String cmd_guidance ); + G4UIcmdWith3VectorAndUnit* GetVectorCmdWithUnit( const G4String cmd_name, const G4String cmd_guidance, const G4String default_unit, const G4String unit_candidates ); + + protected: + GateExtendedVSource* pSource = nullptr; + std::unique_ptr upCmdSetEnableDeexcitation; + std::unique_ptr upCmdSetFixedEmissionDirection; + std::unique_ptr upCmdSetEnableFixedEmissionDirection; + std::unique_ptr upCmdSetEmissionEnergy; + std::unique_ptr upCmdSetSeed; + std::unique_ptr upCmdSetPostroniumLifetime; + std::unique_ptr upCmdSetPromptGammaEnergy; + std::unique_ptr upCmdSetPositroniumFraction; + //Supporting commands - disable for user + std::unique_ptr upCmdSetLifetime; + +}; +} + +#endif From 70338f0721079ae25360d429cd21d0dc9ddffb9d Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 23 Jan 2026 16:24:05 +0100 Subject: [PATCH 095/144] Remove obsolete headers from include --- source/physics/include/GateExtendedVSource.hh | 56 ---------------- .../include/GateExtendedVSourceMessenger.hh | 64 ------------------- 2 files changed, 120 deletions(-) delete mode 100644 source/physics/include/GateExtendedVSource.hh delete mode 100644 source/physics/include/GateExtendedVSourceMessenger.hh diff --git a/source/physics/include/GateExtendedVSource.hh b/source/physics/include/GateExtendedVSource.hh deleted file mode 100644 index 3db2c851c..000000000 --- a/source/physics/include/GateExtendedVSource.hh +++ /dev/null @@ -1,56 +0,0 @@ -/** ---------------------- - Copyright (C): OpenGATE Collaboration - This software is distributed under the terms - of the GNU Lesser General Public Licence (LGPL) - See LICENSE.md for further details - ----------------------*/ -#ifndef GateExtendedVSource_hh -#define GateExtendedVSource_hh - -#include - -#include "GateVSource.hh" -#include "GateExtendedVSourceMessenger.hh" -#include "GateGammaEmissionModel.hh" - -/** Author: Mateusz Bała - * Email: bala.mateusz@gmail.com - * Refactored by: Wojciech Krzemien - * About class: Extended version of GateVSource. It focuses on generating gammas from positronium decay. - **/ -class GateExtendedVSource : public GateVSource -{ -public: - - enum class ModelKind { - NotDefined, //by default - in this case this class will behave like GateVSource - ParaPositronium, //generate gammas from para-positronium decay - OrthoPositronium, //generate gammas from ortho-positronium decay - Positronium}; // generate gammas from mixed model - - explicit GateExtendedVSource(const G4String& name); - virtual ~GateExtendedVSource() = default; - - /** Generate gammas for event - **/ - virtual G4int GeneratePrimaries( G4Event* event ) override; - - protected: - /** Set model used for this source. If is not defined then this class will behave like GateVSource. - **/ - void SetModel(const G4String &model_name); - /** Prepare model to work - set all settings from user to model - **/ - void PrepareModel(); - - protected: - std::unique_ptr pModel; - std::unique_ptr pMessenger; - //User settings: - ModelKind fModelKind = ModelKind::NotDefined; - - //Set by PrepareModel() and used in GeneratePrimaries() - G4bool fBehaveLikeVSource = false; -}; - -#endif diff --git a/source/physics/include/GateExtendedVSourceMessenger.hh b/source/physics/include/GateExtendedVSourceMessenger.hh deleted file mode 100644 index b84a3f179..000000000 --- a/source/physics/include/GateExtendedVSourceMessenger.hh +++ /dev/null @@ -1,64 +0,0 @@ -/** ---------------------- - Copyright (C): OpenGATE Collaboration - This software is distributed under the terms - of the GNU Lesser General Public Licence (LGPL) - See LICENSE.md for further details - ----------------------*/ -#ifndef GateExtendedVSourceMessenger_hh -#define GateExtendedVSourceMessenger_hh - -#include - -#include "GateVSourceMessenger.hh" -#include "G4UIcmdWithAnInteger.hh" -#include "G4UIcmdWithADoubleAndUnit.hh" -#include "G4UIcmdWithABool.hh" -#include "G4UIcmdWith3Vector.hh" -#include "G4UIcmdWithAString.hh" -#include "G4UIcmdWith3VectorAndUnit.hh" - -#include "GatePositroniumHelper.hh" -#include "GatePositroniumDecayParamsGenerator.hh" - -class GateExtendedVSource; - -/** Author: Mateusz Bała - * Email: bala.mateusz@gmail.com - * Refactored: Wojciech Krzemien - * About class: Messenger for GateExtendedVSource class - **/ -class GateExtendedVSourceMessenger: public GateVSourceMessenger -{ - public: - explicit GateExtendedVSourceMessenger(GateExtendedVSource *source); - ~GateExtendedVSourceMessenger()=default; - - void SetNewValue(G4UIcommand *command, G4String newValue) override; - - PositroniumDecayModelParams generatePositroniumDecayParams(GatePositroniumDecayParamsGenerator::DecayModel model= GatePositroniumDecayParamsGenerator::kPositronium) const; - - protected: - void InitCommands(); - - G4UIcmdWithABool* GetBoolCmd(const G4String& cmd_name, const G4String& cmd_guidance); - G4UIcmdWithADoubleAndUnit* GetDoubleCmdWithUnit(const G4String& cmd_name, const G4String& cmd_guidance, const G4String& default_unit, const G4String& unit_candidates); - G4UIcmdWith3Vector* GetVectorCmd(const G4String& cmd_name, const G4String& cmd_guidance); - G4UIcmdWithAnInteger* GetIntCmd(const G4String& cmd_name, const G4String& cmd_guidance); - G4UIcmdWithAString* GetStringCmd(const G4String& cmd_name, const G4String& cmd_guidance); - G4UIcmdWith3VectorAndUnit* GetVectorCmdWithUnit(const G4String& cmd_name, const G4String& cmd_guidance, const G4String& default_unit, const G4String& unit_candidates); - - protected: - GateExtendedVSource* pSource = nullptr; - - std::unique_ptr upCmdSetPositroniumFractions; - std::unique_ptr upCmdSetPositroniumLifetimes; - std::unique_ptr upCmdSetPromptPhotonProbabilites; - std::unique_ptr upCmdSetPromptPhotonEnergies; - std::unique_ptr upCmdSetDecayKinds; - std::unique_ptr upCmdSetPositronInteractions; - - GatePositroniumDecayParamsGenerator fParamGenerator; - -}; - -#endif From b2b5e26bbfd4f77fcd27028f99f5ecb72b01e895 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 23 Jan 2026 16:30:47 +0100 Subject: [PATCH 096/144] Add legacy GateExtendedVSource and Messenger cc --- .../physics/src/legacy/GateExtendedVSource.cc | 112 +++++++++++++ .../legacy/GateExtendedVSourceMessenger.cc | 148 ++++++++++++++++++ 2 files changed, 260 insertions(+) create mode 100644 source/physics/src/legacy/GateExtendedVSource.cc create mode 100644 source/physics/src/legacy/GateExtendedVSourceMessenger.cc diff --git a/source/physics/src/legacy/GateExtendedVSource.cc b/source/physics/src/legacy/GateExtendedVSource.cc new file mode 100644 index 000000000..89815461c --- /dev/null +++ b/source/physics/src/legacy/GateExtendedVSource.cc @@ -0,0 +1,112 @@ +/** ---------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + ----------------------*/ +#include "legacy/GateExtendedVSource.hh" +#include +#include "legacy/GatePositroniumDecayModel.hh" +#include "G4Event.hh" + +namespace GateLegacy{ + +GateExtendedVSource::GateExtendedVSource( G4String name ) : GateVSource( name ) +{ + pMessenger = new GateExtendedVSourceMessenger( this ); +} + +GateExtendedVSource::~GateExtendedVSource() +{ + if ( pMessenger != nullptr ) { delete pMessenger; } + if ( pModel != nullptr ) { delete pModel; } +} + +void GateExtendedVSource::SetModel( const G4String& model_name ) +{ + if ( model_name == "sg" ) { fModelKind = GateExtendedVSource::ModelKind::SingleGamma; } + else if ( model_name == "pPs" ) { fModelKind = GateExtendedVSource::ModelKind::ParaPositronium; } + else if ( model_name == "oPs" ) { fModelKind = GateExtendedVSource::ModelKind::OrthoPositronium; } + else if ( model_name == "Ps" ) { fModelKind = GateExtendedVSource::ModelKind::Positronium; } + else + { + fBehaveLikeVSource = true; + G4cout << "GateExtendedVSource::SetModel : Unknown gamma source model. Enable: sg, pPs, oPs, Ps. Switching to GateVSource behavour." << G4endl; } +} + +void GateExtendedVSource::SetEnableDeexcitation( const G4bool& enable_deexcitation ) { fEnableDeexcitation.Set( enable_deexcitation ); } + +void GateExtendedVSource::SetFixedEmissionDirection( const G4ThreeVector& fixed_emission_direction ) { fFixedEmissionDirection.Set( fixed_emission_direction ); } + +void GateExtendedVSource::SetEnableFixedEmissionDirection( const G4bool& enable_fixed_emission_direction ) { fEnableFixedEmissionDirection.Set( enable_fixed_emission_direction ); } + +void GateExtendedVSource::SetEmissionEnergy( const G4double& energy ) { fEmissionEnergy.Set( energy ); } + +void GateExtendedVSource::SetSeed( const G4long& seed ) { fSeed.Set( seed ); } + +void GateExtendedVSource::SetPostroniumLifetime( const G4String& positronium_name, const G4double& life_time ) +{ + if ( positronium_name == kParaPositroniumName ) { fParaPostroniumLifetime.Set( life_time ); } + else if ( positronium_name == kOrthoPositroniumName ) { fOrthoPostroniumLifetime.Set( life_time ); } + else { GateError( "GateExtendedVSource::SetPostroniumLifetime : incorrect positronium name - try: pPs or oPs" ); } +} + +void GateExtendedVSource::SetPromptGammaEnergy( const G4double& energy ) { fPromptGammaEnergy.Set( energy ); } + +void GateExtendedVSource::SetPositroniumFraction( const G4String& positronium_kind, const G4double& fraction ) +{ + if ( fraction > 1.0 || fraction < 0.0 ) + { + GateError( "GateExtendedVSource::SetPositroniumFraction : incorrect fraction value - required: 0.0 <= fraction <= 1.0 " ); + } + + G4double pPs_fraction = 0.0; + + if ( positronium_kind == kParaPositroniumName ) { pPs_fraction = fraction; } + else if ( positronium_kind == kOrthoPositroniumName ) { pPs_fraction = 1.0 - fraction; } + else { GateError( "GateExtendedVSource::SetPositroniumFraction : incorrect positronium kind - enable are: pPs, oPs" ); } + + fParaPositroniumFraction.Set( pPs_fraction ); +} + +void GateExtendedVSource::PrepareModel() +{ + SetModel( GetType() ); + + if ( fBehaveLikeVSource ) { return; } + + if ( fModelKind == GateExtendedVSource::ModelKind::ParaPositronium || fModelKind == GateExtendedVSource::ModelKind::OrthoPositronium || fModelKind == GateExtendedVSource::ModelKind::Positronium ) + { + pModel = new GatePositroniumDecayModel(); + GatePositroniumDecayModel* model = dynamic_cast( pModel ); + + if ( fModelKind == GateExtendedVSource::ModelKind::OrthoPositronium ) { model->SetPositroniumKind( GatePositroniumDecayModel::PositroniumKind::oPs ); } + if ( fModelKind == GateExtendedVSource::ModelKind::Positronium && fParaPositroniumFraction.IsSetted() ) { model->SetParaPositroniumFraction( fParaPositroniumFraction.Get() ); } + + if ( fEnableDeexcitation.IsSetted() && fEnableDeexcitation.Get() ) { model->SetDecayModel( GatePositroniumDecayModel::DecayModel::WithPrompt ); } + if ( fParaPostroniumLifetime.IsSetted() ) { model->SetPostroniumLifetime( kParaPositroniumName, fParaPostroniumLifetime.Get() ); } + if ( fOrthoPostroniumLifetime.IsSetted() ) { model->SetPostroniumLifetime( kOrthoPositroniumName, fOrthoPostroniumLifetime.Get() ); } + if ( fPromptGammaEnergy.IsSetted() ) { model->SetPromptGammaEnergy( fPromptGammaEnergy.Get() ); } + } + else if ( fModelKind == GateExtendedVSource::ModelKind::SingleGamma ) { pModel = new GateGammaEmissionModel(); } + else { GateError( "GateExtendedVSource::PrepareModel - unknown model." ); } + + if ( fFixedEmissionDirection.IsSetted() ) { pModel->SetFixedEmissionDirection( fFixedEmissionDirection.Get() ); } + if ( fEnableFixedEmissionDirection.IsSetted() ) { pModel->SetEnableFixedEmissionDirection( fEnableFixedEmissionDirection.Get() ); } + if ( fEmissionEnergy.IsSetted() ) { pModel->SetEmissionEnergy( fEmissionEnergy.Get() ); } + if ( fSeed.IsSetted() ) { pModel->SetSeed( fSeed.Get() ); } + +} + +G4int GateExtendedVSource::GeneratePrimaries( G4Event* event ) +{ + if ( !fBehaveLikeVSource && pModel == nullptr ) { PrepareModel(); } + if ( fBehaveLikeVSource ) { return GateVSource::GeneratePrimaries( event ); } + + G4double particle_time = GetTime(); + G4ThreeVector particle_position = GetPosDist()->GenerateOne(); + ChangeParticlePositionRelativeToAttachedVolume( particle_position ); + return pModel->GeneratePrimaryVertices( event, particle_time, particle_position); +} +} + diff --git a/source/physics/src/legacy/GateExtendedVSourceMessenger.cc b/source/physics/src/legacy/GateExtendedVSourceMessenger.cc new file mode 100644 index 000000000..00821e10e --- /dev/null +++ b/source/physics/src/legacy/GateExtendedVSourceMessenger.cc @@ -0,0 +1,148 @@ +/** ---------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + ----------------------*/ + +#include "legacy/GateExtendedVSourceMessenger.hh" +#include "legacy/GateExtendedVSource.hh" +#include + +namespace GateLegacy +{ +GateExtendedVSourceMessenger::GateExtendedVSourceMessenger( GateExtendedVSource* source ) : GateVSourceMessenger( source ) +{ + pSource = source; + InitCommands(); +} + +GateExtendedVSourceMessenger::~GateExtendedVSourceMessenger() {} + +G4UIcmdWithABool* GateExtendedVSourceMessenger::GetBoolCmd( const G4String cmd_name, const G4String cmd_guidance ) +{ + G4String cmd_path = GetDirectoryName() + cmd_name; + G4UIcmdWithABool* cmd = new G4UIcmdWithABool( cmd_path, this ); + cmd->SetGuidance( cmd_guidance ); + cmd->SetParameterName( cmd_name, false ); + return cmd; +} + +G4UIcmdWithADoubleAndUnit* GateExtendedVSourceMessenger::GetDoubleCmdWithUnit( const G4String cmd_name, const G4String cmd_guidance, const G4String default_unit, const G4String unit_candidates ) +{ + G4String cmd_path = GetDirectoryName() + cmd_name; + G4UIcmdWithADoubleAndUnit* cmd = new G4UIcmdWithADoubleAndUnit( cmd_path , this ); + cmd->SetGuidance( cmd_guidance ); + cmd->SetParameterName( cmd_name, false ); + cmd->SetDefaultUnit( default_unit.c_str() ); + cmd->SetUnitCandidates( unit_candidates.c_str() ); + return cmd; +} + +G4UIcmdWith3Vector* GateExtendedVSourceMessenger::GetVectorCmd( const G4String cmd_name, const G4String cmd_guidance ) +{ + G4String cmd_path = GetDirectoryName() + cmd_name; + G4UIcmdWith3Vector* cmd = new G4UIcmdWith3Vector( cmd_path, this ); + cmd->SetGuidance( cmd_guidance ); + cmd->SetParameterName( G4String( cmd_name + "_x" ), G4String( cmd_name + "y" ), G4String( cmd_name + "z" ), false ); + return cmd; +} + +G4UIcmdWithAnInteger* GateExtendedVSourceMessenger::GetIntCmd( const G4String cmd_name, const G4String cmd_guidance ) +{ + G4String cmd_path = GetDirectoryName() + cmd_name; + G4UIcmdWithAnInteger* cmd = new G4UIcmdWithAnInteger( cmd_path, this ); + cmd->SetGuidance( cmd_guidance ); + cmd->SetParameterName( cmd_name, false ); + return cmd; +} + +G4UIcmdWithAString* GateExtendedVSourceMessenger::GetStringCmd(const G4String cmd_name, const G4String cmd_guidance ) +{ + G4String cmd_path = GetDirectoryName() + cmd_name; + G4UIcmdWithAString* cmd = new G4UIcmdWithAString( cmd_path, this ); + cmd->SetGuidance( cmd_guidance ); + cmd->SetParameterName( cmd_name, false ); + return cmd; +} + +G4UIcmdWith3VectorAndUnit* GateExtendedVSourceMessenger::GetVectorCmdWithUnit( const G4String cmd_name, const G4String cmd_guidance, const G4String default_unit, const G4String unit_candidates ) +{ + G4String cmd_path = GetDirectoryName() + cmd_name; + G4UIcmdWith3VectorAndUnit* cmd = new G4UIcmdWith3VectorAndUnit( cmd_path , this ); + cmd->SetGuidance( cmd_guidance ); + cmd->SetParameterName( G4String( cmd_name + "_x" ), G4String( cmd_name + "y" ), G4String( cmd_name + "z" ), false ); + cmd->SetDefaultUnit( default_unit.c_str() ); + cmd->SetUnitCandidates( unit_candidates.c_str() ); + return cmd; +} + +void GateExtendedVSourceMessenger::InitCommands() +{ + upCmdSetEnableDeexcitation.reset( GetBoolCmd( "setEnableDeexcitation", "Generate prompt gamma from positron source which precedes positronium formation and decay." ) ); + upCmdSetFixedEmissionDirection.reset( GetVectorCmd( "setFixedEmissionDirection", "Set fixed direction for single and prompt gamma." ) ); + upCmdSetEnableFixedEmissionDirection.reset( GetBoolCmd( "setEnableFixedEmissionDirection", "Set fixed direction enable/disable." ) ); + upCmdSetEmissionEnergy.reset( GetDoubleCmdWithUnit( "setEmissionEnergy", "Set energy for single gamma.", "keV", "keV MeV GeV" ) ); + upCmdSetSeed.reset( GetIntCmd("setSeed", "Set seed for random generator" ) ); + upCmdSetPostroniumLifetime.reset( GetStringCmd( "setPostroniumLifetime", "Set life-time of positronium." ) ); + upCmdSetLifetime.reset( GetDoubleCmdWithUnit( "setLifetime", "Set life-time of positronium - disable for user.", "ns", "ps ns" ) ); + upCmdSetPromptGammaEnergy.reset( GetDoubleCmdWithUnit( "setPromptGammaEnergy", "Set energy for prompt gamma.", "keV", "keV MeV GeV" ) ); + upCmdSetPositroniumFraction.reset( GetStringCmd( "setPositroniumFraction", "\"positronium_kind fraction\" - where positronium_kind = {pPs, oPs} and fraction in [0.0, 1.0]" ) ); +} + +void GateExtendedVSourceMessenger::SetNewValue( G4UIcommand* command, G4String new_value ) +{ + if ( command == upCmdSetEnableDeexcitation.get() ) + { + pSource->SetEnableDeexcitation( upCmdSetEnableDeexcitation->GetNewBoolValue( new_value ) ); + } + else if ( command == upCmdSetFixedEmissionDirection.get() ) + { + pSource->SetFixedEmissionDirection( upCmdSetFixedEmissionDirection->GetNew3VectorValue( new_value ) ); + } + else if ( command == upCmdSetEnableFixedEmissionDirection.get() ) + { + pSource->SetEnableFixedEmissionDirection( upCmdSetEnableFixedEmissionDirection->GetNewBoolValue( new_value ) ); + } + else if ( command == upCmdSetEmissionEnergy.get() ) + { + pSource->SetEmissionEnergy( upCmdSetEmissionEnergy->GetNewDoubleValue( new_value ) ); + } + else if ( command == upCmdSetSeed.get() ) + { + pSource->SetSeed( static_cast( upCmdSetSeed->GetNewIntValue( new_value ) ) ); + } + else if ( command == upCmdSetPostroniumLifetime.get() ) + { + G4String positronium_name = ""; + G4String units = ""; + G4double value = 0.0; + std::stringstream ss; + ss << new_value; + ss >> positronium_name >> value >> units; + G4String new_value_lifetime = std::to_string( value ) + " " + units; + pSource->SetPostroniumLifetime( positronium_name, upCmdSetLifetime->GetNewDoubleValue( new_value_lifetime ) ); + + } + else if ( command == upCmdSetPromptGammaEnergy.get() ) + { + pSource->SetPromptGammaEnergy( upCmdSetPromptGammaEnergy->GetNewDoubleValue( new_value ) ); + } + else if ( command == upCmdSetPositroniumFraction.get() ) + { + G4double fraction = 0.0; + G4String positronium_kind = ""; + std::stringstream ss; + ss << new_value; + ss >> positronium_kind >> fraction; + pSource->SetPositroniumFraction( positronium_kind, fraction ); + } + else + { + GateVSourceMessenger::SetNewValue( command, new_value ); + } +} + +} + + From bfd974b582452da114dd2d0c7b8c974da0261c39 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Wed, 28 Jan 2026 17:18:41 +0100 Subject: [PATCH 097/144] Remove obsolete GateExtendedVSource impl --- source/physics/src/GateExtendedVSource.cc | 79 -------- .../src/GateExtendedVSourceMessenger.cc | 168 ------------------ 2 files changed, 247 deletions(-) delete mode 100644 source/physics/src/GateExtendedVSource.cc delete mode 100644 source/physics/src/GateExtendedVSourceMessenger.cc diff --git a/source/physics/src/GateExtendedVSource.cc b/source/physics/src/GateExtendedVSource.cc deleted file mode 100644 index a54308aa1..000000000 --- a/source/physics/src/GateExtendedVSource.cc +++ /dev/null @@ -1,79 +0,0 @@ -/** ---------------------- - Copyright (C): OpenGATE Collaboration - This software is distributed under the terms - of the GNU Lesser General Public Licence (LGPL) - See LICENSE.md for further details - ----------------------*/ -#include - -#include "G4Event.hh" - -#include "GateExtendedVSource.hh" -#include "GatePositroniumDecayModel.hh" - - -GateExtendedVSource::GateExtendedVSource(const G4String &name) - : GateVSource(name), - pMessenger(std::make_unique(this)) -{ -} - -void GateExtendedVSource::SetModel(const G4String &model_name) -{ - static const std::map models{ - {"pPs", GateExtendedVSource::ModelKind::ParaPositronium}, - {"oPs", GateExtendedVSource::ModelKind::OrthoPositronium}, - {"Ps", GateExtendedVSource::ModelKind::Positronium}}; - - auto it = models.find(model_name); - if (it != models.end()) - { - fModelKind = it->second; - } else { - fBehaveLikeVSource = true; - G4cout << "GateExtendedVSource::SetModel : Unknown gamma source model. " - "Enable: pPs, oPs, Ps. Switching to GateVSource behavour." - << G4endl; - } -} - -void GateExtendedVSource::PrepareModel() -{ - SetModel(GetType()); - - if (fBehaveLikeVSource) { - return; - } - - if (fModelKind == GateExtendedVSource::ModelKind::Positronium) { - auto params = pMessenger->generatePositroniumDecayParams(); - pModel = std::make_unique(params); - } else { - if (fModelKind == GateExtendedVSource::ModelKind::ParaPositronium) { - auto params = pMessenger->generatePositroniumDecayParams( - GatePositroniumDecayParamsGenerator::kParaPositronium); - pModel = std::make_unique(params); - - } else { - if (fModelKind == GateExtendedVSource::ModelKind::OrthoPositronium) { - auto params = pMessenger->generatePositroniumDecayParams( - GatePositroniumDecayParamsGenerator::kOrthoPositronium); - pModel = std::make_unique(params); - } else { - GateError("GateExtendedVSource::PrepareModel - unknown model."); - } - } - } -} - -G4int GateExtendedVSource::GeneratePrimaries(G4Event* event) -{ - if (!fBehaveLikeVSource && !pModel) { PrepareModel(); } - if (fBehaveLikeVSource) { return GateVSource::GeneratePrimaries(event); } - - G4double particle_time = GetTime(); - G4ThreeVector particle_position = GetPosDist()->GenerateOne(); - ChangeParticlePositionRelativeToAttachedVolume(particle_position); - return pModel->GeneratePrimaryVertices(event, particle_time, particle_position); -} - diff --git a/source/physics/src/GateExtendedVSourceMessenger.cc b/source/physics/src/GateExtendedVSourceMessenger.cc deleted file mode 100644 index 723e10265..000000000 --- a/source/physics/src/GateExtendedVSourceMessenger.cc +++ /dev/null @@ -1,168 +0,0 @@ -/** ---------------------- - Copyright (C): OpenGATE Collaboration - This software is distributed under the terms - of the GNU Lesser General Public Licence (LGPL) - See LICENSE.md for further details - ----------------------*/ - -#include - -#include "GateExtendedVSourceMessenger.hh" -#include "GateExtendedVSource.hh" - -GateExtendedVSourceMessenger::GateExtendedVSourceMessenger(GateExtendedVSource *source): GateVSourceMessenger(source), pSource(source) -{ - InitCommands(); -} - -G4UIcmdWithABool* GateExtendedVSourceMessenger::GetBoolCmd(const G4String& cmd_name, const G4String& cmd_guidance ) -{ - G4String cmd_path = GetDirectoryName() + cmd_name; - G4UIcmdWithABool* cmd = new G4UIcmdWithABool( cmd_path, this ); - cmd->SetGuidance( cmd_guidance ); - cmd->SetParameterName( cmd_name, false ); - return cmd; -} - -G4UIcmdWithADoubleAndUnit* GateExtendedVSourceMessenger::GetDoubleCmdWithUnit( const G4String& cmd_name, const G4String& cmd_guidance, const G4String& default_unit, const G4String& unit_candidates ) -{ - G4String cmd_path = GetDirectoryName() + cmd_name; - G4UIcmdWithADoubleAndUnit* cmd = new G4UIcmdWithADoubleAndUnit( cmd_path , this ); - cmd->SetGuidance( cmd_guidance ); - cmd->SetParameterName( cmd_name, false ); - cmd->SetDefaultUnit( default_unit.c_str() ); - cmd->SetUnitCandidates( unit_candidates.c_str() ); - return cmd; -} - -G4UIcmdWith3Vector* GateExtendedVSourceMessenger::GetVectorCmd( const G4String& cmd_name, const G4String& cmd_guidance ) -{ - G4String cmd_path = GetDirectoryName() + cmd_name; - G4UIcmdWith3Vector* cmd = new G4UIcmdWith3Vector( cmd_path, this ); - cmd->SetGuidance( cmd_guidance ); - cmd->SetParameterName( G4String( cmd_name + "_x" ), G4String( cmd_name + "y" ), G4String( cmd_name + "z" ), false ); - return cmd; -} - -G4UIcmdWithAnInteger* GateExtendedVSourceMessenger::GetIntCmd( const G4String& cmd_name, const G4String& cmd_guidance ) -{ - G4String cmd_path = GetDirectoryName() + cmd_name; - G4UIcmdWithAnInteger* cmd = new G4UIcmdWithAnInteger( cmd_path, this ); - cmd->SetGuidance( cmd_guidance ); - cmd->SetParameterName( cmd_name, false ); - return cmd; -} - -G4UIcmdWithAString* GateExtendedVSourceMessenger::GetStringCmd(const G4String& cmd_name, const G4String& cmd_guidance ) -{ - G4String cmd_path = GetDirectoryName() + cmd_name; - G4UIcmdWithAString* cmd = new G4UIcmdWithAString( cmd_path, this ); - cmd->SetGuidance( cmd_guidance ); - cmd->SetParameterName( cmd_name, false ); - return cmd; -} - -G4UIcmdWith3VectorAndUnit* GateExtendedVSourceMessenger::GetVectorCmdWithUnit( const G4String& cmd_name, const G4String& cmd_guidance, const G4String& default_unit, const G4String& unit_candidates ) -{ - G4String cmd_path = GetDirectoryName() + cmd_name; - G4UIcmdWith3VectorAndUnit* cmd = new G4UIcmdWith3VectorAndUnit( cmd_path , this ); - cmd->SetGuidance( cmd_guidance ); - cmd->SetParameterName( G4String( cmd_name + "_x" ), G4String( cmd_name + "y" ), G4String( cmd_name + "z" ), false ); - cmd->SetDefaultUnit( default_unit.c_str() ); - cmd->SetUnitCandidates( unit_candidates.c_str() ); - return cmd; -} - -void GateExtendedVSourceMessenger::InitCommands() -{ - upCmdSetPositroniumFractions.reset(GetStringCmd( "setPositroniumFractions", "\"f1, f2, f3 .., fn\" - where fi in [0.0, 1.0] and sum of all fi ==1" ) ); - upCmdSetPositroniumLifetimes.reset(GetStringCmd( "setPositroniumLifetimes", "\"t1, t2, t3 .., tn\" - where" ) ); - upCmdSetDecayKinds.reset(GetStringCmd( "setDecayKinds", "\"k1, k2, k3 .., kn\" - where ki is k2gamma or k3gamma" ) ); - upCmdSetPositronInteractions.reset(GetStringCmd( "setPositronInteractions", "\"k1, k2, k3 .., kn\" - where ki is kpPs, kdirect or koPs, of a given element in vector of components. Used to properly recalculate intensities of the components from the theory" ) ); - upCmdSetPromptPhotonProbabilites.reset(GetStringCmd( "setPromptPhotonProbabilites", "\"f1, f2, f3 .., fn\" - where fi in [0.0, 1.0] " ) ); - upCmdSetPromptPhotonEnergies.reset(GetStringCmd( "setPromptPhotonEnergies", "\"e1, e2, e3 .., fn\" - where ei are energies " ) ); -} - -void GateExtendedVSourceMessenger::SetNewValue( G4UIcommand* command, G4String new_value ) -{ - if(command == upCmdSetPositroniumFractions.get()) - { - std::vector fractions; - std::stringstream ss(new_value); - G4double num; - while (ss >> num) { - fractions.push_back(num); - } - fParamGenerator.SetPositroniumFraction(fractions); - } - else if(command == upCmdSetPositroniumLifetimes.get()) - { - std::vector lifetimes; - std::stringstream ss(new_value); - G4double num; - while (ss >> num) { - lifetimes.push_back(num); - } - fParamGenerator.SetPositroniumLifetimes(lifetimes); - } - else if(command == upCmdSetPromptPhotonProbabilites.get()) - { - std::vector promptPhotonProb; - std::stringstream ss(new_value); - float prob; - while (ss >> prob) { - prob = prob < 0 ? 0 : prob; - prob = prob > 1 ? 1 : prob; - promptPhotonProb.push_back(prob); - } - fParamGenerator.SetPromptGammaProbabilities(promptPhotonProb); - } - else if(command == upCmdSetPromptPhotonEnergies.get()) { - std::vector promptPhotonEnergies; - std::stringstream ss(new_value); - float energy; - while (ss >> energy) { - promptPhotonEnergies.push_back(energy); - } - fParamGenerator.SetPromptGammaEnergies(promptPhotonEnergies); - } - else if(command == upCmdSetDecayKinds.get()) { - std::vector decayKinds; - std::stringstream ss(new_value); - std::string kind; - while (ss >> kind) { - if (kind == "k2Gamma") { - decayKinds.push_back(k2Gamma); - } else { - decayKinds.push_back(k3Gamma); - } - } - fParamGenerator.SetDecayKinds(decayKinds); - } - else if(command == upCmdSetPositronInteractions.get()) { - std::vector positronInteractions; - std::stringstream ss(new_value); - std::string inter; - while (ss >> inter) { - if (inter == "kpPs") { - positronInteractions.push_back(PositronElectronInteraction::kParaPs); - } else if (inter == "kdirect") { - positronInteractions.push_back(PositronElectronInteraction::kDirect); - } else { - positronInteractions.push_back(PositronElectronInteraction::kOrthoPs); - } - } - fParamGenerator.SetPositronInteractions(positronInteractions); - } - else - { - GateVSourceMessenger::SetNewValue(command, new_value); - } -} - -PositroniumDecayModelParams GateExtendedVSourceMessenger::generatePositroniumDecayParams(GatePositroniumDecayParamsGenerator::DecayModel model) const -{ - return fParamGenerator.generatePositroniumDecayParams(model); -} - - From f60493fbb7c53541aa29337c2c9850456363c092 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 23 Jan 2026 16:33:10 +0100 Subject: [PATCH 098/144] Change to legacy version in GateSourceMgr --- source/physics/src/GateSourceMgr.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/physics/src/GateSourceMgr.cc b/source/physics/src/GateSourceMgr.cc index 85299dee0..b71f162f2 100644 --- a/source/physics/src/GateSourceMgr.cc +++ b/source/physics/src/GateSourceMgr.cc @@ -29,7 +29,7 @@ #include "G4RunManager.hh" #include "GateSourceOfPromptGamma.hh" #include "GateSourcePhaseSpace.hh" -#include "GateExtendedVSource.hh" +#include "legacy/GateExtendedVSource.hh" #include "GatePositroniumSource.hh" //---------------------------------------------------------------------------------------- @@ -235,7 +235,7 @@ G4int GateSourceMgr::AddSource( std::vector sourceVec ) source->SetIfSourceVoxelized(false); // added by I. Martinez-Rovira (immamartinez@gmail.com) } else if (sourceGeomType == "Extended"){ - source = new GateExtendedVSource( sourceName ); + source = new GateLegacy::GateExtendedVSource( sourceName ); source->SetSourceID( m_sourceProgressiveNumber ); } else if (sourceGeomType == "PositroniumSource"){ From e9e11b4f7644fd7b19b8b379b93e227c95e05f1e Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 23 Jan 2026 18:07:11 +0100 Subject: [PATCH 099/144] Remove setting seed It is never passed to the class anyway. --- source/physics/src/legacy/GateExtendedVSource.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/source/physics/src/legacy/GateExtendedVSource.cc b/source/physics/src/legacy/GateExtendedVSource.cc index 89815461c..ddf840912 100644 --- a/source/physics/src/legacy/GateExtendedVSource.cc +++ b/source/physics/src/legacy/GateExtendedVSource.cc @@ -94,7 +94,6 @@ void GateExtendedVSource::PrepareModel() if ( fFixedEmissionDirection.IsSetted() ) { pModel->SetFixedEmissionDirection( fFixedEmissionDirection.Get() ); } if ( fEnableFixedEmissionDirection.IsSetted() ) { pModel->SetEnableFixedEmissionDirection( fEnableFixedEmissionDirection.Get() ); } if ( fEmissionEnergy.IsSetted() ) { pModel->SetEmissionEnergy( fEmissionEnergy.Get() ); } - if ( fSeed.IsSetted() ) { pModel->SetSeed( fSeed.Get() ); } } From 48452228a5662ef0741404ec90a00c4b2cc4fb45 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 23 Jan 2026 18:12:43 +0100 Subject: [PATCH 100/144] Add legacy GatePositroniumDecayModel --- .../src/legacy/GatePositroniumDecayModel.cc | 187 ++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 source/physics/src/legacy/GatePositroniumDecayModel.cc diff --git a/source/physics/src/legacy/GatePositroniumDecayModel.cc b/source/physics/src/legacy/GatePositroniumDecayModel.cc new file mode 100644 index 000000000..c6f62b056 --- /dev/null +++ b/source/physics/src/legacy/GatePositroniumDecayModel.cc @@ -0,0 +1,187 @@ +/** ---------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + ----------------------*/ +#include "legacy/GatePositroniumDecayModel.hh" +#include "Randomize.hh" +#include +#include +#include +#include "G4DecayProducts.hh" +#include "G4LorentzVector.hh" +#include "G4ParticleTable.hh" + +namespace GateLegacy +{ + +GatePositroniumDecayModel::Positronium::Positronium( G4String name, G4double life_time, G4int annihilation_gammas_number ) : fName( name ), fLifeTime( life_time ), fAnnihilationGammasNumber( annihilation_gammas_number ) +{ + G4ParticleDefinition* positronium_def = G4ParticleTable::GetParticleTable()->FindParticle( name ); + G4DecayTable* positronium_decay_table = positronium_def->GetDecayTable(); + pDecayChannel = positronium_decay_table->GetDecayChannel(0); +} + +void GatePositroniumDecayModel::Positronium::SetLifeTime( const G4double& life_time ) { fLifeTime = life_time; } + +G4double GatePositroniumDecayModel::Positronium::GetLifeTime() const { return fLifeTime; } + +G4String GatePositroniumDecayModel::Positronium::GetName() const { return fName; } + +G4int GatePositroniumDecayModel::Positronium::GetAnnihilationGammasNumber() const { return fAnnihilationGammasNumber; } + +G4DecayProducts* GatePositroniumDecayModel::Positronium::GetDecayProducts() { return pDecayChannel->DecayIt(); } + +GatePositroniumDecayModel::GatePositroniumDecayModel() +{ + SetModelName( "GatePositroniumDecayModel" ); +} + +GatePositroniumDecayModel::~GatePositroniumDecayModel() {} + +void GatePositroniumDecayModel::SetPositroniumKind( GatePositroniumDecayModel::PositroniumKind positronium_kind ) { fPositroniumKind = positronium_kind; } + +GatePositroniumDecayModel::PositroniumKind GatePositroniumDecayModel::GetPositroniumKind() const { return fPositroniumKind; } + +void GatePositroniumDecayModel::SetDecayModel( GatePositroniumDecayModel::DecayModel decay_model ) { fDecayModel = decay_model; } + +GatePositroniumDecayModel::DecayModel GatePositroniumDecayModel::GetDecayModel() const { return fDecayModel; } + +void GatePositroniumDecayModel::SetPostroniumLifetime( G4String positronium_name, G4double life_time ) +{ + if ( !( life_time > 0.0 ) ) { NoticeError( G4String( __FUNCTION__ ), "positronium life-time should be positive value." ); } + + if ( positronium_name == fParaPs.GetName() ) { fParaPs.SetLifeTime( life_time ); } + else if ( positronium_name == fOrthoPs.GetName() ) { fOrthoPs.SetLifeTime( life_time ); } + else { NoticeError( G4String( __FUNCTION__ ), "Unknown positronium name." ); } +} + +void GatePositroniumDecayModel::SetPromptGammaEnergy( G4double prompt_energy ) +{ + if ( !( prompt_energy > 0.0 ) ) { NoticeError( G4String( __FUNCTION__ ), "prompt gamma energy should be positive value." ); } + fPromptGammaEnergy = prompt_energy; +} + +G4double GatePositroniumDecayModel::GetPromptGammaEnergy() const { return fPromptGammaEnergy; } + +void GatePositroniumDecayModel::SetParaPositroniumFraction( G4double fraction ) +{ + fUsePositroniumFractions = true; + fParaPositroniumFraction = fraction; +} + +void GatePositroniumDecayModel::PreparePositroniumParametrization() +{ + if ( pInfoPs != nullptr ) + { + if ( !fUsePositroniumFractions ) { return; } + + //Let's draw a positronium decay for current event + + if ( fParaPositroniumFraction >= G4UniformRand() ) { fPositroniumKind = PositroniumKind::pPs; } + else { fPositroniumKind = PositroniumKind::oPs; } + } + + switch ( fPositroniumKind ) + { + case PositroniumKind::pPs: + pInfoPs = &fParaPs; + break; + case PositroniumKind::oPs: + pInfoPs = &fOrthoPs; + break; + default: + NoticeError( G4String( __FUNCTION__ ), "improper chosen positronium kind." ); + break; + }; +} + +GateEmittedGammaInformation* GatePositroniumDecayModel::GetPrimaryParticleInformation( const G4PrimaryParticle* pp, const GateEmittedGammaInformation::GammaKind& gamma_kind ) const +{ + GateEmittedGammaInformation* egi = new GateEmittedGammaInformation(); + + GateEmittedGammaInformation::SourceKind source_kind = GateEmittedGammaInformation::SourceKind::ParaPositronium; + GateEmittedGammaInformation::DecayModel decay_model = GateEmittedGammaInformation::DecayModel::Standard; + + if ( fPositroniumKind == PositroniumKind::oPs ) { source_kind = GateEmittedGammaInformation::SourceKind::OrthoPositronium; } + if ( fDecayModel == DecayModel::WithPrompt ) { decay_model = GateEmittedGammaInformation::DecayModel::Deexcitation; } + + egi->SetSourceKind( source_kind ); + egi->SetDecayModel( decay_model ); + egi->SetGammaKind( gamma_kind ); + egi->SetInitialPolarization( pp->GetPolarization() ); + + if ( gamma_kind == GateEmittedGammaInformation::GammaKind::Annihilation ){ egi->SetTimeShift( pInfoPs->GetLifeTime() ); } + + return egi; +} + +G4int GatePositroniumDecayModel::GeneratePrimaryVertices(G4Event* event, G4double& particle_time, G4ThreeVector& particle_position ) +{ + PreparePositroniumParametrization(); + G4int vertexes_number = 1; + + if ( fDecayModel == DecayModel::WithPrompt ) + { + ++vertexes_number; + event->AddPrimaryVertex( GetPrimaryVertexFromDeexcitation(particle_time, particle_position) ); + } + + event->AddPrimaryVertex( GetPrimaryVertexFromPositroniumAnnihilation(particle_time, particle_position) ); + + //Do testu + /*G4PrimaryVertex* vertex = new G4PrimaryVertex(particle_position, particle_time); + std::vector gammas_ps = GetGammasFromPositroniumAnnihilation(); + vertex->SetPrimary( GetGammaFromDeexcitation() ); + std::for_each( gammas_ps.begin(), gammas_ps.end(), [&]( G4PrimaryParticle* gamma ) { vertex->SetPrimary( gamma ); } );*/ + + return vertexes_number; +} + +G4PrimaryVertex* GatePositroniumDecayModel::GetPrimaryVertexFromDeexcitation(const G4double& particle_time, const G4ThreeVector& particle_position ) +{ + G4PrimaryVertex* vertex = new G4PrimaryVertex(particle_position, particle_time); + vertex->SetPrimary( GetGammaFromDeexcitation() ); + return vertex; +} + +G4PrimaryVertex* GatePositroniumDecayModel::GetPrimaryVertexFromPositroniumAnnihilation( const G4double& particle_time, const G4ThreeVector& particle_position ) +{ + G4double shifted_particle_time = particle_time + G4RandExponential::shoot( pInfoPs->GetLifeTime() ); + + G4PrimaryVertex* vertex = new G4PrimaryVertex( particle_position, shifted_particle_time ); + std::vector gammas = GetGammasFromPositroniumAnnihilation(); + std::for_each( gammas.begin(), gammas.end(), [&]( G4PrimaryParticle* gamma ) { vertex->SetPrimary( gamma ); } ); + return vertex; +} + +G4PrimaryParticle* GatePositroniumDecayModel::GetGammaFromDeexcitation() +{ + G4PrimaryParticle* gamma = GetSingleGamma( fPromptGammaEnergy ); + gamma->SetUserInformation( GetPrimaryParticleInformation( gamma, GateEmittedGammaInformation::GammaKind::Prompt ) ); + return gamma; +} + +std::vector GatePositroniumDecayModel::GetGammasFromPositroniumAnnihilation() +{ + std::vector gammas( pInfoPs->GetAnnihilationGammasNumber() ) ; + + G4DecayProducts* decay_products = pInfoPs->GetDecayProducts(); + for ( G4int i = 0; i < pInfoPs->GetAnnihilationGammasNumber(); ++i ) + { + G4PrimaryParticle* gamma = new G4PrimaryParticle( pGammaDefinition ); + + G4DynamicParticle* dynamic_gamma = (*decay_products)[i]; + G4LorentzVector lv = dynamic_gamma->Get4Momentum(); + gamma->Set4Momentum( lv.px(), lv.py(), lv.pz(), lv.e() ); + gamma->SetPolarization( dynamic_gamma->GetPolarization() ); + gamma->SetUserInformation( GetPrimaryParticleInformation( gamma, GateEmittedGammaInformation::GammaKind::Annihilation ) ); + gammas[i] = gamma; + } + delete decay_products; + + return gammas; +} +} + From 04c3eae8c099a586f583a0b96fe45e207f0e70bb Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 23 Jan 2026 18:14:09 +0100 Subject: [PATCH 101/144] Add legacy code in CMakeLists --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2026bc8d4..b29d15f0a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -255,6 +255,7 @@ FILE(GLOB sources ${PROJECT_SOURCE_DIR}/source/geometry/src/*.cc ${PROJECT_SOURCE_DIR}/source/digits_hits/src/*.cc ${PROJECT_SOURCE_DIR}/source/physics/src/*.cc + ${PROJECT_SOURCE_DIR}/source/physics/src/legacy/*.cc ${PROJECT_SOURCE_DIR}/source/general/src/*.cc ${PROJECT_SOURCE_DIR}/source/externals/clhep/src/CLHEP/Matrix/*.cc ${PROJECT_SOURCE_DIR}/source/externals/clhep/src/CLHEP/RandomObjects/*.cc) @@ -264,6 +265,7 @@ FILE(GLOB headers ${PROJECT_SOURCE_DIR}/source/arf/include/*.hh ${PROJECT_SOURCE_DIR}/source/geometry/include/*.hh ${PROJECT_SOURCE_DIR}/source/physics/include/*.hh + ${PROJECT_SOURCE_DIR}/source/physics/include/legacy/*.hh ${PROJECT_SOURCE_DIR}/source/digits_hits/include/*.hh ${PROJECT_SOURCE_DIR}/source/general/include/*.hh ${PROJECT_SOURCE_DIR}/source/externals/clhep/include/*.hh) From 529a439f5d3a3b55f986ae4c944080f55bdb58ba Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 23 Jan 2026 18:37:04 +0100 Subject: [PATCH 102/144] Move positronium code to legacy --- .../{ => legacy}/GateOrthoPositronium.hh | 22 ++- .../{ => legacy}/GateParaPositronium.hh | 23 ++- .../legacy/GatePositroniumDecayChannel.hh | 70 ++++++++ source/physics/src/GateParaPositronium.cc | 62 ------- source/physics/src/GatePhysicsList.cc | 8 +- .../src/{ => legacy}/GateOrthoPositronium.cc | 16 +- .../physics/src/legacy/GateParaPositronium.cc | 66 ++++++++ .../src/legacy/GatePositroniumDecayChannel.cc | 160 ++++++++++++++++++ 8 files changed, 329 insertions(+), 98 deletions(-) rename source/physics/include/{ => legacy}/GateOrthoPositronium.hh (70%) rename source/physics/include/{ => legacy}/GateParaPositronium.hh (70%) create mode 100644 source/physics/include/legacy/GatePositroniumDecayChannel.hh delete mode 100644 source/physics/src/GateParaPositronium.cc rename source/physics/src/{ => legacy}/GateOrthoPositronium.cc (82%) create mode 100644 source/physics/src/legacy/GateParaPositronium.cc create mode 100644 source/physics/src/legacy/GatePositroniumDecayChannel.cc diff --git a/source/physics/include/GateOrthoPositronium.hh b/source/physics/include/legacy/GateOrthoPositronium.hh similarity index 70% rename from source/physics/include/GateOrthoPositronium.hh rename to source/physics/include/legacy/GateOrthoPositronium.hh index f1d7ac915..47a742b96 100644 --- a/source/physics/include/GateOrthoPositronium.hh +++ b/source/physics/include/legacy/GateOrthoPositronium.hh @@ -4,31 +4,29 @@ of the GNU Lesser General Public Licence (LGPL) See LICENSE.md for further details ----------------------*/ -#ifndef GateOrthoPositronium_hh -#define GateOrthoPositronium_hh +#ifndef Legacy_GateOrthoPositronium_hh +#define Legacy_GateOrthoPositronium_hh #include "G4ParticleDefinition.hh" +namespace GateLegacy{ + /** Author: Mateusz Bała * Email: bala.mateusz@gmail.com + * Organization: J-PET (http://koza.if.uj.edu.pl/pet/) * About class: Generate ortho-positronium definition and set decay chanel for oPs. **/ class GateOrthoPositronium : public G4ParticleDefinition { + private: + static GateOrthoPositronium* theInstance; + GateOrthoPositronium() {} + ~GateOrthoPositronium() {} public: static GateOrthoPositronium* Definition(); static GateOrthoPositronium* OrthoPositroniumDefinition(); static GateOrthoPositronium* OrthoPositronium(); - - private: - static GateOrthoPositronium* theInstance; - - GateOrthoPositronium() = default; - ~GateOrthoPositronium() override = default; - - public: - GateOrthoPositronium(const GateOrthoPositronium&) = delete; - GateOrthoPositronium& operator=(const GateOrthoPositronium&) = delete; }; +} #endif diff --git a/source/physics/include/GateParaPositronium.hh b/source/physics/include/legacy/GateParaPositronium.hh similarity index 70% rename from source/physics/include/GateParaPositronium.hh rename to source/physics/include/legacy/GateParaPositronium.hh index bce3eeb95..1efddd5fd 100644 --- a/source/physics/include/GateParaPositronium.hh +++ b/source/physics/include/legacy/GateParaPositronium.hh @@ -4,32 +4,29 @@ of the GNU Lesser General Public Licence (LGPL) See LICENSE.md for further details ----------------------*/ -#ifndef GateParaPositronium_hh -#define GateParaPositronium_hh +#ifndef Legacy_GateParaPositronium_hh +#define Legacy_GateParaPositronium_hh #include "G4ParticleDefinition.hh" +namespace GateLegacy{ + /** Author: Mateusz Bała * Email: bala.mateusz@gmail.com + * Organization: J-PET (http://koza.if.uj.edu.pl/pet/) * About class: Generate para-positronium definition and set decay chanel for pPs. **/ class GateParaPositronium : public G4ParticleDefinition { + private: + static GateParaPositronium* theInstance; + GateParaPositronium() {} + ~GateParaPositronium() {} public: static GateParaPositronium* Definition(); static GateParaPositronium* ParaPositroniumDefinition(); static GateParaPositronium* ParaPositronium(); - - private: - static GateParaPositronium* theInstance; - - GateParaPositronium() = default; - ~GateParaPositronium() override = default; - - public: - GateParaPositronium(const GateParaPositronium&) = delete; - GateParaPositronium& operator=(const GateParaPositronium&) = delete; - }; +} #endif diff --git a/source/physics/include/legacy/GatePositroniumDecayChannel.hh b/source/physics/include/legacy/GatePositroniumDecayChannel.hh new file mode 100644 index 000000000..859a22d52 --- /dev/null +++ b/source/physics/include/legacy/GatePositroniumDecayChannel.hh @@ -0,0 +1,70 @@ +/** ---------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + ----------------------*/ +#ifndef Legacy_GatePositroniumDecayChannel_hh +#define Legacy_GatePositroniumDecayChannel_hh + +#include "globals.hh" +#include "G4GeneralPhaseSpaceDecay.hh" +#include "G4PhysicalConstants.hh" +#include "G4SystemOfUnits.hh" + +namespace GateLegacy{ + +/** Author: Mateusz Bała + * Email: bala.mateusz@gmail.com + * Theorem author for oPs decay: Daria Kamińska ( Eur. Phys. J. C (2016) 76:445 ) + * Organization: J-PET (http://koza.if.uj.edu.pl/pet/) + * About class: Implements decay of positronium ( pPs and oPs ). Provides support for polarization. + **/ +class GatePositroniumDecayChannel : public G4GeneralPhaseSpaceDecay +{ + public: + + //Describes for which positronium we need decay + enum PositroniumKind { NotDefined, ParaPositronium, OrthoPositronium }; + + GatePositroniumDecayChannel( const G4String& theParentName, G4double theBR); + virtual ~GatePositroniumDecayChannel(); + /** Return gammas from positronium decay + **/ + virtual G4DecayProducts* DecayIt(G4double) override; + + protected: + /** Return gammas from para-positronium decay + **/ + G4DecayProducts* DecayParaPositronium(); + /** Return gammas from ortho-positronium decay + **/ + G4DecayProducts* DecayOrthoPositronium(); + /** Calculate cross section Mij matrix element + * Based on "Quantum electrodynamics" V. B. BERESTETSKY. + * Chapter: 89. Annihilation of positronium + * Exquantation: 89.14 + **/ + G4double GetOrthoPsM( const G4double w1, const G4double w2, const G4double w3 ) const; + /** Calculate polarization orthogonal to momentum direction + **/ + G4ThreeVector GetPolarization( const G4ThreeVector& momentum ) const; + /** Generate perpendiculator vector ( to calculate orthogonal polarization ) + **/ + G4ThreeVector GetPerpendicularVector(const G4ThreeVector& v) const; + + protected: + //Decay constants + const G4String kParaPositroniumName = "pPs"; + const G4String kOrthoPositroniumName = "oPs"; + const G4String kDaughterName = "gamma"; + const G4int kParaPositroniumAnnihilationGammasNumber = 2; + const G4int kOrthoPositroniumAnnihilationGammasNumber = 3; + const G4double kPositroniumMass = 2.0 * electron_mass_c2; + PositroniumKind fPositroniumKind = PositroniumKind::NotDefined; + ///This is maxiaml number which can be calculated by function GetOrthoPsM() - based on 10^7 iterations + const G4double kOrthoPsMMax = 7.65928; + const G4double kElectronMass = electron_mass_c2; //[MeV] +}; +} +#endif diff --git a/source/physics/src/GateParaPositronium.cc b/source/physics/src/GateParaPositronium.cc deleted file mode 100644 index 24316820e..000000000 --- a/source/physics/src/GateParaPositronium.cc +++ /dev/null @@ -1,62 +0,0 @@ -/** ---------------------- - Copyright (C): OpenGATE Collaboration - This software is distributed under the terms - of the GNU Lesser General Public Licence (LGPL) - See LICENSE.md for further details - ----------------------*/ -#include "GateParaPositronium.hh" -#include "G4DecayTable.hh" -#include "G4ParticleTable.hh" -#include "G4PhysicalConstants.hh" -#include "G4SystemOfUnits.hh" -#include "GatePositroniumDecayChannel.hh" - -GateParaPositronium *GateParaPositronium::theInstance = nullptr; - -GateParaPositronium *GateParaPositronium::Definition() { - if (theInstance) - return theInstance; - - const G4String name = "pPs"; - const G4double mass = 2.0 * electron_mass_c2; - const G4int spin = 0; - const G4int parity = 1; - const G4double lifetime = 0.1244 * ns; - const G4double BR = 1.0; - - // search in particle table - G4ParticleTable *pTable = G4ParticleTable::GetParticleTable(); - G4ParticleDefinition *anInstance = pTable->FindParticle(name); - - if (!anInstance) { - // create particle - - // Arguments for constructor: - // name, mass, width, charge, - // spin, parity, C-conjugation, - // isospin, isospin3, G-parity, - // type, lepton number, baryon number, - // PDG encoding, stable, lifetime, - // decay table, shortlived, subType - anInstance = new G4ParticleDefinition(name, mass, 0.0, 0, spin, parity, 0, - 0, 0, 0, "lepton", 2, 0, 0, false, - lifetime, nullptr, false, "e"); - - // create Decay Table - auto table = new G4DecayTable(); - // create a decay channel - auto mode = new GatePositroniumDecayChannel(name, BR); - table->Insert(mode); - anInstance->SetDecayTable(table); - } - theInstance = dynamic_cast(anInstance); - return theInstance; -} - -GateParaPositronium *GateParaPositronium::ParaPositroniumDefinition() { - return Definition(); -} - -GateParaPositronium *GateParaPositronium::ParaPositronium() { - return Definition(); -} diff --git a/source/physics/src/GatePhysicsList.cc b/source/physics/src/GatePhysicsList.cc index 8c89e0943..e3f4e76c5 100644 --- a/source/physics/src/GatePhysicsList.cc +++ b/source/physics/src/GatePhysicsList.cc @@ -72,8 +72,8 @@ #include "G4OpticalPhoton.hh" #include "G4OpticalPhysics.hh" -#include "GateParaPositronium.hh" -#include "GateOrthoPositronium.hh" +#include "legacy/GateParaPositronium.hh" +#include "legacy/GateOrthoPositronium.hh" //----------------------------------------------------------------------------------------- @@ -498,10 +498,10 @@ void GatePhysicsList::ConstructParticle() emDNAActivator->ConstructParticle(); //Construct positroniums - GateParaPositronium::ParaPositroniumDefinition(); - GateOrthoPositronium::OrthoPositroniumDefinition(); G4QuasiOpticalPhoton::QuasiOpticalPhotonDefinition(); + GateLegacy::GateParaPositronium::ParaPositroniumDefinition(); + GateLegacy::GateOrthoPositronium::OrthoPositroniumDefinition(); } //----------------------------------------------------------------------------------------- diff --git a/source/physics/src/GateOrthoPositronium.cc b/source/physics/src/legacy/GateOrthoPositronium.cc similarity index 82% rename from source/physics/src/GateOrthoPositronium.cc rename to source/physics/src/legacy/GateOrthoPositronium.cc index 710a7e9e1..6fbb33619 100644 --- a/source/physics/src/GateOrthoPositronium.cc +++ b/source/physics/src/legacy/GateOrthoPositronium.cc @@ -4,19 +4,20 @@ of the GNU Lesser General Public Licence (LGPL) See LICENSE.md for further details ----------------------*/ -#include "GateOrthoPositronium.hh" +#include "legacy/GateOrthoPositronium.hh" #include "G4PhysicalConstants.hh" #include "G4SystemOfUnits.hh" #include "G4ParticleTable.hh" #include "G4DecayTable.hh" -#include "GatePositroniumDecayChannel.hh" +#include "legacy/GatePositroniumDecayChannel.hh" +namespace GateLegacy{ -GateOrthoPositronium* GateOrthoPositronium::theInstance = nullptr; +GateOrthoPositronium* GateOrthoPositronium::theInstance = 0; GateOrthoPositronium* GateOrthoPositronium::Definition() { - if (theInstance) return theInstance; + if (theInstance !=0) return theInstance; const G4String name = "oPs"; const G4double mass = 2.0 * electron_mass_c2; @@ -29,7 +30,7 @@ GateOrthoPositronium* GateOrthoPositronium::Definition() G4ParticleTable* pTable = G4ParticleTable::GetParticleTable(); G4ParticleDefinition* anInstance = pTable->FindParticle(name); - if (!anInstance) + if ( anInstance == 0 ) { // create particle @@ -49,9 +50,9 @@ GateOrthoPositronium* GateOrthoPositronium::Definition() nullptr, false, "e" ); //create Decay Table - auto table = new G4DecayTable(); + G4DecayTable* table = new G4DecayTable(); // create a decay channel - auto mode = new GatePositroniumDecayChannel( name, BR ); + G4VDecayChannel* mode = new GatePositroniumDecayChannel( name, BR ); table->Insert(mode); anInstance->SetDecayTable(table); } @@ -62,3 +63,4 @@ GateOrthoPositronium* GateOrthoPositronium::Definition() GateOrthoPositronium* GateOrthoPositronium::OrthoPositroniumDefinition() { return Definition(); } GateOrthoPositronium* GateOrthoPositronium::OrthoPositronium() { return Definition(); } +} diff --git a/source/physics/src/legacy/GateParaPositronium.cc b/source/physics/src/legacy/GateParaPositronium.cc new file mode 100644 index 000000000..dc58bc895 --- /dev/null +++ b/source/physics/src/legacy/GateParaPositronium.cc @@ -0,0 +1,66 @@ +/** ---------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + ----------------------*/ +#include "legacy/GateParaPositronium.hh" +#include "G4PhysicalConstants.hh" +#include "G4SystemOfUnits.hh" +#include "G4ParticleTable.hh" +#include "G4DecayTable.hh" +#include "legacy/GatePositroniumDecayChannel.hh" + +namespace GateLegacy{ + +GateParaPositronium* GateParaPositronium::theInstance = 0; + +GateParaPositronium* GateParaPositronium::Definition() +{ + if (theInstance !=0) return theInstance; + + const G4String name = "pPs"; + const G4double mass = 2.0 * electron_mass_c2; + const G4int spin = 0; + const G4int parity = 1; + const G4double lifetime = 0.1244 * ns; + const G4double BR = 1.0; + + // search in particle table + G4ParticleTable* pTable = G4ParticleTable::GetParticleTable(); + G4ParticleDefinition* anInstance = pTable->FindParticle(name); + + if ( anInstance == 0 ) + { + // create particle + + // Arguments for constructor: + // name, mass, width, charge, + // spin, parity, C-conjugation, + // isospin, isospin3, G-parity, + // type, lepton number, baryon number, + // PDG encoding, stable, lifetime, + // decay table, shortlived, subType + anInstance = new G4ParticleDefinition( + name, mass, 0.0, 0, + spin, parity, 0, + 0, 0, 0, + "lepton", 2, 0, + 0, false, lifetime, + nullptr, false, "e" ); + + //create Decay Table + G4DecayTable* table = new G4DecayTable(); + // create a decay channel + G4VDecayChannel* mode = new GatePositroniumDecayChannel( name, BR ); + table->Insert(mode); + anInstance->SetDecayTable(table); + } + theInstance = dynamic_cast(anInstance); + return theInstance; +} + +GateParaPositronium* GateParaPositronium::ParaPositroniumDefinition() { return Definition(); } + +GateParaPositronium* GateParaPositronium::ParaPositronium() { return Definition(); } +} diff --git a/source/physics/src/legacy/GatePositroniumDecayChannel.cc b/source/physics/src/legacy/GatePositroniumDecayChannel.cc new file mode 100644 index 000000000..04ef926bd --- /dev/null +++ b/source/physics/src/legacy/GatePositroniumDecayChannel.cc @@ -0,0 +1,160 @@ +/** ---------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + ----------------------*/ +#include "legacy/GatePositroniumDecayChannel.hh" +#include "G4DynamicParticle.hh" +#include "G4DecayProducts.hh" +#include "Randomize.hh" +#include "G4LorentzVector.hh" + +namespace GateLegacy{ + +GatePositroniumDecayChannel::GatePositroniumDecayChannel(const G4String& theParentName, G4double theBR) +{ + G4int daughters_number = 0; + + if ( theParentName == kParaPositroniumName ) + { + daughters_number = kParaPositroniumAnnihilationGammasNumber; + fPositroniumKind = GatePositroniumDecayChannel::PositroniumKind::ParaPositronium; + } + else if ( theParentName == kOrthoPositroniumName ) + { + daughters_number = kOrthoPositroniumAnnihilationGammasNumber; + fPositroniumKind = GatePositroniumDecayChannel::PositroniumKind::OrthoPositronium; + } + else + { + #ifdef G4VERBOSE + if (GetVerboseLevel()>0) + { + G4cout << "GatePositroniumDecayChannel:: constructor :"; + G4cout << " parent particle is not positronium (pPs,oPs) but "; + G4cout << theParentName << G4endl; + } + #endif + } + + SetParentMass( kPositroniumMass ); + SetBR( theBR ); + SetParent( theParentName ); + SetNumberOfDaughters( daughters_number ); + for ( G4int daughter_index = 0; daughter_index < daughters_number; ++daughter_index ) { SetDaughter( daughter_index, kDaughterName ); } +} + +GatePositroniumDecayChannel::~GatePositroniumDecayChannel() {} + +G4DecayProducts* GatePositroniumDecayChannel::DecayIt(G4double) +{ + switch ( fPositroniumKind ) + { + case GatePositroniumDecayChannel::PositroniumKind::ParaPositronium: + return DecayParaPositronium(); + case GatePositroniumDecayChannel::PositroniumKind::OrthoPositronium: + return DecayOrthoPositronium(); + default: + G4cout << "GatePositroniumDecayChannel::DecayIt "; + G4cout << *parent_name << " can not decay " << G4endl; + DumpInfo(); + return nullptr; + }; +} + +G4DecayProducts* GatePositroniumDecayChannel::DecayParaPositronium() +{ + G4DecayProducts* decay_products = G4GeneralPhaseSpaceDecay::DecayIt(); + + G4DynamicParticle* gamma_1 = (*decay_products)[0]; + G4DynamicParticle* gamma_2 = (*decay_products)[1]; + + ///Polarization + G4ThreeVector polarization_gamma_1 = GetPolarization( gamma_1->GetMomentumDirection() ); + G4ThreeVector polarization_gamma_2 = gamma_1->GetMomentumDirection().cross( polarization_gamma_1 ); + + gamma_1->SetPolarization( polarization_gamma_1.x(), polarization_gamma_1.y(), polarization_gamma_1.z() ); + gamma_2->SetPolarization( polarization_gamma_2.x(), polarization_gamma_2.y(), polarization_gamma_2.z() ); + + return decay_products; +} + +G4DecayProducts* GatePositroniumDecayChannel::DecayOrthoPositronium() +{ + G4LorentzVector lv_gamma_1, lv_gamma_2, lv_gamma_3; + G4double weight = 0.0, random_weight = 0.0; + G4DecayProducts* decay_products = nullptr; + G4DynamicParticle* gamma_1 = nullptr; + G4DynamicParticle* gamma_2 = nullptr; + G4DynamicParticle* gamma_3 = nullptr; + + do + { + if ( decay_products != nullptr ) { delete decay_products; } + + decay_products = G4GeneralPhaseSpaceDecay::DecayIt(); + + gamma_1 = (*decay_products)[0]; + gamma_2 = (*decay_products)[1]; + gamma_3 = (*decay_products)[2]; + + lv_gamma_1 = gamma_1->Get4Momentum(); + lv_gamma_2 = gamma_2->Get4Momentum(); + lv_gamma_3 = gamma_3->Get4Momentum(); + + weight = GetOrthoPsM( lv_gamma_1.e(), lv_gamma_2.e(), lv_gamma_3.e() ); + random_weight = kOrthoPsMMax * G4UniformRand(); + } + while( random_weight > weight ); + + ///Polarization + G4ThreeVector polarization_gamma_1 = GetPolarization( gamma_1->GetMomentumDirection() ); + G4ThreeVector polarization_gamma_2 = GetPolarization( gamma_2->GetMomentumDirection() ); + G4ThreeVector polarization_gamma_3 = GetPolarization( gamma_3->GetMomentumDirection() ); + + gamma_1->SetPolarization( polarization_gamma_1.x(), polarization_gamma_1.y(), polarization_gamma_1.z() ); + gamma_2->SetPolarization( polarization_gamma_2.x(), polarization_gamma_2.y(), polarization_gamma_2.z() ); + gamma_3->SetPolarization( polarization_gamma_3.x(), polarization_gamma_3.y(), polarization_gamma_3.z() ); + + return decay_products; +} + +G4double GatePositroniumDecayChannel::GetOrthoPsM( const G4double w1, const G4double w2, const G4double w3 ) const +{ + return pow( ( kElectronMass - w1 ) / ( w2 * w3 ), 2 ) + pow( ( kElectronMass - w2 ) / ( w1 * w3 ), 2 ) + pow( ( kElectronMass - w3 ) / ( w1 * w2 ), 2 ); +} + +G4ThreeVector GatePositroniumDecayChannel::GetPolarization( const G4ThreeVector& momentum ) const +{ + G4ThreeVector polarization(0.0,0.0,0.0); + + G4ThreeVector a0,b0,d0; + d0 = momentum.unit(); + a0 = GetPerpendicularVector( d0 ).unit(); + b0 = d0.cross( a0 ).unit(); + G4double angle_radians = G4UniformRand() * M_PI; + polarization = std::cos( angle_radians ) * a0 + std::sin( angle_radians ) * b0; + polarization.unit(); + return polarization; +} + +G4ThreeVector GatePositroniumDecayChannel::GetPerpendicularVector(const G4ThreeVector& v) const +{ + G4double dx = v.x(); + G4double dy = v.y(); + G4double dz = v.z(); + + G4double x = dx < 0.0 ? -dx : dx; + G4double y = dy < 0.0 ? -dy : dy; + G4double z = dz < 0.0 ? -dz : dz; + + if (x < y) { return x < z ? G4ThreeVector(-dy,dx,0) : G4ThreeVector(0,-dz,dy); } + else { return y < z ? G4ThreeVector(dz,0,-dx) : G4ThreeVector(-dy,dx,0); } +} +} + + + + + From b4b983c84f29e34793b396596e44bf7fbc510bb2 Mon Sep 17 00:00:00 2001 From: MateuszBala Date: Thu, 5 Feb 2026 02:22:26 +0100 Subject: [PATCH 103/144] feat: reduce GatePositroniumSource structure --- .../physics/include/GatePositroniumSource.hh | 10 ----- source/physics/src/GatePositroniumSource.cc | 42 +++---------------- 2 files changed, 6 insertions(+), 46 deletions(-) diff --git a/source/physics/include/GatePositroniumSource.hh b/source/physics/include/GatePositroniumSource.hh index 50bdd7c74..a0f89c33e 100644 --- a/source/physics/include/GatePositroniumSource.hh +++ b/source/physics/include/GatePositroniumSource.hh @@ -16,27 +16,17 @@ class GatePositroniumSource : public GateVSource { public: - - enum class ModelKind { - NotDefined, - ParaPositronium, - OrthoPositronium, - Positronium - }; - explicit GatePositroniumSource(const G4String& name); virtual ~GatePositroniumSource() = default; virtual G4int GeneratePrimaries( G4Event* event ) override; protected: - void SetModel(const G4String &model_name); void PrepareModel(); protected: std::unique_ptr pModel; std::unique_ptr pMessenger; - ModelKind fModelKind = ModelKind::NotDefined; }; #endif diff --git a/source/physics/src/GatePositroniumSource.cc b/source/physics/src/GatePositroniumSource.cc index 3c1edcd36..317ecf17e 100644 --- a/source/physics/src/GatePositroniumSource.cc +++ b/source/physics/src/GatePositroniumSource.cc @@ -18,49 +18,19 @@ GatePositroniumSource::GatePositroniumSource(const G4String &name) { } -void GatePositroniumSource::SetModel(const G4String &model_name) -{ - static const std::map models{ - {"pPs", GatePositroniumSource::ModelKind::ParaPositronium}, - {"oPs", GatePositroniumSource::ModelKind::OrthoPositronium}, - {"Ps", GatePositroniumSource::ModelKind::Positronium}}; - - auto it = models.find(model_name); - if (it != models.end()) - { - fModelKind = it->second; - } else { - GateError("GatePositroniumSource::SetModel: Unknown gamma source model."); - } -} - void GatePositroniumSource::PrepareModel() { - SetModel(GetType()); - - if (fModelKind == GatePositroniumSource::ModelKind::Positronium) { - auto params = pMessenger->generatePositroniumDecayParams(); - pModel = std::make_unique(params); - } else { - if (fModelKind == GatePositroniumSource::ModelKind::ParaPositronium) { - auto params = pMessenger->generatePositroniumDecayParams( - GatePositroniumDecayParamsGenerator::kParaPositronium); - pModel = std::make_unique(params); - - } else { - if (fModelKind == GatePositroniumSource::ModelKind::OrthoPositronium) { - auto params = pMessenger->generatePositroniumDecayParams( - GatePositroniumDecayParamsGenerator::kOrthoPositronium); - pModel = std::make_unique(params); - } else { - GateError("GatePositroniumSource::PrepareModel - unknown model."); - } - } + if (GetType() != "Ps") { + GateError("GatePositroniumSource::PrepareModel - model type is not Positronium. Current type: " + GetType()); } + + auto params = pMessenger->generatePositroniumDecayParams(); + pModel = std::make_unique(params); } G4int GatePositroniumSource::GeneratePrimaries(G4Event* event) { + if (!pModel) { PrepareModel(); } G4double particle_time = GetTime(); From 9a8160dbf8b4af856ea8860ef72403fbcc6d6e54 Mon Sep 17 00:00:00 2001 From: kdulski Date: Thu, 19 Feb 2026 18:07:35 +0100 Subject: [PATCH 104/144] Adding test if the last argument in setting lifetime or energy is a unit --- .../include/GatePositroniumSourceMessenger.hh | 2 + .../src/GatePositroniumSourceMessenger.cc | 56 +++++++++++++++++-- 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/source/physics/include/GatePositroniumSourceMessenger.hh b/source/physics/include/GatePositroniumSourceMessenger.hh index 6c8744c87..adb5ec8df 100644 --- a/source/physics/include/GatePositroniumSourceMessenger.hh +++ b/source/physics/include/GatePositroniumSourceMessenger.hh @@ -17,6 +17,7 @@ #include "G4UIcmdWithAString.hh" #include "G4UIcmdWith3VectorAndUnit.hh" +#include "GatePositroniumHelper.hh" #include "GatePositroniumDecayParamsGenerator.hh" class GatePositroniumSource; @@ -31,6 +32,7 @@ class GatePositroniumSourceMessenger: public GateVSourceMessenger ~GatePositroniumSourceMessenger()=default; void SetNewValue(G4UIcommand *command, G4String newValue) override; + G4double CheckIfUnit(std::string val); PositroniumDecayModelParams generatePositroniumDecayParams(GatePositroniumDecayParamsGenerator::DecayModel model= GatePositroniumDecayParamsGenerator::kPositronium) const; diff --git a/source/physics/src/GatePositroniumSourceMessenger.cc b/source/physics/src/GatePositroniumSourceMessenger.cc index 908b60246..7c79bddc0 100644 --- a/source/physics/src/GatePositroniumSourceMessenger.cc +++ b/source/physics/src/GatePositroniumSourceMessenger.cc @@ -96,10 +96,16 @@ void GatePositroniumSourceMessenger::SetNewValue(G4UIcommand *command, G4String } else if (command == upCmdSetPositroniumLifetimes.get()) { std::vector lifetimes; std::stringstream ss(new_value); - G4double num; + std::string num; while (ss >> num) { - lifetimes.push_back(num); + if (ss.good()) { + lifetimes.push_back(stod(num)); + } else { + G4double unitVal = CheckIfUnit(num); + } } + for (unsigned i=0; i promptPhotonProb; @@ -114,10 +120,16 @@ void GatePositroniumSourceMessenger::SetNewValue(G4UIcommand *command, G4String } else if (command == upCmdSetPromptPhotonEnergies.get()) { std::vector promptPhotonEnergies; std::stringstream ss(new_value); - float energy; + std::string energy; while (ss >> energy) { - promptPhotonEnergies.push_back(energy); + if (ss.good()) { + promptPhotonEnergies.push_back(stod(energy)); + } else { + G4double unitVal = CheckIfUnit(energy); + } } + for (unsigned i=0; i decayKinds; @@ -164,4 +176,40 @@ PositroniumDecayModelParams GatePositroniumSourceMessenger::generatePositroniumD return fParamGenerator.generatePositroniumDecayParams(model); } +G4double GatePositroniumSourceMessenger::CheckIfUnit(std::string val) +{ + int strSize = val.size(); + G4double unitValue = 1; + if (!strSize || strSize > 3) + return unitValue; + + char firstChar = val.at(0); + char lastChar = val.at(strSize-1); + if (lastChar == 's') { + unitValue = 1.e+9; // default is ns + } else if (lastChar == 'm') { + unitValue = 1.e+4; // default is mm + } else if (lastChar == 'V' && strSize > 1) { + if (val.at(strSize-2) == 'e') + unitValue = 1.e-6; // default is MeV + } // Bq is the nominal value therefore does not need additional handling + + if (firstChar == 'G') { + unitValue *= 1.e+9; + } else if (firstChar == 'M') { + unitValue *= 1.e+6; + } else if (firstChar == 'k') { + unitValue *= 1.e+3; + } else if (firstChar == 'c') { + unitValue *= 1.e-2; + } else if (firstChar == 'm') { + unitValue *= 1.e-3; + } else if (firstChar == 'u') { + unitValue *= 1.e-6; + } else if (firstChar == 'n') { + unitValue *= 1.e-9; + } + + return unitValue; +} From e7375f33ce185ba7e1a62e33bc265fb4a70faa3a Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 20 Feb 2026 13:21:00 +0100 Subject: [PATCH 105/144] Add definition of unitVal that will make the code compile :P --- source/physics/src/GatePositroniumSourceMessenger.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/physics/src/GatePositroniumSourceMessenger.cc b/source/physics/src/GatePositroniumSourceMessenger.cc index 7c79bddc0..1029d68f6 100644 --- a/source/physics/src/GatePositroniumSourceMessenger.cc +++ b/source/physics/src/GatePositroniumSourceMessenger.cc @@ -85,6 +85,7 @@ void GatePositroniumSourceMessenger::InitCommands() void GatePositroniumSourceMessenger::SetNewValue(G4UIcommand *command, G4String new_value) { + G4double unitVal = 1; if (command == upCmdSetPositroniumFractions.get()) { std::vector fractions; std::stringstream ss(new_value); @@ -101,7 +102,7 @@ void GatePositroniumSourceMessenger::SetNewValue(G4UIcommand *command, G4String if (ss.good()) { lifetimes.push_back(stod(num)); } else { - G4double unitVal = CheckIfUnit(num); + unitVal = CheckIfUnit(num); } } for (unsigned i=0; i Date: Fri, 20 Feb 2026 13:33:14 +0100 Subject: [PATCH 106/144] Uncomment messanger test It was commented I don't know why. --- tests/test_GatePositroniumSourceMessenger.cpp | 216 ++++++++++++++++++ 1 file changed, 216 insertions(+) create mode 100644 tests/test_GatePositroniumSourceMessenger.cpp diff --git a/tests/test_GatePositroniumSourceMessenger.cpp b/tests/test_GatePositroniumSourceMessenger.cpp new file mode 100644 index 000000000..8ff5072a8 --- /dev/null +++ b/tests/test_GatePositroniumSourceMessenger.cpp @@ -0,0 +1,216 @@ +#include +#include + +#include "GatePositroniumSourceMessenger.hh" +#include "GatePositroniumSource.hh" +#include "GateRunManager.hh" +#include "GateDetectorConstruction.hh" +#include "GatePhysicsList.hh" + +#include "TestingTools.h" + +class DummyPositroniumSource : public GatePositroniumSource { +public: + DummyPositroniumSource() : GatePositroniumSource(nullptr) {} +}; + +class TestablePositroniumMessenger : public GatePositroniumSourceMessenger { +public: + using GatePositroniumSourceMessenger::GatePositroniumSourceMessenger; + + G4UIcommand* CmdFractions() { return upCmdSetPositroniumFractions.get(); } + G4UIcommand* CmdLifetimes() { return upCmdSetPositroniumLifetimes.get(); } + G4UIcommand* CmdDecayKinds() { return upCmdSetDecayKinds.get(); } + G4UIcommand* CmdPromptProb() { return upCmdSetPromptPhotonProbabilites.get(); } + G4UIcommand* CmdPromptEnergy() { return upCmdSetPromptPhotonEnergies.get(); } + G4UIcommand* CmdInteractions() { return upCmdSetPositronInteractions.get(); } +}; + +void initializeGateRunManager(GateRunManager* runManager) +{ + GateDetectorConstruction* gateDC = new GateDetectorConstruction(); + runManager->SetUserInitialization(gateDC); + runManager->SetUserInitialization(GatePhysicsList::GetInstance()); + runManager->InitializeAll(); +} + + +bool test_default_params() +{ + std::cout << "test_default_params\n"; + + DummyPositroniumSource src; + //TestablePositroniumMessenger msg(&src); + + //auto p = msg.generatePositroniumDecayParams(); + + //CHECK(p.fFractions.size() == 1, "default fractions size != 1"); + //CHECK(p.fFractions[0] == 1.0f, "default fraction != 1"); + //CHECK(p.fPromptGammaProbabilities[0] == 0.0f, "default prompt prob != 0"); + + return true; +} + +bool test_set_fractions() +{ + std::cout << "test_set_fractions\n"; + + DummyPositroniumSource src; + TestablePositroniumMessenger msg(&src); + + msg.SetNewValue(msg.CmdFractions(), "0.2 0.3 0.5"); + + auto p = msg.generatePositroniumDecayParams(); + + CHECK(p.fFractions.size() == 3, "fractions size wrong"); + CHECK(p.fFractions[0] == 0.2f, "fraction[0] wrong"); + CHECK(p.fFractions[1] == 0.3f, "fraction[1] wrong"); + CHECK(p.fFractions[2] == 0.5f, "fraction[2] wrong"); + + return true; +} + +bool test_set_lifetimes() +{ + std::cout << "test_set_lifetimes\n"; + + DummyPositroniumSource src; + TestablePositroniumMessenger msg(&src); + + msg.SetNewValue(msg.CmdLifetimes(), "0.1 10 100"); + + auto p = msg.generatePositroniumDecayParams(); + + CHECK(p.fLifetimes.size() == 3, "lifetimes size wrong"); + CHECK(p.fLifetimes[1] == 10.0f, "lifetime[1] wrong"); + + return true; +} + +bool test_prompt_gamma_clipping() +{ + std::cout << "test_prompt_gamma_clipping\n"; + + DummyPositroniumSource src; + TestablePositroniumMessenger msg(&src); + + msg.SetNewValue(msg.CmdPromptProb(), "-1 0.5 2"); + + auto p = msg.generatePositroniumDecayParams(); + + CHECK(p.fPromptGammaProbabilities[0] == 0.0f, "prob <0 not clipped"); + CHECK(p.fPromptGammaProbabilities[1] == 0.5f, "prob normal wrong"); + CHECK(p.fPromptGammaProbabilities[2] == 1.0f, "prob >1 not clipped"); + + return true; +} + +bool test_decay_kinds() +{ + std::cout << "test_decay_kinds\n"; + + DummyPositroniumSource src; + TestablePositroniumMessenger msg(&src); + + msg.SetNewValue(msg.CmdDecayKinds(), "k2Gamma k3Gamma"); + + auto p = msg.generatePositroniumDecayParams(); + + CHECK(p.fDecayKind.size() == 2, "decayKinds size wrong"); + CHECK(p.fDecayKind[0] == k2Gamma, "decayKind[0] wrong"); + CHECK(p.fDecayKind[1] == k3Gamma, "decayKind[1] wrong"); + + return true; +} + +bool test_interactions() +{ + std::cout << "test_interactions\n"; + + DummyPositroniumSource src; + TestablePositroniumMessenger msg(&src); + + msg.SetNewValue(msg.CmdInteractions(), "kParaPs kDirect kOrthoPs"); + + auto p = msg.generatePositroniumDecayParams(); + + CHECK(p.fPositronInteractions.size() == 3, "interaction size wrong"); + CHECK(p.fPositronInteractions[0] == PositronElectronInteraction::kParaPs, "wrong interaction[0]"); + CHECK(p.fPositronInteractions[1] == PositronElectronInteraction::kDirect, "wrong interaction[1]"); + CHECK(p.fPositronInteractions[2] == PositronElectronInteraction::kOrthoPs, "wrong interaction[2]"); + + return true; +} + +bool test_full_custom() +{ + std::cout << "test_full_custom\n"; + + DummyPositroniumSource src; + TestablePositroniumMessenger msg(&src); + + msg.SetNewValue(msg.CmdFractions(), "0.3 0.7"); + msg.SetNewValue(msg.CmdLifetimes(), "0.1 100"); + msg.SetNewValue(msg.CmdDecayKinds(), "k2Gamma k3Gamma"); + msg.SetNewValue(msg.CmdPromptProb(), "0.2 0.8"); + msg.SetNewValue(msg.CmdPromptEnergy(), "0.5 1.0"); + + auto p = msg.generatePositroniumDecayParams(); + + CHECK(p.fFractions.size() == 2, "wrong size"); + CHECK(p.fFractions[1] == 0.7f, "fraction wrong"); + CHECK(p.fDecayKind[1] == k3Gamma, "decay kind wrong"); + CHECK(p.fPromptGammaEnergy[1] == 1.0f, "energy wrong"); + + return true; +} + +bool test_vector_size_mismatch() +{ + std::cout << "test_vector_size_mismatch\n"; + + DummyPositroniumSource src; + TestablePositroniumMessenger msg(&src); + + msg.SetNewValue(msg.CmdFractions(), "0.5 0.5"); + msg.SetNewValue(msg.CmdLifetimes(), "0.1"); // mismatch + + try { + msg.generatePositroniumDecayParams(); + CHECK(false, "expected failure due to size mismatch"); + } catch (...) { + return true; + } + + return true; +} + +// ========================================================== +// main() +// ========================================================== +int main() +{ + std::unique_ptr runManager(new GateRunManager); + initializeGateRunManager(runManager.get()); + + bool res = true; + + res &= test_default_params(); + res &= test_set_fractions(); + res &= test_set_lifetimes(); + res &= test_prompt_gamma_clipping(); + res &= test_decay_kinds(); + res &= test_interactions(); + res &= test_full_custom(); + res &= test_vector_size_mismatch(); + + if (res) { + std::cout << " All GatePositroniumSourceMessenger tests passed\n"; + return 0; + } else { + std::cerr << " Some GatePositroniumSourceMessenger tests failed\n"; + return -1; + } +} + + From b245c5b6a91dbe7623f5818e2829793e1999ce24 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 20 Feb 2026 14:22:08 +0100 Subject: [PATCH 107/144] Add tests of PositroniumMessenger that passes in master But don't pass with units --- CMakeLists.txt | 5 + tests/test_GatePositroniumSourceMessenger.cpp | 132 +++--------------- 2 files changed, 25 insertions(+), 112 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b29d15f0a..7677ab14e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,6 +49,11 @@ IF(BUILD_TESTING) ADD_TEST(NAME test_GatePositroniumDecyParamsGenerator COMMAND test_GatePositroniumDecayParamsGenerator) + ADD_EXECUTABLE(test_GatePositroniumSourceMessenger tests/test_GatePositroniumSourceMessenger.cpp) + TARGET_LINK_LIBRARIES(test_GatePositroniumSourceMessenger GateLib) + + ADD_TEST(NAME test_GatePositroniumSourceMessenger COMMAND test_GatePositroniumSourceMessenger) + ENDIF(BUILD_TESTING) #========================================================= diff --git a/tests/test_GatePositroniumSourceMessenger.cpp b/tests/test_GatePositroniumSourceMessenger.cpp index 8ff5072a8..a5edba9f8 100644 --- a/tests/test_GatePositroniumSourceMessenger.cpp +++ b/tests/test_GatePositroniumSourceMessenger.cpp @@ -11,7 +11,7 @@ class DummyPositroniumSource : public GatePositroniumSource { public: - DummyPositroniumSource() : GatePositroniumSource(nullptr) {} + DummyPositroniumSource() : GatePositroniumSource("Ps") {} }; class TestablePositroniumMessenger : public GatePositroniumSourceMessenger { @@ -35,11 +35,11 @@ void initializeGateRunManager(GateRunManager* runManager) } -bool test_default_params() -{ - std::cout << "test_default_params\n"; +//bool test_default_params() +//{ + //std::cout << "test_default_params\n"; - DummyPositroniumSource src; + //DummyPositroniumSource src; //TestablePositroniumMessenger msg(&src); //auto p = msg.generatePositroniumDecayParams(); @@ -48,103 +48,38 @@ bool test_default_params() //CHECK(p.fFractions[0] == 1.0f, "default fraction != 1"); //CHECK(p.fPromptGammaProbabilities[0] == 0.0f, "default prompt prob != 0"); - return true; -} - -bool test_set_fractions() -{ - std::cout << "test_set_fractions\n"; + //return true; +//} - DummyPositroniumSource src; - TestablePositroniumMessenger msg(&src); - msg.SetNewValue(msg.CmdFractions(), "0.2 0.3 0.5"); - - auto p = msg.generatePositroniumDecayParams(); - - CHECK(p.fFractions.size() == 3, "fractions size wrong"); - CHECK(p.fFractions[0] == 0.2f, "fraction[0] wrong"); - CHECK(p.fFractions[1] == 0.3f, "fraction[1] wrong"); - CHECK(p.fFractions[2] == 0.5f, "fraction[2] wrong"); - - return true; -} - -bool test_set_lifetimes() -{ - std::cout << "test_set_lifetimes\n"; - - DummyPositroniumSource src; - TestablePositroniumMessenger msg(&src); - msg.SetNewValue(msg.CmdLifetimes(), "0.1 10 100"); - - auto p = msg.generatePositroniumDecayParams(); - - CHECK(p.fLifetimes.size() == 3, "lifetimes size wrong"); - CHECK(p.fLifetimes[1] == 10.0f, "lifetime[1] wrong"); - - return true; -} - -bool test_prompt_gamma_clipping() -{ - std::cout << "test_prompt_gamma_clipping\n"; - - DummyPositroniumSource src; - TestablePositroniumMessenger msg(&src); - - msg.SetNewValue(msg.CmdPromptProb(), "-1 0.5 2"); - - auto p = msg.generatePositroniumDecayParams(); - - CHECK(p.fPromptGammaProbabilities[0] == 0.0f, "prob <0 not clipped"); - CHECK(p.fPromptGammaProbabilities[1] == 0.5f, "prob normal wrong"); - CHECK(p.fPromptGammaProbabilities[2] == 1.0f, "prob >1 not clipped"); - - return true; -} - -bool test_decay_kinds() +bool test_full_custom() { - std::cout << "test_decay_kinds\n"; + std::cout << "test_full_custom\n"; DummyPositroniumSource src; TestablePositroniumMessenger msg(&src); + msg.SetNewValue(msg.CmdFractions(), "0.3 0.7"); + msg.SetNewValue(msg.CmdLifetimes(), "0.1 100"); msg.SetNewValue(msg.CmdDecayKinds(), "k2Gamma k3Gamma"); + msg.SetNewValue(msg.CmdPromptProb(), "0.2 0.8"); + msg.SetNewValue(msg.CmdPromptEnergy(), "0.5 1.0"); auto p = msg.generatePositroniumDecayParams(); - CHECK(p.fDecayKind.size() == 2, "decayKinds size wrong"); - CHECK(p.fDecayKind[0] == k2Gamma, "decayKind[0] wrong"); - CHECK(p.fDecayKind[1] == k3Gamma, "decayKind[1] wrong"); + CHECK(p.fFractions.size() == 2, "wrong size"); + CHECK(p.fFractions[1] == 0.7f, "fraction wrong"); + CHECK(p.fDecayKind[1] == k3Gamma, "decay kind wrong"); + CHECK(p.fPromptGammaEnergy[1] == 1.0f, "energy wrong"); return true; } -bool test_interactions() -{ - std::cout << "test_interactions\n"; - - DummyPositroniumSource src; - TestablePositroniumMessenger msg(&src); - - msg.SetNewValue(msg.CmdInteractions(), "kParaPs kDirect kOrthoPs"); - - auto p = msg.generatePositroniumDecayParams(); - - CHECK(p.fPositronInteractions.size() == 3, "interaction size wrong"); - CHECK(p.fPositronInteractions[0] == PositronElectronInteraction::kParaPs, "wrong interaction[0]"); - CHECK(p.fPositronInteractions[1] == PositronElectronInteraction::kDirect, "wrong interaction[1]"); - CHECK(p.fPositronInteractions[2] == PositronElectronInteraction::kOrthoPs, "wrong interaction[2]"); - return true; -} - -bool test_full_custom() +bool test_unit_handling_in_commands() { - std::cout << "test_full_custom\n"; + std::cout << "test_unit_handling_in_commands\n"; DummyPositroniumSource src; TestablePositroniumMessenger msg(&src); @@ -164,27 +99,6 @@ bool test_full_custom() return true; } - -bool test_vector_size_mismatch() -{ - std::cout << "test_vector_size_mismatch\n"; - - DummyPositroniumSource src; - TestablePositroniumMessenger msg(&src); - - msg.SetNewValue(msg.CmdFractions(), "0.5 0.5"); - msg.SetNewValue(msg.CmdLifetimes(), "0.1"); // mismatch - - try { - msg.generatePositroniumDecayParams(); - CHECK(false, "expected failure due to size mismatch"); - } catch (...) { - return true; - } - - return true; -} - // ========================================================== // main() // ========================================================== @@ -195,14 +109,8 @@ int main() bool res = true; - res &= test_default_params(); - res &= test_set_fractions(); - res &= test_set_lifetimes(); - res &= test_prompt_gamma_clipping(); - res &= test_decay_kinds(); - res &= test_interactions(); res &= test_full_custom(); - res &= test_vector_size_mismatch(); + res &= test_unit_handling_in_commands(); if (res) { std::cout << " All GatePositroniumSourceMessenger tests passed\n"; From 5e5d61a050bb9e0c58637d809b376ee0422789d5 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 20 Feb 2026 15:10:50 +0100 Subject: [PATCH 108/144] Add helper function to parse list of params with units Also, use it instead of loder code --- .../src/GatePositroniumSourceMessenger.cc | 44 +++++++++---------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/source/physics/src/GatePositroniumSourceMessenger.cc b/source/physics/src/GatePositroniumSourceMessenger.cc index 1029d68f6..d9f0409b8 100644 --- a/source/physics/src/GatePositroniumSourceMessenger.cc +++ b/source/physics/src/GatePositroniumSourceMessenger.cc @@ -83,6 +83,24 @@ void GatePositroniumSourceMessenger::InitCommands() upCmdSetPromptPhotonEnergies.reset(GetStringCmd( "setPromptPhotonEnergies", "\"e1, e2, e3 .., fn\" - where ei are energies " ) ); } +std::vector parseListOfParamsWithUnit(const G4String& input_string) +{ + std::stringstream ss(input_string); + std::vector values; + std::vector tokens; + std::string token; + while (ss >> token) { + tokens.push_back(token); + } + G4String unit = tokens.back(); + G4double unitValue = G4UnitDefinition::GetValueOf(unit); + for (unsigned i=0; i lifetimes; - std::stringstream ss(new_value); - std::string num; - while (ss >> num) { - if (ss.good()) { - lifetimes.push_back(stod(num)); - } else { - unitVal = CheckIfUnit(num); - } - } - for (unsigned i=0; i promptPhotonProb; @@ -119,18 +126,7 @@ void GatePositroniumSourceMessenger::SetNewValue(G4UIcommand *command, G4String } fParamGenerator.SetPromptGammaProbabilities(promptPhotonProb); } else if (command == upCmdSetPromptPhotonEnergies.get()) { - std::vector promptPhotonEnergies; - std::stringstream ss(new_value); - std::string energy; - while (ss >> energy) { - if (ss.good()) { - promptPhotonEnergies.push_back(stod(energy)); - } else { - unitVal = CheckIfUnit(energy); - } - } - for (unsigned i=0; i decayKinds; From ec9a1a5f8058fe9c07df5a6e245d748bb43dcbd6 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 20 Feb 2026 15:11:36 +0100 Subject: [PATCH 109/144] Update tests to use units for prompt energies and lifetimes --- tests/test_GatePositroniumSourceMessenger.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/test_GatePositroniumSourceMessenger.cpp b/tests/test_GatePositroniumSourceMessenger.cpp index a5edba9f8..ed91c7f73 100644 --- a/tests/test_GatePositroniumSourceMessenger.cpp +++ b/tests/test_GatePositroniumSourceMessenger.cpp @@ -61,10 +61,10 @@ bool test_full_custom() TestablePositroniumMessenger msg(&src); msg.SetNewValue(msg.CmdFractions(), "0.3 0.7"); - msg.SetNewValue(msg.CmdLifetimes(), "0.1 100"); + msg.SetNewValue(msg.CmdLifetimes(), "0.1 100 ns"); msg.SetNewValue(msg.CmdDecayKinds(), "k2Gamma k3Gamma"); msg.SetNewValue(msg.CmdPromptProb(), "0.2 0.8"); - msg.SetNewValue(msg.CmdPromptEnergy(), "0.5 1.0"); + msg.SetNewValue(msg.CmdPromptEnergy(), "0.5 1.0 MeV"); auto p = msg.generatePositroniumDecayParams(); @@ -85,17 +85,17 @@ bool test_unit_handling_in_commands() TestablePositroniumMessenger msg(&src); msg.SetNewValue(msg.CmdFractions(), "0.3 0.7"); - msg.SetNewValue(msg.CmdLifetimes(), "0.1 100"); + msg.SetNewValue(msg.CmdLifetimes(), "0.1 100 ns"); msg.SetNewValue(msg.CmdDecayKinds(), "k2Gamma k3Gamma"); msg.SetNewValue(msg.CmdPromptProb(), "0.2 0.8"); - msg.SetNewValue(msg.CmdPromptEnergy(), "0.5 1.0"); + msg.SetNewValue(msg.CmdPromptEnergy(), "0.5 1.0 keV"); auto p = msg.generatePositroniumDecayParams(); CHECK(p.fFractions.size() == 2, "wrong size"); CHECK(p.fFractions[1] == 0.7f, "fraction wrong"); CHECK(p.fDecayKind[1] == k3Gamma, "decay kind wrong"); - CHECK(p.fPromptGammaEnergy[1] == 1.0f, "energy wrong"); + CHECK(p.fPromptGammaEnergy[1] == 1.0f /1000, "energy wrong"); // Cause we used keV and MeV is the default unit return true; } From e3421e96daad58a3884edee4dd3d743ec53f249d Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 20 Feb 2026 15:13:07 +0100 Subject: [PATCH 110/144] Remove unused code --- .../include/GatePositroniumSourceMessenger.hh | 1 - .../src/GatePositroniumSourceMessenger.cc | 38 ------------------- 2 files changed, 39 deletions(-) diff --git a/source/physics/include/GatePositroniumSourceMessenger.hh b/source/physics/include/GatePositroniumSourceMessenger.hh index adb5ec8df..63fef2db7 100644 --- a/source/physics/include/GatePositroniumSourceMessenger.hh +++ b/source/physics/include/GatePositroniumSourceMessenger.hh @@ -32,7 +32,6 @@ class GatePositroniumSourceMessenger: public GateVSourceMessenger ~GatePositroniumSourceMessenger()=default; void SetNewValue(G4UIcommand *command, G4String newValue) override; - G4double CheckIfUnit(std::string val); PositroniumDecayModelParams generatePositroniumDecayParams(GatePositroniumDecayParamsGenerator::DecayModel model= GatePositroniumDecayParamsGenerator::kPositronium) const; diff --git a/source/physics/src/GatePositroniumSourceMessenger.cc b/source/physics/src/GatePositroniumSourceMessenger.cc index d9f0409b8..fb0f073ee 100644 --- a/source/physics/src/GatePositroniumSourceMessenger.cc +++ b/source/physics/src/GatePositroniumSourceMessenger.cc @@ -172,41 +172,3 @@ PositroniumDecayModelParams GatePositroniumSourceMessenger::generatePositroniumD { return fParamGenerator.generatePositroniumDecayParams(model); } - -G4double GatePositroniumSourceMessenger::CheckIfUnit(std::string val) -{ - int strSize = val.size(); - G4double unitValue = 1; - - if (!strSize || strSize > 3) - return unitValue; - - char firstChar = val.at(0); - char lastChar = val.at(strSize-1); - if (lastChar == 's') { - unitValue = 1.e+9; // default is ns - } else if (lastChar == 'm') { - unitValue = 1.e+4; // default is mm - } else if (lastChar == 'V' && strSize > 1) { - if (val.at(strSize-2) == 'e') - unitValue = 1.e-6; // default is MeV - } // Bq is the nominal value therefore does not need additional handling - - if (firstChar == 'G') { - unitValue *= 1.e+9; - } else if (firstChar == 'M') { - unitValue *= 1.e+6; - } else if (firstChar == 'k') { - unitValue *= 1.e+3; - } else if (firstChar == 'c') { - unitValue *= 1.e-2; - } else if (firstChar == 'm') { - unitValue *= 1.e-3; - } else if (firstChar == 'u') { - unitValue *= 1.e-6; - } else if (firstChar == 'n') { - unitValue *= 1.e-9; - } - - return unitValue; -} From a67d0940167dcd0914a0b9d6eded14d96f0fad26 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 20 Feb 2026 15:16:33 +0100 Subject: [PATCH 111/144] Remove unused include --- source/physics/include/GatePositroniumSourceMessenger.hh | 1 - 1 file changed, 1 deletion(-) diff --git a/source/physics/include/GatePositroniumSourceMessenger.hh b/source/physics/include/GatePositroniumSourceMessenger.hh index 63fef2db7..6c8744c87 100644 --- a/source/physics/include/GatePositroniumSourceMessenger.hh +++ b/source/physics/include/GatePositroniumSourceMessenger.hh @@ -17,7 +17,6 @@ #include "G4UIcmdWithAString.hh" #include "G4UIcmdWith3VectorAndUnit.hh" -#include "GatePositroniumHelper.hh" #include "GatePositroniumDecayParamsGenerator.hh" class GatePositroniumSource; From bcdf01861f1d3917797508f15d908ffbe8d3d037 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 20 Feb 2026 15:20:47 +0100 Subject: [PATCH 112/144] Remove unused variable --- source/physics/src/GatePositroniumSourceMessenger.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/source/physics/src/GatePositroniumSourceMessenger.cc b/source/physics/src/GatePositroniumSourceMessenger.cc index fb0f073ee..c1899db81 100644 --- a/source/physics/src/GatePositroniumSourceMessenger.cc +++ b/source/physics/src/GatePositroniumSourceMessenger.cc @@ -103,7 +103,6 @@ std::vector parseListOfParamsWithUnit(const G4String& input_string) void GatePositroniumSourceMessenger::SetNewValue(G4UIcommand *command, G4String new_value) { - G4double unitVal = 1; if (command == upCmdSetPositroniumFractions.get()) { std::vector fractions; std::stringstream ss(new_value); From 655b1a65b43962ed2a79295f5482e905750ca160 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 20 Feb 2026 15:36:13 +0100 Subject: [PATCH 113/144] Add some basic error handling Also, improve the description of macro commands. --- .../src/GatePositroniumSourceMessenger.cc | 41 +++++++++++-------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/source/physics/src/GatePositroniumSourceMessenger.cc b/source/physics/src/GatePositroniumSourceMessenger.cc index c1899db81..05f22b0c0 100644 --- a/source/physics/src/GatePositroniumSourceMessenger.cc +++ b/source/physics/src/GatePositroniumSourceMessenger.cc @@ -76,29 +76,36 @@ G4UIcmdWith3VectorAndUnit* GatePositroniumSourceMessenger::GetVectorCmdWithUnit( void GatePositroniumSourceMessenger::InitCommands() { upCmdSetPositroniumFractions.reset(GetStringCmd( "setPositroniumFractions", "\"f1, f2, f3 .., fn\" - where fi in [0.0, 1.0] and sum of all fi ==1" ) ); - upCmdSetPositroniumLifetimes.reset(GetStringCmd( "setPositroniumLifetimes", "\"t1, t2, t3 .., tn\" - where ti corresponds to lifetime constants of the components" ) ); + upCmdSetPositroniumLifetimes.reset(GetStringCmd( "setPositroniumLifetimes", "\"t1, t2, t3 .., tn t_unit\" - where ti corresponds to lifetime constants and t_unit is one of the Geant4 time units e.g. ns" ) ); upCmdSetDecayKinds.reset(GetStringCmd( "setDecayKinds", "\"k1, k2, k3 .., kn\" - where ki is k2Gamma or k3Gamma" ) ); upCmdSetPositronInteractions.reset(GetStringCmd( "setPositronInteractions", "\"k1, k2, k3 .., kn\" - where ki is kParaPs, kDirect or kOrthoPs, of a given element in vector of components. Used to properly recalculate intensities of the components from the theory" ) ); upCmdSetPromptPhotonProbabilites.reset(GetStringCmd( "setPromptPhotonProbabilites", "\"f1, f2, f3 .., fn\" - where fi in [0.0, 1.0] " ) ); - upCmdSetPromptPhotonEnergies.reset(GetStringCmd( "setPromptPhotonEnergies", "\"e1, e2, e3 .., fn\" - where ei are energies " ) ); + upCmdSetPromptPhotonEnergies.reset(GetStringCmd( "setPromptPhotonEnergies", "\"e1, e2, e3 .., en e_unit\" - where ei are energies and e_unit is one of the Geant4 energy units e.g. MeV" ) ); } -std::vector parseListOfParamsWithUnit(const G4String& input_string) +std::vector parseListOfParamsWithUnit(const G4String& input) { - std::stringstream ss(input_string); - std::vector values; - std::vector tokens; - std::string token; - while (ss >> token) { - tokens.push_back(token); - } - G4String unit = tokens.back(); - G4double unitValue = G4UnitDefinition::GetValueOf(unit); - for (unsigned i=0; i values; + std::stringstream ss(input); + std::vector tokens; + std::string token; + + while (ss >> token) { + tokens.push_back(token); + } + + if (tokens.size() < 2) { + GateError("parseListOfParamsWithUnit(): Need at least one value and one unit."); + } + + G4String unitStr = tokens.back(); + G4double unitValue = G4UnitDefinition::GetValueOf(unitStr); + + for (unsigned i=0; i Date: Fri, 20 Feb 2026 15:38:21 +0100 Subject: [PATCH 114/144] Add parsing function as method --- source/physics/include/GatePositroniumSourceMessenger.hh | 2 ++ source/physics/src/GatePositroniumSourceMessenger.cc | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/source/physics/include/GatePositroniumSourceMessenger.hh b/source/physics/include/GatePositroniumSourceMessenger.hh index 6c8744c87..2f93467e1 100644 --- a/source/physics/include/GatePositroniumSourceMessenger.hh +++ b/source/physics/include/GatePositroniumSourceMessenger.hh @@ -54,6 +54,8 @@ class GatePositroniumSourceMessenger: public GateVSourceMessenger std::unique_ptr upCmdSetDecayKinds; std::unique_ptr upCmdSetPositronInteractions; + std::vector parseListOfParamsWithUnit(const G4String& input); + GatePositroniumDecayParamsGenerator fParamGenerator; }; diff --git a/source/physics/src/GatePositroniumSourceMessenger.cc b/source/physics/src/GatePositroniumSourceMessenger.cc index 05f22b0c0..cbc488781 100644 --- a/source/physics/src/GatePositroniumSourceMessenger.cc +++ b/source/physics/src/GatePositroniumSourceMessenger.cc @@ -83,7 +83,7 @@ void GatePositroniumSourceMessenger::InitCommands() upCmdSetPromptPhotonEnergies.reset(GetStringCmd( "setPromptPhotonEnergies", "\"e1, e2, e3 .., en e_unit\" - where ei are energies and e_unit is one of the Geant4 energy units e.g. MeV" ) ); } -std::vector parseListOfParamsWithUnit(const G4String& input) +std::vector GatePositroniumSourceMessenger::parseListOfParamsWithUnit(const G4String& input) { std::vector values; std::stringstream ss(input); From 5f910caa6aa8217eec39c32e00b37ea1801c63e6 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 20 Feb 2026 15:40:34 +0100 Subject: [PATCH 115/144] Remove commented code --- tests/test_GatePositroniumSourceMessenger.cpp | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/tests/test_GatePositroniumSourceMessenger.cpp b/tests/test_GatePositroniumSourceMessenger.cpp index ed91c7f73..76b30152d 100644 --- a/tests/test_GatePositroniumSourceMessenger.cpp +++ b/tests/test_GatePositroniumSourceMessenger.cpp @@ -34,25 +34,6 @@ void initializeGateRunManager(GateRunManager* runManager) runManager->InitializeAll(); } - -//bool test_default_params() -//{ - //std::cout << "test_default_params\n"; - - //DummyPositroniumSource src; - //TestablePositroniumMessenger msg(&src); - - //auto p = msg.generatePositroniumDecayParams(); - - //CHECK(p.fFractions.size() == 1, "default fractions size != 1"); - //CHECK(p.fFractions[0] == 1.0f, "default fraction != 1"); - //CHECK(p.fPromptGammaProbabilities[0] == 0.0f, "default prompt prob != 0"); - - //return true; -//} - - - bool test_full_custom() { std::cout << "test_full_custom\n"; From 4a5ec10f5058604eee7306e266abc993af569b57 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Mon, 23 Feb 2026 16:36:09 +0100 Subject: [PATCH 116/144] Add const to the method --- source/physics/include/GatePositroniumSourceMessenger.hh | 2 +- source/physics/src/GatePositroniumSourceMessenger.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/physics/include/GatePositroniumSourceMessenger.hh b/source/physics/include/GatePositroniumSourceMessenger.hh index 2f93467e1..af6e27a74 100644 --- a/source/physics/include/GatePositroniumSourceMessenger.hh +++ b/source/physics/include/GatePositroniumSourceMessenger.hh @@ -54,7 +54,7 @@ class GatePositroniumSourceMessenger: public GateVSourceMessenger std::unique_ptr upCmdSetDecayKinds; std::unique_ptr upCmdSetPositronInteractions; - std::vector parseListOfParamsWithUnit(const G4String& input); + std::vector parseListOfParamsWithUnit(const G4String& input) const; GatePositroniumDecayParamsGenerator fParamGenerator; diff --git a/source/physics/src/GatePositroniumSourceMessenger.cc b/source/physics/src/GatePositroniumSourceMessenger.cc index cbc488781..0f1d11e3d 100644 --- a/source/physics/src/GatePositroniumSourceMessenger.cc +++ b/source/physics/src/GatePositroniumSourceMessenger.cc @@ -83,7 +83,7 @@ void GatePositroniumSourceMessenger::InitCommands() upCmdSetPromptPhotonEnergies.reset(GetStringCmd( "setPromptPhotonEnergies", "\"e1, e2, e3 .., en e_unit\" - where ei are energies and e_unit is one of the Geant4 energy units e.g. MeV" ) ); } -std::vector GatePositroniumSourceMessenger::parseListOfParamsWithUnit(const G4String& input) +std::vector GatePositroniumSourceMessenger::parseListOfParamsWithUnit(const G4String& input) const { std::vector values; std::stringstream ss(input); From 2667f116b3538e8a5e4d7080eba80bd428abba6a Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Mon, 23 Feb 2026 16:39:58 +0100 Subject: [PATCH 117/144] Fix formatting --- source/physics/src/GatePositroniumSourceMessenger.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/source/physics/src/GatePositroniumSourceMessenger.cc b/source/physics/src/GatePositroniumSourceMessenger.cc index 0f1d11e3d..f52765622 100644 --- a/source/physics/src/GatePositroniumSourceMessenger.cc +++ b/source/physics/src/GatePositroniumSourceMessenger.cc @@ -10,7 +10,7 @@ #include "GatePositroniumSourceMessenger.hh" #include "GatePositroniumSource.hh" -GatePositroniumSourceMessenger::GatePositroniumSourceMessenger(GatePositroniumSource *source): GateVSourceMessenger(source), pSource(source) +GatePositroniumSourceMessenger::GatePositroniumSourceMessenger(GatePositroniumSource *source): GateVSourceMessenger(source), pSource(source) { InitCommands(); } @@ -70,7 +70,7 @@ G4UIcmdWith3VectorAndUnit* GatePositroniumSourceMessenger::GetVectorCmdWithUnit( cmd->SetParameterName( G4String( cmd_name + "_x" ), G4String( cmd_name + "_y" ), G4String( cmd_name + "_z" ), false ); cmd->SetDefaultUnit( default_unit.c_str() ); cmd->SetUnitCandidates( unit_candidates.c_str() ); - return cmd; + return cmd; } void GatePositroniumSourceMessenger::InitCommands() @@ -106,9 +106,9 @@ std::vector GatePositroniumSourceMessenger::parseListOfParamsWithUnit(con } return values; -} +} -void GatePositroniumSourceMessenger::SetNewValue(G4UIcommand *command, G4String new_value) +void GatePositroniumSourceMessenger::SetNewValue(G4UIcommand *command, G4String new_value) { if (command == upCmdSetPositroniumFractions.get()) { std::vector fractions; @@ -119,7 +119,7 @@ void GatePositroniumSourceMessenger::SetNewValue(G4UIcommand *command, G4String } fParamGenerator.SetPositroniumFraction(fractions); } else if (command == upCmdSetPositroniumLifetimes.get()) { - auto lifetimes =parseListOfParamsWithUnit(new_value); + auto lifetimes = parseListOfParamsWithUnit(new_value); fParamGenerator.SetPositroniumLifetimes(lifetimes); } else if (command == upCmdSetPromptPhotonProbabilites.get()) { std::vector promptPhotonProb; From dd6fcc3f7d9e7371fd0e130c93e0dc1674077803 Mon Sep 17 00:00:00 2001 From: MateuszBala Date: Tue, 17 Mar 2026 10:52:30 +0100 Subject: [PATCH 118/144] feat: add decayInex propagation from decay to root file --- source/digits_hits/include/GateHit.hh | 4 ++++ source/digits_hits/include/GateToTree.hh | 1 + source/digits_hits/src/GateCrystalSD.cc | 3 +++ source/digits_hits/src/GateToTree.cc | 8 ++++++++ source/general/include/GateRootDefs.hh | 3 +-- source/general/src/GateRootDefs.cc | 5 +++++ source/physics/include/GateEmittedGammaInformation.hh | 8 ++++++++ source/physics/src/GateEmittedGammaInformation.cc | 4 ++++ source/physics/src/GatePositroniumDecayModel.cc | 8 ++++++-- 9 files changed, 40 insertions(+), 4 deletions(-) diff --git a/source/digits_hits/include/GateHit.hh b/source/digits_hits/include/GateHit.hh index 658cc4f1e..ccf8079f3 100644 --- a/source/digits_hits/include/GateHit.hh +++ b/source/digits_hits/include/GateHit.hh @@ -108,6 +108,7 @@ public: G4int m_sourceType = 0;//0 means 'by default not known'; sourceType says what type of positronium (Ps) is used: pPs, oPs G4int m_decayType = 0;//0 means 'by default not known'; decayType says what type of Ps decay is used: standard (without prompt gamma), deexcitation (with prompt gamma) G4int m_gammaType = 0;//0 means 'by default not known'; gammaType says what type of gamma is emitted: annihilation, prompt, other + G4int m_decayIndex = -1;//decay channel index; -1 means 'by default not known' public: inline void SetEdep(G4double de) { m_edep = de; } @@ -256,6 +257,9 @@ public: inline void SetGammaType(G4int value){ m_gammaType = value; } inline G4int GetGammaType() const { return m_gammaType; } + + inline void SetDecayIndex(G4int value){ m_decayIndex = value; } + inline G4int GetDecayIndex() const { return m_decayIndex; } }; diff --git a/source/digits_hits/include/GateToTree.hh b/source/digits_hits/include/GateToTree.hh index 46de07d5c..f04aa78c1 100644 --- a/source/digits_hits/include/GateToTree.hh +++ b/source/digits_hits/include/GateToTree.hh @@ -201,6 +201,7 @@ private: G4int m_sourceType = 0; G4int m_decayType = 0; G4int m_gammaType = 0; + G4int m_decayIndex = -1; G4float m_sourceEnergy; G4int m_sourcePDG; diff --git a/source/digits_hits/src/GateCrystalSD.cc b/source/digits_hits/src/GateCrystalSD.cc index 0e753befa..63d42e943 100644 --- a/source/digits_hits/src/GateCrystalSD.cc +++ b/source/digits_hits/src/GateCrystalSD.cc @@ -144,6 +144,7 @@ G4bool GateCrystalSD::ProcessHits(G4Step*aStep, G4TouchableHistory*) G4int source_type = static_cast(GateEmittedGammaInformation::SourceKind::NotDefined); G4int decay_type = static_cast(GateEmittedGammaInformation::DecayModel::None); G4int gamma_type = static_cast(GateEmittedGammaInformation::GammaKind::Unknown); + G4int decay_index = -1; G4PrimaryParticle* primary_particle = aTrack->GetDynamicParticle()->GetPrimaryParticle(); if( primary_particle != nullptr ) { @@ -153,6 +154,7 @@ G4bool GateCrystalSD::ProcessHits(G4Step*aStep, G4TouchableHistory*) source_type = static_cast( info->GetSourceKind() ); decay_type = static_cast( info->GetDecayModel() ); gamma_type = static_cast( info->GetGammaKind() ); + decay_index = info->GetDecayIndex(); } } @@ -289,6 +291,7 @@ G4bool GateCrystalSD::ProcessHits(G4Step*aStep, G4TouchableHistory*) aHit->SetSourceType( source_type ); aHit->SetDecayType( decay_type ); aHit->SetGammaType( gamma_type ); + aHit->SetDecayIndex( decay_index ); // OK GND 2023 for CC diff --git a/source/digits_hits/src/GateToTree.cc b/source/digits_hits/src/GateToTree.cc index 0fa5671ef..51ae649d6 100644 --- a/source/digits_hits/src/GateToTree.cc +++ b/source/digits_hits/src/GateToTree.cc @@ -103,6 +103,7 @@ GateToTree::GateToTree(const G4String &name, GateOutputMgr *outputMgr, DigiMode m_hitsParams_to_write.emplace("sourceType", SaveDataParam()); m_hitsParams_to_write.emplace("decayType", SaveDataParam()); m_hitsParams_to_write.emplace("gammaType", SaveDataParam()); + m_hitsParams_to_write.emplace("decayIndex", SaveDataParam()); //CC m_hitsParams_to_write.emplace("sourceEnergy",SaveDataParam()); m_hitsParams_to_write.emplace("sourcePDG",SaveDataParam()); @@ -360,6 +361,9 @@ void GateToTree::RecordBeginOfAcquisition() { if (m_hitsParams_to_write.at("gammaType").toSave()) m_manager_hits.write_variable("gammaType", &m_gammaType); + if (m_hitsParams_to_write.at("decayIndex").toSave()) + m_manager_hits.write_variable("decayIndex", &m_decayIndex); + m_manager_hits.write_header(); @@ -517,6 +521,9 @@ void GateToTree::RecordBeginOfAcquisition() { if (m_hitsParams_to_write.at("gammaType").toSave()) mm.write_variable("gammaType", &m_gammaType); + if (m_hitsParams_to_write.at("decayIndex").toSave()) + mm.write_variable("decayIndex", &m_decayIndex); + if(m_cc_enabled) { if (m_hitsParams_to_write.at("sourceEnergy").toSave()) @@ -980,6 +987,7 @@ void GateToTree::RecordEndOfEvent(const G4Event *event) { m_sourceType = hit->GetSourceType(); m_decayType = hit->GetDecayType(); m_gammaType = hit->GetGammaType(); + m_decayIndex = hit->GetDecayIndex(); if(m_cc_enabled) { diff --git a/source/general/include/GateRootDefs.hh b/source/general/include/GateRootDefs.hh index cc199af8f..1fdf12c66 100644 --- a/source/general/include/GateRootDefs.hh +++ b/source/general/include/GateRootDefs.hh @@ -237,8 +237,7 @@ class GateRootHitBuffer Int_t sourceType = 0; //Type of gamma source (check ExtendedVSource) Int_t decayType = 0; //Type of positronium decay (check ExtendedVSource) Int_t gammaType = 0; //Gamma type - single, annhilation, prompt (check ExtendedVSource) - - + Int_t decayIndex = -1; //Decay channel index //OK GND for CC G4bool m_CCflag; Float_t sourceEnergy; diff --git a/source/general/src/GateRootDefs.cc b/source/general/src/GateRootDefs.cc index b5840963b..17d5e65da 100644 --- a/source/general/src/GateRootDefs.cc +++ b/source/general/src/GateRootDefs.cc @@ -139,6 +139,7 @@ void GateRootHitBuffer::Clear() sourceType = 0; decayType = 0; gammaType = 0; + decayIndex = -1; strcpy (comptonVolumeName," "); strcpy (RayleighVolumeName," "); } @@ -216,6 +217,7 @@ void GateRootHitBuffer::Fill(GateHit* aHit) sourceType = aHit->GetSourceType(); decayType = aHit->GetDecayType(); gammaType = aHit->GetGammaType(); + decayIndex = aHit->GetDecayIndex(); } else { @@ -284,6 +286,7 @@ GateHit* GateRootHitBuffer::CreateHit() aHit->SetSourceType(sourceType); aHit->SetDecayType(decayType); aHit->SetGammaType(gammaType); + aHit->SetDecayIndex(decayIndex); aHit->SetSourceEnergy(GetSourceEnergy()); aHit->SetSourcePDG(GetSourcePDG()); @@ -354,6 +357,7 @@ void GateHitTree::Init(GateRootHitBuffer& buffer) Branch("sourceType", &buffer.sourceType,"sourceType/I"); Branch("decayType", &buffer.decayType,"decayType/I"); Branch("gammaType", &buffer.gammaType,"gammaType/I"); + Branch("decayIndex", &buffer.decayIndex,"decayIndex/I"); } else { @@ -424,6 +428,7 @@ void GateHitTree::SetBranchAddresses(TTree* hitTree,GateRootHitBuffer& buffer) hitTree->SetBranchAddress("sourceType",&buffer.sourceType); hitTree->SetBranchAddress("decayType",&buffer.decayType); hitTree->SetBranchAddress("gammaType",&buffer.gammaType); + hitTree->SetBranchAddress("decayIndex",&buffer.decayIndex); } else diff --git a/source/physics/include/GateEmittedGammaInformation.hh b/source/physics/include/GateEmittedGammaInformation.hh index 157b1d686..2ead4b0c1 100644 --- a/source/physics/include/GateEmittedGammaInformation.hh +++ b/source/physics/include/GateEmittedGammaInformation.hh @@ -61,6 +61,13 @@ class GateEmittedGammaInformation : public G4VUserPrimaryParticleInformation void SetGammaKind( GammaKind gamma_kind ); GammaKind GetGammaKind() const; + /** Set decay channel index + **/ + void SetDecayIndex( G4int decay_index ); + /** Get decay channel index + **/ + G4int GetDecayIndex() const; + /** Set polarization of gamma at the moment when it was emitted **/ void SetInitialPolarization( const G4ThreeVector& polarization ); @@ -80,6 +87,7 @@ class GateEmittedGammaInformation : public G4VUserPrimaryParticleInformation SourceKind fSourceKind = SourceKind::NotDefined; DecayModel fDecayModel = DecayModel::None; GammaKind fGammaKind = GammaKind::Unknown; + G4int fDecayIndex = -1; G4ThreeVector fInitialPolarization = G4ThreeVector( 0.0, 0.0, 0.0 ); G4double fTimeShift = 0.0;//[ns] }; diff --git a/source/physics/src/GateEmittedGammaInformation.cc b/source/physics/src/GateEmittedGammaInformation.cc index 651dd2b37..40f37c963 100644 --- a/source/physics/src/GateEmittedGammaInformation.cc +++ b/source/physics/src/GateEmittedGammaInformation.cc @@ -22,6 +22,10 @@ void GateEmittedGammaInformation::SetGammaKind( GateEmittedGammaInformation::Gam GateEmittedGammaInformation::GammaKind GateEmittedGammaInformation::GetGammaKind() const { return fGammaKind; } +void GateEmittedGammaInformation::SetDecayIndex( G4int decay_index ) { fDecayIndex = decay_index; } + +G4int GateEmittedGammaInformation::GetDecayIndex() const { return fDecayIndex; } + void GateEmittedGammaInformation::SetInitialPolarization( const G4ThreeVector& polarization ) { fInitialPolarization = polarization; } G4ThreeVector GateEmittedGammaInformation::GetInitialPolarization() const { return fInitialPolarization; } diff --git a/source/physics/src/GatePositroniumDecayModel.cc b/source/physics/src/GatePositroniumDecayModel.cc index 8eafeffc1..7eae8771a 100644 --- a/source/physics/src/GatePositroniumDecayModel.cc +++ b/source/physics/src/GatePositroniumDecayModel.cc @@ -64,7 +64,9 @@ G4int GatePositroniumDecayModel::GeneratePrimaryVertices(G4Event* event, G4doubl G4PrimaryParticle* GatePositroniumDecayModel::GetGammaFromDeexcitation(int decayIndex) { G4PrimaryParticle* gamma = GetSingleGamma(fModelParams.fPromptGammaEnergy[decayIndex]); - gamma->SetUserInformation( GetPrimaryParticleInformation( gamma, GateEmittedGammaInformation::GammaKind::Prompt ) ); + GateEmittedGammaInformation* info = GetPrimaryParticleInformation( gamma, GateEmittedGammaInformation::GammaKind::Prompt ); + info->SetDecayIndex( decayIndex ); + gamma->SetUserInformation( info ); return gamma; } @@ -89,7 +91,9 @@ std::vector GatePositroniumDecayModel::GetGammasFromPositron G4LorentzVector lv = dynamic_gamma->Get4Momentum(); gamma->Set4Momentum( lv.px(), lv.py(), lv.pz(), lv.e() ); gamma->SetPolarization( dynamic_gamma->GetPolarization() ); - gamma->SetUserInformation( GetPrimaryParticleInformation( gamma, GateEmittedGammaInformation::GammaKind::Annihilation ) ); + GateEmittedGammaInformation* info = GetPrimaryParticleInformation( gamma, GateEmittedGammaInformation::GammaKind::Annihilation ); + info->SetDecayIndex( decayIndex ); + gamma->SetUserInformation( info ); gammas[i] = gamma; } delete decay_products; From 527eb6dbf9ca2327f99df524bf0e704b63492f5a Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Mon, 23 Feb 2026 21:40:55 +0100 Subject: [PATCH 119/144] Add legacy versions of GateEmittedGammaInformation Also of GateGammaEmissionModel --- .../legacy/GateEmittedGammaInformation.hh | 90 +++++++++++ .../include/legacy/GateExtendedVSource.hh | 2 +- .../include/legacy/GateGammaEmissionModel.hh | 90 +++++++++++ .../src/legacy/GateGammaEmissionModel.cc | 146 ++++++++++++++++++ 4 files changed, 327 insertions(+), 1 deletion(-) create mode 100644 source/physics/include/legacy/GateEmittedGammaInformation.hh create mode 100644 source/physics/include/legacy/GateGammaEmissionModel.hh create mode 100644 source/physics/src/legacy/GateGammaEmissionModel.cc diff --git a/source/physics/include/legacy/GateEmittedGammaInformation.hh b/source/physics/include/legacy/GateEmittedGammaInformation.hh new file mode 100644 index 000000000..aad9a10e3 --- /dev/null +++ b/source/physics/include/legacy/GateEmittedGammaInformation.hh @@ -0,0 +1,90 @@ +/** ---------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + ----------------------*/ +#ifndef Legacy_GateEmittedGammaInformation_hh +#define Legacy_GateEmittedGammaInformation_hh + +#include "G4VUserPrimaryParticleInformation.hh" +#include "G4ThreeVector.hh" + +namespace GateLegacy{ + +/** Author: Mateusz Bała + * Email: bala.mateusz@gmail.com + * Organization: J-PET (http://koza.if.uj.edu.pl/pet/) + * About class: + **/ +class GateEmittedGammaInformation : public G4VUserPrimaryParticleInformation +{ + public: + GateEmittedGammaInformation(); + virtual ~GateEmittedGammaInformation(); + + /** This enum specifies what is source of gamma. + Each class which inherits from GateEmissionModel class should add + here its own kinds. + **/ + enum SourceKind + { + NotDefined = 0, // by default + SingleGammaEmitter = 1, // just emitted single gamma with specfic energy ( by class GateGammaEmissionModel ) + ParaPositronium = 2, // 2 gammas ( plus prompt if it is required ) from pPs decay ( by GatePositroniumDecayModel ) + OrthoPositronium = 3 // 3 gammas ( plus prompt if it is required ) from oPs decay ( by GatePositroniumDecayModel ) + }; + + /** This enum specifies model of source decay ( if it is present ) + **/ + enum DecayModel + { + None = 0, // Not specify - for example when there is not decay ( e.g. class GateGammaEmissionModel ) + Standard = 1, // Standard decay of source ( e.g. pPs -> 2 gamma ) + Deexcitation = 2 // If only deexcitation is present or when is part of standard decay changel ( Na22* -> Na22 + prompt gamma, Na22 -> Ne22 + e+, pPs-> 2 gamma <==> pPs* --> 2 gamma + prompt ) + }; + + /** This enum specifies of gamma emitted by source + **/ + enum GammaKind + { + Unknown = 0, // by default + Single = 1, // gamma is emitted alone ( by class GateGammaEmissionModel ) + Annihilation = 2, // gamma is a product of annihilation + Prompt = 3 // gamma is emitted from deexcitation process + }; + + void SetSourceKind( SourceKind source_kind ); + SourceKind GetSourceKind() const; + + void SetDecayModel( DecayModel decay_model ); + DecayModel GetDecayModel() const; + + void SetGammaKind( GammaKind gamma_kind ); + GammaKind GetGammaKind() const; + + /** Set polarization of gamma at the moment when it was emitted + **/ + void SetInitialPolarization( const G4ThreeVector& polarization ); + /** Get polarization of gamma at the moment when it was emitted + **/ + G4ThreeVector GetInitialPolarization() const; + /** Set time shift - caused by positronium lifetime + **/ + void SetTimeShift( const G4double& time_shift ); + /** Get time shift - caused by positronium lifetime + **/ + G4double GetTimeShift() const; + + virtual void Print() const; + + protected: + SourceKind fSourceKind = SourceKind::NotDefined; + DecayModel fDecayModel = DecayModel::None; + GammaKind fGammaKind = GammaKind::Unknown; + G4ThreeVector fInitialPolarization = G4ThreeVector( 0.0, 0.0, 0.0 ); + G4double fTimeShift = 0.0;//[ns] +}; +} + +#endif diff --git a/source/physics/include/legacy/GateExtendedVSource.hh b/source/physics/include/legacy/GateExtendedVSource.hh index 6cbd71d17..6b2e8c9ad 100644 --- a/source/physics/include/legacy/GateExtendedVSource.hh +++ b/source/physics/include/legacy/GateExtendedVSource.hh @@ -9,7 +9,7 @@ #include "GateVSource.hh" #include "legacy/GateExtendedVSourceMessenger.hh" -#include "GateGammaEmissionModel.hh" +#include "legacy/GateGammaEmissionModel.hh" namespace GateLegacy{ diff --git a/source/physics/include/legacy/GateGammaEmissionModel.hh b/source/physics/include/legacy/GateGammaEmissionModel.hh new file mode 100644 index 000000000..a2c4eee90 --- /dev/null +++ b/source/physics/include/legacy/GateGammaEmissionModel.hh @@ -0,0 +1,90 @@ +/** ---------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + ----------------------*/ +#ifndef Legacy_GateGammaEmissionModel_hh +#define Legacy_GateGammaEmissionModel_hh + +#include "G4PrimaryParticle.hh" +#include +#include +#include "legacy/GateEmittedGammaInformation.hh" +#include "G4Event.hh" +#include "G4SystemOfUnits.hh" + +namespace GateLegacy{ + +/** Author: Mateusz Bała + * Email: bala.mateusz@gmail.com + * Organization: J-PET (http://koza.if.uj.edu.pl/pet/) + * About class: Basic class for other model of gammas emission. Provides basic tools for calculation and can generate single gamma. + **/ +class GateGammaEmissionModel +{ + public: + + GateGammaEmissionModel(); + virtual ~GateGammaEmissionModel(); + + /** Force gamma emissions in only one direction. When this method is called fUseFixedEmissionDirection flag is switched to TRUE. + **/ + void SetFixedEmissionDirection( const G4ThreeVector& momentum_direction ); + /** Force gamma emissions in only one direction. When this method is fUseFixedEmissionDirection flag is switched to value=enable. + **/ + void SetEnableFixedEmissionDirection( const G4bool enable ); + + /** Set single gamma kinematic energy. + **/ + void SetEmissionEnergy( const G4double& energy ); + /** Get single gamma kinematic energy. + **/ + G4double GetEmissionEnergy() const; + + /** Set seed for generators from "Randomize.hh" + **/ + void SetSeed( G4long seed ); + /** Get seed for generators from "Randomize.hh" + **/ + G4long GetSeed() const; + + /** Generate single vertex with single gamma + **/ + virtual G4int GeneratePrimaryVertices(G4Event* event, G4double& particle_time, G4ThreeVector& particle_position); + + protected: + /** Provides additional information for user about gamma + **/ + virtual GateEmittedGammaInformation* GetPrimaryParticleInformation( const G4PrimaryParticle* pp, const GateEmittedGammaInformation::GammaKind& gamma_kind ) const; + /** Generate random unit vector from uniform sphere distribution + **/ + G4ThreeVector GetUniformOnSphere() const; + /** Generate gamma polarization + **/ + G4ThreeVector GetPolarization( const G4ThreeVector& momentum ) const; + /** Generate perpendicular vector ( required for polarization ) + **/ + G4ThreeVector GetPerpendicularVector(const G4ThreeVector& v) const; + /** Set model name ( class name ) + **/ + void SetModelName( const G4String model_name ); + /** Report error + **/ + void NoticeError( G4String method_name, G4String exception_description ) const; + /** Generate single gamma with desired energy + **/ + G4PrimaryParticle* GetSingleGamma( const G4double& energy ) const; + + protected: + G4ThreeVector fFixedEmissionDirection = G4ThreeVector( 0.0, 0.0, 0.0 ); + G4bool fUseFixedEmissionDirection = false; + G4double fEmissionEnergy = 0.511 * MeV;//[MeV] + G4String fModelName = "GateGammaEmissionModel"; + //Gamma definiotion - the same is used in each one model + G4ParticleDefinition* pGammaDefinition = nullptr; + +}; +} + +#endif diff --git a/source/physics/src/legacy/GateGammaEmissionModel.cc b/source/physics/src/legacy/GateGammaEmissionModel.cc new file mode 100644 index 000000000..45f4845f5 --- /dev/null +++ b/source/physics/src/legacy/GateGammaEmissionModel.cc @@ -0,0 +1,146 @@ +/** ---------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + ----------------------*/ +#include "legacy/GateGammaEmissionModel.hh" +#include +#include "G4LorentzVector.hh" +#include "Randomize.hh" +#include +#include "G4PrimaryVertex.hh" +#include "G4PrimaryParticle.hh" +#include "G4ParticleTable.hh" + +namespace GateLegacy{ + +GateGammaEmissionModel::GateGammaEmissionModel() { pGammaDefinition = G4ParticleTable::GetParticleTable()->FindParticle( "gamma" ); } + +GateGammaEmissionModel::~GateGammaEmissionModel() {} + +G4int GateGammaEmissionModel::GeneratePrimaryVertices(G4Event* event, G4double& particle_time, G4ThreeVector& particle_position) +{ + G4PrimaryVertex* vertex = new G4PrimaryVertex(particle_position, particle_time); + G4PrimaryParticle* gamma = GetSingleGamma( fEmissionEnergy ); + gamma->SetUserInformation( GetPrimaryParticleInformation( gamma, GateEmittedGammaInformation::GammaKind::Single ) ); + vertex->SetPrimary( gamma ); + event->AddPrimaryVertex( vertex ); + + return 1; +} + +G4PrimaryParticle* GateGammaEmissionModel::GetSingleGamma( const G4double& energy ) const +{ + G4PrimaryParticle* gamma = new G4PrimaryParticle( pGammaDefinition ); + + G4ThreeVector momentum_direction; + + if ( fUseFixedEmissionDirection ) { momentum_direction = fFixedEmissionDirection; } + else { momentum_direction = GetUniformOnSphere(); } + + G4LorentzVector lv_gamma( momentum_direction.x(), momentum_direction.y(), momentum_direction.z(), 1.0 ); + lv_gamma *= energy; + //Update gamma + ///Momentum + gamma->Set4Momentum( lv_gamma.px(), lv_gamma.py(), lv_gamma.pz(), lv_gamma.e() ); + ///Polarization + gamma->SetPolarization( GetPolarization( gamma->GetMomentumDirection() ) ); + + return gamma; +} + +void GateGammaEmissionModel::SetFixedEmissionDirection( const G4ThreeVector& momentum_direction ) +{ + if ( momentum_direction.mag() == 0 ) + { + NoticeError( G4String( __FUNCTION__ ), "fixed momemntum direction vector should be non zero vector" ); + } + fFixedEmissionDirection = momentum_direction; + fFixedEmissionDirection = fFixedEmissionDirection.unit(); + fUseFixedEmissionDirection = true; +} + +void GateGammaEmissionModel::SetEnableFixedEmissionDirection( const G4bool enable ) { fUseFixedEmissionDirection = enable; } + +void GateGammaEmissionModel::SetEmissionEnergy( const G4double& energy ) +{ + if ( !( energy > 0.0 ) ) { NoticeError( G4String( __FUNCTION__ ), "emission energy should be positive value." ); } + fEmissionEnergy = energy; +} + +G4double GateGammaEmissionModel::GetEmissionEnergy() const { return fEmissionEnergy; } + +void GateGammaEmissionModel::SetSeed( G4long seed ) +{ + if ( seed < 0 ) { NoticeError( G4String( __FUNCTION__ ), "seed should be positive value." ); } + G4Random::setTheSeed( seed ); +} + +G4long GateGammaEmissionModel::GetSeed() const { return G4Random::getTheSeed (); } + +G4ThreeVector GateGammaEmissionModel::GetUniformOnSphere() const +{ + //Based on TRandom::Sphere + G4double a = 0,b = 0, r2 = 1; + while ( r2 > 0.25 ) + { + a = G4UniformRand() - 0.5; + b = G4UniformRand() - 0.5; + r2 = a*a + b*b; + } + + G4double scale = 8.0 * sqrt(0.25 - r2); + return G4ThreeVector( a * scale, b * scale, -1. + 8.0 * r2 ); +} + +G4ThreeVector GateGammaEmissionModel::GetPolarization( const G4ThreeVector& momentum ) const +{ + G4ThreeVector polarization(0.0,0.0,0.0); + + G4ThreeVector a0,b0,d0; + d0 = momentum.unit(); + a0 = GetPerpendicularVector( d0 ).unit(); + b0 = d0.cross( a0 ).unit(); + G4double angle_radians = G4UniformRand() * M_PI; + polarization = std::cos( angle_radians ) * a0 + std::sin( angle_radians ) * b0; + polarization.unit(); + return polarization; +} + +G4ThreeVector GateGammaEmissionModel::GetPerpendicularVector(const G4ThreeVector& v) const +{ + G4double dx = v.x(); + G4double dy = v.y(); + G4double dz = v.z(); + + G4double x = dx < 0.0 ? -dx : dx; + G4double y = dy < 0.0 ? -dy : dy; + G4double z = dz < 0.0 ? -dz : dz; + + if (x < y) { return x < z ? G4ThreeVector(-dy,dx,0) : G4ThreeVector(0,-dz,dy); } + else { return y < z ? G4ThreeVector(dz,0,-dx) : G4ThreeVector(-dy,dx,0); } +} + +void GateGammaEmissionModel::SetModelName( const G4String model_name ) +{ + if ( model_name.size() == 0 ) { NoticeError( G4String( __FUNCTION__ ), "not provided model name." ); } + fModelName = model_name; +} + +void GateGammaEmissionModel::NoticeError( G4String method_name, G4String exception_description ) const +{ + G4String error_msg = fModelName + "::" + method_name + " : " + exception_description; + GateError( error_msg ); +} + +GateEmittedGammaInformation* GateGammaEmissionModel::GetPrimaryParticleInformation( const G4PrimaryParticle* pp, const GateEmittedGammaInformation::GammaKind& gamma_kind ) const +{ + GateEmittedGammaInformation* egi = new GateEmittedGammaInformation(); + egi->SetSourceKind( GateEmittedGammaInformation::SourceKind::SingleGammaEmitter ); + egi->SetDecayModel( GateEmittedGammaInformation::DecayModel::None ); + egi->SetGammaKind( gamma_kind ); + egi->SetInitialPolarization( pp->GetPolarization() ); + return egi; +} +} From b94236df43b7cd6439a619e86b1f0b0ce8990cc6 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Mon, 23 Feb 2026 21:46:30 +0100 Subject: [PATCH 120/144] Add missing GateEmittedGammaInformation.cc to legacy --- .../src/legacy/GateEmittedGammaInformation.cc | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 source/physics/src/legacy/GateEmittedGammaInformation.cc diff --git a/source/physics/src/legacy/GateEmittedGammaInformation.cc b/source/physics/src/legacy/GateEmittedGammaInformation.cc new file mode 100644 index 000000000..912984197 --- /dev/null +++ b/source/physics/src/legacy/GateEmittedGammaInformation.cc @@ -0,0 +1,37 @@ +/** ---------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + ----------------------*/ +#include "legacy/GateEmittedGammaInformation.hh" + +namespace GateLegacy{ + +GateEmittedGammaInformation::GateEmittedGammaInformation() {} + +GateEmittedGammaInformation::~GateEmittedGammaInformation() {} + +void GateEmittedGammaInformation::SetSourceKind( GateEmittedGammaInformation::SourceKind source_kind ) { fSourceKind = source_kind; } + +GateEmittedGammaInformation::SourceKind GateEmittedGammaInformation::GetSourceKind() const { return fSourceKind; } + +void GateEmittedGammaInformation::SetDecayModel( GateEmittedGammaInformation::DecayModel decay_model ) { fDecayModel = decay_model; } + +GateEmittedGammaInformation::DecayModel GateEmittedGammaInformation::GetDecayModel() const { return fDecayModel; } + +void GateEmittedGammaInformation::SetGammaKind( GateEmittedGammaInformation::GammaKind gamma_kind ) { fGammaKind = gamma_kind; } + +GateEmittedGammaInformation::GammaKind GateEmittedGammaInformation::GetGammaKind() const { return fGammaKind; } + +void GateEmittedGammaInformation::SetInitialPolarization( const G4ThreeVector& polarization ) { fInitialPolarization = polarization; } + +G4ThreeVector GateEmittedGammaInformation::GetInitialPolarization() const { return fInitialPolarization; } + +void GateEmittedGammaInformation::SetTimeShift( const G4double& time_shift ) { fTimeShift = time_shift; } + +G4double GateEmittedGammaInformation::GetTimeShift() const { return fTimeShift; } + +void GateEmittedGammaInformation::Print() const { G4cout << "GateEmittedGammaInformation" << G4endl; } + +} From 7d9e11d676f9a0a5b5de0562294e39350776559a Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 10 Apr 2026 17:01:39 +0200 Subject: [PATCH 121/144] Fix include by adding legacy. --- source/physics/include/legacy/GatePositroniumDecayModel.hh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/physics/include/legacy/GatePositroniumDecayModel.hh b/source/physics/include/legacy/GatePositroniumDecayModel.hh index 4b80f3cb1..df7db0a37 100644 --- a/source/physics/include/legacy/GatePositroniumDecayModel.hh +++ b/source/physics/include/legacy/GatePositroniumDecayModel.hh @@ -14,8 +14,8 @@ #include "G4GeneralPhaseSpaceDecay.hh" #include "G4DecayTable.hh" #include "G4ParticleDefinition.hh" -#include "GateEmittedGammaInformation.hh" -#include "GateGammaEmissionModel.hh" +#include "legacy/GateEmittedGammaInformation.hh" +#include "legacy/GateGammaEmissionModel.hh" #include "G4PhysicalConstants.hh" #include "G4SystemOfUnits.hh" @@ -97,7 +97,7 @@ class GatePositroniumDecayModel : public GateGammaEmissionModel /** Depends on used model and setted fractions it chooses positronium which decay will be used to generate gammas **/ void PreparePositroniumParametrization(); - /** Generate vertex for deexcitation gamma ( prompt gamma ) - position and time is the same as generted by source + /** Generate vertex for deexcitation gamma ( prompt gamma ) - position and time is the same as generted by source **/ G4PrimaryVertex* GetPrimaryVertexFromDeexcitation(const G4double& particle_time, const G4ThreeVector& particle_position ); /** Generate vertex for annihilation gammas - position is the same as generted by source, but time is shifted by positronium lifetime ( T0 + f(lifetime)) From 0d75d471cd063fb0b99ef01fe9dd0b473a9ec43b Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 20 Mar 2026 14:07:25 +0100 Subject: [PATCH 122/144] Add function introducing vertex shift due to positron range --- .../include/GatePositroniumDecayModel.hh | 1 + .../physics/src/GatePositroniumDecayModel.cc | 22 ++++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/source/physics/include/GatePositroniumDecayModel.hh b/source/physics/include/GatePositroniumDecayModel.hh index 0a9aeac30..90b5b371b 100644 --- a/source/physics/include/GatePositroniumDecayModel.hh +++ b/source/physics/include/GatePositroniumDecayModel.hh @@ -20,6 +20,7 @@ class GatePositroniumDecayModel:public GateGammaEmissionModel { public: static int getPositroniumDecayIndex(const std::vector& fractions); + static G4ThreeVector AddPositronRangeShift(const G4ThreeVector& original_position, G4double mean_positron_range); explicit GatePositroniumDecayModel(const PositroniumDecayModelParams& modelParams); diff --git a/source/physics/src/GatePositroniumDecayModel.cc b/source/physics/src/GatePositroniumDecayModel.cc index 7eae8771a..a2ab50d48 100644 --- a/source/physics/src/GatePositroniumDecayModel.cc +++ b/source/physics/src/GatePositroniumDecayModel.cc @@ -23,6 +23,17 @@ int GatePositroniumDecayModel::getPositroniumDecayIndex(const std::vector return -1; } +G4ThreeVector GatePositroniumDecayModel::AddPositronRangeShift(const G4ThreeVector& original_position, G4double mean_positron_range) +{ + // r = sqrt(x**2+y**2+z**2) + // = sigma * sqrt(8/Pi) // matching mean for 3-D Gaussian + G4double sigma = mean_positron_range/1.595; + G4ThreeVector shift(G4RandGauss::shoot(0., sigma), + G4RandGauss::shoot(0., sigma), + G4RandGauss::shoot(0., sigma)); + return original_position + shift; +} + GatePositroniumDecayModel::GatePositroniumDecayModel(const PositroniumDecayModelParams& modelParams):fModelParams(modelParams) { auto num_of_decay_channels = fModelParams.fDecayKind.size(); @@ -36,12 +47,21 @@ GatePositroniumDecayModel::GatePositroniumDecayModel(const PositroniumDecayModel } } + G4PrimaryVertex* GatePositroniumDecayModel::GetPrimaryVertexFromPositroniumAnnihilation(G4double particle_time, const G4ThreeVector& particle_position, int decayIndex) { + bool is_positron_range_enabled = false; + G4double mean_positron_range = 0.1; G4double shifted_particle_time = particle_time + G4RandExponential::shoot(fModelParams.fLifetimes[decayIndex]); - G4PrimaryVertex* vertex = new G4PrimaryVertex( particle_position, shifted_particle_time ); + auto shifted_particle_position = particle_position; + if (is_positron_range_enabled) + { + shifted_particle_position = AddPositronRangeShift(particle_position, mean_positron_range); + } + + G4PrimaryVertex* vertex = new G4PrimaryVertex( shifted_particle_position, shifted_particle_time ); std::vector gammas = GetGammasFromPositroniumAnnihilation(decayIndex); std::for_each( gammas.begin(), gammas.end(), [&]( G4PrimaryParticle* gamma ) { vertex->SetPrimary( gamma ); } ); return vertex; From fa86011408cef6d8f0779a8d0fecfe4fbe225549 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 20 Mar 2026 14:59:23 +0100 Subject: [PATCH 123/144] Add test of expected range --- tests/test_GatePositroniumDecayModel.cpp | 27 ++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/test_GatePositroniumDecayModel.cpp b/tests/test_GatePositroniumDecayModel.cpp index 773a9c620..3335b041e 100644 --- a/tests/test_GatePositroniumDecayModel.cpp +++ b/tests/test_GatePositroniumDecayModel.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include @@ -74,11 +75,37 @@ bool run_tests() return res; } +bool run_tests_positron_range() { + bool res = true; + G4ThreeVector vec(0, 0, 0); + G4double expectedRange = 0.1 * mm; + double epsilon = 3e-3 * mm; + G4int ntrials = 1000000; + std::vector shiftedPos; + std::vector shiftedRadii(ntrials); + for (int i = 0; i < ntrials; i++) { + shiftedPos.push_back( + GatePositroniumDecayModel::AddPositronRangeShift(vec, expectedRange)); + } + std::transform(shiftedPos.begin(), shiftedPos.end(), shiftedRadii.begin(), + [](const G4ThreeVector &vec) { return vec.mag(); }); + auto radiiSum = + std::accumulate(shiftedRadii.begin(), shiftedRadii.end(), 0.0); + auto radiusMean = radiiSum / ntrials; + if (std::abs(radiusMean - expectedRange) > epsilon) { + res = false; + std::cerr << "assumed range and estimated range differ, expected:" + << expectedRange << ", determined:" << radiusMean << std::endl; + } + return res; +} + int main() { bool res = true; res = res & run_tests(); res = res & run_tests2(); + res = res & run_tests_positron_range(); if (res) { std::cout << "All tests have passed" << std::endl; From 0890ed5e096347284eeace8f46b5673dd5346d65 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 20 Mar 2026 15:00:07 +0100 Subject: [PATCH 124/144] Add const instead of a magic number --- source/physics/src/GatePositroniumDecayModel.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/physics/src/GatePositroniumDecayModel.cc b/source/physics/src/GatePositroniumDecayModel.cc index a2ab50d48..b78bcae9e 100644 --- a/source/physics/src/GatePositroniumDecayModel.cc +++ b/source/physics/src/GatePositroniumDecayModel.cc @@ -27,7 +27,8 @@ G4ThreeVector GatePositroniumDecayModel::AddPositronRangeShift(const G4ThreeVect { // r = sqrt(x**2+y**2+z**2) // = sigma * sqrt(8/Pi) // matching mean for 3-D Gaussian - G4double sigma = mean_positron_range/1.595; + const G4double sqrt8_over_pi = std::sqrt(8.0/CLHEP::pi); + G4double sigma = mean_positron_range/sqrt8_over_pi ; G4ThreeVector shift(G4RandGauss::shoot(0., sigma), G4RandGauss::shoot(0., sigma), G4RandGauss::shoot(0., sigma)); From a6c1ad8c9d4f476c69ffc9fba3257dbacc9c4e97 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 20 Mar 2026 15:48:18 +0100 Subject: [PATCH 125/144] Add handling of the positron range parameter by Messanger --- .../GatePositroniumDecayModelParams.hh | 2 ++ .../GatePositroniumDecayParamsGenerator.hh | 3 ++ .../include/GatePositroniumSourceMessenger.hh | 2 ++ .../physics/src/GatePositroniumDecayModel.cc | 5 ++- .../GatePositroniumDecayParamsGenerator.cc | 33 +++++++++++++++++++ .../src/GatePositroniumSourceMessenger.cc | 4 +++ tests/test_GatePositroniumSourceMessenger.cpp | 1 + 7 files changed, 47 insertions(+), 3 deletions(-) diff --git a/source/physics/include/GatePositroniumDecayModelParams.hh b/source/physics/include/GatePositroniumDecayModelParams.hh index f9a495388..cc6244bc5 100644 --- a/source/physics/include/GatePositroniumDecayModelParams.hh +++ b/source/physics/include/GatePositroniumDecayModelParams.hh @@ -21,6 +21,8 @@ struct PositroniumDecayModelParams std::vector fPromptGammaEnergy; std::vector fDecayKind; std::vector fPositronInteractions; + std::vector fMeanPositronRangeEnabled; + std::vector fMeanPositronRange; }; #endif diff --git a/source/physics/include/GatePositroniumDecayParamsGenerator.hh b/source/physics/include/GatePositroniumDecayParamsGenerator.hh index e65d570e1..accf40db6 100644 --- a/source/physics/include/GatePositroniumDecayParamsGenerator.hh +++ b/source/physics/include/GatePositroniumDecayParamsGenerator.hh @@ -33,6 +33,7 @@ public: void SetDecayKinds(const std::vector& decayKinds); void SetPositronInteractions(const std::vector& positronInteractions); void SetPositroniumFraction(const std::vector& positroniumFractions); + void SetMeanPositronRange(const std::vector& meanPositronRange); PositroniumDecayModelParams generatePositroniumDecayParams(DecayModel model=kPositronium) const; @@ -43,5 +44,7 @@ private: std::optional> fPromptGammaEnergies; std::optional> fDecayKinds; std::optional> fPositronInteractions; + std::optional> fMeanPositronRangeEnabled; + std::optional> fMeanPositronRange; }; #endif diff --git a/source/physics/include/GatePositroniumSourceMessenger.hh b/source/physics/include/GatePositroniumSourceMessenger.hh index af6e27a74..53a583a4b 100644 --- a/source/physics/include/GatePositroniumSourceMessenger.hh +++ b/source/physics/include/GatePositroniumSourceMessenger.hh @@ -54,6 +54,8 @@ class GatePositroniumSourceMessenger: public GateVSourceMessenger std::unique_ptr upCmdSetDecayKinds; std::unique_ptr upCmdSetPositronInteractions; + std::unique_ptr upCmdSetMeanPositronRange; + std::vector parseListOfParamsWithUnit(const G4String& input) const; GatePositroniumDecayParamsGenerator fParamGenerator; diff --git a/source/physics/src/GatePositroniumDecayModel.cc b/source/physics/src/GatePositroniumDecayModel.cc index b78bcae9e..d97557b74 100644 --- a/source/physics/src/GatePositroniumDecayModel.cc +++ b/source/physics/src/GatePositroniumDecayModel.cc @@ -51,15 +51,14 @@ GatePositroniumDecayModel::GatePositroniumDecayModel(const PositroniumDecayModel G4PrimaryVertex* GatePositroniumDecayModel::GetPrimaryVertexFromPositroniumAnnihilation(G4double particle_time, const G4ThreeVector& particle_position, int decayIndex) { - bool is_positron_range_enabled = false; - G4double mean_positron_range = 0.1; + bool is_positron_range_enabled = fModelParams.fMeanPositronRangeEnabled[decayIndex]; G4double shifted_particle_time = particle_time + G4RandExponential::shoot(fModelParams.fLifetimes[decayIndex]); auto shifted_particle_position = particle_position; if (is_positron_range_enabled) { - shifted_particle_position = AddPositronRangeShift(particle_position, mean_positron_range); + shifted_particle_position = AddPositronRangeShift(particle_position, fModelParams.fMeanPositronRange[decayIndex]); } G4PrimaryVertex* vertex = new G4PrimaryVertex( shifted_particle_position, shifted_particle_time ); diff --git a/source/physics/src/GatePositroniumDecayParamsGenerator.cc b/source/physics/src/GatePositroniumDecayParamsGenerator.cc index 6e2b52d2a..f9e33e16d 100644 --- a/source/physics/src/GatePositroniumDecayParamsGenerator.cc +++ b/source/physics/src/GatePositroniumDecayParamsGenerator.cc @@ -32,6 +32,12 @@ void GatePositroniumDecayParamsGenerator::SetPositroniumFraction(const std::vect fPositroniumFractions = positroniumFractions; } +void GatePositroniumDecayParamsGenerator::SetMeanPositronRange(const std::vector& meanPositironRange) +{ + fMeanPositronRange = meanPositironRange; +} + + PositroniumDecayModelParams GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams(const GatePositroniumDecayParamsGenerator::DecayModel model) const { PositroniumDecayModelParams params; @@ -49,6 +55,15 @@ PositroniumDecayModelParams GatePositroniumDecayParamsGenerator::generatePositro params.fPromptGammaProbabilities={0.0}; params.fPromptGammaEnergy ={0.0}; } + + if(fMeanPositronRange.has_value()) { + params.fMeanPositronRange=fMeanPositronRange.value(); + assert(params.fMeanPositronRange.size() == 1); + params.fMeanPositronRangeEnabled={true}; + } else { + params.fMeanPositronRangeEnabled={false}; + params.fMeanPositronRange ={0.0}; + } } if (model == GatePositroniumDecayParamsGenerator::kOrthoPositronium) { params.fFractions= {1}; @@ -64,6 +79,15 @@ PositroniumDecayModelParams GatePositroniumDecayParamsGenerator::generatePositro params.fPromptGammaProbabilities={0.0}; params.fPromptGammaEnergy ={0.0}; } + + if(fMeanPositronRange.has_value()) { + params.fMeanPositronRange=fMeanPositronRange.value(); + assert(params.fMeanPositronRange.size() == 1); + params.fMeanPositronRangeEnabled={true}; + } else { + params.fMeanPositronRangeEnabled={false}; + params.fMeanPositronRange ={0.0}; + } } if (model == GatePositroniumDecayParamsGenerator::kPositronium) { @@ -105,6 +129,15 @@ PositroniumDecayModelParams GatePositroniumDecayParamsGenerator::generatePositro } else { GateError("GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams: Positronium decay kinds are not set and one or more sets that can calculate them (positronInteractions, lifetimes or fractions) is/are empty"); } + + auto nElements = fPositroniumFractions.value().size(); + if(fMeanPositronRange.has_value()) { + params.fMeanPositronRangeEnabled.assign(nElements, true); + params.fMeanPositronRange=fMeanPositronRange.value(); + } else { + params.fMeanPositronRangeEnabled.assign(nElements, false); + params.fMeanPositronRange.assign(nElements, 0.0); + } } auto ref_param_number = params.fDecayKind.size(); diff --git a/source/physics/src/GatePositroniumSourceMessenger.cc b/source/physics/src/GatePositroniumSourceMessenger.cc index f52765622..632889099 100644 --- a/source/physics/src/GatePositroniumSourceMessenger.cc +++ b/source/physics/src/GatePositroniumSourceMessenger.cc @@ -81,6 +81,7 @@ void GatePositroniumSourceMessenger::InitCommands() upCmdSetPositronInteractions.reset(GetStringCmd( "setPositronInteractions", "\"k1, k2, k3 .., kn\" - where ki is kParaPs, kDirect or kOrthoPs, of a given element in vector of components. Used to properly recalculate intensities of the components from the theory" ) ); upCmdSetPromptPhotonProbabilites.reset(GetStringCmd( "setPromptPhotonProbabilites", "\"f1, f2, f3 .., fn\" - where fi in [0.0, 1.0] " ) ); upCmdSetPromptPhotonEnergies.reset(GetStringCmd( "setPromptPhotonEnergies", "\"e1, e2, e3 .., en e_unit\" - where ei are energies and e_unit is one of the Geant4 energy units e.g. MeV" ) ); + upCmdSetMeanPositronRange.reset(GetStringCmd( "setMeanPositronRange", "\"r1, r2, r3 .., rn r_unit\" - where ri are mean positron range and r_unit is one of the Geant 4 distance units e.g. mm " ) ); } std::vector GatePositroniumSourceMessenger::parseListOfParamsWithUnit(const G4String& input) const @@ -134,6 +135,9 @@ void GatePositroniumSourceMessenger::SetNewValue(G4UIcommand *command, G4String } else if (command == upCmdSetPromptPhotonEnergies.get()) { auto promptPhotonEnergies = parseListOfParamsWithUnit(new_value); fParamGenerator.SetPromptGammaEnergies(promptPhotonEnergies); + } else if (command == upCmdSetMeanPositronRange.get()) { + auto positronRange = parseListOfParamsWithUnit(new_value); + fParamGenerator.SetMeanPositronRange(positronRange); } else if (command == upCmdSetDecayKinds.get()) { std::vector decayKinds; std::stringstream ss(new_value); diff --git a/tests/test_GatePositroniumSourceMessenger.cpp b/tests/test_GatePositroniumSourceMessenger.cpp index 76b30152d..28835ff0f 100644 --- a/tests/test_GatePositroniumSourceMessenger.cpp +++ b/tests/test_GatePositroniumSourceMessenger.cpp @@ -24,6 +24,7 @@ class TestablePositroniumMessenger : public GatePositroniumSourceMessenger { G4UIcommand* CmdPromptProb() { return upCmdSetPromptPhotonProbabilites.get(); } G4UIcommand* CmdPromptEnergy() { return upCmdSetPromptPhotonEnergies.get(); } G4UIcommand* CmdInteractions() { return upCmdSetPositronInteractions.get(); } + G4UIcommand* CmdMeanPositronRange() { return upCmdSetMeanPositronRange.get(); } }; void initializeGateRunManager(GateRunManager* runManager) From a221848f45d46ea65dbfe07f30643dd92e04e786 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 20 Mar 2026 23:52:56 +0100 Subject: [PATCH 126/144] Add tests of handling mean positron range macro commands --- tests/test_GatePositroniumSourceMessenger.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/test_GatePositroniumSourceMessenger.cpp b/tests/test_GatePositroniumSourceMessenger.cpp index 28835ff0f..ba5f5b267 100644 --- a/tests/test_GatePositroniumSourceMessenger.cpp +++ b/tests/test_GatePositroniumSourceMessenger.cpp @@ -50,10 +50,15 @@ bool test_full_custom() auto p = msg.generatePositroniumDecayParams(); + CHECK(p.fMeanPositronRangeEnabled[0] == false, "positron range enable wrong"); + CHECK(p.fMeanPositronRange[0] == 0.0, "positron range enable wrong"); + CHECK(p.fFractions.size() == 2, "wrong size"); CHECK(p.fFractions[1] == 0.7f, "fraction wrong"); CHECK(p.fDecayKind[1] == k3Gamma, "decay kind wrong"); CHECK(p.fPromptGammaEnergy[1] == 1.0f, "energy wrong"); + CHECK(p.fMeanPositronRangeEnabled[1] == false, "positron range enable wrong"); + CHECK(p.fMeanPositronRange[1] == 0.0, "positron range enable wrong"); return true; } @@ -71,13 +76,19 @@ bool test_unit_handling_in_commands() msg.SetNewValue(msg.CmdDecayKinds(), "k2Gamma k3Gamma"); msg.SetNewValue(msg.CmdPromptProb(), "0.2 0.8"); msg.SetNewValue(msg.CmdPromptEnergy(), "0.5 1.0 keV"); + msg.SetNewValue(msg.CmdMeanPositronRange(), "0.1 0.0 cm"); auto p = msg.generatePositroniumDecayParams(); + CHECK(p.fMeanPositronRangeEnabled[0] == true, "positron range enable wrong"); + CHECK(p.fMeanPositronRange[0] == 0.1 * 10, "positron range enable wrong"); // Cause mm is the default unit + CHECK(p.fFractions.size() == 2, "wrong size"); CHECK(p.fFractions[1] == 0.7f, "fraction wrong"); CHECK(p.fDecayKind[1] == k3Gamma, "decay kind wrong"); CHECK(p.fPromptGammaEnergy[1] == 1.0f /1000, "energy wrong"); // Cause we used keV and MeV is the default unit + CHECK(p.fMeanPositronRangeEnabled[1] == true, "positron range enable wrong"); + CHECK(p.fMeanPositronRange[1] == 0.0 *10 , "positron range enable wrong"); return true; } From 7156f0c1c8c12804cd4d61e6f5b9491f84eb06d9 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Wed, 15 Apr 2026 12:44:23 +0200 Subject: [PATCH 127/144] Fix wrong error message in the test --- tests/test_GatePositroniumSourceMessenger.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_GatePositroniumSourceMessenger.cpp b/tests/test_GatePositroniumSourceMessenger.cpp index ba5f5b267..81b9ec957 100644 --- a/tests/test_GatePositroniumSourceMessenger.cpp +++ b/tests/test_GatePositroniumSourceMessenger.cpp @@ -50,6 +50,9 @@ bool test_full_custom() auto p = msg.generatePositroniumDecayParams(); + CHECK(p.fMeanPositronRangeEnabled.size() == 2, "wrong size of fMeanPositronRangeEnabled"); + CHECK(p.fMeanPositronRange.size() == 2, "wrong size of fMeanPositronRange"); + CHECK(p.fMeanPositronRangeEnabled[0] == false, "positron range enable wrong"); CHECK(p.fMeanPositronRange[0] == 0.0, "positron range enable wrong"); From e9d0a8374a1bb8f290df7270d4616dbd86354fd1 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 17 Apr 2026 13:36:30 +0200 Subject: [PATCH 128/144] Fix error command in the test --- tests/test_GatePositroniumSourceMessenger.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_GatePositroniumSourceMessenger.cpp b/tests/test_GatePositroniumSourceMessenger.cpp index 81b9ec957..e9f9dc20e 100644 --- a/tests/test_GatePositroniumSourceMessenger.cpp +++ b/tests/test_GatePositroniumSourceMessenger.cpp @@ -83,15 +83,15 @@ bool test_unit_handling_in_commands() auto p = msg.generatePositroniumDecayParams(); - CHECK(p.fMeanPositronRangeEnabled[0] == true, "positron range enable wrong"); - CHECK(p.fMeanPositronRange[0] == 0.1 * 10, "positron range enable wrong"); // Cause mm is the default unit + CHECK(p.fMeanPositronRangeEnabled[0] == true, "positron range enabled wrong"); + CHECK(p.fMeanPositronRange[0] == 0.1 * 10, "positron range wrong"); // Cause mm is the default unit CHECK(p.fFractions.size() == 2, "wrong size"); CHECK(p.fFractions[1] == 0.7f, "fraction wrong"); CHECK(p.fDecayKind[1] == k3Gamma, "decay kind wrong"); CHECK(p.fPromptGammaEnergy[1] == 1.0f /1000, "energy wrong"); // Cause we used keV and MeV is the default unit - CHECK(p.fMeanPositronRangeEnabled[1] == true, "positron range enable wrong"); - CHECK(p.fMeanPositronRange[1] == 0.0 *10 , "positron range enable wrong"); + CHECK(p.fMeanPositronRangeEnabled[1] == true, "positron range enabled wrong"); + CHECK(p.fMeanPositronRange[1] == 0.0 *10 , "positron range wrong"); return true; } From 3d328897153ba0a3ef4008d875a7834ba40cce3b Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 6 Mar 2026 17:22:12 +0100 Subject: [PATCH 129/144] Add handling of Electron Capture Also, add a dedicated command to set the probabilites of electron captures from macro --- .../GatePositroniumDecayModelParams.hh | 1 + .../GatePositroniumDecayParamsGenerator.hh | 2 ++ .../include/GatePositroniumSourceMessenger.hh | 1 + .../physics/src/GatePositroniumDecayModel.cc | 9 ++++++-- .../GatePositroniumDecayParamsGenerator.cc | 21 +++++++++++++++++++ .../src/GatePositroniumSourceMessenger.cc | 11 ++++++++++ ...st_GatePositroniumDecayParamsGenerator.cpp | 3 +++ tests/test_GatePositroniumSourceMessenger.cpp | 3 +++ 8 files changed, 49 insertions(+), 2 deletions(-) diff --git a/source/physics/include/GatePositroniumDecayModelParams.hh b/source/physics/include/GatePositroniumDecayModelParams.hh index cc6244bc5..ef9a4c3f7 100644 --- a/source/physics/include/GatePositroniumDecayModelParams.hh +++ b/source/physics/include/GatePositroniumDecayModelParams.hh @@ -17,6 +17,7 @@ struct PositroniumDecayModelParams { std::vector fFractions; std::vector fLifetimes; + std::vector fElectronCaptureProbabilities; std::vector fPromptGammaProbabilities; std::vector fPromptGammaEnergy; std::vector fDecayKind; diff --git a/source/physics/include/GatePositroniumDecayParamsGenerator.hh b/source/physics/include/GatePositroniumDecayParamsGenerator.hh index accf40db6..d6f115e03 100644 --- a/source/physics/include/GatePositroniumDecayParamsGenerator.hh +++ b/source/physics/include/GatePositroniumDecayParamsGenerator.hh @@ -27,6 +27,7 @@ public: GatePositroniumDecayParamsGenerator()=default; virtual ~GatePositroniumDecayParamsGenerator()=default; + void SetElectronCaptureProbabilities(const std::vector& electronCaptureProb); void SetPromptGammaProbabilities(const std::vector& promptGammaProb); void SetPromptGammaEnergies(const std::vector& energies); void SetPositroniumLifetimes(const std::vector& fPositroniumLifetimes); @@ -42,6 +43,7 @@ private: std::optional> fPositroniumLifetimes; std::optional> fPromptGammaProbabilities; std::optional> fPromptGammaEnergies; + std::optional> fElectronCaptureProbabilities; std::optional> fDecayKinds; std::optional> fPositronInteractions; std::optional> fMeanPositronRangeEnabled; diff --git a/source/physics/include/GatePositroniumSourceMessenger.hh b/source/physics/include/GatePositroniumSourceMessenger.hh index 53a583a4b..b8190127f 100644 --- a/source/physics/include/GatePositroniumSourceMessenger.hh +++ b/source/physics/include/GatePositroniumSourceMessenger.hh @@ -51,6 +51,7 @@ class GatePositroniumSourceMessenger: public GateVSourceMessenger std::unique_ptr upCmdSetPositroniumLifetimes; std::unique_ptr upCmdSetPromptPhotonProbabilites; std::unique_ptr upCmdSetPromptPhotonEnergies; + std::unique_ptr upCmdSetElectronCaptureProbabilites; std::unique_ptr upCmdSetDecayKinds; std::unique_ptr upCmdSetPositronInteractions; diff --git a/source/physics/src/GatePositroniumDecayModel.cc b/source/physics/src/GatePositroniumDecayModel.cc index d97557b74..fb822d33c 100644 --- a/source/physics/src/GatePositroniumDecayModel.cc +++ b/source/physics/src/GatePositroniumDecayModel.cc @@ -71,13 +71,18 @@ G4int GatePositroniumDecayModel::GeneratePrimaryVertices(G4Event* event, G4doubl { auto decayIndex = GatePositroniumDecayModel::getPositroniumDecayIndex(fModelParams.fFractions); - G4int number_of_vertices = 1; + G4int number_of_vertices = 0; if(fModelParams.fPromptGammaProbabilities[decayIndex] > G4UniformRand()) { ++number_of_vertices; event->AddPrimaryVertex(GetPrimaryVertexFromDeexcitation(particle_time, particle_position, decayIndex)); } - event->AddPrimaryVertex(GetPrimaryVertexFromPositroniumAnnihilation(particle_time, particle_position, decayIndex)); + + if(fModelParams.fElectronCaptureProbabilities[decayIndex] < G4UniformRand()) + { + ++number_of_vertices; + event->AddPrimaryVertex(GetPrimaryVertexFromPositroniumAnnihilation(particle_time, particle_position, decayIndex)); + } return number_of_vertices; } diff --git a/source/physics/src/GatePositroniumDecayParamsGenerator.cc b/source/physics/src/GatePositroniumDecayParamsGenerator.cc index f9e33e16d..75fe9b817 100644 --- a/source/physics/src/GatePositroniumDecayParamsGenerator.cc +++ b/source/physics/src/GatePositroniumDecayParamsGenerator.cc @@ -7,6 +7,11 @@ using namespace GatePositroniumConstants; +void GatePositroniumDecayParamsGenerator::SetElectronCaptureProbabilities(const std::vector& electronCaptureProb) +{ + fElectronCaptureProbabilities = electronCaptureProb; +} + void GatePositroniumDecayParamsGenerator::SetPromptGammaProbabilities(const std::vector& promptGammaProb) { fPromptGammaProbabilities = promptGammaProb; @@ -64,6 +69,11 @@ PositroniumDecayModelParams GatePositroniumDecayParamsGenerator::generatePositro params.fMeanPositronRangeEnabled={false}; params.fMeanPositronRange ={0.0}; } + if(fElectronCaptureProbabilities.has_value()){ + params.fElectronCaptureProbabilities=fElectronCaptureProbabilities.value(); + } else { + params.fElectronCaptureProbabilities={0.0}; + } } if (model == GatePositroniumDecayParamsGenerator::kOrthoPositronium) { params.fFractions= {1}; @@ -88,6 +98,11 @@ PositroniumDecayModelParams GatePositroniumDecayParamsGenerator::generatePositro params.fMeanPositronRangeEnabled={false}; params.fMeanPositronRange ={0.0}; } + if(fElectronCaptureProbabilities.has_value()){ + params.fElectronCaptureProbabilities=fElectronCaptureProbabilities.value(); + } else { + params.fElectronCaptureProbabilities={0.0}; + } } if (model == GatePositroniumDecayParamsGenerator::kPositronium) { @@ -122,6 +137,12 @@ PositroniumDecayModelParams GatePositroniumDecayParamsGenerator::generatePositro GateError("GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams: Prompt gamma energies are not set"); } + if(fElectronCaptureProbabilities.has_value()) { + params.fElectronCaptureProbabilities=fElectronCaptureProbabilities.value(); + } else { + GateError("GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams: Positronium electron capture probabilities are not set"); + } + if(fDecayKinds.has_value()) { params.fDecayKind=fDecayKinds.value(); } else if (!params.fPositronInteractions.empty() && !params.fLifetimes.empty() && !params.fFractions.empty()) { diff --git a/source/physics/src/GatePositroniumSourceMessenger.cc b/source/physics/src/GatePositroniumSourceMessenger.cc index 632889099..baa4c4dcf 100644 --- a/source/physics/src/GatePositroniumSourceMessenger.cc +++ b/source/physics/src/GatePositroniumSourceMessenger.cc @@ -82,6 +82,7 @@ void GatePositroniumSourceMessenger::InitCommands() upCmdSetPromptPhotonProbabilites.reset(GetStringCmd( "setPromptPhotonProbabilites", "\"f1, f2, f3 .., fn\" - where fi in [0.0, 1.0] " ) ); upCmdSetPromptPhotonEnergies.reset(GetStringCmd( "setPromptPhotonEnergies", "\"e1, e2, e3 .., en e_unit\" - where ei are energies and e_unit is one of the Geant4 energy units e.g. MeV" ) ); upCmdSetMeanPositronRange.reset(GetStringCmd( "setMeanPositronRange", "\"r1, r2, r3 .., rn r_unit\" - where ri are mean positron range and r_unit is one of the Geant 4 distance units e.g. mm " ) ); + upCmdSetElectronCaptureProbabilites.reset(GetStringCmd( "setElectronCaptureProbabilites", "\"f1, f2, f3 .., fn\" - where fi in [0.0, 1.0] " ) ); } std::vector GatePositroniumSourceMessenger::parseListOfParamsWithUnit(const G4String& input) const @@ -138,6 +139,16 @@ void GatePositroniumSourceMessenger::SetNewValue(G4UIcommand *command, G4String } else if (command == upCmdSetMeanPositronRange.get()) { auto positronRange = parseListOfParamsWithUnit(new_value); fParamGenerator.SetMeanPositronRange(positronRange); + } else if (command == upCmdSetElectronCaptureProbabilites.get()) { + std::vector electronCaptureProb; + std::stringstream ss(new_value); + float prob; + while (ss >> prob) { + prob = prob < 0 ? 0 : prob; + prob = prob > 1 ? 1 : prob; + electronCaptureProb.push_back(prob); + } + fParamGenerator.SetElectronCaptureProbabilities(electronCaptureProb); } else if (command == upCmdSetDecayKinds.get()) { std::vector decayKinds; std::stringstream ss(new_value); diff --git a/tests/test_GatePositroniumDecayParamsGenerator.cpp b/tests/test_GatePositroniumDecayParamsGenerator.cpp index bef1600df..d494d66c7 100644 --- a/tests/test_GatePositroniumDecayParamsGenerator.cpp +++ b/tests/test_GatePositroniumDecayParamsGenerator.cpp @@ -70,6 +70,7 @@ bool test_positronium_custom() gen.SetDecayKinds({k2Gamma, k3Gamma}); gen.SetPromptGammaProbabilities({0, 1.0}); gen.SetPromptGammaEnergies({0.0f, 1.2f}); + gen.SetElectronCaptureProbabilities({0.0, 0.5}); auto p = gen.generatePositroniumDecayParams(GatePositroniumDecayParamsGenerator::kPositronium); @@ -78,12 +79,14 @@ bool test_positronium_custom() CHECK(p.fDecayKind[0] == k2Gamma, "PS decay kind mismatch"); CHECK(p.fPromptGammaProbabilities[0] == 0.0, "PS prompt probability mismatch"); CHECK(p.fPromptGammaEnergy[0] == 0.0f, "PS prompt energy mismatch"); + CHECK(p.fElectronCaptureProbabilities[0] == 0.0f, "PS electron capture mismatch"); CHECK(p.fFractions[1] == 0.7f, "PS fraction mismatch"); CHECK(p.fLifetimes[1] == 140.0f, "PS lifetime mismatch"); CHECK(p.fDecayKind[1] == k3Gamma, "PS decay kind mismatch"); CHECK(p.fPromptGammaProbabilities[1] == 1.0, "PS prompt probability mismatch"); CHECK(p.fPromptGammaEnergy[1] == 1.2f, "PS prompt energy mismatch"); + CHECK(p.fElectronCaptureProbabilities[1] == 0.5f, "PS electron capture mismatch"); return true; } diff --git a/tests/test_GatePositroniumSourceMessenger.cpp b/tests/test_GatePositroniumSourceMessenger.cpp index e9f9dc20e..d721ddaaa 100644 --- a/tests/test_GatePositroniumSourceMessenger.cpp +++ b/tests/test_GatePositroniumSourceMessenger.cpp @@ -22,6 +22,7 @@ class TestablePositroniumMessenger : public GatePositroniumSourceMessenger { G4UIcommand* CmdLifetimes() { return upCmdSetPositroniumLifetimes.get(); } G4UIcommand* CmdDecayKinds() { return upCmdSetDecayKinds.get(); } G4UIcommand* CmdPromptProb() { return upCmdSetPromptPhotonProbabilites.get(); } + G4UIcommand* CmdElectronCaptureProb() { return upCmdSetElectronCaptureProbabilites.get(); } G4UIcommand* CmdPromptEnergy() { return upCmdSetPromptPhotonEnergies.get(); } G4UIcommand* CmdInteractions() { return upCmdSetPositronInteractions.get(); } G4UIcommand* CmdMeanPositronRange() { return upCmdSetMeanPositronRange.get(); } @@ -47,6 +48,7 @@ bool test_full_custom() msg.SetNewValue(msg.CmdDecayKinds(), "k2Gamma k3Gamma"); msg.SetNewValue(msg.CmdPromptProb(), "0.2 0.8"); msg.SetNewValue(msg.CmdPromptEnergy(), "0.5 1.0 MeV"); + msg.SetNewValue(msg.CmdElectronCaptureProb(), "0.3 0.0"); auto p = msg.generatePositroniumDecayParams(); @@ -80,6 +82,7 @@ bool test_unit_handling_in_commands() msg.SetNewValue(msg.CmdPromptProb(), "0.2 0.8"); msg.SetNewValue(msg.CmdPromptEnergy(), "0.5 1.0 keV"); msg.SetNewValue(msg.CmdMeanPositronRange(), "0.1 0.0 cm"); + msg.SetNewValue(msg.CmdElectronCaptureProb(), "0.0 0.0"); auto p = msg.generatePositroniumDecayParams(); From cf5e0efc865a5eb7bafb5c787d7eec465d9cdefe Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 6 Mar 2026 23:46:55 +0100 Subject: [PATCH 130/144] Add following fix for electron capture case If, no prompt emission and electron capture happens, then we repeat the sampling of the channel. --- .../physics/src/GatePositroniumDecayModel.cc | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/source/physics/src/GatePositroniumDecayModel.cc b/source/physics/src/GatePositroniumDecayModel.cc index fb822d33c..1c47d5f8a 100644 --- a/source/physics/src/GatePositroniumDecayModel.cc +++ b/source/physics/src/GatePositroniumDecayModel.cc @@ -69,19 +69,21 @@ G4PrimaryVertex* GatePositroniumDecayModel::GetPrimaryVertexFromPositroniumAnnih G4int GatePositroniumDecayModel::GeneratePrimaryVertices(G4Event* event, G4double& particle_time, G4ThreeVector& particle_position) { - auto decayIndex = GatePositroniumDecayModel::getPositroniumDecayIndex(fModelParams.fFractions); - G4int number_of_vertices = 0; - if(fModelParams.fPromptGammaProbabilities[decayIndex] > G4UniformRand()) - { - ++number_of_vertices; - event->AddPrimaryVertex(GetPrimaryVertexFromDeexcitation(particle_time, particle_position, decayIndex)); - } + while (number_of_vertices <=0) { + auto decayIndex = GatePositroniumDecayModel::getPositroniumDecayIndex(fModelParams.fFractions); + + if(fModelParams.fPromptGammaProbabilities[decayIndex] >= G4UniformRand()) + { + ++number_of_vertices; + event->AddPrimaryVertex(GetPrimaryVertexFromDeexcitation(particle_time, particle_position, decayIndex)); + } - if(fModelParams.fElectronCaptureProbabilities[decayIndex] < G4UniformRand()) - { - ++number_of_vertices; - event->AddPrimaryVertex(GetPrimaryVertexFromPositroniumAnnihilation(particle_time, particle_position, decayIndex)); + if(fModelParams.fElectronCaptureProbabilities[decayIndex] < G4UniformRand()) + { + ++number_of_vertices; + event->AddPrimaryVertex(GetPrimaryVertexFromPositroniumAnnihilation(particle_time, particle_position, decayIndex)); + } } return number_of_vertices; } From 397cc2d2066543a33533a4c5f054ed946b24c0bd Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 17 Apr 2026 14:01:47 +0200 Subject: [PATCH 131/144] Fix typo in the command name --- source/physics/include/GatePositroniumSourceMessenger.hh | 2 +- source/physics/src/GatePositroniumSourceMessenger.cc | 6 +++++- tests/test_GatePositroniumSourceMessenger.cpp | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/source/physics/include/GatePositroniumSourceMessenger.hh b/source/physics/include/GatePositroniumSourceMessenger.hh index b8190127f..d50e17b0e 100644 --- a/source/physics/include/GatePositroniumSourceMessenger.hh +++ b/source/physics/include/GatePositroniumSourceMessenger.hh @@ -51,7 +51,7 @@ class GatePositroniumSourceMessenger: public GateVSourceMessenger std::unique_ptr upCmdSetPositroniumLifetimes; std::unique_ptr upCmdSetPromptPhotonProbabilites; std::unique_ptr upCmdSetPromptPhotonEnergies; - std::unique_ptr upCmdSetElectronCaptureProbabilites; + std::unique_ptr upCmdSetElectronCaptureProbabilities; std::unique_ptr upCmdSetDecayKinds; std::unique_ptr upCmdSetPositronInteractions; diff --git a/source/physics/src/GatePositroniumSourceMessenger.cc b/source/physics/src/GatePositroniumSourceMessenger.cc index baa4c4dcf..3e51d97ee 100644 --- a/source/physics/src/GatePositroniumSourceMessenger.cc +++ b/source/physics/src/GatePositroniumSourceMessenger.cc @@ -82,7 +82,7 @@ void GatePositroniumSourceMessenger::InitCommands() upCmdSetPromptPhotonProbabilites.reset(GetStringCmd( "setPromptPhotonProbabilites", "\"f1, f2, f3 .., fn\" - where fi in [0.0, 1.0] " ) ); upCmdSetPromptPhotonEnergies.reset(GetStringCmd( "setPromptPhotonEnergies", "\"e1, e2, e3 .., en e_unit\" - where ei are energies and e_unit is one of the Geant4 energy units e.g. MeV" ) ); upCmdSetMeanPositronRange.reset(GetStringCmd( "setMeanPositronRange", "\"r1, r2, r3 .., rn r_unit\" - where ri are mean positron range and r_unit is one of the Geant 4 distance units e.g. mm " ) ); - upCmdSetElectronCaptureProbabilites.reset(GetStringCmd( "setElectronCaptureProbabilites", "\"f1, f2, f3 .., fn\" - where fi in [0.0, 1.0] " ) ); + upCmdSetElectronCaptureProbabilities.reset(GetStringCmd( "setElectronCaptureProbabilities", "\"f1, f2, f3 .., fn\" - where fi in [0.0, 1.0] " ) ); } std::vector GatePositroniumSourceMessenger::parseListOfParamsWithUnit(const G4String& input) const @@ -136,10 +136,14 @@ void GatePositroniumSourceMessenger::SetNewValue(G4UIcommand *command, G4String } else if (command == upCmdSetPromptPhotonEnergies.get()) { auto promptPhotonEnergies = parseListOfParamsWithUnit(new_value); fParamGenerator.SetPromptGammaEnergies(promptPhotonEnergies); +<<<<<<< HEAD } else if (command == upCmdSetMeanPositronRange.get()) { auto positronRange = parseListOfParamsWithUnit(new_value); fParamGenerator.SetMeanPositronRange(positronRange); } else if (command == upCmdSetElectronCaptureProbabilites.get()) { +======= + } else if (command == upCmdSetElectronCaptureProbabilities.get()) { +>>>>>>> ab0fda98 (Fix typo in the command name) std::vector electronCaptureProb; std::stringstream ss(new_value); float prob; diff --git a/tests/test_GatePositroniumSourceMessenger.cpp b/tests/test_GatePositroniumSourceMessenger.cpp index d721ddaaa..d17052d0e 100644 --- a/tests/test_GatePositroniumSourceMessenger.cpp +++ b/tests/test_GatePositroniumSourceMessenger.cpp @@ -22,7 +22,7 @@ class TestablePositroniumMessenger : public GatePositroniumSourceMessenger { G4UIcommand* CmdLifetimes() { return upCmdSetPositroniumLifetimes.get(); } G4UIcommand* CmdDecayKinds() { return upCmdSetDecayKinds.get(); } G4UIcommand* CmdPromptProb() { return upCmdSetPromptPhotonProbabilites.get(); } - G4UIcommand* CmdElectronCaptureProb() { return upCmdSetElectronCaptureProbabilites.get(); } + G4UIcommand* CmdElectronCaptureProb() { return upCmdSetElectronCaptureProbabilities.get(); } G4UIcommand* CmdPromptEnergy() { return upCmdSetPromptPhotonEnergies.get(); } G4UIcommand* CmdInteractions() { return upCmdSetPositronInteractions.get(); } G4UIcommand* CmdMeanPositronRange() { return upCmdSetMeanPositronRange.get(); } From 4b144c5bb9b7fd2b9db614784534270a5486bbde Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 17 Apr 2026 14:29:48 +0200 Subject: [PATCH 132/144] Make positron capture as optional --- source/physics/src/GatePositroniumDecayParamsGenerator.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/physics/src/GatePositroniumDecayParamsGenerator.cc b/source/physics/src/GatePositroniumDecayParamsGenerator.cc index 75fe9b817..d0d84a153 100644 --- a/source/physics/src/GatePositroniumDecayParamsGenerator.cc +++ b/source/physics/src/GatePositroniumDecayParamsGenerator.cc @@ -137,10 +137,11 @@ PositroniumDecayModelParams GatePositroniumDecayParamsGenerator::generatePositro GateError("GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams: Prompt gamma energies are not set"); } + auto nElements = fPositroniumFractions.value().size(); if(fElectronCaptureProbabilities.has_value()) { params.fElectronCaptureProbabilities=fElectronCaptureProbabilities.value(); } else { - GateError("GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams: Positronium electron capture probabilities are not set"); + params.fElectronCaptureProbabilities.assign(nElements, 0.0); } if(fDecayKinds.has_value()) { From c71770becd0ed87f442e3981bbbba32e54b19510 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 17 Apr 2026 14:32:14 +0200 Subject: [PATCH 133/144] Add cross-check of Electron Capture number of elements --- source/physics/src/GatePositroniumDecayParamsGenerator.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/source/physics/src/GatePositroniumDecayParamsGenerator.cc b/source/physics/src/GatePositroniumDecayParamsGenerator.cc index d0d84a153..46ba3f4f9 100644 --- a/source/physics/src/GatePositroniumDecayParamsGenerator.cc +++ b/source/physics/src/GatePositroniumDecayParamsGenerator.cc @@ -166,13 +166,14 @@ PositroniumDecayModelParams GatePositroniumDecayParamsGenerator::generatePositro bool size_mismatch = (ref_param_number != params.fFractions.size()) || (ref_param_number != params.fPromptGammaProbabilities.size()) || (ref_param_number != params.fLifetimes.size()) || - (ref_param_number != params.fPromptGammaEnergy.size()); + (ref_param_number != params.fPromptGammaEnergy.size()) || + (ref_param_number != params.fElectronCaptureProbabilities.size()); if (size_mismatch) { - std::cout << ref_param_number << " " << params.fFractions.size() << " " << params.fPromptGammaProbabilities.size() << " " << params.fLifetimes.size() << " " << params.fPromptGammaEnergy.size() << std::endl; + std::cout << ref_param_number << " " << params.fFractions.size() << " " << params.fPromptGammaProbabilities.size() << " " << params.fLifetimes.size() << " " << params.fPromptGammaEnergy.size() << params.fElectronCaptureProbabilities.size() << std::endl; GateError( "GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams: " "number of provided parameters in Fractions, PromptGamma, Lifetimes, " - "Gamma Energies are not the same"); + "Gamma Energies, Electron Capture Probabilites are not the same"); } return params; From 28247df0e3f41e4d75550ebb03d7b938faf03f72 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 17 Apr 2026 15:12:29 +0200 Subject: [PATCH 134/144] Add a dedicated validation method to check generated parameters --- .../GatePositroniumDecayParamsGenerator.hh | 2 ++ .../src/GatePositroniumDecayParamsGenerator.cc | 17 +++++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/source/physics/include/GatePositroniumDecayParamsGenerator.hh b/source/physics/include/GatePositroniumDecayParamsGenerator.hh index d6f115e03..732e12572 100644 --- a/source/physics/include/GatePositroniumDecayParamsGenerator.hh +++ b/source/physics/include/GatePositroniumDecayParamsGenerator.hh @@ -37,6 +37,8 @@ public: void SetMeanPositronRange(const std::vector& meanPositronRange); PositroniumDecayModelParams generatePositroniumDecayParams(DecayModel model=kPositronium) const; + void validatePositroniumDecayParams(const PositroniumDecayModelParams& params) const; + private: std::optional> fPositroniumFractions; diff --git a/source/physics/src/GatePositroniumDecayParamsGenerator.cc b/source/physics/src/GatePositroniumDecayParamsGenerator.cc index 46ba3f4f9..cc1309f5b 100644 --- a/source/physics/src/GatePositroniumDecayParamsGenerator.cc +++ b/source/physics/src/GatePositroniumDecayParamsGenerator.cc @@ -162,6 +162,13 @@ PositroniumDecayModelParams GatePositroniumDecayParamsGenerator::generatePositro } } + validatePositroniumDecayParams(params); + + return params; +} + +void GatePositroniumDecayParamsGenerator::validatePositroniumDecayParams(const PositroniumDecayModelParams& params) const +{ auto ref_param_number = params.fDecayKind.size(); bool size_mismatch = (ref_param_number != params.fFractions.size()) || (ref_param_number != params.fPromptGammaProbabilities.size()) || @@ -175,6 +182,12 @@ PositroniumDecayModelParams GatePositroniumDecayParamsGenerator::generatePositro "number of provided parameters in Fractions, PromptGamma, Lifetimes, " "Gamma Energies, Electron Capture Probabilites are not the same"); } - - return params; + constexpr double kProb_of_no_particle_limit = 10e-2; + for (int i = 0; i < ref_param_number; i++) { + auto electron_capture_prob = params.fElectronCaptureProbabilities[i]; + auto no_prompt_prob = 1 -params.fPromptGammaProbabilities[i]; + if (electron_capture_prob *no_prompt_prob< kProb_of_no_particle_limit) { + GateWarning("The probability of 0 prompt emission times probability of electron capture (no anihillation) is very small. Please check channel definitions! Otherwise simulations can take a lot of time"); + } + } } From 8716d34e4c55bddce8407067d26c225b10f97d09 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 17 Apr 2026 15:14:15 +0200 Subject: [PATCH 135/144] Refactor the if conditions for clarity --- source/physics/src/GatePositroniumDecayModel.cc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/source/physics/src/GatePositroniumDecayModel.cc b/source/physics/src/GatePositroniumDecayModel.cc index 1c47d5f8a..a46758c04 100644 --- a/source/physics/src/GatePositroniumDecayModel.cc +++ b/source/physics/src/GatePositroniumDecayModel.cc @@ -6,6 +6,7 @@ ----------------------*/ #include #include +#include #include "Randomize.hh" #include "G4DecayProducts.hh" @@ -72,14 +73,16 @@ G4int GatePositroniumDecayModel::GeneratePrimaryVertices(G4Event* event, G4doubl G4int number_of_vertices = 0; while (number_of_vertices <=0) { auto decayIndex = GatePositroniumDecayModel::getPositroniumDecayIndex(fModelParams.fFractions); + auto no_electron_capture_prob = 1- fModelParams.fElectronCaptureProbabilities[decayIndex]; + assert(no_electron_capture_prob>=0); - if(fModelParams.fPromptGammaProbabilities[decayIndex] >= G4UniformRand()) + if(G4UniformRand() <= fModelParams.fPromptGammaProbabilities[decayIndex]) { ++number_of_vertices; event->AddPrimaryVertex(GetPrimaryVertexFromDeexcitation(particle_time, particle_position, decayIndex)); } - if(fModelParams.fElectronCaptureProbabilities[decayIndex] < G4UniformRand()) + if(G4UniformRand() <= no_electron_capture_prob) { ++number_of_vertices; event->AddPrimaryVertex(GetPrimaryVertexFromPositroniumAnnihilation(particle_time, particle_position, decayIndex)); From 2ad40248ff67028d8624e8f5381c59def9499524 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 24 Apr 2026 13:47:01 +0200 Subject: [PATCH 136/144] Fix errors due to the unremoved git markers pointing to the conflict --- source/physics/src/GatePositroniumDecayParamsGenerator.cc | 1 - source/physics/src/GatePositroniumSourceMessenger.cc | 4 ---- 2 files changed, 5 deletions(-) diff --git a/source/physics/src/GatePositroniumDecayParamsGenerator.cc b/source/physics/src/GatePositroniumDecayParamsGenerator.cc index cc1309f5b..b1bcdf2e5 100644 --- a/source/physics/src/GatePositroniumDecayParamsGenerator.cc +++ b/source/physics/src/GatePositroniumDecayParamsGenerator.cc @@ -152,7 +152,6 @@ PositroniumDecayModelParams GatePositroniumDecayParamsGenerator::generatePositro GateError("GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams: Positronium decay kinds are not set and one or more sets that can calculate them (positronInteractions, lifetimes or fractions) is/are empty"); } - auto nElements = fPositroniumFractions.value().size(); if(fMeanPositronRange.has_value()) { params.fMeanPositronRangeEnabled.assign(nElements, true); params.fMeanPositronRange=fMeanPositronRange.value(); diff --git a/source/physics/src/GatePositroniumSourceMessenger.cc b/source/physics/src/GatePositroniumSourceMessenger.cc index 3e51d97ee..41dacf07e 100644 --- a/source/physics/src/GatePositroniumSourceMessenger.cc +++ b/source/physics/src/GatePositroniumSourceMessenger.cc @@ -136,14 +136,10 @@ void GatePositroniumSourceMessenger::SetNewValue(G4UIcommand *command, G4String } else if (command == upCmdSetPromptPhotonEnergies.get()) { auto promptPhotonEnergies = parseListOfParamsWithUnit(new_value); fParamGenerator.SetPromptGammaEnergies(promptPhotonEnergies); -<<<<<<< HEAD } else if (command == upCmdSetMeanPositronRange.get()) { auto positronRange = parseListOfParamsWithUnit(new_value); fParamGenerator.SetMeanPositronRange(positronRange); - } else if (command == upCmdSetElectronCaptureProbabilites.get()) { -======= } else if (command == upCmdSetElectronCaptureProbabilities.get()) { ->>>>>>> ab0fda98 (Fix typo in the command name) std::vector electronCaptureProb; std::stringstream ss(new_value); float prob; From 968ae90fe4b2980416c8ab21d33589aec6dd3d80 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Sat, 25 Apr 2026 00:45:29 +0200 Subject: [PATCH 137/144] Add headers and some generated description fo the classes --- source/physics/include/GatePositronium.hh | 8 ++++++++ source/physics/include/GatePositroniumConstants.hh | 8 ++++++++ .../include/GatePositroniumDecayModelParams.hh | 8 ++++++++ .../include/GatePositroniumDecayParamsGenerator.hh | 8 ++++++++ source/physics/include/GatePositroniumHelper.hh | 14 ++++++++++++++ source/physics/include/GatePositroniumSource.hh | 8 ++++++++ .../src/GatePositroniumDecayParamsGenerator.cc | 7 +++++++ source/physics/src/GatePositroniumHelper.cc | 7 +++++++ tests/TestingTools.h | 14 ++++++++++++++ tests/test_GateExtendedVSource.cpp | 7 +++++++ tests/test_GatePositronium.cpp | 7 +++++++ tests/test_GatePositroniumDecayModel.cpp | 7 +++++++ tests/test_GatePositroniumDecayParamsGenerator.cpp | 7 +++++++ tests/test_GatePositroniumHelper.cpp | 7 +++++++ tests/test_GatePositroniumSourceMessenger.cpp | 7 +++++++ tests/test_dummy.cpp | 7 +++++++ 16 files changed, 131 insertions(+) diff --git a/source/physics/include/GatePositronium.hh b/source/physics/include/GatePositronium.hh index c91b73ddc..ebece3dd6 100644 --- a/source/physics/include/GatePositronium.hh +++ b/source/physics/include/GatePositronium.hh @@ -4,6 +4,14 @@ of the GNU Lesser General Public Licence (LGPL) See LICENSE.md for further details ----------------------*/ + +/** Authors: Wojciech Krzemień, Mateusz Bała and Kamil Dulski + * Emails: wojciech.krzemien@ncbj.gov.pl, mateusz.bala@ncbj.gov.pl and kamil.dulski@gmail.com + * Organization: National Centre For Nuclear Research (NCBJ, https://ncbj.gov.pl), Poland + * Developed within the IMPET project: https://pet.ncbj.gov.pl/ + * About class: Represents a single positronium species (para-Ps or ortho-Ps), wrapping the Geant4 particle decay table to provide access to lifetime, annihilation gamma count, and decay products. + **/ + #ifndef GatePositronium_hh #define GatePositronium_hh diff --git a/source/physics/include/GatePositroniumConstants.hh b/source/physics/include/GatePositroniumConstants.hh index d1e823a58..c8071d50e 100644 --- a/source/physics/include/GatePositroniumConstants.hh +++ b/source/physics/include/GatePositroniumConstants.hh @@ -4,6 +4,14 @@ of the GNU Lesser General Public Licence (LGPL) See LICENSE.md for further details ----------------------*/ + +/** Authors: Wojciech Krzemień, Mateusz Bała and Kamil Dulski + * Emails: wojciech.krzemien@ncbj.gov.pl, mateusz.bala@ncbj.gov.pl and kamil.dulski@gmail.com + * Organization: National Centre For Nuclear Research (NCBJ, https://ncbj.gov.pl), Poland + * Developed within the IMPET project: https://pet.ncbj.gov.pl/ + * About class: Namespace of physical constants for positronium physics: para-Ps lifetime (0.1244 ns), ortho-Ps mean lifetime (142 ns), para-to-ortho fraction (1/3), and hyperfine coefficient (372). + **/ + #ifndef GatePositroniumConstants_hh #define GatePositroniumConstants_hh diff --git a/source/physics/include/GatePositroniumDecayModelParams.hh b/source/physics/include/GatePositroniumDecayModelParams.hh index ef9a4c3f7..717ec8e00 100644 --- a/source/physics/include/GatePositroniumDecayModelParams.hh +++ b/source/physics/include/GatePositroniumDecayModelParams.hh @@ -4,6 +4,14 @@ of the GNU Lesser General Public Licence (LGPL) See LICENSE.md for further details ----------------------*/ + +/** Authors: Wojciech Krzemień, Mateusz Bała and Kamil Dulski + * Emails: wojciech.krzemien@ncbj.gov.pl, mateusz.bala@ncbj.gov.pl and kamil.dulski@gmail.com + * Organization: National Centre For Nuclear Research (NCBJ, https://ncbj.gov.pl), Poland + * Developed within the IMPET project: https://pet.ncbj.gov.pl/ + * About class: Data structure holding all per-component parameters for the positronium decay model: fractions, lifetimes, decay kinds (2γ/3γ), positron-electron interaction types, electron capture probabilities, prompt gamma parameters, and positron range settings. + **/ + #ifndef GatePositroniumDecayModelParams_hh #define GatePositroniumDecayModelParams_hh diff --git a/source/physics/include/GatePositroniumDecayParamsGenerator.hh b/source/physics/include/GatePositroniumDecayParamsGenerator.hh index 732e12572..2932fc2dd 100644 --- a/source/physics/include/GatePositroniumDecayParamsGenerator.hh +++ b/source/physics/include/GatePositroniumDecayParamsGenerator.hh @@ -4,6 +4,14 @@ of the GNU Lesser General Public Licence (LGPL) See LICENSE.md for further details ----------------------*/ + +/** Authors: Wojciech Krzemień, Mateusz Bała and Kamil Dulski + * Emails: wojciech.krzemien@ncbj.gov.pl, mateusz.bala@ncbj.gov.pl and kamil.dulski@gmail.com + * Organization: National Centre For Nuclear Research (NCBJ, https://ncbj.gov.pl), Poland + * Developed within the IMPET project: https://pet.ncbj.gov.pl/ + * About class: Builder that assembles and validates a PositroniumDecayModelParams from individually set parameters; supports three preset modes (para-Ps only, ortho-Ps only, and mixed Ps with user-defined components including electron capture). + **/ + #ifndef GatePositroniumDecayParamsGenerator_hh #define GatePositroniumDecayParamsGenerator_hh diff --git a/source/physics/include/GatePositroniumHelper.hh b/source/physics/include/GatePositroniumHelper.hh index b90c3dd64..658dd179c 100644 --- a/source/physics/include/GatePositroniumHelper.hh +++ b/source/physics/include/GatePositroniumHelper.hh @@ -1,3 +1,17 @@ +/** ---------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + ----------------------*/ + +/** Authors: Wojciech Krzemień, Mateusz Bała and Kamil Dulski + * Emails: wojciech.krzemien@ncbj.gov.pl, mateusz.bala@ncbj.gov.pl and kamil.dulski@gmail.com + * Organization: National Centre For Nuclear Research (NCBJ, https://ncbj.gov.pl), Poland + * Developed within the IMPET project: https://pet.ncbj.gov.pl/ + * About class: Utility class computing per-component decay fractions from lifetimes and positron-electron interaction types (ortho-Ps, direct annihilation, para-Ps), using the hyperfine coefficient and mean ortho-Ps lifetime; also normalizes fraction vectors. + **/ + #ifndef GatePositroniumHelper_h #define GatePositroniumHelper_h 1 diff --git a/source/physics/include/GatePositroniumSource.hh b/source/physics/include/GatePositroniumSource.hh index a0f89c33e..e4e76ccf9 100644 --- a/source/physics/include/GatePositroniumSource.hh +++ b/source/physics/include/GatePositroniumSource.hh @@ -4,6 +4,14 @@ of the GNU Lesser General Public Licence (LGPL) See LICENSE.md for further details ----------------------*/ + +/** Authors: Wojciech Krzemień, Mateusz Bała and Kamil Dulski + * Emails: wojciech.krzemien@ncbj.gov.pl, mateusz.bala@ncbj.gov.pl and kamil.dulski@gmail.com + * Organization: National Centre For Nuclear Research (NCBJ, https://ncbj.gov.pl), Poland + * Developed within the IMPET project: https://pet.ncbj.gov.pl/ + * About class: Geant4 primary event source for positronium annihilation; lazily initializes a GatePositroniumDecayModel from messenger-supplied parameters on the first event and delegates primary vertex generation to the model. + **/ + #ifndef GatePositroniumSource_hh #define GatePositroniumSource_hh diff --git a/source/physics/src/GatePositroniumDecayParamsGenerator.cc b/source/physics/src/GatePositroniumDecayParamsGenerator.cc index b1bcdf2e5..0d216dd66 100644 --- a/source/physics/src/GatePositroniumDecayParamsGenerator.cc +++ b/source/physics/src/GatePositroniumDecayParamsGenerator.cc @@ -1,3 +1,10 @@ +/** ---------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + ----------------------*/ + #include #include "GateMessageManager.hh" diff --git a/source/physics/src/GatePositroniumHelper.cc b/source/physics/src/GatePositroniumHelper.cc index 746747554..8685d0a87 100644 --- a/source/physics/src/GatePositroniumHelper.cc +++ b/source/physics/src/GatePositroniumHelper.cc @@ -1,3 +1,10 @@ +/** ---------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + ----------------------*/ + #include #include #include diff --git a/tests/TestingTools.h b/tests/TestingTools.h index 7556ac205..652f77e5e 100644 --- a/tests/TestingTools.h +++ b/tests/TestingTools.h @@ -1,3 +1,17 @@ +/** ---------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + ----------------------*/ + +/** Authors: Wojciech Krzemień, Mateusz Bała and Kamil Dulski + * Emails: wojciech.krzemien@ncbj.gov.pl, mateusz.bala@ncbj.gov.pl and kamil.dulski@gmail.com + * Organization: National Centre For Nuclear Research (NCBJ, https://ncbj.gov.pl), Poland + * Developed within the IMPET project: https://pet.ncbj.gov.pl/ + * About class: Testing utility header providing the CHECK macro for assertion-style test failure reporting with function name and line number; used by all positronium unit tests. + **/ + #ifndef TestingTools_h #define TestingTools_h diff --git a/tests/test_GateExtendedVSource.cpp b/tests/test_GateExtendedVSource.cpp index 980f3122d..fcbb755c5 100644 --- a/tests/test_GateExtendedVSource.cpp +++ b/tests/test_GateExtendedVSource.cpp @@ -1,3 +1,10 @@ +/** ---------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + ----------------------*/ + #include #include diff --git a/tests/test_GatePositronium.cpp b/tests/test_GatePositronium.cpp index d96d06184..a20bac170 100644 --- a/tests/test_GatePositronium.cpp +++ b/tests/test_GatePositronium.cpp @@ -1,3 +1,10 @@ +/** ---------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + ----------------------*/ + #include #include diff --git a/tests/test_GatePositroniumDecayModel.cpp b/tests/test_GatePositroniumDecayModel.cpp index 3335b041e..acf59d6ee 100644 --- a/tests/test_GatePositroniumDecayModel.cpp +++ b/tests/test_GatePositroniumDecayModel.cpp @@ -1,3 +1,10 @@ +/** ---------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + ----------------------*/ + #include #include #include diff --git a/tests/test_GatePositroniumDecayParamsGenerator.cpp b/tests/test_GatePositroniumDecayParamsGenerator.cpp index d494d66c7..40c06d60a 100644 --- a/tests/test_GatePositroniumDecayParamsGenerator.cpp +++ b/tests/test_GatePositroniumDecayParamsGenerator.cpp @@ -1,3 +1,10 @@ +/** ---------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + ----------------------*/ + #include #include #include diff --git a/tests/test_GatePositroniumHelper.cpp b/tests/test_GatePositroniumHelper.cpp index 77b0e0c9d..4cc470afe 100644 --- a/tests/test_GatePositroniumHelper.cpp +++ b/tests/test_GatePositroniumHelper.cpp @@ -1,3 +1,10 @@ +/** ---------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + ----------------------*/ + #include #include diff --git a/tests/test_GatePositroniumSourceMessenger.cpp b/tests/test_GatePositroniumSourceMessenger.cpp index d17052d0e..fb90fb96b 100644 --- a/tests/test_GatePositroniumSourceMessenger.cpp +++ b/tests/test_GatePositroniumSourceMessenger.cpp @@ -1,3 +1,10 @@ +/** ---------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + ----------------------*/ + #include #include diff --git a/tests/test_dummy.cpp b/tests/test_dummy.cpp index 98a6b13e1..d8c58cf31 100644 --- a/tests/test_dummy.cpp +++ b/tests/test_dummy.cpp @@ -1,3 +1,10 @@ +/** ---------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + ----------------------*/ + #include #include bool run_tests() From fc66f7fee4fea201e0950d6c4a6f36c516d6d080 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Tue, 28 Apr 2026 01:02:22 +0200 Subject: [PATCH 138/144] Fix the warning condition for EC and no prompt It was reversed. --- source/physics/src/GatePositroniumDecayParamsGenerator.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/physics/src/GatePositroniumDecayParamsGenerator.cc b/source/physics/src/GatePositroniumDecayParamsGenerator.cc index 0d216dd66..d4b958999 100644 --- a/source/physics/src/GatePositroniumDecayParamsGenerator.cc +++ b/source/physics/src/GatePositroniumDecayParamsGenerator.cc @@ -188,12 +188,12 @@ void GatePositroniumDecayParamsGenerator::validatePositroniumDecayParams(const P "number of provided parameters in Fractions, PromptGamma, Lifetimes, " "Gamma Energies, Electron Capture Probabilites are not the same"); } - constexpr double kProb_of_no_particle_limit = 10e-2; + constexpr double kProb_of_no_particle_limit = 0.9; for (int i = 0; i < ref_param_number; i++) { auto electron_capture_prob = params.fElectronCaptureProbabilities[i]; auto no_prompt_prob = 1 -params.fPromptGammaProbabilities[i]; - if (electron_capture_prob *no_prompt_prob< kProb_of_no_particle_limit) { - GateWarning("The probability of 0 prompt emission times probability of electron capture (no anihillation) is very small. Please check channel definitions! Otherwise simulations can take a lot of time"); + if (electron_capture_prob *no_prompt_prob >= kProb_of_no_particle_limit) { + GateWarning("The probability of 0 prompt emission times probability of electron capture (no anihillation) is larger than 90%. Please check channel definitions! Otherwise simulations can take a lot of time"); } } } From b1b335ee61eb56fa7951daeda8f6f5428ad89a8a Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Tue, 28 Apr 2026 01:30:32 +0200 Subject: [PATCH 139/144] Change return -1 from getPositorniumDecayIndex to return static_cast(fractions.size()) - 1; This -1 could lead to undefined behaviour, since it was used as a index. --- source/physics/src/GatePositroniumDecayModel.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/physics/src/GatePositroniumDecayModel.cc b/source/physics/src/GatePositroniumDecayModel.cc index a46758c04..f9b40fe72 100644 --- a/source/physics/src/GatePositroniumDecayModel.cc +++ b/source/physics/src/GatePositroniumDecayModel.cc @@ -21,7 +21,7 @@ int GatePositroniumDecayModel::getPositroniumDecayIndex(const std::vector curr_frac_cumulative = curr_frac_cumulative + fractions[i]; if(r<= curr_frac_cumulative) return i; } - return -1; + return static_cast(fractions.size()) - 1; } G4ThreeVector GatePositroniumDecayModel::AddPositronRangeShift(const G4ThreeVector& original_position, G4double mean_positron_range) From 86adfbe62de9374e963fc24be7e60ef5a1b09ef2 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Tue, 28 Apr 2026 01:32:58 +0200 Subject: [PATCH 140/144] Add two tests that show two bug that are already fixed --- tests/test_GatePositroniumDecayModel.cpp | 45 ++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/tests/test_GatePositroniumDecayModel.cpp b/tests/test_GatePositroniumDecayModel.cpp index acf59d6ee..76be4cda5 100644 --- a/tests/test_GatePositroniumDecayModel.cpp +++ b/tests/test_GatePositroniumDecayModel.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -17,6 +18,8 @@ #include "GatePhysicsList.hh" #include "GateDetectorConstruction.hh" +#include "TestingTools.h" + void initializeGateRunManager(GateRunManager* runManager) { // Set the DetectorConstruction @@ -107,12 +110,54 @@ bool run_tests_positron_range() { return res; } +// Bug #2: getPositroniumDecayIndex returns -1 when fractions do not sum to 1.0. +// The caller (GeneratePrimaryVertices) uses the return value directly as a vector +// index on fElectronCaptureProbabilities, fMeanPositronRangeEnabled, +// fMeanPositronRange, and fPositroniumDecayChannel (lines 55, 76, 96, 112 of +// GatePositroniumDecayModel.cc), so -1 causes undefined behaviour. + +bool test_decay_index_in_bounds_for_sub_unity_fractions() +{ + // Fractions {0.3, 0.3} sum to 0.6: roughly 40 % of calls return -1. + // The test checks that the return value is always a valid index; + // it fails reliably under the current implementation. + const std::vector fractions = {0.3f, 0.3f}; + for (int i = 0; i < 10000; ++i) { + int index = GatePositroniumDecayModel::getPositroniumDecayIndex(fractions); + CHECK(index >= 0 && index < static_cast(fractions.size()), + "getPositroniumDecayIndex returned " + std::to_string(index) + + " for a vector of size " + std::to_string(fractions.size()) + + " — out-of-bounds index causes UB in GeneratePrimaryVertices (Bug #2)"); + } + return true; +} + +bool test_decay_index_float_fractions_sum_below_unity() +{ + // Even fractions that appear normalized can fail: in IEEE 754 single precision + // (1.0f/3.0f)*3 = 1 - 2^-24 < 1.0, leaving a gap that G4UniformRand (a double) + // can land in, returning -1 from getPositroniumDecayIndex. + // This test confirms the precondition: the cumulative float sum of three equal + // thirds is strictly below 1.0, so the gap exists. + const std::vector fractions = {1.0f/3.0f, 1.0f/3.0f, 1.0f/3.0f}; + float cumulative = 0.0f; + for (float f : fractions) cumulative += f; + CHECK(cumulative == 1.0f, + "Float fractions do not sum to exactly 1.0 (actual sum = " + + std::to_string(cumulative) + + "). Any G4UniformRand() value in (sum, 1.0) causes getPositroniumDecayIndex" + " to return -1 instead of the last valid index (Bug #2)"); + return true; +} + int main() { bool res = true; res = res & run_tests(); res = res & run_tests2(); res = res & run_tests_positron_range(); + res = res & test_decay_index_in_bounds_for_sub_unity_fractions(); + res = res & test_decay_index_float_fractions_sum_below_unity(); if (res) { std::cout << "All tests have passed" << std::endl; From 9fbc396c4e961f8631486295c777fce5269983af Mon Sep 17 00:00:00 2001 From: MateuszBala Date: Fri, 8 May 2026 11:27:20 +0200 Subject: [PATCH 141/144] fix: positronium source set correct source and decay type --- .../include/GateEmittedGammaInformation.hh | 3 +- .../include/GatePositroniumDecayModel.hh | 10 +++++++ .../physics/src/GatePositroniumDecayModel.cc | 30 +++++++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/source/physics/include/GateEmittedGammaInformation.hh b/source/physics/include/GateEmittedGammaInformation.hh index 2ead4b0c1..d731e9c70 100644 --- a/source/physics/include/GateEmittedGammaInformation.hh +++ b/source/physics/include/GateEmittedGammaInformation.hh @@ -30,7 +30,8 @@ class GateEmittedGammaInformation : public G4VUserPrimaryParticleInformation NotDefined = 0, // by default SingleGammaEmitter = 1, // just emitted single gamma with specfic energy ( by class GateGammaEmissionModel ) ParaPositronium = 2, // 2 gammas ( plus prompt if it is required ) from pPs decay ( by GatePositroniumDecayModel ) - OrthoPositronium = 3 // 3 gammas ( plus prompt if it is required ) from oPs decay ( by GatePositroniumDecayModel ) + OrthoPositronium = 3, // 3 gammas ( plus prompt if it is required ) from oPs decay ( by GatePositroniumDecayModel ) + DirectAnnihilation = 4 // 2 gammas from direct annihilation of positron without formation of positronium }; /** This enum specifies model of source decay ( if it is present ) diff --git a/source/physics/include/GatePositroniumDecayModel.hh b/source/physics/include/GatePositroniumDecayModel.hh index 90b5b371b..df704e081 100644 --- a/source/physics/include/GatePositroniumDecayModel.hh +++ b/source/physics/include/GatePositroniumDecayModel.hh @@ -30,6 +30,16 @@ class GatePositroniumDecayModel:public GateGammaEmissionModel G4PrimaryVertex* GetPrimaryVertexFromPositroniumAnnihilation(G4double particle_time, const G4ThreeVector &particle_position, int decayIndex); G4PrimaryParticle* GetGammaFromDeexcitation(int decayIndex); std::vector GetGammasFromPositroniumAnnihilation(int decayIndex); + /* The decay model is determined by presence of prompt gamma - if it is present (probability > 0) then it is Deexcitation model, + * otherwise it is Standard model. + */ + GateEmittedGammaInformation::DecayModel GetDecayModel(const int decayIndex) const; + + /* + * The source kind is determined by type of positron-electron interaction + * ( paraPs, orthoPs or direct annihilation without positronium formation ) + */ + GateEmittedGammaInformation::SourceKind GetSourceKind(int decayIndex) const; private: PositroniumDecayModelParams fModelParams; diff --git a/source/physics/src/GatePositroniumDecayModel.cc b/source/physics/src/GatePositroniumDecayModel.cc index f9b40fe72..410a91b61 100644 --- a/source/physics/src/GatePositroniumDecayModel.cc +++ b/source/physics/src/GatePositroniumDecayModel.cc @@ -91,11 +91,39 @@ G4int GatePositroniumDecayModel::GeneratePrimaryVertices(G4Event* event, G4doubl return number_of_vertices; } +GateEmittedGammaInformation::DecayModel GatePositroniumDecayModel::GetDecayModel(const int decayIndex) const +{ + if (fModelParams.fPromptGammaProbabilities[decayIndex] > 0) { + return GateEmittedGammaInformation::DecayModel::Deexcitation; + } + return GateEmittedGammaInformation::DecayModel::Standard; +} + +GateEmittedGammaInformation::SourceKind GatePositroniumDecayModel::GetSourceKind(int decayIndex) const +{ + const PositronElectronInteraction interaction = fModelParams.fPositronInteractions[decayIndex]; + + switch (interaction) + { + case PositronElectronInteraction::kParaPs: + return GateEmittedGammaInformation::SourceKind::ParaPositronium; + case PositronElectronInteraction::kOrthoPs: + return GateEmittedGammaInformation::SourceKind::OrthoPositronium; + case PositronElectronInteraction::kDirect: + return GateEmittedGammaInformation::SourceKind::DirectAnnihilation; + default: + break; + } + return GateEmittedGammaInformation::SourceKind::NotDefined; +} + G4PrimaryParticle* GatePositroniumDecayModel::GetGammaFromDeexcitation(int decayIndex) { G4PrimaryParticle* gamma = GetSingleGamma(fModelParams.fPromptGammaEnergy[decayIndex]); GateEmittedGammaInformation* info = GetPrimaryParticleInformation( gamma, GateEmittedGammaInformation::GammaKind::Prompt ); info->SetDecayIndex( decayIndex ); + info->SetDecayModel( GetDecayModel(decayIndex) ); + info->SetSourceKind( GetSourceKind(decayIndex) ); gamma->SetUserInformation( info ); return gamma; } @@ -123,6 +151,8 @@ std::vector GatePositroniumDecayModel::GetGammasFromPositron gamma->SetPolarization( dynamic_gamma->GetPolarization() ); GateEmittedGammaInformation* info = GetPrimaryParticleInformation( gamma, GateEmittedGammaInformation::GammaKind::Annihilation ); info->SetDecayIndex( decayIndex ); + info->SetDecayModel( GetDecayModel(decayIndex) ); + info->SetSourceKind( GetSourceKind(decayIndex) ); gamma->SetUserInformation( info ); gammas[i] = gamma; } From 0b3916f8c6b0ec858de54ff2aacd4a0d90345250 Mon Sep 17 00:00:00 2001 From: wkrzemien Date: Fri, 8 May 2026 13:42:15 +0200 Subject: [PATCH 142/144] Remove unused decayModel arguments Also, remove relevant tests and fix method signatures. --- .../GatePositroniumDecayParamsGenerator.hh | 3 +- .../include/GatePositroniumSourceMessenger.hh | 2 +- .../GatePositroniumDecayParamsGenerator.cc | 158 ++++++------------ .../src/GatePositroniumSourceMessenger.cc | 4 +- ...st_GatePositroniumDecayParamsGenerator.cpp | 45 +---- 5 files changed, 52 insertions(+), 160 deletions(-) diff --git a/source/physics/include/GatePositroniumDecayParamsGenerator.hh b/source/physics/include/GatePositroniumDecayParamsGenerator.hh index 2932fc2dd..77d92b2d5 100644 --- a/source/physics/include/GatePositroniumDecayParamsGenerator.hh +++ b/source/physics/include/GatePositroniumDecayParamsGenerator.hh @@ -30,7 +30,6 @@ class GatePositroniumDecayParamsGenerator { public: - enum DecayModel {kParaPositronium, kOrthoPositronium, kPositronium}; GatePositroniumDecayParamsGenerator()=default; virtual ~GatePositroniumDecayParamsGenerator()=default; @@ -44,7 +43,7 @@ public: void SetPositroniumFraction(const std::vector& positroniumFractions); void SetMeanPositronRange(const std::vector& meanPositronRange); - PositroniumDecayModelParams generatePositroniumDecayParams(DecayModel model=kPositronium) const; + PositroniumDecayModelParams generatePositroniumDecayParams() const; void validatePositroniumDecayParams(const PositroniumDecayModelParams& params) const; diff --git a/source/physics/include/GatePositroniumSourceMessenger.hh b/source/physics/include/GatePositroniumSourceMessenger.hh index d50e17b0e..3c3e77b1b 100644 --- a/source/physics/include/GatePositroniumSourceMessenger.hh +++ b/source/physics/include/GatePositroniumSourceMessenger.hh @@ -32,7 +32,7 @@ class GatePositroniumSourceMessenger: public GateVSourceMessenger void SetNewValue(G4UIcommand *command, G4String newValue) override; - PositroniumDecayModelParams generatePositroniumDecayParams(GatePositroniumDecayParamsGenerator::DecayModel model= GatePositroniumDecayParamsGenerator::kPositronium) const; + PositroniumDecayModelParams generatePositroniumDecayParams() const; protected: void InitCommands(); diff --git a/source/physics/src/GatePositroniumDecayParamsGenerator.cc b/source/physics/src/GatePositroniumDecayParamsGenerator.cc index d4b958999..26c042d72 100644 --- a/source/physics/src/GatePositroniumDecayParamsGenerator.cc +++ b/source/physics/src/GatePositroniumDecayParamsGenerator.cc @@ -10,9 +10,6 @@ #include "GateMessageManager.hh" #include "GatePositroniumHelper.hh" #include "GatePositroniumDecayParamsGenerator.hh" -#include "GatePositroniumConstants.hh" - -using namespace GatePositroniumConstants; void GatePositroniumDecayParamsGenerator::SetElectronCaptureProbabilities(const std::vector& electronCaptureProb) { @@ -50,127 +47,66 @@ void GatePositroniumDecayParamsGenerator::SetMeanPositronRange(const std::vector } -PositroniumDecayModelParams GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams(const GatePositroniumDecayParamsGenerator::DecayModel model) const +PositroniumDecayModelParams GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams() const { PositroniumDecayModelParams params; - if (model == GatePositroniumDecayParamsGenerator::kParaPositronium) { - params.fFractions= {1}; - params.fLifetimes= {kParaPsLifetime_ns}; // [ns] - params.fDecayKind= {k2Gamma}; - params.fPositronInteractions= {kParaPs}; - if(fPromptGammaProbabilities.has_value() && fPromptGammaEnergies.has_value()) { - params.fPromptGammaProbabilities=fPromptGammaProbabilities.value(); - assert(params.fPromptGammaProbabilities.size()==1); - params.fPromptGammaEnergy=fPromptGammaEnergies.value(); - assert(params.fPromptGammaEnergy.size()==1); - } else { - params.fPromptGammaProbabilities={0.0}; - params.fPromptGammaEnergy ={0.0}; - } - - if(fMeanPositronRange.has_value()) { - params.fMeanPositronRange=fMeanPositronRange.value(); - assert(params.fMeanPositronRange.size() == 1); - params.fMeanPositronRangeEnabled={true}; - } else { - params.fMeanPositronRangeEnabled={false}; - params.fMeanPositronRange ={0.0}; - } - if(fElectronCaptureProbabilities.has_value()){ - params.fElectronCaptureProbabilities=fElectronCaptureProbabilities.value(); - } else { - params.fElectronCaptureProbabilities={0.0}; - } + GatePositroniumHelper positronHelper; + if(fPositroniumFractions.has_value()) { + params.fFractions=positronHelper.NormalizeFractions(fPositroniumFractions.value()); + } else { + GateError("GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams: Positronium decay fractions are not set"); } - if (model == GatePositroniumDecayParamsGenerator::kOrthoPositronium) { - params.fFractions= {1}; - params.fLifetimes= {kOrthoPsMeanLifetime_ns}; // [ns] - params.fDecayKind= {k3Gamma}; - params.fPositronInteractions={kOrthoPs}; - if(fPromptGammaProbabilities.has_value() && fPromptGammaEnergies.has_value()) { - params.fPromptGammaProbabilities=fPromptGammaProbabilities.value(); - assert(params.fPromptGammaProbabilities.size()==1); - params.fPromptGammaEnergy=fPromptGammaEnergies.value(); - assert(params.fPromptGammaEnergy.size()==1); - } else { - params.fPromptGammaProbabilities={0.0}; - params.fPromptGammaEnergy ={0.0}; - } - if(fMeanPositronRange.has_value()) { - params.fMeanPositronRange=fMeanPositronRange.value(); - assert(params.fMeanPositronRange.size() == 1); - params.fMeanPositronRangeEnabled={true}; - } else { - params.fMeanPositronRangeEnabled={false}; - params.fMeanPositronRange ={0.0}; - } - if(fElectronCaptureProbabilities.has_value()){ - params.fElectronCaptureProbabilities=fElectronCaptureProbabilities.value(); - } else { - params.fElectronCaptureProbabilities={0.0}; - } + if(fPositroniumLifetimes.has_value()) { + params.fLifetimes=fPositroniumLifetimes.value(); + } else { + GateError("GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams: Positronium lifetimes are not set"); } - if (model == GatePositroniumDecayParamsGenerator::kPositronium) { - GatePositroniumHelper positronHelper; - if(fPositroniumFractions.has_value()) { - params.fFractions=positronHelper.NormalizeFractions(fPositroniumFractions.value()); - } else { - GateError("GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams: Positronium decay fractions are not set"); - } - - if(fPositroniumLifetimes.has_value()) { - params.fLifetimes=fPositroniumLifetimes.value(); - } else { - GateError("GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams: Positronium lifetimes are not set"); - } - - if(fPositronInteractions.has_value()) { - params.fPositronInteractions=fPositronInteractions.value(); - } else if (!fDecayKinds.has_value()) { - GateError("GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams: Positronium interactions are not set and decay kinds are also empty"); - } + if(fPositronInteractions.has_value()) { + params.fPositronInteractions=fPositronInteractions.value(); + } else if (!fDecayKinds.has_value()) { + GateError("GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams: Positronium interactions are not set and decay kinds are also empty"); + } - if(fPromptGammaProbabilities.has_value()) { - params.fPromptGammaProbabilities=fPromptGammaProbabilities.value(); - } else { - GateError("GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams: Positronium prompt gamma probabilities are not set"); - } + if(fPromptGammaProbabilities.has_value()) { + params.fPromptGammaProbabilities=fPromptGammaProbabilities.value(); + } else { + GateError("GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams: Positronium prompt gamma probabilities are not set"); + } - if(fPromptGammaEnergies.has_value()) { - params.fPromptGammaEnergy=fPromptGammaEnergies.value(); - } else { - GateError("GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams: Prompt gamma energies are not set"); - } + if(fPromptGammaEnergies.has_value()) { + params.fPromptGammaEnergy=fPromptGammaEnergies.value(); + } else { + GateError("GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams: Prompt gamma energies are not set"); + } - auto nElements = fPositroniumFractions.value().size(); - if(fElectronCaptureProbabilities.has_value()) { - params.fElectronCaptureProbabilities=fElectronCaptureProbabilities.value(); - } else { - params.fElectronCaptureProbabilities.assign(nElements, 0.0); - } + auto nElements = fPositroniumFractions.value().size(); + if(fElectronCaptureProbabilities.has_value()) { + params.fElectronCaptureProbabilities=fElectronCaptureProbabilities.value(); + } else { + params.fElectronCaptureProbabilities.assign(nElements, 0.0); + } - if(fDecayKinds.has_value()) { - params.fDecayKind=fDecayKinds.value(); - } else if (!params.fPositronInteractions.empty() && !params.fLifetimes.empty() && !params.fFractions.empty()) { - params = positronHelper.CalculateFractionsFromLifetimes(params); - } else { - GateError("GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams: Positronium decay kinds are not set and one or more sets that can calculate them (positronInteractions, lifetimes or fractions) is/are empty"); - } + if(fDecayKinds.has_value()) { + params.fDecayKind=fDecayKinds.value(); + } else if (!params.fPositronInteractions.empty() && !params.fLifetimes.empty() && !params.fFractions.empty()) { + params = positronHelper.CalculateFractionsFromLifetimes(params); + } else { + GateError("GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams: Positronium decay kinds are not set and one or more sets that can calculate them (positronInteractions, lifetimes or fractions) is/are empty"); + } - if(fMeanPositronRange.has_value()) { - params.fMeanPositronRangeEnabled.assign(nElements, true); - params.fMeanPositronRange=fMeanPositronRange.value(); - } else { - params.fMeanPositronRangeEnabled.assign(nElements, false); - params.fMeanPositronRange.assign(nElements, 0.0); - } + if(fMeanPositronRange.has_value()) { + params.fMeanPositronRangeEnabled.assign(nElements, true); + params.fMeanPositronRange=fMeanPositronRange.value(); + } else { + params.fMeanPositronRangeEnabled.assign(nElements, false); + params.fMeanPositronRange.assign(nElements, 0.0); } - validatePositroniumDecayParams(params); +validatePositroniumDecayParams(params); - return params; +return params; } void GatePositroniumDecayParamsGenerator::validatePositroniumDecayParams(const PositroniumDecayModelParams& params) const @@ -190,7 +126,7 @@ void GatePositroniumDecayParamsGenerator::validatePositroniumDecayParams(const P } constexpr double kProb_of_no_particle_limit = 0.9; for (int i = 0; i < ref_param_number; i++) { - auto electron_capture_prob = params.fElectronCaptureProbabilities[i]; + auto electron_capture_prob = params.fElectronCaptureProbabilities[i]; auto no_prompt_prob = 1 -params.fPromptGammaProbabilities[i]; if (electron_capture_prob *no_prompt_prob >= kProb_of_no_particle_limit) { GateWarning("The probability of 0 prompt emission times probability of electron capture (no anihillation) is larger than 90%. Please check channel definitions! Otherwise simulations can take a lot of time"); diff --git a/source/physics/src/GatePositroniumSourceMessenger.cc b/source/physics/src/GatePositroniumSourceMessenger.cc index 41dacf07e..0e3abb8bd 100644 --- a/source/physics/src/GatePositroniumSourceMessenger.cc +++ b/source/physics/src/GatePositroniumSourceMessenger.cc @@ -189,7 +189,7 @@ void GatePositroniumSourceMessenger::SetNewValue(G4UIcommand *command, G4String } } -PositroniumDecayModelParams GatePositroniumSourceMessenger::generatePositroniumDecayParams(GatePositroniumDecayParamsGenerator::DecayModel model) const +PositroniumDecayModelParams GatePositroniumSourceMessenger::generatePositroniumDecayParams() const { - return fParamGenerator.generatePositroniumDecayParams(model); + return fParamGenerator.generatePositroniumDecayParams(); } diff --git a/tests/test_GatePositroniumDecayParamsGenerator.cpp b/tests/test_GatePositroniumDecayParamsGenerator.cpp index 40c06d60a..f6c5f9e95 100644 --- a/tests/test_GatePositroniumDecayParamsGenerator.cpp +++ b/tests/test_GatePositroniumDecayParamsGenerator.cpp @@ -25,47 +25,7 @@ void initializeGateRunManager(GateRunManager* runManager) runManager->InitializeAll(); } -bool test_para_default() -{ - std::cout << "test_para_default" << std::endl; - GatePositroniumDecayParamsGenerator gen; - auto p = gen.generatePositroniumDecayParams(GatePositroniumDecayParamsGenerator::kParaPositronium); - - CHECK(p.fFractions[0] == 1, "para fraction != 1"); - CHECK(p.fLifetimes[0] == 0.1244f, "para lifetime wrong"); - CHECK(p.fDecayKind[0] == k2Gamma, "para decay kind wrong"); - CHECK(p.fPromptGammaProbabilities[0] == 0.0, "para prompt probability wrong"); - CHECK(p.fPromptGammaEnergy[0] == 0.0f, "para prompt energy wrong"); - return true; -} -bool test_ortho_default() -{ - std::cout << "test_ortho_default" << std::endl; - GatePositroniumDecayParamsGenerator gen; - auto p = gen.generatePositroniumDecayParams(GatePositroniumDecayParamsGenerator::kOrthoPositronium); - - CHECK(p.fFractions[0] == 1, "ortho fraction != 1"); - CHECK(p.fLifetimes[0] == 142.0f, "ortho lifetime wrong"); - CHECK(p.fDecayKind[0] == k3Gamma, "ortho decay kind wrong"); - CHECK(p.fPromptGammaProbabilities[0] == 0.0, "ortho prompt probability wrong"); - CHECK(p.fPromptGammaEnergy[0] == 0.0f, "ortho prompt energy wrong"); - return true; -} - -bool test_para_prompt_gamma() -{ - std::cout << "test_prompt_gamma" << std::endl; - GatePositroniumDecayParamsGenerator gen; - gen.SetPromptGammaProbabilities({1.0}); - gen.SetPromptGammaEnergies({1.2f}); - - auto p = gen.generatePositroniumDecayParams(GatePositroniumDecayParamsGenerator::kParaPositronium); - - CHECK(p.fPromptGammaProbabilities[0] == 1.0, "para prompt photon probability wrong"); - CHECK(p.fPromptGammaEnergy[0] == 1.2f, "prompt photon energy wrong"); - return true; -} bool test_positronium_custom() { @@ -79,7 +39,7 @@ bool test_positronium_custom() gen.SetPromptGammaEnergies({0.0f, 1.2f}); gen.SetElectronCaptureProbabilities({0.0, 0.5}); - auto p = gen.generatePositroniumDecayParams(GatePositroniumDecayParamsGenerator::kPositronium); + auto p = gen.generatePositroniumDecayParams(); CHECK(p.fFractions[0] == 0.3f, "PS fraction mismatch"); CHECK(p.fLifetimes[0] == 0.12f, "PS lifetime mismatch"); @@ -145,9 +105,6 @@ int main() initializeGateRunManager(runManager.get()); bool res = true; - res &= test_para_default(); - res &= test_ortho_default(); - res &= test_para_prompt_gamma(); res &= test_positronium_custom(); res &= test_missing_params_should_fail(); res &= test_vector_size_mismatch(); From bfde9605642fa0916a0c1b77e156a06d726eb31e Mon Sep 17 00:00:00 2001 From: MateuszBala Date: Fri, 8 May 2026 14:18:36 +0200 Subject: [PATCH 143/144] feat: add multiphoton analysis --- source/digits_hits/include/GateHit.hh | 4 + .../include/GateMultiPhotonAnalysis.hh | 166 +++++++ .../include/GateMultiPhotonAnalysisHelpers.hh | 117 +++++ .../GateMultiPhotonAnalysisMessenger.hh | 61 +++ .../GateMultiPhotonTrajectoryNavigator.hh | 139 ++++++ ...teMultiPhotonTrajectoryNavigatorHelpers.hh | 212 +++++++++ source/digits_hits/include/GateOutputMgr.hh | 10 + source/digits_hits/src/GateHit.cc | 3 +- .../src/GateMultiPhotonAnalysis.cc | 434 ++++++++++++++++++ .../src/GateMultiPhotonAnalysisMessenger.cc | 39 ++ .../src/GateMultiPhotonTrajectoryNavigator.cc | 180 ++++++++ source/digits_hits/src/GateOutputMgr.cc | 24 +- source/general/include/GateRootDefs.hh | 1 + source/general/src/GateRootDefs.cc | 10 + source/physics/include/GatePositronium.hh | 4 +- 15 files changed, 1397 insertions(+), 7 deletions(-) create mode 100644 source/digits_hits/include/GateMultiPhotonAnalysis.hh create mode 100644 source/digits_hits/include/GateMultiPhotonAnalysisHelpers.hh create mode 100644 source/digits_hits/include/GateMultiPhotonAnalysisMessenger.hh create mode 100644 source/digits_hits/include/GateMultiPhotonTrajectoryNavigator.hh create mode 100644 source/digits_hits/include/GateMultiPhotonTrajectoryNavigatorHelpers.hh create mode 100644 source/digits_hits/src/GateMultiPhotonAnalysis.cc create mode 100644 source/digits_hits/src/GateMultiPhotonAnalysisMessenger.cc create mode 100644 source/digits_hits/src/GateMultiPhotonTrajectoryNavigator.cc diff --git a/source/digits_hits/include/GateHit.hh b/source/digits_hits/include/GateHit.hh index ccf8079f3..6bbcaacc7 100644 --- a/source/digits_hits/include/GateHit.hh +++ b/source/digits_hits/include/GateHit.hh @@ -78,6 +78,7 @@ public: G4int m_nCrystalCompton; // # of compton processes in the crystal occurred to the photon G4int m_nPhantomRayleigh; // # of Rayleigh processes in the phantom occurred to the photon G4int m_nCrystalRayleigh; // # of Rayleigh processes in the crystal occurred to the photon + G4int m_nInteractions; // # of non-Transportation interactions in phantom + crystal; -1 when not computed G4String m_comptonVolumeName; // name of the volume of the last (if any) compton scattering G4String m_RayleighVolumeName; // name of the volume of the last (if any) Rayleigh scattering G4int m_primaryID; // primary that caused the hit @@ -171,6 +172,9 @@ public: inline void SetNCrystalRayleigh(G4int j) { m_nCrystalRayleigh = j; } inline G4int GetNCrystalRayleigh() const { return m_nCrystalRayleigh; } + inline void SetNInteractions(G4int j) { m_nInteractions = j; } + inline G4int GetNInteractions() const { return m_nInteractions; } + inline void SetComptonVolumeName(G4String name) { m_comptonVolumeName = name; } inline G4String GetComptonVolumeName() const { return m_comptonVolumeName; } diff --git a/source/digits_hits/include/GateMultiPhotonAnalysis.hh b/source/digits_hits/include/GateMultiPhotonAnalysis.hh new file mode 100644 index 000000000..cb8ef7ab1 --- /dev/null +++ b/source/digits_hits/include/GateMultiPhotonAnalysis.hh @@ -0,0 +1,166 @@ +/*---------------------- + Copyright (C): OpenGATE Collaboration + +This software is distributed under the terms +of the GNU Lesser General Public Licence (LGPL) +See LICENSE.md for further details +----------------------*/ + +#ifndef GateMultiPhotonAnalysis_h +#define GateMultiPhotonAnalysis_h + +#include "GateMultiPhotonAnalysisHelpers.hh" +#include "GateVOutputModule.hh" + +#include + +/** Authors: Wojciech Krzemień, Mateusz Bała and Kamil Dulski + * Emails: wojciech.krzemien@ncbj.gov.pl, mateusz.bala@ncbj.gov.pl and kamil.dulski@gmail.com + * Organization: National Centre For Nuclear Research (NCBJ, https://ncbj.gov.pl), Poland + * Developed within the IMPET project: https://pet.ncbj.gov.pl/ + * About: Multi-photon analysis output-module interface with event processing callbacks and trajectory-missing policies. + **/ + +class GateVVolume; +class GateMultiPhotonTrajectoryNavigator; +class GateMultiPhotonAnalysisMessenger; + +/** + * @brief Multi-photon variant of event-end hit analysis. + * + * The class mirrors the legacy GateAnalysis role while removing the hardcoded + * two-photon mapping assumption. It aggregates per-photon interaction counters + * and assigns resolved values to crystal hits. + * + * Notes: + * - Iteration 1 supports TrackingMode::kBoth only. + * - Tracker/detector split mode handling is explicitly deferred. + */ +class GateMultiPhotonAnalysis : public GateVOutputModule { + public: + /** @brief Policy for handling missing trajectory containers. */ + enum MissingTrajectoryPolicy { + kStrict, + kResilient + }; + + /** + * @brief Constructs the output module. + * + * Args: + * name: Output module name. + * outputMgr: Output manager owner. + * digiMode: Runtime/offline mode. + */ + GateMultiPhotonAnalysis(const G4String &name, GateOutputMgr *outputMgr, DigiMode digiMode); + + virtual ~GateMultiPhotonAnalysis(); + + /** + * @brief Returns module file name placeholder. + * + * Returns: + * Dummy name since this module does not own a file. + */ + const G4String &GiveNameOfFile(); + + /** @brief Acquisition-begin callback. */ + void RecordBeginOfAcquisition(); + /** @brief Acquisition-end callback. */ + void RecordEndOfAcquisition(); + /** @brief Run-begin callback. */ + void RecordBeginOfRun(const G4Run *); + /** @brief Run-end callback. */ + void RecordEndOfRun(const G4Run *); + /** @brief Event-begin callback. */ + void RecordBeginOfEvent(const G4Event *); + + /** + * @brief Performs event-end multi-photon aggregation and hit assignment. + * + * Args: + * event: Event carrying trajectory and hit collections. + */ + void RecordEndOfEvent(const G4Event *event); + + /** + * @brief Step callback (unused by this module). + * + * Args: + * v: Current volume. + * step: Current step. + */ + void RecordStepWithVolume(const GateVVolume *v, const G4Step *step); + + /** @brief Voxel callback (not used). */ + void RecordVoxels(GateVGeometryVoxelStore *) {} + + /** + * @brief Sets module and navigator verbosity. + * + * Args: + * val: Verbose level. + */ + virtual void SetVerboseLevel(G4int val); + + /** + * @brief Sets policy for missing trajectory container handling. + * + * Args: + * policy: Desired behavior policy. + */ + void SetMissingTrajectoryPolicy(MissingTrajectoryPolicy policy); + + /** + * @brief Parses and applies missing trajectory policy from text. + * + * Args: + * policyName: Policy name (`strict` or `resilient`). + */ + void SetMissingTrajectoryPolicyFromString(const G4String &policyName); + + /** + * @brief Returns current missing trajectory policy. + * + * Returns: + * Active policy value. + */ + MissingTrajectoryPolicy GetMissingTrajectoryPolicy() const; + + /** + * @brief Returns current missing trajectory policy as string. + * + * Returns: + * `strict` or `resilient`. + */ + G4String GetMissingTrajectoryPolicyName() const; + + private: + /** + * @brief Checks whether current tracking mode is supported. + * + * Args: + * tracking_mode_code: Integer value of TrackingMode. + * + * Returns: + * True when event processing should continue for the current mode. + */ + bool IsTrackingModeSupported(int tracking_mode_code) const; + + /** + * @brief Returns policy value for legacy photonID field. + * + * Returns: + * Always 0 in multiphoton mode (explicit policy for iteration 1). + */ + int ResolveLegacyPhotonIDPolicy() const; + + GateMultiPhotonTrajectoryNavigator *m_trajectoryNavigator; + GateMultiPhotonAnalysisMessenger *m_messenger; + G4String m_noFileName; + MissingTrajectoryPolicy m_missingTrajectoryPolicy; + G4int m_missingTrajectoryEventCount; + G4int m_missingTrajectoryWithHitsCount; +}; + +#endif diff --git a/source/digits_hits/include/GateMultiPhotonAnalysisHelpers.hh b/source/digits_hits/include/GateMultiPhotonAnalysisHelpers.hh new file mode 100644 index 000000000..ef10a140a --- /dev/null +++ b/source/digits_hits/include/GateMultiPhotonAnalysisHelpers.hh @@ -0,0 +1,117 @@ +/** ---------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + ----------------------*/ + +#ifndef GateMultiPhotonAnalysisHelpers_h +#define GateMultiPhotonAnalysisHelpers_h + +#include +#include + +/** Authors: Wojciech Krzemień, Mateusz Bała and Kamil Dulski + * Emails: wojciech.krzemien@ncbj.gov.pl, mateusz.bala@ncbj.gov.pl and kamil.dulski@gmail.com + * Organization: National Centre For Nuclear Research (NCBJ, https://ncbj.gov.pl), Poland + * Developed within the IMPET project: https://pet.ncbj.gov.pl/ + * About namespace: Helper functions for multi-photon analysis, including a structure to hold gamma interaction statistics and functions to accumulate statistics based on interaction process names. + **/ +namespace MultiPhotonAnalysisHelpers { + + /** @brief Aggregated interaction counters for a single tracked gamma. */ + struct GammaStatistics { + int phantomCompton = 0; + int phantomRayleigh = 0; + int crystalCompton = 0; + int crystalRayleigh = 0; + int phantomInteractions = 0; + int crystalInteractions = 0; + std::string comptonVolumeName = "NULL"; + std::string rayleighVolumeName = "NULL"; + }; + + /** @brief Interaction process classes used by multi-photon analysis. */ + enum class InteractionProcess { + Unknown, + Compton, + Rayleigh, + Transportation + }; + + /** + * @brief Classifies process name into a compact process category. + * + * Args: + * processName: Geant4 process name. + * + * Returns: + * Recognized interaction category or Unknown. + */ + constexpr InteractionProcess GetInteractionProcess(const std::string_view processName) { + if (processName.size() >= 4) { + if (processName.substr(0, 4) == "Tran") { + return InteractionProcess::Transportation; + } + if (processName.substr(0, 4) == "Comp" || processName.substr(0, 4) == "comp") { + return InteractionProcess::Compton; + } + if (processName.substr(0, 4) == "Rayl" || processName.substr(0, 4) == "rayl") { + return InteractionProcess::Rayleigh; + } + } + return InteractionProcess::Unknown; + } + + /** + * @brief Accumulates crystal-side interaction statistics. + * + * Args: + * processName: Geant4 process name at crystal hit. + * gammaStatistics: Statistics object to update. + */ + inline void AccumulateCrystal(const std::string_view processName, GammaStatistics& gammaStatistics) { + const InteractionProcess process = GetInteractionProcess(processName); + if (process != InteractionProcess::Transportation) { + gammaStatistics.crystalInteractions++; + switch (process) { + case InteractionProcess::Compton: + gammaStatistics.crystalCompton++; + break; + case InteractionProcess::Rayleigh: + gammaStatistics.crystalRayleigh++; + break; + default: + break; + } + } + } + + /** + * @brief Accumulates phantom-side interaction statistics. + * + * Args: + * processName: Geant4 process name at phantom hit. + * gammaStatistics: Statistics object to update. + */ + inline void AccumulatePhantom(const std::string_view processName, GammaStatistics& gammaStatistics) { + const InteractionProcess process = GetInteractionProcess(processName); + if (process != InteractionProcess::Transportation) { + gammaStatistics.phantomInteractions++; + switch (process) { + case InteractionProcess::Compton: + gammaStatistics.phantomCompton++; + break; + case InteractionProcess::Rayleigh: + gammaStatistics.phantomRayleigh++; + break; + default: + break; + } + } + } + +}// namespace MultiPhotonAnalysisHelpers + + +#endif // GateMultiPhotonAnalysisHelpers_h diff --git a/source/digits_hits/include/GateMultiPhotonAnalysisMessenger.hh b/source/digits_hits/include/GateMultiPhotonAnalysisMessenger.hh new file mode 100644 index 000000000..8d3734b38 --- /dev/null +++ b/source/digits_hits/include/GateMultiPhotonAnalysisMessenger.hh @@ -0,0 +1,61 @@ +/*---------------------- + Copyright (C): OpenGATE Collaboration + +This software is distributed under the terms +of the GNU Lesser General Public Licence (LGPL) +See LICENSE.md for further details +----------------------*/ + +#ifndef GateMultiPhotonAnalysisMessenger_h +#define GateMultiPhotonAnalysisMessenger_h 1 + +#include "GateOutputModuleMessenger.hh" + +/** Authors: Wojciech Krzemień, Mateusz Bała and Kamil Dulski + * Emails: wojciech.krzemien@ncbj.gov.pl, mateusz.bala@ncbj.gov.pl and kamil.dulski@gmail.com + * Organization: National Centre For Nuclear Research (NCBJ, https://ncbj.gov.pl), Poland + * Developed within the IMPET project: https://pet.ncbj.gov.pl/ + * About: UI messenger for configuring and controlling the multi-photon analysis output module. + **/ + +class G4UIcmdWithAString; + +class GateMultiPhotonAnalysis; + +/** + * @brief Messenger for GateMultiPhotonAnalysis output module. + * + * The messenger inherits standard output-module commands (`enable`, `disable`, + * `verbose`, `describe`) and adds `setMissingTrajectoryPolicy` for configuring + * how missing trajectory containers are handled. + */ +class GateMultiPhotonAnalysisMessenger : public GateOutputModuleMessenger { + public: + /** + * @brief Creates a messenger bound to GateMultiPhotonAnalysis. + * + * Args: + * gateMultiPhotonAnalysis: Owner module. + */ + explicit GateMultiPhotonAnalysisMessenger(GateMultiPhotonAnalysis *gateMultiPhotonAnalysis); + + ~GateMultiPhotonAnalysisMessenger(); + + /** + * @brief Handles UI command updates. + * + * Args: + * command: UI command. + * newValue: Command value. + */ + virtual void SetNewValue(G4UIcommand *command, G4String newValue); + + protected: + /** @brief Bound multi-photon analysis module instance. */ + GateMultiPhotonAnalysis *m_gateMultiPhotonAnalysis; + + /** @brief UI command for setting missing trajectory policy. */ + G4UIcmdWithAString *m_setMissingTrajectoryPolicyCmd; +}; + +#endif diff --git a/source/digits_hits/include/GateMultiPhotonTrajectoryNavigator.hh b/source/digits_hits/include/GateMultiPhotonTrajectoryNavigator.hh new file mode 100644 index 000000000..bf2d71a41 --- /dev/null +++ b/source/digits_hits/include/GateMultiPhotonTrajectoryNavigator.hh @@ -0,0 +1,139 @@ +/*---------------------- + Copyright (C): OpenGATE Collaboration + +This software is distributed under the terms +of the GNU Lesser General Public Licence (LGPL) +See LICENSE.md for further details +----------------------*/ + +#ifndef GateMultiPhotonTrajectoryNavigator_h +#define GateMultiPhotonTrajectoryNavigator_h + +#include "G4ThreeVector.hh" + +#include +#include +#include + +/** Authors: Wojciech Krzemień, Mateusz Bała and Kamil Dulski + * Emails: wojciech.krzemien@ncbj.gov.pl, mateusz.bala@ncbj.gov.pl and kamil.dulski@gmail.com + * Organization: National Centre For Nuclear Research (NCBJ, https://ncbj.gov.pl), Poland + * Developed within the IMPET project: https://pet.ncbj.gov.pl/ + * About: Event-level trajectory index and ancestry resolution API for multi-photon analysis. + **/ + +class G4TrajectoryContainer; + +/** + * @brief Resolves multi-photon trajectory ancestry for one event. + * + * This class builds per-event lookup tables that map track IDs to parent IDs + * and provides cached ancestry queries for ancestor photon and primary track + * resolution. + */ +class GateMultiPhotonTrajectoryNavigator { + public: + /** + * @brief Constructs an empty event-scoped navigator. + */ + GateMultiPhotonTrajectoryNavigator(); + + virtual ~GateMultiPhotonTrajectoryNavigator(); + + /** + * @brief Sets trajectory container for current event and resets caches. + * + * Args: + * trajectoryContainer: Event trajectory container. + */ + void SetTrajectoryContainer(G4TrajectoryContainer *trajectoryContainer); + + /** + * @brief Builds all lookup indices for current trajectory container. + */ + void BuildIndex(); + + /** + * @brief Returns source position inferred from source track. + * + * Returns: + * Source position when available, or default vector otherwise. + */ + G4ThreeVector FindSourcePosition() const; + + /** + * @brief Returns list of reference photon track IDs for current event. + * + * Returns: + * Reference photon track identifiers. + */ + std::vector FindReferencePhotonTrackIDs() const; + + /** + * @brief Resolves ancestor reference photon for a track. + * + * Args: + * trackID: Track identifier to resolve. + * + * Returns: + * Ancestor reference photon track ID, or 0 if unresolved. + */ + int FindAncestorPhotonTrackID(int trackID) const; + + /** + * @brief Resolves primary track for a track. + * + * Args: + * trackID: Track identifier to resolve. + * + * Returns: + * Primary track ID, or 0 if unresolved. + */ + int FindPrimaryTrackID(int trackID) const; + + /** + * @brief Sets verbose level used for diagnostic warnings. + * + * Args: + * v: Verbose level. + */ + void SetVerboseLevel(int v); + + private: + /** + * @brief Clears all internal caches and indices. + */ + void Reset(); + + /** + * @brief Builds reference photon set according to legacy-compatible policy. + */ + void BuildReferencePhotonSet(); + + /** @brief PDG code for positron. */ + const int kPositronPDG = -11; + + /** @brief PDG code for photon. */ + const int kPhotonPDG = 22; + + G4TrajectoryContainer *m_tc; + + std::unordered_map m_parentByTrack; + std::unordered_map m_pdgByTrack; + std::unordered_map m_chargeByTrack; + std::unordered_set m_referencePhotonTrackIDs; + + mutable std::unordered_map m_ancestorPhotonCache; + mutable std::unordered_map m_primaryCache; + + int m_positronTrackID; + int m_ionID; + int m_verboseLevel; + + bool m_hasSourcePosition; + double m_sourceX; + double m_sourceY; + double m_sourceZ; +}; + +#endif diff --git a/source/digits_hits/include/GateMultiPhotonTrajectoryNavigatorHelpers.hh b/source/digits_hits/include/GateMultiPhotonTrajectoryNavigatorHelpers.hh new file mode 100644 index 000000000..41fe708b2 --- /dev/null +++ b/source/digits_hits/include/GateMultiPhotonTrajectoryNavigatorHelpers.hh @@ -0,0 +1,212 @@ +/*---------------------- + Copyright (C): OpenGATE Collaboration + +This software is distributed under the terms +of the GNU Lesser General Public Licence (LGPL) +See LICENSE.md for further details +----------------------*/ + +#ifndef GateMultiPhotonTrajectoryNavigatorHelpers_h +#define GateMultiPhotonTrajectoryNavigatorHelpers_h + +#include +#include +#include + +/** Authors: Wojciech Krzemień, Mateusz Bała and Kamil Dulski + * Emails: wojciech.krzemien@ncbj.gov.pl, mateusz.bala@ncbj.gov.pl and kamil.dulski@gmail.com + * Organization: National Centre For Nuclear Research (NCBJ, https://ncbj.gov.pl), Poland + * Developed within the IMPET project: https://pet.ncbj.gov.pl/ + * About: Pure helper algorithms for resolving ancestor and primary tracks from parent-track maps. + **/ + +namespace MultiphotonTrajectoryResolver { + +/** + * @brief Resolves ancestor and primary track identifiers from parent links. + * + * This helper is intentionally independent from Geant4 trajectory classes so + * that algorithmic behavior can be unit-tested with synthetic graphs. + */ + /** + * @brief Marks all visited tracks as unresolved in cache. + * + * Args: + * visited: Traversed track IDs. + * cache: Optional cache to update. + */ + inline void clean_cache(const std::vector &visited, std::unordered_map *cache) { + if (cache) { + for (const int v : visited) { + (*cache)[v] = 0; + } + } + } + + /** + * @brief Tries to resolve current track using cache and propagates result. + * + * Args: + * current: Current track ID being resolved. + * visited: Traversed track IDs. + * cache: Optional memoization cache. + * resolved: Output resolved track ID. + * + * Returns: + * True when cached resolution was found. + */ + inline bool try_get_cached_resolution( + int current, + const std::vector &visited, + std::unordered_map *cache, + int *resolved) { + if (!cache || !resolved) { + return false; + } + + const auto it_cache = cache->find(current); + if (it_cache == cache->end()) { + return false; + } + + *resolved = it_cache->second; + for (const int v : visited) { + (*cache)[v] = *resolved; + } + return true; + } + + /** + * @brief Writes resolved track ID for current and visited nodes. + * + * Args: + * current: Current track ID. + * visited: Traversed track IDs. + * cache: Optional memoization cache. + * resolved: Resolved track ID to store. + */ + inline void cache_resolution_for_current_and_visited( + int current, + const std::vector &visited, + std::unordered_map *cache, + int resolved) { + if (!cache) { + return; + } + + (*cache)[current] = resolved; + for (const int v : visited) { + (*cache)[v] = resolved; + } + } + + /** + * @brief Resolves the nearest ancestor belonging to a reference photon set. + * + * Args: + * track_id: Start track identifier to resolve. + * parent_by_track: Mapping track -> parent track. + * reference_photon_track_ids: Set of accepted ancestor photon tracks. + * cache: Optional memoization cache for resolved ancestors. + * max_steps: Optional traversal guard; <=0 enables auto-guard. + * + * Returns: + * Matching ancestor photon track ID, or 0 when unresolved. + */ + inline int ResolveAncestorPhotonTrackID( + int track_id, + const std::unordered_map &parent_by_track, + const std::unordered_set &reference_photon_track_ids, + std::unordered_map *cache, + int max_steps = -1) { + if (track_id <= 0) { + return 0; + } + + std::vector visited; + int current = track_id; + int resolved = 0; + const int guard = max_steps > 0 ? max_steps : static_cast(parent_by_track.size()) + 2; + + for (int step = 0; step < guard; ++step) { + if (try_get_cached_resolution(current, visited, cache, &resolved)) { + return resolved; + } + + if (reference_photon_track_ids.find(current) != reference_photon_track_ids.end()) { + cache_resolution_for_current_and_visited(current, visited, cache, current); + return current; + } + + visited.push_back(current); + + const auto it_parent = parent_by_track.find(current); + if (it_parent == parent_by_track.end()) { + break; + } + + current = it_parent->second; + if (current == 0) { + break; + } + } + + clean_cache(visited, cache); + + return 0; + } + + /** + * @brief Resolves the primary track ID for a given track. + * + * Args: + * track_id: Start track identifier to resolve. + * parent_by_track: Mapping track -> parent track. + * cache: Optional memoization cache for primary track IDs. + * max_steps: Optional traversal guard; <=0 enables auto-guard. + * + * Returns: + * Primary track ID, or 0 when unresolved. + */ + inline int ResolvePrimaryTrackID( + int track_id, + const std::unordered_map &parent_by_track, + std::unordered_map *cache, + int max_steps = -1) { + if (track_id <= 0) { + return 0; + } + + std::vector visited; + int current = track_id; + int resolved = 0; + const int guard = max_steps > 0 ? max_steps : static_cast(parent_by_track.size()) + 2; + + for (int step = 0; step < guard; ++step) { + if (try_get_cached_resolution(current, visited, cache, &resolved)) { + return resolved; + } + + visited.push_back(current); + + const auto it_parent = parent_by_track.find(current); + if (it_parent == parent_by_track.end()) { + break; + } + + const int parent = it_parent->second; + if (parent == 0) { + cache_resolution_for_current_and_visited(current, visited, cache, current); + return current; + } + + current = parent; + } + + clean_cache(visited, cache); + + return 0; + } +} // namespace MultiphotonTrajectoryResolver + +#endif diff --git a/source/digits_hits/include/GateOutputMgr.hh b/source/digits_hits/include/GateOutputMgr.hh index 854313bd2..a7e2b7a12 100644 --- a/source/digits_hits/include/GateOutputMgr.hh +++ b/source/digits_hits/include/GateOutputMgr.hh @@ -19,6 +19,16 @@ #include "GateDigi.hh" #include "GateCoincidenceDigi.hh" +/** + * @brief Evaluates if at least one analysis-capable output module is enabled. + */ +inline bool IsAnyAnalysisModuleEnabled( + bool analysis_enabled, + bool fastanalysis_enabled, + bool multianalysis_enabled) { + return analysis_enabled || fastanalysis_enabled || multianalysis_enabled; +} + class G4Run; class G4Step; class G4Event; diff --git a/source/digits_hits/src/GateHit.cc b/source/digits_hits/src/GateHit.cc index b152bbf1b..7f767fba0 100644 --- a/source/digits_hits/src/GateHit.cc +++ b/source/digits_hits/src/GateHit.cc @@ -30,7 +30,8 @@ GateHit::GateHit() m_systemID(-1), m_sourceEnergy(-1), m_sourcePDG(0), - m_nCrystalConv(0) + m_nInteractions(-1), + m_nCrystalConv(0) {;} //--------------------------------------------------------------------- diff --git a/source/digits_hits/src/GateMultiPhotonAnalysis.cc b/source/digits_hits/src/GateMultiPhotonAnalysis.cc new file mode 100644 index 000000000..36417e26c --- /dev/null +++ b/source/digits_hits/src/GateMultiPhotonAnalysis.cc @@ -0,0 +1,434 @@ +/*---------------------- + Copyright (C): OpenGATE Collaboration + +This software is distributed under the terms +of the GNU Lesser General Public Licence (LGPL) +See LICENSE.md for further details +----------------------*/ + +#include "GateMultiPhotonAnalysis.hh" + +#include "GateActions.hh" +#include "GateDigitizerMgr.hh" +#include "GateHit.hh" +#include "GateMultiPhotonAnalysisMessenger.hh" +#include "GateMultiPhotonTrajectoryNavigator.hh" +#include "GateOutputMgr.hh" +#include "GatePhantomHit.hh" +#include "GateRunManager.hh" +#include "GateSourceMgr.hh" + +#include "G4Event.hh" +#include "G4HCofThisEvent.hh" +#include "G4Run.hh" +#include "G4TrajectoryContainer.hh" + +#include +#include +#include +#include +#include + +namespace { + +struct TimelineEntry { + double time = 0.0; + std::size_t sequence = 0; + bool is_phantom = false; + int track_id = 0; + int ancestor_photon = 0; + GatePhantomHit *phantom_hit = 0; + GateHit *crystal_hit = 0; +}; + +struct EventContext { + G4int event_id = 0; + G4int run_id = 0; + G4int source_id = -1; + G4ThreeVector source_vertex = G4ThreeVector(-1, -1, -1); +}; + +bool EventHasProcessableHits(const std::vector &CHC_vector, + GatePhantomHitsCollection *PHC) { + if (PHC && PHC->entries() > 0) { + return true; + } + + for (std::size_t i = 0; i < CHC_vector.size(); ++i) { + GateHitsCollection *CHC = CHC_vector[i]; + if (CHC && CHC->entries() > 0) { + return true; + } + } + + return false; +} + +std::vector BuildBasePhantomTimeline( + GatePhantomHitsCollection *PHC, + GateMultiPhotonTrajectoryNavigator *trajectoryNavigator) { + std::vector basePhantomTimeline; + if (!PHC) { + return basePhantomTimeline; + } + + std::size_t sequence = 0; + const G4int NpHits = PHC->entries(); + basePhantomTimeline.reserve(static_cast(NpHits)); + + for (G4int iPHit = 0; iPHit < NpHits; ++iPHit) { + GatePhantomHit *phantomHit = (*PHC)[iPHit]; + if (!phantomHit) { + continue; + } + + const G4int trackID = phantomHit->GetTrackID(); + const int ancestorPhoton = trajectoryNavigator->FindAncestorPhotonTrackID(trackID); + if (ancestorPhoton == 0) { + continue; + } + + TimelineEntry entry; + entry.time = phantomHit->GetTime(); + entry.sequence = sequence++; + entry.is_phantom = true; + entry.track_id = trackID; + entry.ancestor_photon = ancestorPhoton; + entry.phantom_hit = phantomHit; + basePhantomTimeline.push_back(entry); + } + + return basePhantomTimeline; +} + +EventContext BuildEventContext( + const G4Event *event, + GateRunManager *runManager, + GateMultiPhotonTrajectoryNavigator *trajectoryNavigator) { + EventContext context; + context.event_id = event->GetEventID(); + context.run_id = runManager->GetCurrentRun()->GetRunID(); + + GateSourceMgr *sourceMgr = GateSourceMgr::GetInstance(); + const std::vector &sourcesForThisEvent = sourceMgr->GetSourcesForThisEvent(); + if (!sourcesForThisEvent.empty()) { + context.source_id = sourcesForThisEvent[0]->GetSourceID(); + context.source_vertex = trajectoryNavigator->FindSourcePosition(); + } + + return context; +} + +std::vector BuildTimelineForCrystalCollection( + const std::vector &basePhantomTimeline, + GateHitsCollection *CHC, + GateMultiPhotonTrajectoryNavigator *trajectoryNavigator) { + std::vector timeline = basePhantomTimeline; + std::size_t sequence = timeline.size(); + + const G4int NbHits = CHC->entries(); + timeline.reserve(basePhantomTimeline.size() + static_cast(NbHits)); + for (G4int iHit = 0; iHit < NbHits; ++iHit) { + GateHit *crystalHit = (*CHC)[iHit]; + if (!crystalHit) { + continue; + } + + const G4int trackID = crystalHit->GetTrackID(); + const int ancestorPhoton = trajectoryNavigator->FindAncestorPhotonTrackID(trackID); + if (ancestorPhoton == 0) { + continue; + } + + TimelineEntry entry; + entry.time = crystalHit->GetTime(); + entry.sequence = sequence++; + entry.is_phantom = false; + entry.track_id = trackID; + entry.ancestor_photon = ancestorPhoton; + entry.crystal_hit = crystalHit; + timeline.push_back(entry); + } + + std::sort(timeline.begin(), timeline.end(), [](const TimelineEntry &a, const TimelineEntry &b) { + if (a.time < b.time) { + return true; + } + if (a.time > b.time) { + return false; + } + return a.sequence < b.sequence; + }); + + return timeline; +} + +void ProcessTimeline( + std::vector *timeline, + GateMultiPhotonTrajectoryNavigator *trajectoryNavigator, + const EventContext &context, + int legacyPhotonIDPolicy) { + std::unordered_map runningStatsByPhotonTrackId; + runningStatsByPhotonTrackId.reserve(timeline->size()); + + for (std::size_t idx = 0; idx < timeline->size(); ++idx) { + TimelineEntry &entry = (*timeline)[idx]; + MultiPhotonAnalysisHelpers::GammaStatistics &runningStats = runningStatsByPhotonTrackId[entry.ancestor_photon]; + + if (!entry.is_phantom && entry.crystal_hit && entry.crystal_hit->GoodForAnalysis()) { + GateHit *hit = entry.crystal_hit; + const int primaryID = trajectoryNavigator->FindPrimaryTrackID(entry.track_id); + const int nInteractions = runningStats.phantomInteractions + runningStats.crystalInteractions; + + hit->SetSourceID(context.source_id); + hit->SetSourcePosition(context.source_vertex); + hit->SetNPhantomCompton(runningStats.phantomCompton); + hit->SetNPhantomRayleigh(runningStats.phantomRayleigh); + if (runningStats.comptonVolumeName != "NULL") { + hit->SetComptonVolumeName(runningStats.comptonVolumeName.c_str()); + } + if (runningStats.rayleighVolumeName != "NULL") { + hit->SetRayleighVolumeName(runningStats.rayleighVolumeName.c_str()); + } + hit->SetPhotonID(legacyPhotonIDPolicy); + hit->SetPrimaryID(primaryID); + hit->SetEventID(context.event_id); + hit->SetRunID(context.run_id); + hit->SetNCrystalCompton(runningStats.crystalCompton); + hit->SetNCrystalRayleigh(runningStats.crystalRayleigh); + // nInteractions is intentionally filled only in the multiphoton analysis path. + hit->SetNInteractions(nInteractions); + } + + if (entry.is_phantom) { + if (!entry.phantom_hit) { + continue; + } + + MultiPhotonAnalysisHelpers::AccumulatePhantom(entry.phantom_hit->GetProcess(), runningStats); + continue; + } + + if (entry.crystal_hit) { + MultiPhotonAnalysisHelpers::AccumulateCrystal(entry.crystal_hit->GetProcess(), runningStats); + } + } +} + +void RunDigitizersIfNeeded() { + GateDigitizerMgr *digitizerMgr = GateDigitizerMgr::GetInstance(); + if (!digitizerMgr->m_alreadyRun) { + if (digitizerMgr->m_recordSingles || digitizerMgr->m_recordCoincidences) { + digitizerMgr->RunDigitizers(); + digitizerMgr->RunCoincidenceSorters(); + digitizerMgr->RunCoincidenceDigitizers(); + } + } +} + +} // namespace + +GateMultiPhotonAnalysis::GateMultiPhotonAnalysis(const G4String &name, GateOutputMgr *outputMgr, DigiMode digiMode) + : GateVOutputModule(name, outputMgr, digiMode), + m_trajectoryNavigator(new GateMultiPhotonTrajectoryNavigator()), + m_messenger(new GateMultiPhotonAnalysisMessenger(this)), + m_missingTrajectoryPolicy(kResilient), + m_missingTrajectoryEventCount(0), + m_missingTrajectoryWithHitsCount(0) { + m_isEnabled = false; + SetVerboseLevel(0); +} + +GateMultiPhotonAnalysis::~GateMultiPhotonAnalysis() { + delete m_messenger; + m_messenger = 0; + delete m_trajectoryNavigator; + m_trajectoryNavigator = 0; +} + +const G4String &GateMultiPhotonAnalysis::GiveNameOfFile() { + m_noFileName = " "; + return m_noFileName; +} + +void GateMultiPhotonAnalysis::RecordBeginOfAcquisition() { + if (nVerboseLevel > 2) { + G4cout << "GateMultiPhotonAnalysis::RecordBeginOfAcquisition" << G4endl; + } +} + +void GateMultiPhotonAnalysis::RecordEndOfAcquisition() { + if (nVerboseLevel > 2) { + G4cout << "GateMultiPhotonAnalysis::RecordEndOfAcquisition" << G4endl; + } +} + +void GateMultiPhotonAnalysis::RecordBeginOfRun(const G4Run *) { + if (nVerboseLevel > 2) { + G4cout << "GateMultiPhotonAnalysis::RecordBeginOfRun" << G4endl; + } +} + +void GateMultiPhotonAnalysis::RecordEndOfRun(const G4Run *) { + if (nVerboseLevel > 2) { + G4cout << "GateMultiPhotonAnalysis::RecordEndOfRun" << G4endl; + } + + if (m_missingTrajectoryEventCount > 0) { + G4cout + << "[GateMultiPhotonAnalysis] Missing trajectory container in " + << m_missingTrajectoryEventCount + << " event(s), including " + << m_missingTrajectoryWithHitsCount + << " event(s) with processable hits. Policy=" + << GetMissingTrajectoryPolicyName() + << G4endl; + } +} + +void GateMultiPhotonAnalysis::RecordBeginOfEvent(const G4Event *) { + if (nVerboseLevel > 2) { + G4cout << "GateMultiPhotonAnalysis::RecordBeginOfEvent" << G4endl; + } +} + +void GateMultiPhotonAnalysis::RecordEndOfEvent(const G4Event *event) { + if (!event) { + return; + } + + GateRunManager *runManager = GateRunManager::GetRunManager(); + GateSteppingAction *steppingAction = (GateSteppingAction *)(runManager->GetUserSteppingAction()); + TrackingMode mode = steppingAction->GetMode(); + const int tracking_mode_code = static_cast(mode); + if (!IsTrackingModeSupported(tracking_mode_code)) { + G4Exception( + "GateMultiPhotonAnalysis::RecordEndOfEvent", + "GateMultiPhotonAnalysisUnsupportedTrackingMode", + FatalException, + "GateMultiPhotonAnalysis cannot process the current tracking mode. " + "Only TrackingMode::kBoth is supported, and continuing would skip the " + "remaining end-of-event processing, including digitizer output."); + return; + } + + std::vector CHC_vector = GetOutputMgr()->GetHitCollections(); + GatePhantomHitsCollection *PHC = GetOutputMgr()->GetPhantomHitCollection(); + + G4TrajectoryContainer *trajectoryContainer = event->GetTrajectoryContainer(); + if (!trajectoryContainer) { + ++m_missingTrajectoryEventCount; + + const bool hasProcessableHits = EventHasProcessableHits(CHC_vector, PHC); + if (hasProcessableHits) { + ++m_missingTrajectoryWithHitsCount; + } + + G4int runID = -1; + if (runManager->GetCurrentRun()) { + runID = runManager->GetCurrentRun()->GetRunID(); + } + const G4int eventID = event->GetEventID(); + + G4String details = "GateMultiPhotonAnalysis missing trajectory container for run=" + + std::to_string(runID) + + ", event=" + + std::to_string(eventID) + + ". hasProcessableHits=" + + (hasProcessableHits ? "true" : "false") + + ". Policy=" + + GetMissingTrajectoryPolicyName() + + "."; + + if (m_missingTrajectoryPolicy == kStrict) { + G4Exception( + "GateMultiPhotonAnalysis::RecordEndOfEvent", + "GateMultiPhotonAnalysisMissingTrajectoryContainer", + FatalException, + details.c_str()); + return; + } + + G4Exception( + "GateMultiPhotonAnalysis::RecordEndOfEvent", + "GateMultiPhotonAnalysisMissingTrajectoryContainer", + JustWarning, + details.c_str()); + return; + } + + m_trajectoryNavigator->SetTrajectoryContainer(trajectoryContainer); + m_trajectoryNavigator->BuildIndex(); + std::vector basePhantomTimeline = BuildBasePhantomTimeline(PHC, m_trajectoryNavigator); + const EventContext context = BuildEventContext(event, runManager, m_trajectoryNavigator); + const int legacyPhotonIDPolicy = ResolveLegacyPhotonIDPolicy(); + + for (size_t i = 0; i < CHC_vector.size(); ++i) { + GateHitsCollection *CHC = CHC_vector[i]; + if (!CHC) { + continue; + } + + std::vector timeline = BuildTimelineForCrystalCollection( + basePhantomTimeline, + CHC, + m_trajectoryNavigator); + ProcessTimeline(&timeline, m_trajectoryNavigator, context, legacyPhotonIDPolicy); + } + + RunDigitizersIfNeeded(); +} + +void GateMultiPhotonAnalysis::RecordStepWithVolume(const GateVVolume *, const G4Step *) { + if (nVerboseLevel > 2) { + G4cout << "GateMultiPhotonAnalysis::RecordStepWithVolume" << G4endl; + } +} + +void GateMultiPhotonAnalysis::SetVerboseLevel(G4int val) { + nVerboseLevel = val; + if (m_trajectoryNavigator) { + m_trajectoryNavigator->SetVerboseLevel(val); + } +} + +bool GateMultiPhotonAnalysis::IsTrackingModeSupported(int tracking_mode_code) const { + return tracking_mode_code == static_cast(TrackingMode::kBoth); +} + +int GateMultiPhotonAnalysis::ResolveLegacyPhotonIDPolicy() const { return 0; } + +void GateMultiPhotonAnalysis::SetMissingTrajectoryPolicy(MissingTrajectoryPolicy policy) { + m_missingTrajectoryPolicy = policy; +} + +void GateMultiPhotonAnalysis::SetMissingTrajectoryPolicyFromString(const G4String &policyName) { + if (policyName == "strict") { + m_missingTrajectoryPolicy = kStrict; + return; + } + if (policyName == "resilient") { + m_missingTrajectoryPolicy = kResilient; + return; + } + + G4String message = "Unsupported missing trajectory policy: " + policyName + + ". Expected one of: strict resilient."; + G4Exception( + "GateMultiPhotonAnalysis::SetMissingTrajectoryPolicyFromString", + "GateMultiPhotonAnalysisInvalidPolicy", + JustWarning, + message.c_str()); +} + +GateMultiPhotonAnalysis::MissingTrajectoryPolicy GateMultiPhotonAnalysis::GetMissingTrajectoryPolicy() const { + return m_missingTrajectoryPolicy; +} + +G4String GateMultiPhotonAnalysis::GetMissingTrajectoryPolicyName() const { + if (m_missingTrajectoryPolicy == kStrict) { + return "strict"; + } + return "resilient"; +} + diff --git a/source/digits_hits/src/GateMultiPhotonAnalysisMessenger.cc b/source/digits_hits/src/GateMultiPhotonAnalysisMessenger.cc new file mode 100644 index 000000000..21eaf3dd1 --- /dev/null +++ b/source/digits_hits/src/GateMultiPhotonAnalysisMessenger.cc @@ -0,0 +1,39 @@ +/*---------------------- + Copyright (C): OpenGATE Collaboration + +This software is distributed under the terms +of the GNU Lesser General Public Licence (LGPL) +See LICENSE.md for further details +----------------------*/ + +#include "GateMultiPhotonAnalysisMessenger.hh" + +#include "GateMultiPhotonAnalysis.hh" + +#include "G4UIcmdWithAString.hh" + +GateMultiPhotonAnalysisMessenger::GateMultiPhotonAnalysisMessenger( + GateMultiPhotonAnalysis *gateMultiPhotonAnalysis) + : GateOutputModuleMessenger(gateMultiPhotonAnalysis), + m_gateMultiPhotonAnalysis(gateMultiPhotonAnalysis), + m_setMissingTrajectoryPolicyCmd(0) { + G4String cmdName = GetDirectoryName() + "setMissingTrajectoryPolicy"; + m_setMissingTrajectoryPolicyCmd = new G4UIcmdWithAString(cmdName, this); + m_setMissingTrajectoryPolicyCmd->SetGuidance("Set policy for missing trajectory container handling."); + m_setMissingTrajectoryPolicyCmd->SetGuidance("Options: strict resilient"); + m_setMissingTrajectoryPolicyCmd->SetCandidates("strict resilient"); + m_setMissingTrajectoryPolicyCmd->SetParameterName("policy", false); +} + +GateMultiPhotonAnalysisMessenger::~GateMultiPhotonAnalysisMessenger() { + delete m_setMissingTrajectoryPolicyCmd; +} + +void GateMultiPhotonAnalysisMessenger::SetNewValue(G4UIcommand *command, G4String newValue) { + if (command == m_setMissingTrajectoryPolicyCmd) { + m_gateMultiPhotonAnalysis->SetMissingTrajectoryPolicyFromString(newValue); + return; + } + + GateOutputModuleMessenger::SetNewValue(command, newValue); +} diff --git a/source/digits_hits/src/GateMultiPhotonTrajectoryNavigator.cc b/source/digits_hits/src/GateMultiPhotonTrajectoryNavigator.cc new file mode 100644 index 000000000..70cdefb58 --- /dev/null +++ b/source/digits_hits/src/GateMultiPhotonTrajectoryNavigator.cc @@ -0,0 +1,180 @@ +/*---------------------- + Copyright (C): OpenGATE Collaboration + +This software is distributed under the terms +of the GNU Lesser General Public Licence (LGPL) +See LICENSE.md for further details +----------------------*/ + +#include "GateMultiPhotonTrajectoryNavigator.hh" + +#include "GateMultiPhotonTrajectoryNavigatorHelpers.hh" + +#include "G4ThreeVector.hh" +#include "G4Trajectory.hh" +#include "G4TrajectoryContainer.hh" + +#include "G4ios.hh" + +GateMultiPhotonTrajectoryNavigator::GateMultiPhotonTrajectoryNavigator() + : m_tc(0), + m_positronTrackID(0), + m_ionID(0), + m_verboseLevel(0), + m_hasSourcePosition(false), + m_sourceX(0.0), + m_sourceY(0.0), + m_sourceZ(0.0) {} + +GateMultiPhotonTrajectoryNavigator::~GateMultiPhotonTrajectoryNavigator() {} + +void GateMultiPhotonTrajectoryNavigator::SetTrajectoryContainer(G4TrajectoryContainer *trajectoryContainer) { + m_tc = trajectoryContainer; + Reset(); +} + +void GateMultiPhotonTrajectoryNavigator::BuildIndex() { + Reset(); + + if (!m_tc) { + if (m_verboseLevel > 0) { + G4cout << "GateMultiPhotonTrajectoryNavigator::BuildIndex: WARNING null trajectory container" << G4endl; + } + return; + } + + const int n_trajectories = m_tc->entries(); + const std::size_t expected_track_count = static_cast(n_trajectories); + m_parentByTrack.reserve(expected_track_count); + m_pdgByTrack.reserve(expected_track_count); + m_chargeByTrack.reserve(expected_track_count); + m_referencePhotonTrackIDs.reserve(expected_track_count); + m_ancestorPhotonCache.reserve(expected_track_count); + m_primaryCache.reserve(expected_track_count); + + for (int i = 0; i < n_trajectories; ++i) { + G4Trajectory *trj = (G4Trajectory *)((*m_tc)[i]); + if (!trj) { + continue; + } + + const int track_id = trj->GetTrackID(); + m_parentByTrack[track_id] = trj->GetParentID(); + m_pdgByTrack[track_id] = trj->GetPDGEncoding(); + m_chargeByTrack[track_id] = trj->GetCharge(); + + if (track_id == 1 && trj->GetPointEntries() > 0) { + const G4ThreeVector p = ((G4TrajectoryPoint *)(trj->GetPoint(0)))->GetPosition(); + m_sourceX = p.x(); + m_sourceY = p.y(); + m_sourceZ = p.z(); + m_hasSourcePosition = true; + } + } + + for (std::unordered_map::const_iterator it = m_chargeByTrack.begin(); it != m_chargeByTrack.end(); ++it) { + if (it->second > 2.0) { + m_ionID = 1; + break; + } + } + + for (std::unordered_map::const_iterator it = m_pdgByTrack.begin(); it != m_pdgByTrack.end(); ++it) { + const int track_id = it->first; + const int pdg = it->second; + const std::unordered_map::const_iterator parent_it = m_parentByTrack.find(track_id); + if (parent_it == m_parentByTrack.end()) { + continue; + } + if (parent_it->second == m_ionID && pdg == kPositronPDG) { + m_positronTrackID = track_id; + break; + } + } + + BuildReferencePhotonSet(); +} + +G4ThreeVector GateMultiPhotonTrajectoryNavigator::FindSourcePosition() const { + if (!m_hasSourcePosition) { + return G4ThreeVector(); + } + return G4ThreeVector(m_sourceX, m_sourceY, m_sourceZ); +} + +std::vector GateMultiPhotonTrajectoryNavigator::FindReferencePhotonTrackIDs() const { + std::vector out; + out.reserve(m_referencePhotonTrackIDs.size()); + for (std::unordered_set::const_iterator it = m_referencePhotonTrackIDs.begin(); it != m_referencePhotonTrackIDs.end(); ++it) { + out.push_back(*it); + } + return out; +} + +int GateMultiPhotonTrajectoryNavigator::FindAncestorPhotonTrackID(int trackID) const { + return MultiphotonTrajectoryResolver::ResolveAncestorPhotonTrackID( + trackID, + m_parentByTrack, + m_referencePhotonTrackIDs, + &m_ancestorPhotonCache); +} + +int GateMultiPhotonTrajectoryNavigator::FindPrimaryTrackID(int trackID) const { + return MultiphotonTrajectoryResolver::ResolvePrimaryTrackID(trackID, m_parentByTrack, &m_primaryCache); +} + +void GateMultiPhotonTrajectoryNavigator::SetVerboseLevel(int v) { m_verboseLevel = v; } + +void GateMultiPhotonTrajectoryNavigator::Reset() { + m_parentByTrack.clear(); + m_pdgByTrack.clear(); + m_chargeByTrack.clear(); + m_referencePhotonTrackIDs.clear(); + m_ancestorPhotonCache.clear(); + m_primaryCache.clear(); + m_positronTrackID = 0; + m_ionID = 0; + m_hasSourcePosition = false; + m_sourceX = 0.0; + m_sourceY = 0.0; + m_sourceZ = 0.0; +} + +void GateMultiPhotonTrajectoryNavigator::BuildReferencePhotonSet() { + for (std::unordered_map::const_iterator it = m_pdgByTrack.begin(); it != m_pdgByTrack.end(); ++it) { + const int track_id = it->first; + const int pdg = it->second; + if (pdg != kPhotonPDG) { + continue; + } + + const std::unordered_map::const_iterator parent_it = m_parentByTrack.find(track_id); + if (parent_it == m_parentByTrack.end()) { + continue; + } + + const int parent_id = parent_it->second; + + if (m_positronTrackID != 0) { + if (parent_id == m_positronTrackID) { + m_referencePhotonTrackIDs.insert(track_id); + } + } else { + bool is_reference_photon = false; + if (parent_id == 0) { + is_reference_photon = true; + } else if (m_ionID != 0 && parent_id == m_ionID) { + is_reference_photon = true; + } else { + const std::unordered_map::const_iterator parent_pdg_it = m_pdgByTrack.find(parent_id); + if (parent_pdg_it != m_pdgByTrack.end() && parent_pdg_it->second == kPositronPDG) { + is_reference_photon = true; + } + } + + if (is_reference_photon) { + m_referencePhotonTrackIDs.insert(track_id); + } + } + } +} diff --git a/source/digits_hits/src/GateOutputMgr.cc b/source/digits_hits/src/GateOutputMgr.cc index e587b3f32..bf1c4b3f4 100644 --- a/source/digits_hits/src/GateOutputMgr.cc +++ b/source/digits_hits/src/GateOutputMgr.cc @@ -13,6 +13,7 @@ #include "GateOutputMgrMessenger.hh" #include "GateConfiguration.h" #include "GateAnalysis.hh" +#include "GateMultiPhotonAnalysis.hh" #ifdef GATE_USE_OPTICAL #include "GateFastAnalysis.hh" #endif @@ -93,6 +94,9 @@ GateOutputMgr::GateOutputMgr(const G4String name) GateAnalysis* gateAnalysis = new GateAnalysis("analysis", this,m_digiMode); AddOutputModule((GateVOutputModule*)gateAnalysis); + GateMultiPhotonAnalysis* gateMultiPhotonAnalysis = new GateMultiPhotonAnalysis("multianalysis", this, m_digiMode); + AddOutputModule((GateVOutputModule*)gateMultiPhotonAnalysis); + } @@ -262,11 +266,23 @@ void GateOutputMgr::RecordBeginOfAcquisition() G4cout << "GateOutputMgr::RecordBeginOfAcquisition\n"; //OK GND GateDigitizerMgr* digitizerMgr=GateDigitizerMgr::GetInstance(); - if((digitizerMgr->m_recordSingles|| digitizerMgr->m_recordCoincidences) - && !this->FindOutputModule("analysis")->IsEnabled() - && !this->FindOutputModule("fastanalysis")->IsEnabled()) + bool analysisEnabled = false; + bool fastanalysisEnabled = false; + bool multianalysisEnabled = false; + for (size_t iMod=0; iModGetName(); + if (moduleName == "analysis") { + analysisEnabled = m_outputModules[iMod]->IsEnabled(); + } else if (moduleName == "fastanalysis") { + fastanalysisEnabled = m_outputModules[iMod]->IsEnabled(); + } else if (moduleName == "multianalysis") { + multianalysisEnabled = m_outputModules[iMod]->IsEnabled(); + } + } + if((digitizerMgr->m_recordSingles|| digitizerMgr->m_recordCoincidences) + && !IsAnyAnalysisModuleEnabled(analysisEnabled, fastanalysisEnabled, multianalysisEnabled)) { - GateError("***ERROR*** Digitizer Manager is not initialized properly. Please, enable analysis or fastanalysis Output Modules to write down Singles or Coincidences.\n Use, /gate/output/analysis/enable or /gate/output/fastanalysis/enable.\n"); + GateError("***ERROR*** Digitizer Manager is not initialized properly. Please, enable analysis, fastanalysis or multianalysis Output Modules to write down Singles or Coincidences.\n Use, /gate/output/analysis/enable or /gate/output/fastanalysis/enable or /gate/output/multianalysis/enable.\n"); } diff --git a/source/general/include/GateRootDefs.hh b/source/general/include/GateRootDefs.hh index 1fdf12c66..349b85548 100644 --- a/source/general/include/GateRootDefs.hh +++ b/source/general/include/GateRootDefs.hh @@ -222,6 +222,7 @@ class GateRootHitBuffer Int_t nCrystalCompton; //!< Number of Compton interactions in the crystam Int_t nPhantomRayleigh; //!< Number of Rayleigh interactions in the phantom Int_t nCrystalRayleigh; //!< Number of Rayleigh interactions in the crystam + Int_t nInteractions; //!< Number of non-Transportation interactions in phantom + crystal Int_t primaryID; //!< Primary ID Float_t sourcePosX,sourcePosY,sourcePosZ; //!< Global decay position (in millimeters) Int_t sourceID; //!< Source ID diff --git a/source/general/src/GateRootDefs.cc b/source/general/src/GateRootDefs.cc index 17d5e65da..aa8028855 100644 --- a/source/general/src/GateRootDefs.cc +++ b/source/general/src/GateRootDefs.cc @@ -133,6 +133,7 @@ void GateRootHitBuffer::Clear() nCrystalCompton = -1; nPhantomRayleigh = -1; nCrystalRayleigh = -1; + nInteractions = -1; primaryID = -1; axialPos = 0.; rotationAngle = 0.; @@ -208,6 +209,7 @@ void GateRootHitBuffer::Fill(GateHit* aHit) nCrystalCompton = aHit->GetNCrystalCompton(); nPhantomRayleigh = aHit->GetNPhantomRayleigh(); nCrystalRayleigh = aHit->GetNCrystalRayleigh(); + nInteractions = aHit->GetNInteractions(); primaryID = aHit->GetPrimaryID(); momDirX = aHit->GetMomentumDir().x(); momDirY = aHit->GetMomentumDir().y(); @@ -273,6 +275,7 @@ GateHit* GateRootHitBuffer::CreateHit() aHit->SetNCrystalCompton( nCrystalCompton ); aHit->SetNPhantomRayleigh( nPhantomRayleigh); aHit->SetNCrystalRayleigh( nCrystalRayleigh ); + aHit->SetNInteractions( nInteractions ); aHit->SetComptonVolumeName( comptonVolumeName ); aHit->SetRayleighVolumeName( RayleighVolumeName ); aHit->SetPrimaryID( primaryID ); @@ -344,6 +347,7 @@ void GateHitTree::Init(GateRootHitBuffer& buffer) Branch("nCrystalCompton",&buffer.nCrystalCompton,"nCrystalCompton/I"); Branch("nPhantomRayleigh",&buffer.nPhantomRayleigh,"nPhantomRayleigh/I"); Branch("nCrystalRayleigh",&buffer.nCrystalRayleigh,"nCrystalRayleigh/I"); + Branch("nInteractions",&buffer.nInteractions,"nInteractions/I"); Branch("primaryID", &buffer.primaryID,"primaryID/I"); Branch("axialPos", &buffer.axialPos,"axialPos/F"); @@ -380,6 +384,7 @@ void GateHitTree::SetBranchAddresses(TTree* hitTree,GateRootHitBuffer& buffer) hitTree->SetBranchAddress("PDGEncoding",&buffer.PDGEncoding); hitTree->SetBranchAddress("trackID",&buffer.trackID); hitTree->SetBranchAddress("parentID",&buffer.parentID); + hitTree->SetBranchAddress("trackLocalTime",&buffer.trackLocalTime); hitTree->SetBranchAddress("time",&buffer.time); hitTree->SetBranchAddress("edep",&buffer.edep); hitTree->SetBranchAddress("stepLength",&buffer.stepLength); @@ -417,6 +422,11 @@ void GateHitTree::SetBranchAddresses(TTree* hitTree,GateRootHitBuffer& buffer) hitTree->SetBranchAddress("nCrystalCompton",&buffer.nCrystalCompton); hitTree->SetBranchAddress("nPhantomRayleigh",&buffer.nPhantomRayleigh); hitTree->SetBranchAddress("nCrystalRayleigh",&buffer.nCrystalRayleigh); + if (hitTree->GetBranch("nInteractions")) { + hitTree->SetBranchAddress("nInteractions",&buffer.nInteractions); + } else { + buffer.nInteractions = -1; + } hitTree->SetBranchAddress("primaryID",&buffer.primaryID); hitTree->SetBranchAddress("axialPos",&buffer.axialPos); diff --git a/source/physics/include/GatePositronium.hh b/source/physics/include/GatePositronium.hh index ebece3dd6..325a3addb 100644 --- a/source/physics/include/GatePositronium.hh +++ b/source/physics/include/GatePositronium.hh @@ -25,8 +25,8 @@ public: GatePositronium(const GatePositronium&) = delete; GatePositronium& operator=(const GatePositronium&) = delete; - GatePositronium(GatePositronium&&) noexcept = default; - GatePositronium& operator=(GatePositronium&&) noexcept = default; + GatePositronium(GatePositronium&&) = default; + GatePositronium& operator=(GatePositronium&&) = default; G4double GetLifeTime() const; From bcab129396d959afec96956f13089fabaf868676 Mon Sep 17 00:00:00 2001 From: MateuszBala Date: Fri, 8 May 2026 14:57:48 +0200 Subject: [PATCH 144/144] format: remove tab --- source/digits_hits/src/GateHit.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/digits_hits/src/GateHit.cc b/source/digits_hits/src/GateHit.cc index 7f767fba0..99d307d83 100644 --- a/source/digits_hits/src/GateHit.cc +++ b/source/digits_hits/src/GateHit.cc @@ -30,8 +30,8 @@ GateHit::GateHit() m_systemID(-1), m_sourceEnergy(-1), m_sourcePDG(0), - m_nInteractions(-1), - m_nCrystalConv(0) + m_nInteractions(-1), + m_nCrystalConv(0) {;} //---------------------------------------------------------------------