actions
Loading...
Searching...
No Matches
generator_file_events_example.cc
Go to the documentation of this file.
1// actions
3
4// gparticle
5#include "gparticle_reader.h"
6
7// geant4
8#include "G4Event.hh"
9#include "G4Box.hh"
10#include "G4LogicalVolume.hh"
11#include "G4NistManager.hh"
12#include "G4PVPlacement.hh"
13#include "G4RunManagerFactory.hh"
14#include "G4Threading.hh"
15#include "G4UserEventAction.hh"
16#include "G4VUserActionInitialization.hh"
17#include "G4VUserDetectorConstruction.hh"
18#include "QBBC.hh"
19
20// c++
21#include <atomic>
22#include <cstdlib>
23#include <iostream>
24#include <memory>
25
26namespace {
27constexpr int stress_events = 512;
28constexpr int stress_multiplicity = 32;
29
30struct StressResults {
31 std::atomic<int> checked_events{0};
32 std::atomic<int> invalid_events{0};
33 std::atomic<unsigned int> worker_mask{0};
34};
35
36class EmptyDetectorConstruction final : public G4VUserDetectorConstruction {
37public:
38 G4VPhysicalVolume* Construct() override {
39 auto* material = G4NistManager::Instance()->FindOrBuildMaterial("G4_Galactic");
40 auto* solid = new G4Box("generator_stress_world", 1 * CLHEP::m, 1 * CLHEP::m, 1 * CLHEP::m);
41 auto* logical = new G4LogicalVolume(solid, material, "generator_stress_world");
42 return new G4PVPlacement(nullptr, {}, logical, "generator_stress_world", nullptr, false, 0);
43 }
44};
45
46class RuntimeRecordCheckingAction final : public G4UserEventAction {
47public:
48 explicit RuntimeRecordCheckingAction(std::shared_ptr<StressResults> results) : results_(std::move(results)) {}
49
50 void EndOfEventAction(const G4Event* event) override {
51 const auto worker_id = G4Threading::G4GetThreadId();
52 if (worker_id >= 0 && worker_id < 4) {
53 results_->worker_mask.fetch_or(1U << worker_id, std::memory_order_relaxed);
54 }
56 if (records.size() != stress_multiplicity ||
57 event->GetNumberOfPrimaryVertex() != stress_multiplicity) {
58 results_->invalid_events.fetch_add(1, std::memory_order_relaxed);
59 }
60 results_->checked_events.fetch_add(1, std::memory_order_relaxed);
61 }
62
63private:
64 std::shared_ptr<StressResults> results_;
65};
66
67class SharedGeneratorActionInitialization final : public G4VUserActionInitialization {
68public:
69 SharedGeneratorActionInitialization(std::shared_ptr<GOptions> options,
70 std::shared_ptr<std::vector<GparticlePtr>> particles,
71 std::shared_ptr<StressResults> results) :
72 options_(std::move(options)), particles_(std::move(particles)), results_(std::move(results)) {}
73
74 void Build() const override {
75 SetUserAction(new GPrimaryGeneratorAction(options_, particles_));
76 SetUserAction(new RuntimeRecordCheckingAction(results_));
77 }
78
79private:
80 std::shared_ptr<GOptions> options_;
81 std::shared_ptr<std::vector<GparticlePtr>> particles_;
82 std::shared_ptr<StressResults> results_;
83};
84}
85
86int main(int argc, char* argv[]) {
87 auto option_definitions = gparticle::defineOptions();
88 option_definitions += gprimaryaction::defineOptions();
89
90 auto gopts = std::make_shared<GOptions>(argc, argv, option_definitions);
91 auto log = std::make_shared<GLogger>(gopts, FUNCTION_NAME, GPRIMARYGENERATORACTION_LOGGER);
92
93 auto runManager = G4RunManagerFactory::CreateRunManager(G4RunManagerType::Default);
94 runManager->SetNumberOfThreads(4);
95 runManager->SetUserInitialization(new QBBC);
96
97 auto source_events = gparticle::getGParticleEventsFromSources(gopts, log);
98 if (source_events.size() != 11) {
99 std::cerr << "Expected 11 gparticlefile events, got " << source_events.size() << '\n';
100 delete runManager;
101 return EXIT_FAILURE;
102 }
103
104 GPrimaryGeneratorAction generator(gopts);
105 auto inline_particles = gparticle::getGParticlesFromOption(gopts, log);
106 for (size_t event_index = 0; event_index < source_events.size(); event_index++) {
107 G4Event event(static_cast<G4int>(event_index));
108 generator.GeneratePrimaries(&event);
109
110 const auto expected_vertices = static_cast<G4int>(
111 inline_particles.size() + source_events[event_index].size());
112 if (event.GetNumberOfPrimaryVertex() != expected_vertices) {
113 std::cerr << "Event " << event_index << " expected " << expected_vertices
114 << " primary vertices, got " << event.GetNumberOfPrimaryVertex() << '\n';
115 delete runManager;
116 return EXIT_FAILURE;
117 }
119 if (tracked_records.size() != static_cast<size_t>(expected_vertices)) {
120 std::cerr << "Event " << event_index << " expected " << expected_vertices
121 << " tracked runtime records, got " << tracked_records.size() << '\n';
122 delete runManager;
123 return EXIT_FAILURE;
124 }
125 }
126
127 // Reproduce the GUI ownership model: every worker receives the same particle-definition vector. Runtime
128 // records must remain worker-local even when randomized multiplicity creates substantial overlap.
129 char* stress_argv[] = {argv[0], nullptr};
130 auto stress_definitions = gparticle::defineOptions();
131 stress_definitions += gprimaryaction::defineOptions();
132 auto stress_options = std::make_shared<GOptions>(1, stress_argv, stress_definitions);
133 auto stress_log = std::make_shared<GLogger>(stress_options, FUNCTION_NAME, GPRIMARYGENERATORACTION_LOGGER);
134 auto stress_particles = std::make_shared<std::vector<GparticlePtr>>();
135 stress_particles->emplace_back(std::make_shared<Gparticle>(
136 "geantino", stress_multiplicity, 1000 * CLHEP::MeV, 0, "uniform",
137 90 * CLHEP::deg, 0, "uniform", 0, 360 * CLHEP::deg,
138 0, 0, 0, 0, 0, 0, "uniform", stress_log));
139 auto stress_results = std::make_shared<StressResults>();
140
141 runManager->SetUserInitialization(new EmptyDetectorConstruction());
142 runManager->SetUserInitialization(
143 new SharedGeneratorActionInitialization(stress_options, stress_particles, stress_results));
144 runManager->Initialize();
145 runManager->BeamOn(stress_events);
146
147 constexpr unsigned int expected_worker_mask = (1U << 4U) - 1U;
148 if (stress_results->checked_events.load(std::memory_order_relaxed) != stress_events ||
149 stress_results->invalid_events.load(std::memory_order_relaxed) != 0 ||
150 stress_results->worker_mask.load(std::memory_order_relaxed) != expected_worker_mask) {
151 std::cerr << "Shared-generator stress test checked "
152 << stress_results->checked_events.load(std::memory_order_relaxed) << " of " << stress_events
153 << " events and found " << stress_results->invalid_events.load(std::memory_order_relaxed)
154 << " invalid worker-local record sets; worker mask: "
155 << stress_results->worker_mask.load(std::memory_order_relaxed) << '\n';
156 delete runManager;
157 return EXIT_FAILURE;
158 }
159
160 delete runManager;
161 return EXIT_SUCCESS;
162}
Generates the primary vertices for each Geant4 event.
static const GParticleRecordEvent & currentGeneratedTrackedParticleRecords()
Returns the current event's Geant4-tracked generated-particle records.
void GeneratePrimaries(G4Event *event) override
Generates the primary content for one event.
Declares GPrimaryGeneratorAction, the primary-particle generation action for the GEMC actions module.
constexpr const char * GPRIMARYGENERATORACTION_LOGGER
event
#define FUNCTION_NAME
vector< GparticlePtr > getGParticlesFromOption(const std::shared_ptr< GOptions > &gopts, std::shared_ptr< GLogger > &logger)
GOptions defineOptions()
GParticleEvents getGParticleEventsFromSources(const std::shared_ptr< GOptions > &gopts, std::shared_ptr< GLogger > &logger, bool propagated_only=true)
GOptions defineOptions()
Returns the options associated with the primary-generator action scope.
int main(int argc, char *argv[])