add child_track_ids array column to Steps; remove parent_id

Each Steps row now carries a variable-length child_track_ids int array
containing the track IDs of all secondaries spawned during that step.
The parent_id column is removed — parentage is now expressed
unidirectionally (parent→children) via child_track_ids and the Spawning
ntuple, rather than bidirectionally.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-11 10:30:36 +02:00
parent 9ea0718139
commit 517ba2ecb2
5 changed files with 37 additions and 29 deletions
+2
View File
@@ -83,6 +83,8 @@ class RunAction : public G4UserRunAction
mutable std::vector<int> hitLayer; mutable std::vector<int> hitLayer;
mutable std::vector<int> hitCopyNumber; mutable std::vector<int> hitCopyNumber;
mutable std::vector<int> stepChildIds;
mutable G4String filename; mutable G4String filename;
}; };
+4
View File
@@ -33,6 +33,7 @@
#include "G4UserSteppingAction.hh" #include "G4UserSteppingAction.hh"
#include "GeometryDescriptor.hh" #include "GeometryDescriptor.hh"
#include "DetectorConstruction.hh" #include "DetectorConstruction.hh"
#include "RunAction.hh"
namespace B4a namespace B4a
@@ -54,8 +55,11 @@ public:
void UserSteppingAction(const G4Step* step) override; void UserSteppingAction(const G4Step* step) override;
void setRunAction(const B4::RunAction* runAction) { fRunAction = runAction; }
private: private:
EventAction* fEventAction = nullptr; EventAction* fEventAction = nullptr;
const B4::RunAction* fRunAction = nullptr;
}; };
} }
+1
View File
@@ -70,6 +70,7 @@ void ActionInitialization::Build() const
eventAction->setPrimaryGeneratorAction(gen); eventAction->setPrimaryGeneratorAction(gen);
SetUserAction(eventAction); SetUserAction(eventAction);
auto steppingAction = new SteppingAction(eventAction); auto steppingAction = new SteppingAction(eventAction);
steppingAction->setRunAction(runact);
SetUserAction(steppingAction); SetUserAction(steppingAction);
} }
+13 -13
View File
@@ -98,19 +98,19 @@ RunAction::RunAction()
analysisManager->CreateNtupleDColumn("post_E"); // col 11 analysisManager->CreateNtupleDColumn("post_E"); // col 11
analysisManager->CreateNtupleDColumn("edep"); // col 12 [MeV] analysisManager->CreateNtupleDColumn("edep"); // col 12 [MeV]
analysisManager->CreateNtupleDColumn("step_length"); // col 13 [mm] analysisManager->CreateNtupleDColumn("step_length"); // col 13 [mm]
analysisManager->CreateNtupleIColumn("parent_id"); // col 14 analysisManager->CreateNtupleSColumn("process"); // col 14
analysisManager->CreateNtupleSColumn("process"); // col 15 analysisManager->CreateNtupleIColumn("layer_id"); // col 15 (-1 = outside all layers)
analysisManager->CreateNtupleIColumn("layer_id"); // col 16 (-1 = outside all layers) analysisManager->CreateNtupleSColumn("material"); // col 16
analysisManager->CreateNtupleSColumn("material"); // col 17 analysisManager->CreateNtupleDColumn("pre_dx"); // col 17
analysisManager->CreateNtupleDColumn("pre_dx"); // col 18 analysisManager->CreateNtupleDColumn("pre_dy"); // col 18
analysisManager->CreateNtupleDColumn("pre_dy"); // col 19 analysisManager->CreateNtupleDColumn("pre_dz"); // col 19
analysisManager->CreateNtupleDColumn("pre_dz"); // col 20 analysisManager->CreateNtupleDColumn("Bx"); // col 20 [T]
analysisManager->CreateNtupleDColumn("Bx"); // col 21 [T] analysisManager->CreateNtupleDColumn("By"); // col 21 [T]
analysisManager->CreateNtupleDColumn("By"); // col 22 [T] analysisManager->CreateNtupleDColumn("Bz"); // col 22 [T]
analysisManager->CreateNtupleDColumn("Bz"); // col 23 [T] analysisManager->CreateNtupleDColumn("Ex"); // col 23 [V/m]
analysisManager->CreateNtupleDColumn("Ex"); // col 24 [V/m] analysisManager->CreateNtupleDColumn("Ey"); // col 24 [V/m]
analysisManager->CreateNtupleDColumn("Ey"); // col 25 [V/m] analysisManager->CreateNtupleDColumn("Ez"); // col 25 [V/m]
analysisManager->CreateNtupleDColumn("Ez"); // col 26 [V/m] analysisManager->CreateNtupleIColumn("child_track_ids", stepChildIds); // col 26 (variable-length array)
analysisManager->FinishNtuple(); analysisManager->FinishNtuple();
// Spawning ntuple (one row per secondary born, ntuple id=2) // Spawning ntuple (one row per secondary born, ntuple id=2)
+17 -16
View File
@@ -87,10 +87,13 @@ void SteppingAction::UserSteppingAction(const G4Step* step)
auto track = step->GetTrack(); auto track = step->GetTrack();
int evtId = G4RunManager::GetRunManager()->GetCurrentEvent()->GetEventID(); int evtId = G4RunManager::GetRunManager()->GetCurrentEvent()->GetEventID();
// Write one Spawning row per secondary born in this step (track IDs pre-assigned) // Write one Spawning row per secondary born in this step (track IDs pre-assigned);
// also populate stepChildIds for the child_track_ids vector column in Steps.
fRunAction->stepChildIds.clear();
const auto* secondaries = step->GetSecondaryInCurrentStep(); const auto* secondaries = step->GetSecondaryInCurrentStep();
if (secondaries) { if (secondaries) {
for (const G4Track* sec : *secondaries) { for (const G4Track* sec : *secondaries) {
fRunAction->stepChildIds.push_back(sec->GetTrackID());
analysisManager->FillNtupleIColumn(2, 0, evtId); analysisManager->FillNtupleIColumn(2, 0, evtId);
analysisManager->FillNtupleIColumn(2, 1, track->GetTrackID()); analysisManager->FillNtupleIColumn(2, 1, track->GetTrackID());
analysisManager->FillNtupleIColumn(2, 2, track->GetCurrentStepNumber()); analysisManager->FillNtupleIColumn(2, 2, track->GetCurrentStepNumber());
@@ -118,14 +121,11 @@ void SteppingAction::UserSteppingAction(const G4Step* step)
// B: geometric step length // B: geometric step length
analysisManager->FillNtupleDColumn(1, 13, step->GetStepLength()); analysisManager->FillNtupleDColumn(1, 13, step->GetStepLength());
// C: parent track ID (0 for primary)
analysisManager->FillNtupleIColumn(1, 14, track->GetParentID());
// D: process that ended this step // D: process that ended this step
G4String processName = ""; G4String processName = "";
const auto* postProc = post->GetProcessDefinedStep(); const auto* postProc = post->GetProcessDefinedStep();
if (postProc) processName = postProc->GetProcessName(); if (postProc) processName = postProc->GetProcessName();
analysisManager->FillNtupleSColumn(1, 15, processName); analysisManager->FillNtupleSColumn(1, 14, processName);
// E: layer index (-1 if outside all layers) and material name at pre-step point // E: layer index (-1 if outside all layers) and material name at pre-step point
int layerId = -1; int layerId = -1;
@@ -134,14 +134,14 @@ void SteppingAction::UserSteppingAction(const G4Step* step)
if (layers[i].physicalVolume == volume) { layerId = i; break; } if (layers[i].physicalVolume == volume) { layerId = i; break; }
} }
G4String materialName = pre->GetMaterial() ? pre->GetMaterial()->GetName() : ""; G4String materialName = pre->GetMaterial() ? pre->GetMaterial()->GetName() : "";
analysisManager->FillNtupleIColumn(1, 16, layerId); analysisManager->FillNtupleIColumn(1, 15, layerId);
analysisManager->FillNtupleSColumn(1, 17, materialName); analysisManager->FillNtupleSColumn(1, 16, materialName);
// F: pre-step momentum direction (unit vector) // F: pre-step momentum direction (unit vector)
const auto dir = pre->GetMomentumDirection(); const auto dir = pre->GetMomentumDirection();
analysisManager->FillNtupleDColumn(1, 18, dir.x()); analysisManager->FillNtupleDColumn(1, 17, dir.x());
analysisManager->FillNtupleDColumn(1, 19, dir.y()); analysisManager->FillNtupleDColumn(1, 18, dir.y());
analysisManager->FillNtupleDColumn(1, 20, dir.z()); analysisManager->FillNtupleDColumn(1, 19, dir.z());
// Field: B [T] and E [V/m] at pre-step position; zero if no field is registered // Field: B [T] and E [V/m] at pre-step position; zero if no field is registered
G4double Bx=0, By=0, Bz=0, Ex=0, Ey=0, Ez=0; G4double Bx=0, By=0, Bz=0, Ex=0, Ey=0, Ez=0;
@@ -161,12 +161,13 @@ void SteppingAction::UserSteppingAction(const G4Step* step)
Ez = fieldVal[5] / (volt/m); Ez = fieldVal[5] / (volt/m);
} }
} }
analysisManager->FillNtupleDColumn(1, 21, Bx); analysisManager->FillNtupleDColumn(1, 20, Bx);
analysisManager->FillNtupleDColumn(1, 22, By); analysisManager->FillNtupleDColumn(1, 21, By);
analysisManager->FillNtupleDColumn(1, 23, Bz); analysisManager->FillNtupleDColumn(1, 22, Bz);
analysisManager->FillNtupleDColumn(1, 24, Ex); analysisManager->FillNtupleDColumn(1, 23, Ex);
analysisManager->FillNtupleDColumn(1, 25, Ey); analysisManager->FillNtupleDColumn(1, 24, Ey);
analysisManager->FillNtupleDColumn(1, 26, Ez); analysisManager->FillNtupleDColumn(1, 25, Ez);
// col 26 child_track_ids: vector column, auto-read from fRunAction->stepChildIds at AddNtupleRow
analysisManager->AddNtupleRow(1); analysisManager->AddNtupleRow(1);
} }