gdetector
Loading...
Searching...
No Matches
gdetector_example.cc
Go to the documentation of this file.
1
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 std::shared_ptr<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 ", tid, " started");
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 ", sdname, " loadConstants for run ", runNumber);
108
109 if (digi_routine->loadConstants(runNumber, variation) == false) {
110 log->error(ERR_LOADCONSTANTFAIL, "Failed to load constants for ", sdname, " for run ", runNumber, " with variation ", variation);
111 }
112
113 log->debug(NORMAL, "Calling ", sdname, " loadTT for run ", runNumber);
114 if (digi_routine->loadTT(runNumber, variation) == false) {
115 log->error(ERR_LOADTTFAIL, "Failed to load translation table for ", sdname, " for run ", runNumber, " with variation ", variation);
116 }
117 // each event has 10 hits
118 for (unsigned i = 1; i < 11; i++) {
119 auto hit = GHit::create(gopts);
120 auto true_data = digi_routine->collectTrueInformation(hit, i);
121 auto digi_data = digi_routine->digitizeHit(hit, i);
122
123 eventData->addDetectorDigitizedData("flux", std::move(digi_data));
124 eventData->addDetectorTrueInfoData("flux", std::move(true_data));
125 }
126
127 const auto& flux_data_it = eventData->getDataCollectionMap().find("flux");
128
129
130 if (flux_data_it != eventData->getDataCollectionMap().end()) {
131 const auto& digitized_data = flux_data_it->second->getDigitizedData();
132 log->info(0, "worker ", tid, " event ", evn, " has ", digitized_data.size(), " digitized hits");
133 }
134
135 // NOTE: This example does not currently push eventData into localRunData.
136 // localRunData is retained to demonstrate a typical staging/merge pattern
137 // used in multi-threaded pipelines.
138 ++localCount; // tally for this worker
139 }
140
141 // braces to locks the mutex when it's constructed and unlocks when it is destroyed
142 {
143 std::scoped_lock lk(collectorMtx);
144 for (auto& evt : localRunData) { collected.emplace_back(evt); }
145 localRunData.clear();
146 }
147
148
149 log->info(0, "worker ", tid, " processed ", localCount, " events");
150 }); // jthread constructor launches the thread immediately
151 } // pool’s destructor blocks until every jthread has joined
152 return collected;
153}
154
155
170int main(int argc, char* argv[]) {
171 // Create GOptions using gdata::defineOptions, which aggregates options from gdata and gtouchable.
172 auto gopts = std::make_shared<GOptions>(argc, argv, gdetector::defineOptions());
173
174 // Create loggers: one for gdata and one for gtouchable.
175 auto log = std::make_shared<GLogger>(gopts, SFUNCTION_NAME, GDETECTOR_LOGGER);
176
177 constexpr int nevents = 20;
178 constexpr int nthreads = 2;
179
180 auto runManager = G4RunManagerFactory::CreateRunManager(G4RunManagerType::Default);
181 auto physicsList = new QBBC;
182
183 runManager->SetUserInitialization(physicsList);
184
185 auto gdetector = std::make_shared<GDetectorConstruction>(gopts);
186 auto gsystems = gsystem::getSystems(gopts);
187 gdetector->reload_geometry(gsystems);
188
189 auto runDat = run_simulation_in_threads(nevents, nthreads, gopts, log, gdetector);
190
191 return EXIT_SUCCESS;
192}
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 std::shared_ptr< 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[])