diff --git a/docs/source_and_particle_management.rst b/docs/source_and_particle_management.rst index 35b2b45e1..b6f8e3670 100644 --- a/docs/source_and_particle_management.rst +++ b/docs/source_and_particle_management.rst @@ -822,7 +822,7 @@ Required commands ``/gate/source/NAME/setPositroniumLifetimes t1 t2 ... tn unit`` Lifetime of each component. The last token is a Geant4 time unit such as ``ns``. -``/gate/source/NAME/setPromptPhotonProbabilites p1 p2 ... pn`` +``/gate/source/NAME/setPromptPhotonProbabilities p1 p2 ... pn`` Probability of prompt-gamma emission for each component. Values are restricted to the ``[0, 1]`` range by the messenger. ``/gate/source/NAME/setPromptPhotonEnergies e1 e2 ... en unit`` @@ -855,7 +855,7 @@ Optional commands .. note:: - The current implementation requires both ``setPromptPhotonProbabilites`` and ``setPromptPhotonEnergies`` even when the prompt-gamma probability is zero for all components. A common convention is to set the corresponding energies to ``0``. + The current implementation requires both ``setPromptPhotonProbabilities`` and ``setPromptPhotonEnergies`` even when the prompt-gamma probability is zero for all components. A common convention is to set the corresponding energies to ``0``. Event timing and generated vertices ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -958,7 +958,7 @@ The example below defines a two-component source with explicit 2-gamma and 3-gam /gate/source/psSource/setPositroniumLifetimes 0.125 142.0 ns /gate/source/psSource/setDecayKinds k2Gamma k3Gamma /gate/source/psSource/setPositronInteractions kParaPs kOrthoPs - /gate/source/psSource/setPromptPhotonProbabilites 1.0 1.0 + /gate/source/psSource/setPromptPhotonProbabilities 1.0 1.0 /gate/source/psSource/setPromptPhotonEnergies 1.274 1.274 MeV /gate/source/psSource/setElectronCaptureProbabilities 0.0 0.0 /gate/source/psSource/setMeanPositronRange 0.2 0.2 mm diff --git a/source/digits_hits/src/GateDigitizerInitializationModule.cc b/source/digits_hits/src/GateDigitizerInitializationModule.cc index 9815536d9..a61460a5c 100644 --- a/source/digits_hits/src/GateDigitizerInitializationModule.cc +++ b/source/digits_hits/src/GateDigitizerInitializationModule.cc @@ -135,14 +135,15 @@ void GateDigitizerInitializationModule::Digitize() //------------------------------------------------- + // An empty volume name only means that the output module in use did not + // fill it, so it is replaced by the "NULL" placeholder. It tells nothing + // about the source of the event, hence sourceID is left untouched. if ((*inHC)[i]->GetComptonVolumeName().empty()) { Digi->SetComptonVolumeName( "NULL" ); - Digi->SetSourceID( -1 ); } if ((*inHC)[i]->GetRayleighVolumeName().empty()) { Digi->SetRayleighVolumeName( "NULL" ); - Digi->SetSourceID( -1 ); } /* // if (nVerboseLevel>1) diff --git a/source/digits_hits/src/GateHit.cc b/source/digits_hits/src/GateHit.cc index 99d307d83..47183854f 100644 --- a/source/digits_hits/src/GateHit.cc +++ b/source/digits_hits/src/GateHit.cc @@ -27,6 +27,7 @@ GateHit::GateHit() m_PDGEncoding(0), m_trackID(0), m_parentID(0), + m_sourceID(-1), m_systemID(-1), m_sourceEnergy(-1), m_sourcePDG(0), diff --git a/source/digits_hits/src/GateHitConvertor.cc b/source/digits_hits/src/GateHitConvertor.cc index 818a99ef2..f76330d9e 100644 --- a/source/digits_hits/src/GateHitConvertor.cc +++ b/source/digits_hits/src/GateHitConvertor.cc @@ -179,14 +179,15 @@ void GateHitConvertor::ProcessOneHit(const GateHit* hit,GatePulseList* pulseList + // An empty volume name only means that the output module in use did not fill it, + // so it is replaced by the "NULL" placeholder. It tells nothing about the source + // of the event, hence sourceID is left untouched. if (hit->GetComptonVolumeName().empty()) { pulse->SetComptonVolumeName( "NULL" ); - pulse->SetSourceID( -1 ); } if (hit->GetRayleighVolumeName().empty()) { pulse->SetRayleighVolumeName( "NULL" ); - pulse->SetSourceID( -1 ); } if (nVerboseLevel>1) diff --git a/source/digits_hits/src/GateMultiPhotonAnalysis.cc b/source/digits_hits/src/GateMultiPhotonAnalysis.cc index 36417e26c..b94f7a1c2 100644 --- a/source/digits_hits/src/GateMultiPhotonAnalysis.cc +++ b/source/digits_hits/src/GateMultiPhotonAnalysis.cc @@ -226,6 +226,20 @@ void RunDigitizersIfNeeded() { } } +//! Runs the digitizer chain when the enclosing scope is left, whichever path is taken. +//! GateAnalysis calls the digitizer outside of its "no trajectory container" branch, so +//! whether Singles and Coincidences are produced for an event does not depend on the +//! analysis being able to process that event. The guard gives the same guarantee here, +//! where the event is abandoned in more than one place. +class ScopedDigitizerRunner { + public: + ScopedDigitizerRunner() = default; + ~ScopedDigitizerRunner() { RunDigitizersIfNeeded(); } + + ScopedDigitizerRunner(const ScopedDigitizerRunner &) = delete; + ScopedDigitizerRunner &operator=(const ScopedDigitizerRunner &) = delete; +}; + } // namespace GateMultiPhotonAnalysis::GateMultiPhotonAnalysis(const G4String &name, GateOutputMgr *outputMgr, DigiMode digiMode) @@ -312,6 +326,18 @@ void GateMultiPhotonAnalysis::RecordEndOfEvent(const G4Event *event) { return; } + // From here on the event is digitized whatever happens to the analysis itself. + ScopedDigitizerRunner digitizerRunner; + + // An event without any primary vertex carries no track, hence no hit and no trajectory + // container. GateSourceMgr stops generating vertices once the time limit of the run is + // reached, so the last event of every run looks exactly like this. There is nothing to + // analyse and nothing anomalous about it, so it is skipped quietly instead of being + // reported as a missing trajectory container. + if (event->GetNumberOfPrimaryVertex() == 0) { + return; + } + std::vector CHC_vector = GetOutputMgr()->GetHitCollections(); GatePhantomHitsCollection *PHC = GetOutputMgr()->GetPhantomHitCollection(); @@ -375,8 +401,6 @@ void GateMultiPhotonAnalysis::RecordEndOfEvent(const G4Event *event) { m_trajectoryNavigator); ProcessTimeline(&timeline, m_trajectoryNavigator, context, legacyPhotonIDPolicy); } - - RunDigitizersIfNeeded(); } void GateMultiPhotonAnalysis::RecordStepWithVolume(const GateVVolume *, const G4Step *) { diff --git a/source/digits_hits/src/GateToRoot.cc b/source/digits_hits/src/GateToRoot.cc index 24c19de81..a3fa6a925 100644 --- a/source/digits_hits/src/GateToRoot.cc +++ b/source/digits_hits/src/GateToRoot.cc @@ -628,9 +628,17 @@ void GateToRoot::RecordEndOfAcquisition() { // m_hfile = m_treeHit->GetCurrentFile(); - // Get file only for the 1st tree as it is the same file for all trees - m_hfile = m_treesHit[0]->GetCurrentFile(); + // Get file only for the 1st tree as it is the same file for all trees. + // The vector may be empty here: RecordEndOfRun() drops the trees (it saves the + // current file beforehand) and a setup without any sensitive detector never fills + // it. In both cases m_hfile already points to the file to be written. + if (!m_treesHit.empty() && m_treesHit[0]) + m_hfile = m_treesHit[0]->GetCurrentFile(); + if (!m_hfile) { + G4cerr << "GateToRoot::RecordEndOfAcquisition(): no ROOT file to write !\n"; + return; + } if (nVerboseLevel > 0) G4cout << "GateToRoot: ROOT: files writing...\n"; @@ -686,6 +694,12 @@ void GateToRoot::RecordEndOfRun(const G4Run *) { nbPrimaries -= 1.; // Number of primaries increase too much at each end of run ! + // ROOT may have switched to another file while the run was written (files are split + // above 1.9 GBytes), so remember where the trees actually live before dropping them - + // RecordEndOfAcquisition() has no tree left to ask. + if (!m_treesHit.empty() && m_treesHit[0]) + m_hfile = m_treesHit[0]->GetCurrentFile(); + m_hitBuffers.clear(); m_treesHit.clear(); } diff --git a/source/physics/include/GatePositroniumSourceMessenger.hh b/source/physics/include/GatePositroniumSourceMessenger.hh index 3c3e77b1b..af7d07cc1 100644 --- a/source/physics/include/GatePositroniumSourceMessenger.hh +++ b/source/physics/include/GatePositroniumSourceMessenger.hh @@ -49,7 +49,7 @@ class GatePositroniumSourceMessenger: public GateVSourceMessenger std::unique_ptr upCmdSetPositroniumFractions; std::unique_ptr upCmdSetPositroniumLifetimes; - std::unique_ptr upCmdSetPromptPhotonProbabilites; + std::unique_ptr upCmdSetPromptPhotonProbabilities; std::unique_ptr upCmdSetPromptPhotonEnergies; std::unique_ptr upCmdSetElectronCaptureProbabilities; std::unique_ptr upCmdSetDecayKinds; diff --git a/source/physics/src/GatePositroniumDecayParamsGenerator.cc b/source/physics/src/GatePositroniumDecayParamsGenerator.cc index b30471506..1c08bf5ef 100644 --- a/source/physics/src/GatePositroniumDecayParamsGenerator.cc +++ b/source/physics/src/GatePositroniumDecayParamsGenerator.cc @@ -122,7 +122,7 @@ void GatePositroniumDecayParamsGenerator::validatePositroniumDecayParams(const P GateError( "GatePositroniumDecayParamsGenerator::generatePositroniumDecayParams: " "number of provided parameters in Fractions, PromptGamma, Lifetimes, " - "Gamma Energies, Electron Capture Probabilites are not the same"); + "Gamma Energies, Electron Capture Probabilities are not the same"); } if (params.fPositronInteractions.empty()) GateWarning("PositroniumSource: setPositronInteractions was not called. " diff --git a/source/physics/src/GatePositroniumSourceMessenger.cc b/source/physics/src/GatePositroniumSourceMessenger.cc index 0e3abb8bd..f30a4e1d4 100644 --- a/source/physics/src/GatePositroniumSourceMessenger.cc +++ b/source/physics/src/GatePositroniumSourceMessenger.cc @@ -79,7 +79,7 @@ void GatePositroniumSourceMessenger::InitCommands() 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] " ) ); + upCmdSetPromptPhotonProbabilities.reset(GetStringCmd( "setPromptPhotonProbabilities", "\"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 " ) ); upCmdSetElectronCaptureProbabilities.reset(GetStringCmd( "setElectronCaptureProbabilities", "\"f1, f2, f3 .., fn\" - where fi in [0.0, 1.0] " ) ); @@ -123,7 +123,7 @@ void GatePositroniumSourceMessenger::SetNewValue(G4UIcommand *command, G4String } else if (command == upCmdSetPositroniumLifetimes.get()) { auto lifetimes = parseListOfParamsWithUnit(new_value); fParamGenerator.SetPositroniumLifetimes(lifetimes); - } else if (command == upCmdSetPromptPhotonProbabilites.get()) { + } else if (command == upCmdSetPromptPhotonProbabilities.get()) { std::vector promptPhotonProb; std::stringstream ss(new_value); float prob;