vtkGeantEvent

This commit is contained in:
AndreaRigoni
2026-03-16 17:51:53 +00:00
parent c63a1ae047
commit d8ef413216
31 changed files with 509 additions and 155 deletions

View File

@@ -27,9 +27,11 @@ void ActionInitialization::Build() const {
SetUserAction(new EmitterPrimary());
}
// Register stepping action to collect scattering data
// Register actions
if (m_Output) {
SetUserAction(new SteppingAction(m_Output));
SteppingAction *sa = new SteppingAction(m_Output);
SetUserAction(static_cast<G4UserSteppingAction*>(sa));
SetUserAction(static_cast<G4UserEventAction*>(sa));
}
}

View File

@@ -0,0 +1,25 @@
#include "HEP/Geant/GeantEvent.h"
#include <cstddef>
#include <iostream>
using namespace uLib;
void GeantEvent::Print(const size_t size) const {
std::cout << "Event " << m_EventID << ":" << std::endl;
std::cout << " Momentum: " << m_Momentum << std::endl;
std::cout << " GenVector: " << m_GenVector << std::endl;
std::cout << " Path: " << std::endl;
size_t limit = m_Path.size();
if(size > 0 && size < m_Path.size()) {
limit = size;
}
for (size_t i = 0; i < limit; ++i) {
std::cout << " " << i << ": " << m_Path[i].m_Length << " " << m_Path[i].m_Momentum << " " << m_Path[i].m_Direction << " " << m_Path[i].m_SolidName << std::endl;
}
if (limit < m_Path.size()) {
std::cout << " ... (" << m_Path.size() - limit << " more deltas)" << std::endl;
}
}

View File

@@ -71,6 +71,8 @@ public:
uLibConstRefMacro(GenVector, HLine3f)
uLibConstRefMacro(Path, Vector<Delta>)
void Print(const size_t size = 10) const;
private:
Id_t m_EventID;
Scalarf m_Momentum;

View File

