Skip to content

Commit 4802f5b

Browse files
committed
Handle pile-up windows as soon as they expire, to improve time monotonicity in DigitizerPileupActor output
1 parent 38b4271 commit 4802f5b

2 files changed

Lines changed: 67 additions & 22 deletions

File tree

core/opengate_core/opengate_lib/digitizer/GateDigitizerPileupActor.cpp

Lines changed: 56 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ void GateDigitizerPileupActor::BeginOfRunActionMasterThread(int run_id) {
8282
outputIter.TrackAttribute("PreStepUniqueVolumeID", &fTimeSorterOutputVolID);
8383

8484
fVolumePileupWindows.clear();
85+
fWindowExpiry = std::queue<volumeWindowExpiry>();
8586
}
8687

8788
void GateDigitizerPileupActor::SetGroupVolumeDepth(const int depth) {
@@ -111,8 +112,12 @@ void GateDigitizerPileupActor::EndOfRunAction(const G4Run *) {
111112
[this]() { fOutputDigiCollection->FillToRootIfNeeded(true); },
112113
[this]() {
113114
ProcessTimeSortedDigis();
114-
for (auto &[_vol_hash, window] : fVolumePileupWindows) {
115+
// Process all pile-up windows which still have an expiry item.
116+
while (fWindowExpiry.size() > 0) {
117+
auto &window =
118+
fVolumePileupWindows.at(fWindowExpiry.front().volumeHash);
115119
ProcessPileupWindow(window);
120+
fWindowExpiry.pop();
116121
}
117122
});
118123
}
@@ -134,6 +139,7 @@ GateDigitizerPileupActor::GetPileupWindowForCurrentVolume(
134139
} else {
135140
// A PileupWindow object does not yet exist for this volume: create one.
136141
PileupWindow window;
142+
window.hash = vol_hash;
137143
const auto vol_id = volume->get()->GetIdUpToDepth(fGroupVolumeDepth);
138144
// Create a GateDigiCollection for this volume, as a temporary storage for
139145
// digis that belong to the same time window (the name must be unique).
@@ -179,31 +185,40 @@ void GateDigitizerPileupActor::ProcessTimeSortedDigis() {
179185

180186
const auto current_time = *fTimeSorterOutputTime;
181187
const auto current_edep = *fTimeSorterOutputEdep;
188+
189+
// The GlobalTime of digis provided by the time sorter are guaranteed to be
190+
// monotonically increasing. As a consequence, all pile-up windows that have
191+
// expired by the time of the most recently arrived digi can be handled.
192+
ProcessPileupWindows(current_time);
193+
182194
if (window.digis->GetSize() == 0) {
183-
// The window has no digis yet: make the window start at the time of the
184-
// current digi and remember the current edep as highest.
185-
window.startTime = current_time;
186-
window.highestEdep = current_edep;
187-
} else if (current_time - window.startTime > fTimeWindow) {
188-
// The current digi is beyond the time window: process the digis that are
189-
// currently in the window, then make the window start at the time of the
190-
// current digi and remember the current edep as highest.
191-
ProcessPileupWindow(window);
195+
// The window was empty: the newly arrived digi will open it.
192196
window.startTime = current_time;
197+
fWindowExpiry.push({window.hash, window.startTime + fTimeWindow});
193198
window.highestEdep = current_edep;
194-
}
195-
196-
if (fTimeWindowPolicy == TimeWindowPolicy::Paralyzable) {
197-
// In Paralyzable mode, extend the time window to start at the time of
198-
// the current digi.
199-
window.startTime = current_time;
200-
} else if (fTimeWindowPolicy == TimeWindowPolicy::EnergyWinnerParalyzable) {
201-
// In EnergyWinnerParalyzable mode, extend the time window only if
202-
// the current digi has higher energy than the highest-energy
203-
// digi so far.
204-
if (current_edep > window.highestEdep) {
199+
} else {
200+
// The window was already opened: update the window depending on the
201+
// policy.
202+
switch (fTimeWindowPolicy) {
203+
case TimeWindowPolicy::NonParalyzable:
204+
// window start time remains the same.
205+
break;
206+
case TimeWindowPolicy::Paralyzable:
207+
// The current digi moves the start time forward.
205208
window.startTime = current_time;
206-
window.highestEdep = current_edep;
209+
fWindowExpiry.push({window.hash, window.startTime + fTimeWindow});
210+
break;
211+
case TimeWindowPolicy::EnergyWinnerParalyzable:
212+
// The current digi moves the start time forward if its energy is higher
213+
// than previous energies.
214+
if (current_edep > window.highestEdep) {
215+
window.startTime = current_time;
216+
fWindowExpiry.push({window.hash, window.startTime + fTimeWindow});
217+
window.highestEdep = current_edep;
218+
}
219+
break;
220+
default:
221+
Fatal("Unknown time window policy");
207222
}
208223
}
209224

@@ -226,6 +241,10 @@ void GateDigitizerPileupActor::ProcessPileupWindow(PileupWindow &window) {
226241
G4ThreeVector weighted_position;
227242
G4ThreeVector highest_edep_position;
228243

244+
if (window.digis->GetSize() == 0) {
245+
return;
246+
}
247+
229248
// Iterate over all digis in the window from the beginning.
230249
auto &iter = window.digiIter;
231250
iter.GoToBegin();
@@ -286,3 +305,18 @@ void GateDigitizerPileupActor::ProcessPileupWindow(PileupWindow &window) {
286305
// Remove all processed digis from the window.
287306
window.digis->Clear();
288307
}
308+
309+
void GateDigitizerPileupActor::ProcessPileupWindows(double currentTime) {
310+
311+
// Process the expiry items for which the expiry time is before currentTime.
312+
while (fWindowExpiry.size() > 0 &&
313+
currentTime > fWindowExpiry.front().expiryTime) {
314+
auto &window = fVolumePileupWindows.at(fWindowExpiry.front().volumeHash);
315+
// Check again whether the window is actually expired, because its expiry
316+
// time may have been updated in a later expiry item.
317+
if (currentTime > window.startTime + fTimeWindow) {
318+
ProcessPileupWindow(window);
319+
}
320+
fWindowExpiry.pop();
321+
}
322+
}

core/opengate_core/opengate_lib/digitizer/GateDigitizerPileupActor.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <map>
1717
#include <memory>
1818
#include <pybind11/stl.h>
19+
#include <queue>
1920

2021
namespace py = pybind11;
2122

@@ -63,6 +64,8 @@ class GateDigitizerPileupActor : public GateVDigitizerWithOutputActor {
6364
// Struct for storing digis in one particular volume which belong to the same
6465
// time window.
6566
struct PileupWindow {
67+
// Hash of the corresponding volume.
68+
uint64_t hash{};
6669
// Time at which the time window opens.
6770
double startTime{};
6871
// Higehst energy deposit in the window.
@@ -79,8 +82,15 @@ class GateDigitizerPileupActor : public GateVDigitizerWithOutputActor {
7982
std::unique_ptr<GateDigiAttributesFiller> fillerOut;
8083
};
8184

85+
// Struct that represents when a pile-up window expires.
86+
struct volumeWindowExpiry {
87+
uint64_t volumeHash;
88+
double expiryTime;
89+
};
90+
8291
std::unique_ptr<GateTimeSorter> fTimeSorter;
8392
std::map<uint64_t, PileupWindow> fVolumePileupWindows;
93+
std::queue<volumeWindowExpiry> fWindowExpiry;
8494

8595
// Tracking pointers used by GateTimeSorter output iterator.
8696
GateUniqueVolumeID::Pointer *fTimeSorterOutputVolID{};
@@ -97,6 +107,7 @@ class GateDigitizerPileupActor : public GateVDigitizerWithOutputActor {
97107

98108
void ProcessTimeSortedDigis();
99109
void ProcessPileupWindow(PileupWindow &window);
110+
void ProcessPileupWindows(double currentTime);
100111
};
101112

102113
#endif // GateDigitizerPileupActor_h

0 commit comments

Comments
 (0)