gdetector
Loading...
Searching...
No Matches
gdetector_example.cc
Go to the documentation of this file.
1
16
17// gdetector
19#include "gdetector_options.h"
20
21// gemc
22#include "glogger.h"
25#include "gthreads.h"
26
27// geant4
28#include "G4RunManagerFactory.hh"
29#include "QBBC.hh"
30
31// c++
32#include <atomic> // std::atomic<T>: lock-free, thread-safe integers, flags…
33#include <vector>
34#include <memory> // smart pointers
35
36
60 int nthreads,
61 const std::shared_ptr<GOptions>& gopts,
62 const std::shared_ptr<GLogger>& log,
63 const GDetectorConstruction* gdetector) -> std::vector<std::shared_ptr<GEventDataCollection>> {
64 std::mutex collectorMtx;
65 std::vector<std::shared_ptr<GEventDataCollection>> collected;
66
67 // thread-safe integer counter starts at 1.
68 // fetch_add returns the old value *and* bumps.
69 // zero contention: each thread fetches the next free event number.
70 std::atomic<int> next{1};
71
72 // pool of jthreads. jthread joins in its destructor so we don’t need an
73 // explicit loop at the end.
74 // each element represents one worker thread running your event-processing lambda.
75 // std::vector<std::jthread> pool; use this when C++20 is widely available
76 std::vector<jthread_alias> pool; // was std::vector<std::jthread>
77
78 pool.reserve(nthreads);
79
80 for (int tid = 0; tid < nthreads; ++tid) {
81 // The capture [&, tid] gives the thread references to variables like tid
82 pool.emplace_back([&, tid] // capture tid *by value*
83 {
84 log->info(0, "worker %1 started", tid);
85
86 int localCount = 0; // events built by *this* worker
87
88 // Per-thread staging buffer (thread_local) used before final collection.
89 // The collection step below uses a mutex to merge results into "collected".
90 thread_local std::vector<std::shared_ptr<GEventDataCollection>> localRunData;
91
92 while (true) {
93 // repeatedly asks the shared atomic counter for “the next unclaimed event
94 // number,” processes that event, stores the result, and goes back for more.
95 // memory_order_relaxed: we only need *atomicity*, no ordering
96 int evn = next.fetch_add(1, std::memory_order_relaxed); // atomically returns the current value and increments it by 1.
97 if (evn > nevents) break; // exit the while loop
98
99 // flux does not need variation or run number
100 std::string sdname = "flux";
101 int runNumber = 1;
102 std::string variation = "default";
103
104 auto gevent_header = GEventHeader::create(gopts, tid);
105 auto eventData = std::make_shared<GEventDataCollection>(gopts, std::move(gevent_header));
106 auto digi_routine = gdetector->get_digitization_routines_for_sdname(sdname);
107 log->debug(NORMAL, "Calling %1 loadConstants for run %2", sdname, runNumber);
108
109 if (digi_routine->loadConstants(runNumber, variation) == false) {
110 log->error(
111 ERR_LOADCONSTANTFAIL, "Failed to load constants for %1 for run %2 with variation %3",
112 sdname, runNumber, variation);
113 }
114
115 log->debug(NORMAL, "Calling %1 loadTT for run %2", sdname, runNumber);
116 if (digi_routine->loadTT(runNumber, variation) == false) {
117 log->error(
118 ERR_LOADTTFAIL, "Failed to load translation table for %1 for run %2 with variation %3",
119 sdname, runNumber, variation);
120 }
121 // each event has 10 hits
122 for (unsigned i = 1; i < 11; i++) {
123 auto hit = GHit::create(gopts);
124 auto true_data = digi_routine->collectTrueInformation(hit, i);
125 auto digi_data = digi_routine->digitizeHit(hit, i);
126
127 eventData->addDetectorDigitizedData("flux", std::move(digi_data));
128 eventData->addDetectorTrueInfoData("flux", std::move(true_data));
129 }
130
131 const auto& flux_data_it = eventData->getDataCollectionMap().find("flux");
132
133
134 if (flux_data_it != eventData->getDataCollectionMap().end()) {
135 const auto& digitized_data = flux_data_it->second->getDigitizedData();
136 log->info(0, "worker %1 event %2 has %3 digitized hits", tid, evn, digitized_data.size());
137 }
138
139 // NOTE: This example does not currently push eventData into localRunData.
140 // localRunData is retained to demonstrate a typical staging/merge pattern
141 // used in multi-threaded pipelines.
142 ++localCount; // tally for this worker
143 }
144
145 // braces to locks the mutex when it's constructed and unlocks when it is destroyed
146 {
147 std::scoped_lock lk(collectorMtx);
148 for (auto& evt : localRunData) { collected.emplace_back(evt); }
149 localRunData.clear();
150 }
151
152
153 log->info(0, "worker %1 processed %2 events", tid, localCount);
154 }); // jthread constructor launches the thread immediately
155 } // pool’s destructor blocks until every jthread has joined
156 return collected;
157}
158
159
174int main(int argc, char* argv[]) {
175 // Create GOptions using gdata::defineOptions, which aggregates options from gdata and gtouchable.
176 auto gopts = std::make_shared<GOptions>(argc, argv, gdetector::defineOptions());
177
178 // Create loggers: one for gdata and one for gtouchable.
179 auto log = std::make_shared<GLogger>(gopts, SFUNCTION_NAME, GDETECTOR_LOGGER);
180
181 constexpr int nevents = 20;
182 constexpr int nthreads = 2;
183
184 auto runManager = G4RunManagerFactory::CreateRunManager(G4RunManagerType::Default);
185 auto physicsList = new QBBC;
186
187 runManager->SetUserInitialization(physicsList);
188
189 auto gdetector = new GDetectorConstruction(gopts);
190 runManager->SetUserInitialization(gdetector);
191
192 auto gsystems = gsystem::getSystems(gopts);
193 gdetector->reload_geometry(gsystems);
194
195 auto runDat = run_simulation_in_threads(nevents, nthreads, gopts, log, gdetector);
196
197 return EXIT_SUCCESS;
198}
Builds and (optionally) reloads the Geant4 geometry from GEMC world definitions.
static std::unique_ptr< GEventHeader > create(const std::shared_ptr< GOptions > &gopts, int tid=-1)
static GHit * create(const std::shared_ptr< GOptions > &gopts)
Defines the GDetectorConstruction class, the Geant4 detector-construction entry point for the gdetect...
auto run_simulation_in_threads(int nevents, int nthreads, const std::shared_ptr< GOptions > &gopts, const std::shared_ptr< GLogger > &log, const GDetectorConstruction *gdetector) -> std::vector< std::shared_ptr< GEventDataCollection > >
Runs a digitization loop across multiple worker threads and collects event data.
Declares the gdetector module option aggregation entry point.
constexpr const char * GDETECTOR_LOGGER
Logger name used by the gdetector module.
constexpr int ERR_LOADCONSTANTFAIL
constexpr int ERR_LOADTTFAIL
#define SFUNCTION_NAME
NORMAL
GOptions defineOptions()
Builds the aggregated option set used by the gdetector module.
SystemList getSystems(const std::shared_ptr< GOptions > &gopts)
int main(int argc, char *argv[])