@@ -95,6 +95,7 @@ public:
// members //
Vector<Solid *> m_Solids;
Solid *m_World = nullptr;
ContainerBox m_WorldBox;
G4RunManager *m_RunManager;
EmitterPrimary *m_Emitter;
Vector<GeantEvent> *m_Output;
@@ -125,16 +126,22 @@ void Scene::AddSolid(Solid *solid, Solid *parent) {
}
}
void Scene::ConstructWorldBox(const ContainerBox *box, const char *material) {
const Solid* Scene::GetWorld() const { return d->m_World; }
ContainerBox* Scene::GetWorldBox() const { return &d->m_WorldBox; }
void Scene::ConstructWorldBox(const Vector3f &size, const char *material) {
// Get nist material manager
d->m_WorldBox.Scale(size);
d->m_WorldBox.SetPosition(-size/2.0f);
if (!d->m_World) {
d->m_World = new Solid("World");
d->m_World->SetNistMaterial(material);
AddSolid(d->m_World);
}
Vector3f size = box->GetSize();
G4Box *solidWorld = new G4Box("World",
0.5 * size(0),
0.5 * size(1),
@@ -157,9 +164,10 @@ void Scene::ConstructWorldBox(const ContainerBox *box, const char *material) {
true);
d->m_World->SetPhysical(physWorld);
Matrix4f transform = box->GetMatrix();
d->m_World->SetTransform(transform);
// no transforms are allowed for the world box
// Matrix4f transform = box->GetMatrix();
// d->m_World->SetTransform(transform);
}
void Scene::SetEmitter(EmitterPrimary *emitter) {

View File

@@ -47,7 +47,12 @@ public:
void AddSolid(Solid *solid, Solid *parent = nullptr);
void ConstructWorldBox(const ContainerBox *box, const char *material);
void ConstructWorldBox(const Vector3f &size, const char *material);
/// Get the world box
const Solid* GetWorld() const;
ContainerBox* GetWorldBox() const;
/// Set the primary generator (emitter) for the simulation.
/// The Scene does NOT take ownership of the emitter.

View File

@@ -55,10 +55,17 @@ public:
};
Solid::Solid()
: m_Name("unnamed_solid"), m_Material(NULL), m_Logical(NULL), m_Physical(NULL) {}
: m_Name("unnamed_solid"), m_Material(NULL), m_Logical(NULL), m_Physical(NULL),
m_Position(new G4ThreeVector(0,0,0)), m_Rotation(NULL) {}
Solid::Solid(const char *name)
: m_Name(name), m_Material(NULL), m_Logical(NULL), m_Physical(NULL) {}
: m_Name(name), m_Material(NULL), m_Logical(NULL), m_Physical(NULL),
m_Position(new G4ThreeVector(0,0,0)), m_Rotation(NULL) {}
Solid::~Solid() {
if (m_Position) delete m_Position;
if (m_Rotation) delete m_Rotation;
}
void Solid::SetNistMaterial(const char *name) {
G4NistManager *nist = G4NistManager::Instance();
@@ -81,21 +88,22 @@ void Solid::SetTransform(Matrix4f transform) {
uLib::AffineTransform t;
t.SetMatrix(transform);
// 2. Extracto position and rotation for Geant4
// 2. Extract position and rotation for Geant4
Vector3f pos = t.GetPosition();
G4ThreeVector g4pos(pos(0), pos(1), pos(2));
if (!m_Position) m_Position = new G4ThreeVector();
*m_Position = G4ThreeVector(pos(0), pos(1), pos(2));
// Create a G4 rotation matrix from the 4x4 matrix
Matrix3f m = t.GetRotation();
G4RotationMatrix* rot = new G4RotationMatrix();
rot->set(G4ThreeVector(m(0,0), m(1,0), m(2,0)),
G4ThreeVector(m(0,1), m(1,1), m(2,1)),
G4ThreeVector(m(0,2), m(1,2), m(2,2)));
if (!m_Rotation) m_Rotation = new G4RotationMatrix();
m_Rotation->set(G4ThreeVector(m(0,0), m(1,0), m(2,0)),
G4ThreeVector(m(0,1), m(1,1), m(2,1)),
G4ThreeVector(m(0,2), m(1,2), m(2,2)));
// 3. Se l'oggetto è già stato piazzato, aggiorniamo la sua trasformazione
// 3. If object is already placed, update its transformation
if (m_Physical) {
m_Physical->SetTranslation(g4pos);
m_Physical->SetRotation(rot);
m_Physical->SetTranslation(*m_Position);
m_Physical->SetRotation(m_Rotation);
}
}
@@ -121,8 +129,8 @@ void Solid::SetParent(Solid *parent) {
// G4PVPlacement
m_Physical = new G4PVPlacement(
nullptr, // Rotation
G4ThreeVector(0,0,0), // Position (translation) inside the parent
m_Rotation, // Rotation
*m_Position, // Position (translation) inside the parent
m_Logical, // The logical volume of this solid (the child)
GetName(), // Name of the physical volume
parentLogical, // The logical volume of the parent (nullptr if it's the World volume)
@@ -139,7 +147,8 @@ void Solid::SetParent(Solid *parent) {
TessellatedSolid::TessellatedSolid(const char *name)
: BaseClass(name), m_Solid(new G4TessellatedSolid(name)) {}
: BaseClass(name), m_Solid(new G4TessellatedSolid(name)) {
}
void TessellatedSolid::SetMesh(TriangleMesh &mesh) {
G4TessellatedSolid *ts = this->m_Solid;
@@ -161,12 +170,13 @@ void TessellatedSolid::SetMesh(TriangleMesh &mesh) {
BoxSolid::BoxSolid(const char *name, ContainerBox *box) : BaseClass(name) {
m_Solid = new G4Box(name, 0.5, 0.5, 0.5);
m_Solid = new G4Box(name, 1,1,1);
m_Object = box;
Object::connect(box, &ContainerBox::Updated, this, &BoxSolid::Update);
if (m_Logical) {
m_Logical->SetSolid(m_Solid);
}
Update();
}
void BoxSolid::Update() {
@@ -176,8 +186,20 @@ void BoxSolid::Update() {
m_Solid->SetYHalfLength(size(1) * 0.5);
m_Solid->SetZHalfLength(size(2) * 0.5);
this->SetTransform(m_Object->GetMatrix());
// this->m_Logical->SetSolid(m_Solid);
// Geant4 placement is relative to center. uLib Box is anchored at corner.
// 1. Get position and rotation (clean, without scale)
Vector3f pos = m_Object->GetPosition();
Matrix3f rot = m_Object->GetRotation();
// 2. Center = Corner + Rotation * (Half-Size)
// We must rotate the offset vector because uLib box can be rotated.
Vector3f center = pos + rot * (size * 0.5);
uLib::AffineTransform t;
t.SetPosition(center);
t.SetRotation(rot);
this->SetTransform(t.GetMatrix());
}
}

View File

@@ -45,6 +45,7 @@ class Solid : public Object {
public:
Solid();
Solid(const char *name);
virtual ~Solid();
void SetNistMaterial(const char *name);
void SetMaterial(G4Material *material);

View File

@@ -7,6 +7,8 @@
#include "G4LogicalVolume.hh"
#include "G4SystemOfUnits.hh"
#include "G4ParticleDefinition.hh"
#include <set>
#include <iostream>
namespace uLib {
namespace Geant {
@@ -18,9 +20,46 @@ SteppingAction::SteppingAction(Vector<GeantEvent> *output)
m_LastEventID(-1)
{}
SteppingAction::~SteppingAction() {
// Push the last event if any steps were collected
if (m_LastEventID >= 0 && !m_Current.m_Path.empty()) {
SteppingAction::~SteppingAction() {}
void SteppingAction::BeginOfEventAction(const G4Event *event) {
if (!event || !m_Output) return;
// Start a new GeantEvent
m_Current = GeantEvent();
m_Current.m_EventID = static_cast<Id_t>(event->GetEventID());
// Set initial momentum and generation vector from primary vertex
if (event->GetNumberOfPrimaryVertex() > 0) {
G4PrimaryVertex *vtx = event->GetPrimaryVertex(0);
G4ThreeVector pos = vtx->GetPosition();
m_Current.m_GenVector.origin = HPoint3f(pos.x(), pos.y(), pos.z());
if (vtx->GetNumberOfParticle() > 0) {
G4PrimaryParticle *prim = vtx->GetPrimary(0);
G4ThreeVector mom = prim->GetMomentumDirection();
m_Current.m_GenVector.direction = HVector3f(mom.x(), mom.y(), mom.z());
m_Current.m_Momentum = static_cast<Scalarf>(prim->GetTotalMomentum() / MeV);
}
}
}
void SteppingAction::EndOfEventAction(const G4Event *event) {
if (m_Output && !m_Current.m_Path.empty()) {
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
std::set<std::string> volumes;
for (const auto& delta : m_Current.m_Path) {
if (!delta.m_SolidName.empty()) volumes.insert(delta.m_SolidName);
}
if (volumes.size() > 1) {
std::cout << " - Hit volumes: ";
for (const auto& v : volumes) std::cout << v << " ";
std::cout << std::endl;
}
m_Output->push_back(m_Current);
}
}
@@ -34,36 +73,6 @@ void SteppingAction::UserSteppingAction(const G4Step *step) {
// Only record primary particle (muon)
if (track->GetParentID() != 0) return;
// Get current event ID
const G4Event *event = G4RunManager::GetRunManager()->GetCurrentEvent();
if (!event) return;
int eventID = event->GetEventID();
// Detect new event — push completed event and start fresh
if (eventID != m_LastEventID) {
if (m_LastEventID >= 0 && !m_Current.m_Path.empty()) {
m_Output->push_back(m_Current);
}
// Start a new GeantEvent
m_Current = GeantEvent();
m_Current.m_EventID = static_cast<Id_t>(eventID);
m_LastEventID = eventID;
// Set initial momentum and generation vector from primary vertex
if (event->GetNumberOfPrimaryVertex() > 0) {
G4PrimaryVertex *vtx = event->GetPrimaryVertex(0);
G4ThreeVector pos = vtx->GetPosition();
m_Current.m_GenVector.origin = HPoint3f(pos.x(), pos.y(), pos.z());
if (vtx->GetNumberOfParticle() > 0) {
G4PrimaryParticle *prim = vtx->GetPrimary(0);
G4ThreeVector mom = prim->GetMomentumDirection();
m_Current.m_GenVector.direction = HVector3f(mom.x(), mom.y(), mom.z());
m_Current.m_Momentum = static_cast<Scalarf>(prim->GetTotalMomentum() / MeV);
}
}
}
// Record a Delta for this step
GeantEvent::Delta delta;

View File

@@ -2,6 +2,7 @@
#define U_GEANT_STEPPINGACTION_HH
#include "G4UserSteppingAction.hh"
#include "G4UserEventAction.hh"
#include "Core/Vector.h"
#include "GeantEvent.h"
@@ -10,13 +11,15 @@ namespace Geant {
/// SteppingAction collects scattering data at each Geant4 step and
/// builds GeantEvent objects in the output buffer.
class SteppingAction : public G4UserSteppingAction {
class SteppingAction : public G4UserSteppingAction, public G4UserEventAction {
public:
/// @param output pointer to the results vector owned by the Scene
SteppingAction(Vector<GeantEvent> *output);
virtual ~SteppingAction();
virtual void UserSteppingAction(const G4Step *step) override;
virtual void BeginOfEventAction(const G4Event *event) override;
virtual void EndOfEventAction(const G4Event *event) override;
private:
Vector<GeantEvent> *m_Output; ///< destination for finished events

View File

@@ -5,6 +5,7 @@
#include "Math/ContainerBox.h"
#include "Math/TriangleMesh.h"
#include "Math/Dense.h"
#include "Math/Units.h"
#include "testing-prototype.h"
#include <Geant4/G4Material.hh>
#include <Geant4/G4NistManager.hh>
@@ -20,9 +21,8 @@ int main() {
// Test: Scene with iron cube in air, launch muons, collect events //
{
// 1. Create world box (air, 30m x 30m x 30m)
ContainerBox world_box(Vector3f(30000, 30000, 30000)); // mm
Geant::Scene scene;
scene.ConstructWorldBox(&world_box, "G4_AIR");
scene.ConstructWorldBox(Vector3f(30_m, 30_m, 30_m), "G4_AIR");
// 2. Create iron cube (1m x 1m x 1m) at center
ContainerBox iron_box(Vector3f(1000, 1000, 1000)); // mm

View File

@@ -8,10 +8,8 @@ using namespace uLib;
int main() {
uLib::ContainerBox world_box(Vector3f(100, 100, 100));
uLib::Geant::Scene scene;
scene.ConstructWorldBox(&world_box, "G4_AIR");
scene.ConstructWorldBox(Vector3f(100, 100, 100), "G4_AIR");
scene.Initialize();
return 0;

View File

@@ -15,9 +15,12 @@ int main() {
// Test Solid initialization and NIST material //
{
Geant::Solid solid("test_solid");
TEST1(solid.GetLogical() != nullptr);
// Logical volume is not created until material and solid are set
TEST1(solid.GetLogical() == nullptr);
solid.SetNistMaterial("G4_AIR");
// Still null because base Solid has no GetG4Solid()
TEST1(solid.GetLogical() == nullptr);
TEST1(solid.GetMaterial() != nullptr);
TEST1(solid.GetMaterial()->GetName() == "G4_AIR");
}
@@ -25,6 +28,7 @@ int main() {
// Test TessellatedSolid with a simple mesh //
{
Geant::TessellatedSolid tsolid("test_tessellated");
tsolid.SetNistMaterial("G4_AIR");
TEST1(tsolid.GetLogical() != nullptr);
TEST1(tsolid.GetSolid() != nullptr);