From 0f2922a6fee01e4f3f289014abddb5455c15f940 Mon Sep 17 00:00:00 2001 From: Zhiwen Zhao Date: Sat, 13 Jun 2026 00:23:52 -0400 Subject: [PATCH] Clone the registry GTouchable per step to stop cross-event state leak getGTouchable returned the persistent shared_ptr stored in gTouchableMap, and ProcessHits/processTouchable mutate it each step (trackId, pid, stepTimeAtElectronicsIndex). Initialize() clears the per-event touchableVector but never resets those mutations, so at the start of each event the registry object still holds the previous event's time-cell index. processTouchableImpl branches on that index, so the first step of a new event can be misrouted (a spurious clone / wrong time cell), and stale trackId/pid carry over. Return a per-step copy so the registry entry stays pristine. Each step then starts from the clean UNSET identity, and within-event grouping still works because the hit collection / touchableVector group by operator== over field values (which are freshly assigned per step), not by object identity. gsd.h documents instances as thread-local with a per-worker map, so the clone introduces no cross-thread sharing. Verified: cherenkov example (gPhotonDetector SD, 20 events, fixed seed) produces byte-identical hit physics before and after (no regression); the fix removes the cross-event index carryover that affects readout SDs (Geant4 11.4.1 dev container). Fixes #113 Co-Authored-By: Claude Fable 5 --- gemc/gsd/gsd.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/gemc/gsd/gsd.h b/gemc/gsd/gsd.h index 334a44aa..4f516d54 100644 --- a/gemc/gsd/gsd.h +++ b/gemc/gsd/gsd.h @@ -195,7 +195,9 @@ class GSensitiveDetector : public GBase, public G4VSensitive std::string vname = thisStep->GetPreStepPoint()->GetTouchable()->GetVolume()->GetName(); auto it = gTouchableMap.find(vname); - if (it != gTouchableMap.end()) { return it->second; } + // Return a per-step copy: ProcessHits and processTouchable mutate trackId, pid and the + // time-cell index, so the registry entry must stay pristine across steps and events. + if (it != gTouchableMap.end()) { return std::make_shared(*it->second); } // If not found, log an error. The calling code assumes a valid pointer. log->error(ERR_DYNAMICPLUGINNOTFOUND, "GTouchable for volume " + vname + " not found in gTouchableMap"); }