From fbc824fabef702dd7ee408b7d22ee6063582dba9 Mon Sep 17 00:00:00 2001 From: MateuszBala Date: Sat, 29 Aug 2026 09:54:35 +0200 Subject: [PATCH 1/2] fix(multiphoton): skip events without primary vertex quietly --- source/digits_hits/src/GateMultiPhotonAnalysis.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/source/digits_hits/src/GateMultiPhotonAnalysis.cc b/source/digits_hits/src/GateMultiPhotonAnalysis.cc index 36417e26c..72fcfc1f8 100644 --- a/source/digits_hits/src/GateMultiPhotonAnalysis.cc +++ b/source/digits_hits/src/GateMultiPhotonAnalysis.cc @@ -297,6 +297,15 @@ void GateMultiPhotonAnalysis::RecordEndOfEvent(const G4Event *event) { return; } + // 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; + } + GateRunManager *runManager = GateRunManager::GetRunManager(); GateSteppingAction *steppingAction = (GateSteppingAction *)(runManager->GetUserSteppingAction()); TrackingMode mode = steppingAction->GetMode(); From 548e3460d640a2a1950a902ea7800ebadae3615a Mon Sep 17 00:00:00 2001 From: MateuszBala Date: Sat, 29 Aug 2026 10:05:52 +0200 Subject: [PATCH 2/2] fix(multiphoton): always run the digitizer at end of event --- .../src/GateMultiPhotonAnalysis.cc | 37 +++++++++++++------ 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/source/digits_hits/src/GateMultiPhotonAnalysis.cc b/source/digits_hits/src/GateMultiPhotonAnalysis.cc index 72fcfc1f8..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) @@ -297,15 +311,6 @@ void GateMultiPhotonAnalysis::RecordEndOfEvent(const G4Event *event) { return; } - // 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; - } - GateRunManager *runManager = GateRunManager::GetRunManager(); GateSteppingAction *steppingAction = (GateSteppingAction *)(runManager->GetUserSteppingAction()); TrackingMode mode = steppingAction->GetMode(); @@ -321,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(); @@ -384,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 *) {