11/* --------------------------------------------------
22 Copyright (C): OpenGATE Collaboration
33 This software is distributed under the terms
4- of the GNU Lesser General Public Licence (LGPL)
4+ of the GNU Lesser General Public Licence (LGPL)
55 See LICENSE.md for further details
66 -------------------------------------------------- */
77
1212#include < memory>
1313
1414GateDigitizerPileupActor::GateDigitizerPileupActor (py::dict &user_info)
15- : GateVDigitizerWithOutputActor(user_info, false ) {
15+ : GateVDigitizerWithOutputActor(user_info, true ) {
1616 fActions .insert (" EndOfEventAction" );
1717 fActions .insert (" EndOfRunAction" );
1818}
@@ -69,6 +69,22 @@ void GateDigitizerPileupActor::InitializeUserInfo(py::dict &user_info) {
6969 fInputDigiCollectionName = DictGetStr (user_info, " input_digi_collection" );
7070}
7171
72+ void GateDigitizerPileupActor::BeginOfRunActionMasterThread (int run_id) {
73+
74+ fTimeSorter = std::make_unique<GateTimeSorter>(fOutputDigiCollectionName );
75+ fTimeSorter ->Init (fInputDigiCollection );
76+ fTimeSorter ->SetSortingWindow (fSortingTime );
77+ fTimeSorter ->SetMaxSize (fClearEveryNEvents );
78+
79+ auto &outputIter = fTimeSorter ->OutputIterator ();
80+ outputIter.TrackAttribute (" GlobalTime" , &fTimeSorterOutputTime );
81+ outputIter.TrackAttribute (" TotalEnergyDeposit" , &fTimeSorterOutputEdep );
82+ outputIter.TrackAttribute (" PreStepUniqueVolumeID" , &fTimeSorterOutputVolID );
83+
84+ fVolumePileupWindows .clear ();
85+ fWindowExpiry = std::queue<volumeWindowExpiry>();
86+ }
87+
7288void GateDigitizerPileupActor::SetGroupVolumeDepth (const int depth) {
7389 fGroupVolumeDepth = depth;
7490}
@@ -82,41 +98,28 @@ void GateDigitizerPileupActor::DigitInitialize(
8298 a.push_back (" PostPosition" );
8399 GateVDigitizerWithOutputActor::DigitInitialize (a);
84100
85- // Get output attribute pointer
86- fOutputTimeAttribute = fOutputDigiCollection ->GetDigiAttribute (" GlobalTime" );
87- fOutputEdepAttribute =
88- fOutputDigiCollection ->GetDigiAttribute (" TotalEnergyDeposit" );
89- fOutputPosAttribute = fOutputDigiCollection ->GetDigiAttribute (" PostPosition" );
90-
91- // Set up pointers to track specific attributes
92- auto &lr = fThreadLocalVDigitizerData .Get ();
93- auto &l = fThreadLocalData .Get ();
94-
95- l.fTimeSorter .Init (fInputDigiCollection );
96- l.fTimeSorter .OutputIterator ().TrackAttribute (" GlobalTime" , &l.time );
97- l.fTimeSorter .OutputIterator ().TrackAttribute (" TotalEnergyDeposit" , &l.edep );
98- l.fTimeSorter .OutputIterator ().TrackAttribute (" PreStepUniqueVolumeID" ,
99- &l.volID );
100- l.fTimeSorter .SetSortingWindow (fSortingTime );
101- l.fTimeSorter .SetMaxSize (fClearEveryNEvents );
101+ fOutputDigiCollection ->RootInitializeTupleForWorker ();
102102}
103103
104104void GateDigitizerPileupActor::EndOfEventAction (const G4Event *) {
105- auto &l = fThreadLocalData .Get ();
106- l.fTimeSorter .Ingest ();
107- l.fTimeSorter .Process ();
108- ProcessTimeSortedDigis ();
105+
106+ fTimeSorter ->OnEndOfEventAction ([this ]() { ProcessTimeSortedDigis (); });
109107}
110108
111109void GateDigitizerPileupActor::EndOfRunAction (const G4Run *) {
112- auto &l = fThreadLocalData .Get ();
113- l.fTimeSorter .Flush ();
114- ProcessTimeSortedDigis ();
115- for (auto &[_vol_hash, window] : l.fVolumePileupWindows ) {
116- ProcessPileupWindow (window);
117- }
118- // Make sure everything is output into the root file.
119- fOutputDigiCollection ->FillToRootIfNeeded (true );
110+
111+ fTimeSorter ->OnEndOfRunAction (
112+ [this ]() { fOutputDigiCollection ->FillToRootIfNeeded (true ); },
113+ [this ]() {
114+ ProcessTimeSortedDigis ();
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 );
119+ ProcessPileupWindow (window);
120+ fWindowExpiry .pop ();
121+ }
122+ });
120123}
121124
122125GateDigitizerPileupActor::PileupWindow &
@@ -128,31 +131,33 @@ GateDigitizerPileupActor::GetPileupWindowForCurrentVolume(
128131
129132 const auto vol_hash = volume->get ()->GetIdUpToDepthAsHash (fGroupVolumeDepth );
130133
131- // Look up the window based on volume hash
134+ // Look up the window based on volume hash.
132135 auto it = windows.find (vol_hash);
133136 if (it != windows.end ()) {
134137 // Return a reference to the existing PileupWindow object for the volume.
135138 return it->second ;
136139 } else {
137140 // A PileupWindow object does not yet exist for this volume: create one.
138141 PileupWindow window;
142+ window.hash = vol_hash;
139143 const auto vol_id = volume->get ()->GetIdUpToDepth (fGroupVolumeDepth );
140- auto &l = fThreadLocalData .Get ();
141144 // Create a GateDigiCollection for this volume, as a temporary storage for
142145 // digis that belong to the same time window (the name must be unique).
143146 window.digis = GateDigiCollectionManager::GetInstance ()->NewDigiCollection (
144147 GetName () + " _" + vol_id);
145148 window.digis ->InitDigiAttributesFromCopy (fInputDigiCollection );
149+ window.digis ->SetSharedStorage (
150+ true ); // Same storage is accessible by all threads.
146151 // Create an iterator to be used when digis will be combined into one digi,
147152 // due to pile-up.
148153 window.digiIter = window.digis ->NewIterator ();
149- window.digiIter .TrackAttribute (" TotalEnergyDeposit" , &l. edep );
150- window.digiIter .TrackAttribute (" PostPosition" , &l. pos );
154+ window.digiIter .TrackAttribute (" TotalEnergyDeposit" , &fPileupWindowEdep );
155+ window.digiIter .TrackAttribute (" PostPosition" , &fPileupWindowPos );
151156 // Create a filler to copy all digi attributes from the sorted collection
152157 // into the collection of the window.
153158 window.fillerIn = std::make_unique<GateDigiAttributesFiller>(
154- l. fTimeSorter . OutputCollection (), window.digis ,
155- l. fTimeSorter . OutputCollection ()->GetDigiAttributeNames ());
159+ fTimeSorter -> OutputCollection (), window.digis ,
160+ fTimeSorter -> OutputCollection ()->GetDigiAttributeNames ());
156161 // Create a filler to copy digi attributes from the collection of the window
157162 // to the output collection (used for the digis that will result from
158163 // pile-up).
@@ -169,42 +174,51 @@ GateDigitizerPileupActor::GetPileupWindowForCurrentVolume(
169174}
170175
171176void GateDigitizerPileupActor::ProcessTimeSortedDigis () {
172- auto &l = fThreadLocalData . Get ();
173- auto &iter = l. fTimeSorter . OutputIterator ();
177+
178+ auto &iter = fTimeSorter -> OutputIterator ();
174179 iter.GoToBegin ();
175180 while (!iter.IsAtEnd ()) {
176181 // Look up or create the pile-up window object for the volume to which the
177182 // current digi belongs.
178- auto &window =
179- GetPileupWindowForCurrentVolume (l.volID , l.fVolumePileupWindows );
183+ auto &window = GetPileupWindowForCurrentVolume (fTimeSorterOutputVolID ,
184+ fVolumePileupWindows );
185+
186+ const auto current_time = *fTimeSorterOutputTime ;
187+ 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);
180193
181- const auto current_time = *l.time ;
182- const auto current_edep = *l.edep ;
183194 if (window.digis ->GetSize () == 0 ) {
184- // The window has no digis yet: make the window start at the time of the
185- // current digi and remember the current edep as highest.
195+ // The window was empty: the newly arrived digi will open it.
186196 window.startTime = current_time;
197+ fWindowExpiry .push ({window.hash , window.startTime + fTimeWindow });
187198 window.highestEdep = current_edep;
188- } else if (current_time - window.startTime > fTimeWindow ) {
189- // The current digi is beyond the time window: process the digis that are
190- // currently in the window, then make the window start at the time of the
191- // current digi and remember the current edep as highest.
192- ProcessPileupWindow (window);
193- window.startTime = current_time;
194- window.highestEdep = current_edep;
195- }
196-
197- if (fTimeWindowPolicy == TimeWindowPolicy::Paralyzable) {
198- // In Paralyzable mode, extend the time window to start at the time of
199- // the current digi.
200- window.startTime = current_time;
201- } else if (fTimeWindowPolicy == TimeWindowPolicy::EnergyWinnerParalyzable) {
202- // In EnergyWinnerParalyzable mode, extend the time window only if
203- // the current digi has higher energy than the highest-energy
204- // digi so far.
205- 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.
206208 window.startTime = current_time;
207- 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" );
208222 }
209223 }
210224
@@ -213,13 +227,11 @@ void GateDigitizerPileupActor::ProcessTimeSortedDigis() {
213227
214228 iter++;
215229 }
216- l. fTimeSorter . MarkOutputAsProcessed ();
230+ fTimeSorter -> MarkOutputAsProcessed ();
217231}
218232
219233void GateDigitizerPileupActor::ProcessPileupWindow (PileupWindow &window) {
220234 // This function simulates pile-up by combining the digis in the given window
221- // into one digi.
222- auto &l = fThreadLocalData .Get ();
223235
224236 std::optional<double > highest_edep{};
225237 double total_edep = 0.0 ;
@@ -229,22 +241,26 @@ void GateDigitizerPileupActor::ProcessPileupWindow(PileupWindow &window) {
229241 G4ThreeVector weighted_position;
230242 G4ThreeVector highest_edep_position;
231243
232- // Iterate over all digis in the window from the beginning.
244+ if (window.digis ->GetSize () == 0 ) {
245+ return ;
246+ }
233247
234- window.digiIter .Reset ();
235- while (!window.digiIter .IsAtEnd ()) {
236- const auto current_edep = *l.edep ;
237- const auto current_pos = *l.pos ;
248+ // Iterate over all digis in the window from the beginning.
249+ auto &iter = window.digiIter ;
250+ iter.GoToBegin ();
251+ while (!iter.IsAtEnd ()) {
252+ const auto current_edep = *fPileupWindowEdep ;
253+ const auto current_pos = *fPileupWindowPos ;
238254 // Remember the index of the first digi.
239255 if (!first_index) {
240- first_index = window. digiIter .fIndex ;
256+ first_index = iter .fIndex ;
241257 }
242258 // Remember the index of the last digi.
243- last_index = window. digiIter .fIndex ;
259+ last_index = iter .fIndex ;
244260 // Remember the value and index of the highest deposited energy so far.
245261 if (!highest_edep.has_value () || current_edep > highest_edep.value ()) {
246262 highest_edep = current_edep;
247- highest_edep_index = window. digiIter .fIndex ;
263+ highest_edep_index = iter .fIndex ;
248264 highest_edep_position = current_pos;
249265 }
250266 // Accumulate all deposited energy values.
@@ -255,22 +271,28 @@ void GateDigitizerPileupActor::ProcessPileupWindow(PileupWindow &window) {
255271 // Accumulate the energy-weighted position.
256272 weighted_position += current_pos * current_edep;
257273 }
258- window. digiIter ++;
274+ iter ++;
259275 }
260276 if (fPositionAttributePolicy ==
261277 PositionAttributePolicy::EnergyWeightedCentroid) {
262278 weighted_position /= total_edep;
263279 }
264280
281+ // Get output attribute pointer.
282+ auto outputEdepAttribute =
283+ fOutputDigiCollection ->GetDigiAttribute (" TotalEnergyDeposit" );
284+ auto outputPosAttribute =
285+ fOutputDigiCollection ->GetDigiAttribute (" PostPosition" );
286+
265287 // The resulting pile-up digi gets:
266288 // - the total edep value.
267- fOutputEdepAttribute ->FillDValue (total_edep);
289+ outputEdepAttribute ->FillDValue (total_edep);
268290 // - the position according to the position attribute policy.
269291 if (fPositionAttributePolicy == PositionAttributePolicy::EnergyWinner) {
270- fOutputPosAttribute ->Fill3Value (highest_edep_position);
292+ outputPosAttribute ->Fill3Value (highest_edep_position);
271293 } else if (fPositionAttributePolicy ==
272294 PositionAttributePolicy::EnergyWeightedCentroid) {
273- fOutputPosAttribute ->Fill3Value (weighted_position);
295+ outputPosAttribute ->Fill3Value (weighted_position);
274296 }
275297 // All the other attribute values are according to the attribute policy.
276298 if (fAttributePolicy == AttributePolicy::First) {
@@ -283,3 +305,18 @@ void GateDigitizerPileupActor::ProcessPileupWindow(PileupWindow &window) {
283305 // Remove all processed digis from the window.
284306 window.digis ->Clear ();
285307}
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+ }
0 commit comments