add detector simulation

This commit is contained in:
AndreaRigoni
2026-03-20 00:16:55 +00:00
parent c44a7738c0
commit 033fb598c7
17 changed files with 441 additions and 211 deletions

View File

@@ -65,7 +65,9 @@ public:
HLine3f worldPlane; HLine3f worldPlane;
Matrix4f M = this->GetWorldMatrix(); Matrix4f M = this->GetWorldMatrix();
worldPlane.origin = M * m_ProjectionPlane.origin; worldPlane.origin = M * m_ProjectionPlane.origin;
worldPlane.direction = M * m_ProjectionPlane.direction; HVector3f dirNorm = M * m_ProjectionPlane.direction;
dirNorm.normalize(); // Normalize for consistent dot products
worldPlane.direction = dirNorm;
return worldPlane; return worldPlane;
} }

View File

@@ -5,37 +5,26 @@
namespace uLib { namespace uLib {
namespace Geant { namespace Geant {
ActionInitialization::ActionInitialization(EmitterPrimary *emitter, ActionInitialization::ActionInitialization(EmitterPrimary *emitter, SimulationContext *context)
Vector<GeantEvent> *output,
int verbosity)
: G4VUserActionInitialization(), : G4VUserActionInitialization(),
m_Emitter(emitter), m_Emitter(emitter),
m_Output(output), m_Context(context)
m_Verbosity(verbosity)
{} {}
ActionInitialization::~ActionInitialization() {} ActionInitialization::~ActionInitialization() {}
void ActionInitialization::BuildForMaster() const { void ActionInitialization::BuildForMaster() const {}
// Master thread: no per-event actions needed
}
void ActionInitialization::Build() const { void ActionInitialization::Build() const {
// Register the primary generator
if (m_Emitter) { if (m_Emitter) {
SetUserAction(m_Emitter->Clone()); SetUserAction(m_Emitter->Clone());
} else { } else {
// Fallback: default EmitterPrimary
SetUserAction(new EmitterPrimary()); SetUserAction(new EmitterPrimary());
} }
// Register actions SteppingAction *sa = new SteppingAction(m_Context);
if (m_Output) {
SteppingAction *sa = new SteppingAction(m_Output);
sa->SetVerbosity(m_Verbosity);
SetUserAction(static_cast<G4UserSteppingAction*>(sa)); SetUserAction(static_cast<G4UserSteppingAction*>(sa));
SetUserAction(static_cast<G4UserEventAction*>(sa)); SetUserAction(static_cast<G4UserEventAction*>(sa));
}
} }
} // namespace Geant } // namespace Geant

View File

@@ -2,35 +2,24 @@
#define ActionInitialization_h #define ActionInitialization_h
#include "G4VUserActionInitialization.hh" #include "G4VUserActionInitialization.hh"
#include "Core/Vector.h" #include "SimulationContext.h"
namespace uLib { namespace uLib {
namespace Geant { namespace Geant {
class EmitterPrimary; class EmitterPrimary;
class GeantEvent;
class ActionInitialization : public G4VUserActionInitialization { class ActionInitialization : public G4VUserActionInitialization {
public: public:
/// @param emitter the primary generator to use (owned by caller) ActionInitialization(EmitterPrimary *emitter, SimulationContext *context);
/// @param output pointer to the results vector (owned by caller)
ActionInitialization(EmitterPrimary *emitter = nullptr,
Vector<GeantEvent> *output = nullptr,
int verbosity = 0);
~ActionInitialization(); ~ActionInitialization();
void SetVerbosity(int level) { m_Verbosity = level; } virtual void BuildForMaster() const override;
virtual void Build() const override;
// Metodo chiamato solo dal thread principale (Master)
virtual void BuildForMaster() const;
// Metodo chiamato dai thread di lavoro (Worker) o in modalità sequenziale
virtual void Build() const;
private: private:
EmitterPrimary *m_Emitter; EmitterPrimary *m_Emitter;
Vector<GeantEvent> *m_Output; SimulationContext *m_Context;
int m_Verbosity = 0;
}; };
} // namespace Geant } // namespace Geant

View File

@@ -20,6 +20,7 @@ set(HEADERS
PhysicsList.hh PhysicsList.hh
ActionInitialization.hh ActionInitialization.hh
SteppingAction.hh SteppingAction.hh
SimulationContext.h
) )
set(SOURCES set(SOURCES

View File

@@ -0,0 +1,43 @@
#include "DetectorActionInitialization.hh"
#include "EmitterPrimary.hh"
#include "DetectorSteppingAction.hh"
namespace uLib {
namespace Geant {
DetectorActionInitialization::DetectorActionInitialization(EmitterPrimary *emitter,
Vector<MuonEvent> *output,
const Vector<HLine3f> &planes,
int verbosity)
: G4VUserActionInitialization(),
m_Emitter(emitter),
m_Output(output),
m_Planes(planes),
m_Verbosity(verbosity)
{}
DetectorActionInitialization::~DetectorActionInitialization() {}
void DetectorActionInitialization::BuildForMaster() const {}
void DetectorActionInitialization::Build() const {
if (m_Verbosity > 0) {
std::cout << "[Geant] Worker thread Building actions... Output ptr: " << m_Output
<< ", Planes count: " << m_Planes.size() << std::endl;
}
if (m_Emitter) {
SetUserAction(m_Emitter->Clone());
} else {
SetUserAction(new EmitterPrimary());
}
if (m_Output) {
DetectorSteppingAction *sa = new DetectorSteppingAction(m_Output, m_Planes);
sa->SetVerbosity(m_Verbosity);
SetUserAction(static_cast<G4UserSteppingAction*>(sa));
SetUserAction(static_cast<G4UserEventAction*>(sa));
}
}
} // namespace Geant
} // namespace uLib

View File

@@ -0,0 +1,35 @@
#ifndef U_GEANT_DETECTORACTIONINITIALIZATION_HH
#define U_GEANT_DETECTORACTIONINITIALIZATION_HH
#include "G4VUserActionInitialization.hh"
#include "Core/Vector.h"
#include "HEP/Detectors/MuonEvent.h"
#include "Math/Dense.h"
namespace uLib {
namespace Geant {
class EmitterPrimary;
class DetectorActionInitialization : public G4VUserActionInitialization {
public:
DetectorActionInitialization(EmitterPrimary *emitter,
Vector<MuonEvent> *output,
const Vector<HLine3f> &planes,
int verbosity = 0);
~DetectorActionInitialization();
virtual void BuildForMaster() const override;
virtual void Build() const override;
private:
EmitterPrimary *m_Emitter;
Vector<MuonEvent> *m_Output;
Vector<HLine3f> m_Planes;
int m_Verbosity;
};
} // namespace Geant
} // namespace uLib
#endif

View File

@@ -0,0 +1,110 @@
#include "DetectorSteppingAction.hh"
#include <Geant4/G4Step.hh>
#include <Geant4/G4Track.hh>
#include <Geant4/G4Event.hh>
#include <Geant4/G4SystemOfUnits.hh>
#include <cmath>
#include <mutex>
#include <iostream>
static std::mutex g_DetectorOutputMutex;
namespace uLib {
namespace Geant {
DetectorSteppingAction::DetectorSteppingAction(Vector<MuonEvent> *output, const Vector<HLine3f> &planes)
: G4UserSteppingAction(),
G4UserEventAction(),
m_Output(output),
m_Planes(planes),
m_CrossCount(0),
m_Verbosity(1)
{
// std::cout << "[Geant] SteppingAction created with " << m_Planes.size() << " planes." << std::endl;
}
DetectorSteppingAction::~DetectorSteppingAction() {}
void DetectorSteppingAction::BeginOfEventAction(const G4Event* /*event*/) {
m_CrossCount = 0;
// Initialize with NaN
float nan = std::numeric_limits<float>::quiet_NaN();
m_Current.LineIn().origin = HPoint3f(nan, nan, nan);
m_Current.LineIn().direction = HVector3f(nan, nan, nan);
m_Current.LineOut().origin = HPoint3f(nan, nan, nan);
m_Current.LineOut().direction = HVector3f(nan, nan, nan);
m_Current.Momentum() = nan;
}
void DetectorSteppingAction::EndOfEventAction(const G4Event* /*event*/) {
if (m_Output) {
std::lock_guard<std::mutex> lock(g_DetectorOutputMutex);
m_Output->push_back(m_Current);
}
}
void DetectorSteppingAction::UserSteppingAction(const G4Step *step) {
if (!step) return;
if (!m_Output) {
return;
}
const G4Track *track = step->GetTrack();
if (!track) return;
static size_t step_count = 0;
if (++step_count % 1000 == 0 && m_Verbosity > 0) {
std::cout << "[GeantMT] Processed " << step_count << " total steps across events." << std::endl;
}
// Only consider primary muons
if (track->GetParentID() != 0) return;
// Track the momentum at generation/first step if not set
if (std::isnan(m_Current.Momentum())) {
m_Current.Momentum() = static_cast<Scalarf>(track->GetMomentum().mag() / MeV);
}
G4ThreeVector p1 = step->GetPreStepPoint()->GetPosition();
G4ThreeVector p2 = step->GetPostStepPoint()->GetPosition();
G4ThreeVector dir_g4 = track->GetMomentumDirection();
HPoint3f p1f(p1.x(), p1.y(), p1.z());
HPoint3f p2f(p2.x(), p2.y(), p2.z());
HVector3f dirf(dir_g4.x(), dir_g4.y(), dir_g4.z());
// Check intersection with each detector plane
for (const auto& plane : m_Planes) {
// Plane: origin=O, direction=N (normal)
HPoint3f O = plane.origin;
HVector3f N = plane.direction;
float d1 = (p1f - O).dot(N);
float d2 = (p2f - O).dot(N);
// Check if the step crossed the plane
if ((d1 > 0 && d2 <= 0) || (d1 < 0 && d2 >= 0)) {
// Intersection point t = d1 / (d1 - d2)
float t = d1 / (d1 - d2);
HPoint3f intersection = p1f + t * (p2f - p1f);
if (m_CrossCount == 0) {
m_Current.LineIn().origin = intersection;
m_Current.LineIn().direction = dirf;
m_CrossCount++;
if (m_Verbosity > 0) std::cout << "[GeantMT] Hit first plane at " << intersection.transpose() << std::endl;
} else if (m_CrossCount == 1) {
m_Current.LineOut().origin = intersection;
m_Current.LineOut().direction = dirf;
m_CrossCount++;
if (m_Verbosity > 0) std::cout << "[GeantMT] Hit second plane at " << intersection.transpose() << std::endl;
}
// We break to avoid crossing multiple planes in one infinitesimal step (unlikely but possible)
// Actually, we should check ALL planes.
}
}
}
} // namespace Geant
} // namespace uLib

View File

@@ -0,0 +1,36 @@
#ifndef U_GEANT_DETECTORSTEPPINGACTION_HH
#define U_GEANT_DETECTORSTEPPINGACTION_HH
#include "G4UserSteppingAction.hh"
#include "G4UserEventAction.hh"
#include "Core/Vector.h"
#include "HEP/Detectors/MuonEvent.h"
#include "HEP/Detectors/DetectorChamber.h"
#include <mutex>
namespace uLib {
namespace Geant {
class DetectorSteppingAction : public G4UserSteppingAction, public G4UserEventAction {
public:
DetectorSteppingAction(Vector<MuonEvent> *output, const Vector<HLine3f> &planes);
virtual ~DetectorSteppingAction();
virtual void UserSteppingAction(const G4Step *step) override;
virtual void BeginOfEventAction(const G4Event *event) override;
virtual void EndOfEventAction(const G4Event *event) override;
void SetVerbosity(int level) { m_Verbosity = level; }
private:
Vector<MuonEvent> *m_Output;
Vector<HLine3f> m_Planes; // World projection planes
MuonEvent m_Current;
int m_CrossCount = 0;
int m_Verbosity = 0;
};
} // namespace Geant
} // namespace uLib
#endif

View File

@@ -73,6 +73,7 @@ public:
void Print(const size_t size = 10) const; void Print(const size_t size = 10) const;
private: private:
Id_t m_EventID; Id_t m_EventID;
Scalarf m_Momentum; Scalarf m_Momentum;

View File

@@ -1,28 +1,3 @@
/*//////////////////////////////////////////////////////////////////////////////
// CMT Cosmic Muon Tomography project //////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
Copyright (c) 2014, Universita' degli Studi di Padova, INFN sez. di Padova
All rights reserved
Authors: Andrea Rigoni Garola < andrea.rigoni@pd.infn.it >
------------------------------------------------------------------
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3.0 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library.
//////////////////////////////////////////////////////////////////////////////*/
#include <Geant4/G4Box.hh> #include <Geant4/G4Box.hh>
#include <Geant4/G4LogicalVolume.hh> #include <Geant4/G4LogicalVolume.hh>
#include <Geant4/G4Material.hh> #include <Geant4/G4Material.hh>
@@ -42,13 +17,15 @@
#include "Scene.h" #include "Scene.h"
#include "PhysicsList.hh" #include "PhysicsList.hh"
#include "ActionInitialization.hh" #include "ActionInitialization.hh"
#include "SimulationContext.h"
#include "HEP/Detectors/DetectorChamber.h"
namespace uLib { namespace uLib {
namespace Geant { namespace Geant {
class SceneDetectorConstruction : public DetectorConstruction { class SceneDetectorConstruction : public DetectorConstruction {
public: public:
SceneDetectorConstruction(class SceneImpl *owner); SceneDetectorConstruction(class SceneImpl *owner) : DetectorConstruction("Scene"), m_Owner(owner) {}
G4VPhysicalVolume *Construct() override; G4VPhysicalVolume *Construct() override;
private: private:
class SceneImpl *m_Owner; class SceneImpl *m_Owner;
@@ -69,57 +46,50 @@ static void CheckGeant4Environment() {
class SceneImpl { class SceneImpl {
public: public:
// constructor //
SceneImpl() : m_RunManager(G4RunManagerFactory::CreateRunManager(G4RunManagerType::Default)), SceneImpl() : m_RunManager(G4RunManagerFactory::CreateRunManager(G4RunManagerType::Default)),
m_Emitter(nullptr), m_Emitter(nullptr),
m_Output(nullptr), m_InitCalled(false) {
m_Verbosity(0) {
m_RunManager->SetUserInitialization(new PhysicsList); m_RunManager->SetUserInitialization(new PhysicsList);
} }
// destructor //
~SceneImpl() { ~SceneImpl() {
if (m_RunManager) delete m_RunManager; if (m_RunManager) delete m_RunManager;
if (m_World) delete m_World; // m_World deletion is handled in Scene destructor or here
} }
void Initialize() { void Initialize() {
// Set mandatory initialization classes for Geant4 if (m_InitCalled) return;
m_RunManager->SetUserInitialization(new SceneDetectorConstruction(this)); m_RunManager->SetUserInitialization(new SceneDetectorConstruction(this));
m_RunManager->SetUserInitialization( m_RunManager->SetUserInitialization(new ActionInitialization(m_Emitter, &m_Context));
new ActionInitialization(m_Emitter, m_Output, m_Verbosity));
m_RunManager->SetVerboseLevel(m_Verbosity);
// Initialize Geant4
m_RunManager->Initialize(); m_RunManager->Initialize();
m_InitCalled = true;
} }
// members //
Vector<Solid *> m_Solids; Vector<Solid *> m_Solids;
Solid *m_World = nullptr; Solid *m_World = nullptr;
ContainerBox m_WorldBox; ContainerBox m_WorldBox;
G4RunManager *m_RunManager; G4RunManager *m_RunManager;
EmitterPrimary *m_Emitter; EmitterPrimary *m_Emitter;
Vector<GeantEvent> *m_Output; SimulationContext m_Context;
int m_Verbosity; bool m_InitCalled;
}; };
SceneDetectorConstruction::SceneDetectorConstruction(SceneImpl *owner)
: DetectorConstruction("Scene"), m_Owner(owner) {}
G4VPhysicalVolume *SceneDetectorConstruction::Construct() { G4VPhysicalVolume *SceneDetectorConstruction::Construct() {
return m_Owner->m_World->GetPhysical(); return m_Owner->m_World->GetPhysical();
} }
Scene::Scene() { Scene::Scene() {
CheckGeant4Environment(); CheckGeant4Environment();
d = new SceneImpl(); d = new SceneImpl();
} }
Scene::~Scene() { delete d; }
Scene::~Scene() {
// Delete solids
for(auto s : d->m_Solids) delete s;
delete d;
}
void Scene::AddSolid(Solid *solid, Solid *parent) { void Scene::AddSolid(Solid *solid, Solid *parent) {
d->m_Solids.push_back(solid); d->m_Solids.push_back(solid);
@@ -131,12 +101,9 @@ void Scene::AddSolid(Solid *solid, Solid *parent) {
} }
const Solid* Scene::GetWorld() const { return d->m_World; } const Solid* Scene::GetWorld() const { return d->m_World; }
ContainerBox* Scene::GetWorldBox() const { return &d->m_WorldBox; } ContainerBox* Scene::GetWorldBox() const { return &d->m_WorldBox; }
void Scene::ConstructWorldBox(const Vector3f &size, const char *material) { void Scene::ConstructWorldBox(const Vector3f &size, const char *material) {
// Get nist material manager
d->m_WorldBox.Scale(size); d->m_WorldBox.Scale(size);
d->m_WorldBox.SetPosition(-size/2.0f); d->m_WorldBox.SetPosition(-size/2.0f);
@@ -146,61 +113,48 @@ void Scene::ConstructWorldBox(const Vector3f &size, const char *material) {
AddSolid(d->m_World); AddSolid(d->m_World);
} }
G4Box *solidWorld = new G4Box("World", G4Box *solidWorld = new G4Box("World", 0.5 * size(0), 0.5 * size(1), 0.5 * size(2));
0.5 * size(0), G4LogicalVolume *logicWorld = new G4LogicalVolume(solidWorld, d->m_World->GetMaterial(), d->m_World->GetName());
0.5 * size(1),
0.5 * size(2));
G4LogicalVolume *logicWorld = new G4LogicalVolume(solidWorld,
d->m_World->GetMaterial(),
d->m_World->GetName());
d->m_World->SetLogical(logicWorld); d->m_World->SetLogical(logicWorld);
G4PVPlacement *physWorld = new G4PVPlacement( G4PVPlacement *physWorld = new G4PVPlacement(nullptr, G4ThreeVector(0, 0, 0), logicWorld, d->m_World->GetName(), 0, false, 0, true);
nullptr,
G4ThreeVector(0, 0, 0),
logicWorld,
d->m_World->GetName(),
0,
false,
0,
true);
d->m_World->SetPhysical(physWorld); d->m_World->SetPhysical(physWorld);
// no transforms are allowed for the world box
// Matrix4f transform = box->GetMatrix();
// d->m_World->SetTransform(transform);
} }
void Scene::SetEmitter(EmitterPrimary *emitter) { void Scene::SetEmitter(EmitterPrimary *emitter) { d->m_Emitter = emitter; }
d->m_Emitter = emitter; void Scene::Initialize() { d->Initialize(); }
}
void Scene::Initialize() {
d->Initialize();
}
void Scene::SetVerbosity(int level) { void Scene::SetVerbosity(int level) {
d->m_Verbosity = level; d->m_Context.verbosity = level;
if (d->m_RunManager) d->m_RunManager->SetVerboseLevel(level); if (d->m_RunManager) d->m_RunManager->SetVerboseLevel(level);
} }
void Scene::RunSimulation(int nEvents, Vector<GeantEvent> &results) { void Scene::RunSimulation(int nEvents, Vector<GeantEvent> &results) {
d->m_Output = &results; d->Initialize(); // Ensure initialized
d->m_Context.mode = SimulationMode::DETAILED;
d->m_Context.outputGeant = &results;
d->m_Context.outputMuon = nullptr;
// Re-initialize ActionInitialization with the output buffer d->m_RunManager->BeamOn(nEvents);
// (ActionInitialization was already set during Initialize, but we need }
// to ensure the output pointer is current)
d->m_RunManager->SetUserInitialization(
new ActionInitialization(d->m_Emitter, &results, d->m_Verbosity));
// Re-run initialization to propagate the ActionInitialization change to worker threads void Scene::RunDetectorSimulation(int nEvents, Vector<MuonEvent> &results) {
d->m_RunManager->Initialize(); d->Initialize(); // Ensure initialized
d->m_Context.mode = SimulationMode::DETECTOR;
d->m_Context.outputGeant = nullptr;
d->m_Context.outputMuon = &results;
// Find detector planes
d->m_Context.detectorPlanes.clear();
for (Solid* s : d->m_Solids) {
if (BoxSolid* bs = dynamic_cast<BoxSolid*>(s)) {
if (DetectorChamber* dc = dynamic_cast<DetectorChamber*>(bs->GetObject())) {
d->m_Context.detectorPlanes.push_back(dc->GetWorldProjectionPlane());
}
}
}
d->m_RunManager->BeamOn(nEvents); d->m_RunManager->BeamOn(nEvents);
} }
} // namespace Geant } // namespace Geant
} // namespace uLib } // namespace uLib

View File

@@ -32,6 +32,7 @@
#include "Core/Vector.h" #include "Core/Vector.h"
#include "Solid.h" #include "Solid.h"
#include "GeantEvent.h" #include "GeantEvent.h"
#include "HEP/Detectors/MuonEvent.h"
class G4VPhysicalVolume; class G4VPhysicalVolume;
@@ -68,6 +69,9 @@ public:
/// Results are appended to the provided vector. /// Results are appended to the provided vector.
void RunSimulation(int nEvents, Vector<GeantEvent> &results); void RunSimulation(int nEvents, Vector<GeantEvent> &results);
/// Specialized detector simulation trackingMuonEvent line crossings.
void RunDetectorSimulation(int nEvents, Vector<MuonEvent> &results);
private: private:
class SceneImpl *d; class SceneImpl *d;
}; };

View File

@@ -0,0 +1,30 @@
#ifndef U_GEANT_SIMULATIONCONTEXT_H
#define U_GEANT_SIMULATIONCONTEXT_H
#include "Core/Vector.h"
#include "GeantEvent.h"
#include "HEP/Detectors/MuonEvent.h"
#include "Math/Dense.h"
#include <mutex>
namespace uLib {
namespace Geant {
enum class SimulationMode {
DETAILED,
DETECTOR
};
struct SimulationContext {
SimulationMode mode = SimulationMode::DETAILED;
Vector<GeantEvent> *outputGeant = nullptr;
Vector<MuonEvent> *outputMuon = nullptr;
Vector<HLine3f> detectorPlanes;
int verbosity = 0;
std::mutex outputMutex;
};
} // namespace Geant
} // namespace uLib
#endif

View File

@@ -107,6 +107,8 @@ public:
BoxSolid(const char *name, ContainerBox *box); BoxSolid(const char *name, ContainerBox *box);
virtual G4VSolid* GetG4Solid() const override { return (G4VSolid*)m_Solid; } virtual G4VSolid* GetG4Solid() const override { return (G4VSolid*)m_Solid; }
ContainerBox* GetObject() const { return m_Object; }
public slots: public slots:
void Update(); void Update();

View File

@@ -1,111 +1,124 @@
#include "SteppingAction.hh" #include "SteppingAction.hh"
#include <Geant4/G4Step.hh>
#include "G4Step.hh" #include <Geant4/G4Track.hh>
#include "G4Track.hh" #include <Geant4/G4Event.hh>
#include "G4Event.hh" #include <Geant4/G4SystemOfUnits.hh>
#include "G4RunManager.hh"
#include "G4LogicalVolume.hh"
#include "G4SystemOfUnits.hh"
#include "G4ParticleDefinition.hh"
#include <set> #include <set>
#include <iostream> #include <iostream>
#include <mutex> #include <mutex>
#include <cmath>
static std::mutex g_SimulationOutputMutex;
namespace uLib { namespace uLib {
namespace Geant { namespace Geant {
SteppingAction::SteppingAction(Vector<GeantEvent> *output) SteppingAction::SteppingAction(SimulationContext *context)
: G4UserSteppingAction(), : G4UserSteppingAction(),
m_Output(output), G4UserEventAction(),
m_Current(), m_Context(context),
m_LastEventID(-1) m_Verbosity(0)
{} {}
SteppingAction::~SteppingAction() {} SteppingAction::~SteppingAction() {}
void SteppingAction::BeginOfEventAction(const G4Event *event) { void SteppingAction::BeginOfEventAction(const G4Event *event) {
if (!event || !m_Output) return; if (!event || !m_Context) return;
// Start a new GeantEvent if (m_Context->mode == SimulationMode::DETAILED) {
m_Current = GeantEvent(); m_CurrentGeant = GeantEvent();
m_Current.m_EventID = static_cast<Id_t>(event->GetEventID()); m_CurrentGeant.m_EventID = static_cast<Id_t>(event->GetEventID());
// Set initial momentum and generation vector from primary vertex
if (event->GetNumberOfPrimaryVertex() > 0) { if (event->GetNumberOfPrimaryVertex() > 0) {
G4PrimaryVertex *vtx = event->GetPrimaryVertex(0); G4PrimaryVertex *vtx = event->GetPrimaryVertex(0);
G4ThreeVector pos = vtx->GetPosition(); G4ThreeVector pos = vtx->GetPosition();
m_Current.m_GenVector.origin = HPoint3f(pos.x(), pos.y(), pos.z()); m_CurrentGeant.m_GenVector.origin = HPoint3f(pos.x(), pos.y(), pos.z());
if (vtx->GetNumberOfParticle() > 0) { if (vtx->GetNumberOfParticle() > 0) {
G4PrimaryParticle *prim = vtx->GetPrimary(0); G4PrimaryParticle *prim = vtx->GetPrimary(0);
G4ThreeVector mom = prim->GetMomentumDirection(); G4ThreeVector mom = prim->GetMomentumDirection();
m_Current.m_GenVector.direction = HVector3f(mom.x(), mom.y(), mom.z()); m_CurrentGeant.m_GenVector.direction = HVector3f(mom.x(), mom.y(), mom.z());
m_Current.m_Momentum = static_cast<Scalarf>(prim->GetTotalMomentum() / MeV); m_CurrentGeant.m_Momentum = static_cast<Scalarf>(prim->GetTotalMomentum() / MeV);
} }
} }
} else {
// Detector mode
m_MuonCrossCount = 0;
float nan = std::numeric_limits<float>::quiet_NaN();
m_CurrentMuon.LineIn().origin = HPoint3f(nan, nan, nan);
m_CurrentMuon.LineIn().direction = HVector3f(nan, nan, nan);
m_CurrentMuon.LineOut().origin = HPoint3f(nan, nan, nan);
m_CurrentMuon.LineOut().direction = HVector3f(nan, nan, nan);
m_CurrentMuon.Momentum() = nan;
}
} }
void SteppingAction::EndOfEventAction(const G4Event *event) { void SteppingAction::EndOfEventAction(const G4Event *event) {
if (m_Output && !m_Current.m_Path.empty()) { if (!m_Context) return;
if (m_Verbosity > 0) {
std::cout << "[Geant] Finished Event " << m_Current.m_EventID
<< " with " << m_Current.m_Path.size() << " steps." << std::endl;
// Check if we hit anything other than World if (m_Context->mode == SimulationMode::DETAILED && m_Context->outputGeant) {
std::set<std::string> volumes; if (!m_CurrentGeant.m_Path.empty()) {
for (const auto& delta : m_Current.m_Path) { std::lock_guard<std::mutex> lock(m_Context->outputMutex);
if (!delta.m_SolidName.empty()) volumes.insert(delta.m_SolidName); m_Context->outputGeant->push_back(m_CurrentGeant);
}
if (volumes.size() > 1) {
std::cout << " - Hit volumes: ";
for (const auto& v : volumes) std::cout << v << " ";
std::cout << std::endl;
}
}
{
std::lock_guard<std::mutex> lock(g_SimulationOutputMutex);
m_Output->push_back(m_Current);
} }
} else if (m_Context->mode == SimulationMode::DETECTOR && m_Context->outputMuon) {
// In detector mode, we always push the event (to keep indexing consistent)
// or only if hit? User requested "all muon event line in and out ar at first set to nan".
// So we push everything.
std::lock_guard<std::mutex> lock(m_Context->outputMutex);
m_Context->outputMuon->push_back(m_CurrentMuon);
} }
} }
void SteppingAction::UserSteppingAction(const G4Step *step) { void SteppingAction::UserSteppingAction(const G4Step *step) {
if (!step || !m_Output) return; if (!step || !m_Context) return;
const G4Track *track = step->GetTrack(); const G4Track *track = step->GetTrack();
if (!track) return; if (!track || track->GetParentID() != 0) return;
// Only record primary particle (muon) if (m_Context->mode == SimulationMode::DETAILED) {
if (track->GetParentID() != 0) return;
// Record a Delta for this step
GeantEvent::Delta delta; GeantEvent::Delta delta;
// Step length
delta.m_Length = static_cast<Scalarf>(step->GetStepLength() / mm); delta.m_Length = static_cast<Scalarf>(step->GetStepLength() / mm);
delta.m_Momentum = static_cast<Scalarf>(track->GetMomentum().mag() / MeV);
// Post-step momentum
G4ThreeVector postMom = track->GetMomentum();
delta.m_Momentum = static_cast<Scalarf>(postMom.mag() / MeV);
// Post-step direction
G4ThreeVector dir = track->GetMomentumDirection(); G4ThreeVector dir = track->GetMomentumDirection();
delta.m_Direction = HVector3f(static_cast<float>(dir.x()), delta.m_Direction = HVector3f(dir.x(), dir.y(), dir.z());
static_cast<float>(dir.y()),
static_cast<float>(dir.z()));
// Solid name where the step occurred if (track->GetVolume()) {
const G4LogicalVolume *vol = track->GetVolume() delta.m_SolidName = track->GetVolume()->GetName();
? track->GetVolume()->GetLogicalVolume() }
: nullptr; m_CurrentGeant.m_Path.push_back(delta);
if (vol) { } else {
delta.m_SolidName = vol->GetName(); // Detector Mode
if (std::isnan(m_CurrentMuon.Momentum())) {
m_CurrentMuon.Momentum() = static_cast<Scalarf>(track->GetMomentum().mag() / MeV);
} }
m_Current.m_Path.push_back(delta); G4ThreeVector p1 = step->GetPreStepPoint()->GetPosition();
G4ThreeVector p2 = step->GetPostStepPoint()->GetPosition();
G4ThreeVector dir_g4 = track->GetMomentumDirection();
HPoint3f p1f(p1.x(), p1.y(), p1.z());
HPoint3f p2f(p2.x(), p2.y(), p2.z());
HVector3f dirf(dir_g4.x(), dir_g4.y(), dir_g4.z());
for (const auto& plane : m_Context->detectorPlanes) {
float d1 = (p1f - plane.origin).dot(plane.direction);
float d2 = (p2f - plane.origin).dot(plane.direction);
if ((d1 > 0 && d2 <= 0) || (d1 < 0 && d2 >= 0)) {
float t = d1 / (d1 - d2);
HPoint3f intersection = p1f + t * (p2f - p1f);
if (m_MuonCrossCount == 0) {
m_CurrentMuon.LineIn().origin = intersection;
m_CurrentMuon.LineIn().direction = dirf;
m_MuonCrossCount++;
} else if (m_MuonCrossCount == 1) {
m_CurrentMuon.LineOut().origin = intersection;
m_CurrentMuon.LineOut().direction = dirf;
m_MuonCrossCount++;
}
}
}
}
} }
} // namespace Geant } // namespace Geant

View File

@@ -5,16 +5,16 @@
#include "G4UserEventAction.hh" #include "G4UserEventAction.hh"
#include "Core/Vector.h" #include "Core/Vector.h"
#include "GeantEvent.h" #include "GeantEvent.h"
#include "HEP/Detectors/MuonEvent.h"
#include "SimulationContext.h"
namespace uLib { namespace uLib {
namespace Geant { namespace Geant {
/// SteppingAction collects scattering data at each Geant4 step and /// SteppingAction collects scattering data at each Geant4 step.
/// builds GeantEvent objects in the output buffer.
class SteppingAction : public G4UserSteppingAction, public G4UserEventAction { class SteppingAction : public G4UserSteppingAction, public G4UserEventAction {
public: public:
/// @param output pointer to the results vector owned by the Scene SteppingAction(SimulationContext *context);
SteppingAction(Vector<GeantEvent> *output);
virtual ~SteppingAction(); virtual ~SteppingAction();
virtual void UserSteppingAction(const G4Step *step) override; virtual void UserSteppingAction(const G4Step *step) override;
@@ -24,9 +24,10 @@ public:
void SetVerbosity(int level) { m_Verbosity = level; } void SetVerbosity(int level) { m_Verbosity = level; }
private: private:
Vector<GeantEvent> *m_Output; ///< destination for finished events SimulationContext *m_Context;
GeantEvent m_Current; ///< event being built GeantEvent m_CurrentGeant;
int m_LastEventID; ///< track event transitions MuonEvent m_CurrentMuon;
int m_MuonCrossCount = 0;
int m_Verbosity = 0; int m_Verbosity = 0;
}; };

View File

@@ -13,7 +13,7 @@ int main(int argc, char **argv) {
// runManager->SetUserInitialization(new PhysicsList()); // runManager->SetUserInitialization(new PhysicsList());
// 3. INIZIALIZZAZIONE DELLE AZIONI (Il nostro generatore!) // 3. INIZIALIZZAZIONE DELLE AZIONI (Il nostro generatore!)
runManager->SetUserInitialization(new uLib::Geant::ActionInitialization()); runManager->SetUserInitialization(new uLib::Geant::ActionInitialization(nullptr, nullptr));
// ... Inizializzazione del kernel ( runManager->Initialize(); ), UI manager, // ... Inizializzazione del kernel ( runManager->Initialize(); ), UI manager,
// vis manager, ecc. // vis manager, ecc.

View File

@@ -25,8 +25,8 @@ int main(int argc, char** argv) {
scene.ConstructWorldBox(Vector3f(30_m, 30_m, 30_m), "G4_AIR"); scene.ConstructWorldBox(Vector3f(30_m, 30_m, 30_m), "G4_AIR");
ContainerBox iron_box; ContainerBox iron_box;
iron_box.Scale(Vector3f(18_m, 18_m, 18_m)); iron_box.Scale(Vector3f(18_m, 10_cm, 18_m));
iron_box.SetPosition(Vector3f(-9_m, -9_m, -9_m)); iron_box.SetPosition(Vector3f(-9_m, -5_cm, -9_m));
Geant::BoxSolid* iron_cube = new Geant::BoxSolid("IronCube", &iron_box); Geant::BoxSolid* iron_cube = new Geant::BoxSolid("IronCube", &iron_box);
iron_cube->SetNistMaterial("G4_Fe"); iron_cube->SetNistMaterial("G4_Fe");
iron_cube->Update(); iron_cube->Update();
@@ -60,7 +60,9 @@ int main(int argc, char** argv) {
scene.SetEmitter(emitter); scene.SetEmitter(emitter);
scene.SetVerbosity(1); scene.SetVerbosity(1);
scene.Initialize(); // scene.Initialize(); // Removed to avoid premature initialization
std::cout << "Starting simulation of " << nEvents << " events..." << std::endl; std::cout << "Starting simulation of " << nEvents << " events..." << std::endl;
Vector<Geant::GeantEvent> results; Vector<Geant::GeantEvent> results;
@@ -80,5 +82,23 @@ int main(int argc, char** argv) {
std::cout << " Average steps per event: " << static_cast<double>(total_steps) / results.size() << std::endl; std::cout << " Average steps per event: " << static_cast<double>(total_steps) / results.size() << std::endl;
} }
std::cout << "\nStarting Detector Simulation of " << nEvents << " events..." << std::endl;
Vector<MuonEvent> detectorResults;
scene.RunDetectorSimulation(nEvents, detectorResults);
std::cout << "Detector Simulation finished." << std::endl;
size_t hit_count = 0;
for (const auto& ev : detectorResults) {
if (!std::isnan(ev.LineIn().origin.x())) {
hit_count++;
}
}
std::cout << " Muons crossing at least one detector: " << hit_count << std::endl;
if (nEvents > 0) {
std::cout << " Efficiency: " << (100.0 * hit_count / nEvents) << "%" << std::endl;
}
return 0; return 0;
} }