6#include "G4Threading.hh"
20#include <unordered_set>
25 bank.reserve(particles.size());
27 for (
const auto& particle : particles) {
32 particle.multiplicity,
45GAncestorBank make_ancestor_bank(
const std::vector<GTrackRecord>& records) {
47 bank.reserve(records.size());
48 for (
const auto& record : records) {
53 record.kinetic_energy,
65bool scalar_bool_option_enabled(
const std::shared_ptr<GOptions>&
goptions,
const std::string& name) {
66 std::string value =
goptions->getScalarString(name);
67 std::transform(value.begin(), value.end(), value.begin(),
68 [](
unsigned char c) { return static_cast<char>(std::tolower(c)); });
69 return value ==
"true" || value ==
"1" || value ==
"yes" || value ==
"on";
73bool to_non_negative_int(
const std::string& text,
int& out) {
74 if (text.empty())
return false;
77 try { value = std::stoi(text, &pos); }
78 catch (...) {
return false; }
79 if (pos != text.size() || value < 0)
return false;
89 std::shared_ptr<GTrackProvenance> provenance) :
93 track_provenance(std::move(provenance)) {
94 const auto thread_id = G4Threading::G4GetThreadId();
95 const auto desc =
"GEventAction " + std::to_string(thread_id);
104 const auto dash = spec.find(
'-');
105 std::string n_part = dash == std::string::npos ? spec : spec.substr(0, dash);
106 std::string nth_part = dash == std::string::npos ? std::string() : spec.substr(dash + 1);
109 int nthreads =
goptions->getScalarInt(
"nthreads");
110 const int ncores = G4Threading::G4GetNumberOfCores();
111 if (nthreads == 0 || nthreads > ncores) nthreads = ncores;
114 if (!to_non_negative_int(n_part, n) || n == 0) {
117 " : N must be a positive integer.");
119 else if (dash != std::string::npos) {
121 if (!to_non_negative_int(nth_part, nth) || nth >= nthreads) {
124 " : thread id must be in [0, ", nthreads - 1,
"].");
128 log_every_thread = nth;
131 else { log_every_n = n; }
138void GEventAction::log_event_start(
int thread_id) {
139 if (log_every_n <= 0)
return;
140 if (log_every_thread >= 0 && log_every_thread != thread_id)
return;
143 const auto now = std::chrono::steady_clock::now();
144 if (log_events_seen == 0) { log_start_time = now; }
147 if (log_events_seen % log_every_n != 0)
return;
149 const double elapsed_s = std::chrono::duration<double>(now - log_start_time).count();
150 const double rate = elapsed_s > 0.0 ?
static_cast<double>(log_events_seen) / elapsed_s : 0.0;
153 log->
info(0,
"Starting event n. ", log_events_seen,
" in thread ", thread_id,
154 ". Average rate: ", rate,
" events / second");
159 const auto thread_id = G4Threading::G4GetThreadId();
160 const auto event_id =
event->GetEventID();
163 if (track_provenance !=
nullptr) { track_provenance->
clear(); }
165 log_event_start(thread_id);
171 if (run_action ==
nullptr) {
173 " run_action is null - cannot access digitization routines or streamers.");
180 const auto thread_id = G4Threading::G4GetThreadId();
181 const auto event_id =
event->GetEventID();
183 auto gevent_header = std::make_unique<GEventHeader>(
goptions, event_id, thread_id);
184 auto eventDataCollection = std::make_shared<GEventDataCollection>(
goptions, std::move(gevent_header));
185 eventDataCollection->setGeneratedParticles(
187 eventDataCollection->setGeneratedTrackedParticles(
190 auto*
const hcs_this_event =
event->GetHCofThisEvent();
191 if (hcs_this_event ==
nullptr) {
192 if (!eventDataCollection->getGeneratedParticles().empty() ||
193 !eventDataCollection->getGeneratedTrackedParticles().empty()) {
194 publish_event_data(eventDataCollection);
200 if (digi_map ==
nullptr) {
202 " no digitization routines map available in thread ", thread_id);
206 bool has_event_mode_payload =
false;
207 bool has_run_mode_payload =
false;
208 const bool also_reject_true_info = scalar_bool_option_enabled(
goptions,
"also_reject_true_info");
209 std::unordered_set<int> ancestor_track_ids;
213 for (G4int hci = 0; hci < hcs_this_event->GetNumberOfCollections(); ++hci) {
214 auto*
const this_ghc =
static_cast<GHitsCollection*
>(hcs_this_event->GetHC(hci));
215 if (this_ghc ==
nullptr) {
219 const std::string hcSDName = this_ghc->GetSDname();
222 " for event number ", event_id,
223 " for collection number ", hci + 1,
224 " collection name: ", hcSDName);
227 const auto it = digi_map->find(hcSDName);
228 if (it == digi_map->end()) {
230 " no digitization routine registered for collection ", hcSDName,
231 " in thread ", thread_id);
235 const auto& digitization_routine = it->second;
236 if (digitization_routine ==
nullptr) {
238 " digitization routine is null for collection ", hcSDName,
239 " in thread ", thread_id);
243 const auto collection_mode = digitization_routine->collection_mode();
244 size_t accepted_hit_index = 0;
248 for (
size_t hitIndex = 0; hitIndex < this_ghc->GetSize(); ++hitIndex) {
250 auto*
const this_hit =
static_cast<GHit*
>(this_ghc->GetHit(hitIndex));
251 if (this_hit ==
nullptr) {
254 if (save_all_ancestors) {
255 const auto track_ids = this_hit->
getTids();
256 ancestor_track_ids.insert(track_ids.begin(), track_ids.end());
259 auto digi_data = digitization_routine->digitizeHit(this_hit, hitIndex);
260 bool hit_accepted = digi_data !=
nullptr;
267 const bool skip_threshold = digitization_routine->apply_thresholds(this_hit, digi_data.get());
268 const bool skip_efficiency = digitization_routine->apply_efficiency(this_hit, digi_data.get());
269 if (skip_threshold || skip_efficiency) {
271 hit_accepted =
false;
275 if (collection_mode == CollectionMode::event) {
277 ++accepted_hit_index;
278 digi_data->includeVariable(
"hitn",
static_cast<int>(accepted_hit_index));
279 eventDataCollection->addDetectorDigitizedData(hcSDName, std::move(digi_data));
281 if (hit_accepted || !also_reject_true_info) {
282 const size_t output_hit_index = hit_accepted ? accepted_hit_index : hitIndex + 1;
283 auto true_data = digitization_routine->collectTrueInformation(this_hit, output_hit_index);
284 if (save_original_track && track_provenance !=
nullptr && true_data !=
nullptr) {
285 const int tid = this_hit->getTid();
287 true_data->includeVariable(
"otid", track_provenance->
originalTrackId(tid));
288 true_data->includeVariable(
"opid", track_provenance->
originalTrackPid(tid));
289 true_data->includeVariable(
"opx", op.getX());
290 true_data->includeVariable(
"opy", op.getY());
291 true_data->includeVariable(
"opz", op.getZ());
293 eventDataCollection->addDetectorTrueInfoData(hcSDName, std::move(true_data));
294 has_event_mode_payload =
true;
297 else if (collection_mode == CollectionMode::run) {
301 std::move(digi_data));
302 has_run_mode_payload =
true;
308 if (save_all_ancestors && track_provenance !=
nullptr) {
309 eventDataCollection->setAncestors(
314 if (has_run_mode_payload) {
319 if (has_event_mode_payload ||
320 !eventDataCollection->getAncestors().empty() ||
321 !eventDataCollection->getGeneratedParticles().empty() ||
322 !eventDataCollection->getGeneratedTrackedParticles().empty()) {
323 publish_event_data(eventDataCollection);
328void GEventAction::publish_event_data(
const std::shared_ptr<GEventDataCollection>& event_data)
const {
329 if (run_action ==
nullptr || event_data ==
nullptr) {
338 if (gstreamers_threads_map ==
nullptr) {
340 " no thread streamer map available - event will not be published.");
344 for (
const auto& [name,
gstreamer] : *gstreamers_threads_map) {
347 " null gstreamer instance for streamer ", name);
std::shared_ptr< GLogger > log
void EndOfEventAction(const G4Event *event) override
Called by Geant4 at the end of an event.
GEventAction(const std::shared_ptr< GOptions > &gopt, GRunAction *run_a, std::shared_ptr< GTrackProvenance > provenance=nullptr)
Constructs the event action.
void BeginOfEventAction(const G4Event *event) override
Called by Geant4 at the beginning of an event.
std::vector< int > getTids() const
void warning(Args &&... args) const
void debug(debug_type type, Args &&... args) const
void info(int level, Args &&... args) const
void error(int exit_code, Args &&... args) const
static const GParticleRecordEvent & currentGeneratedParticleRecords()
Returns the current event's full generated-particle records.
static const GParticleRecordEvent & currentGeneratedTrackedParticleRecords()
Returns the current event's Geant4-tracked generated-particle records.
Handles run begin/end callbacks and creates the thread-local GRun object.
void increment_run_events_with_payload()
Increments the number of events that produced run-mode payload.
void increment_run_events_processed()
Increments the number of events processed by the current thread for this run.
auto get_digitization_routines_map() const -> std::shared_ptr< gdynamicdigitization::dRoutinesMap >
Returns the shared digitization-routine map used by this run action.
bool has_streamer_threads_map() const
void collect_event_data_collections(const std::string &hcSDName, std::unique_ptr< GDigitizedData > digi_data)
Adds one run-mode digitized payload to the current thread run-data collection.
auto get_streamer_threads_map() const -> std::shared_ptr< const gstreamer::gstreamersMap >
Returns the worker-thread streamer map, if it has been instantiated.
int originalTrackId(int track_id) const
G4ThreeVector originalTrackMomentum(int track_id) const
std::vector< GTrackRecord > ancestorsForTracks(const std::unordered_set< int > &track_ids) const
int originalTrackPid(int track_id) const
Declares GEventAction, the per-event processing action for the GEMC actions module.
constexpr const char * EVENTACTION_LOGGER
constexpr const char * LOG_EVERY_OPTION
Name of the option controlling periodic per-event log messages.
constexpr const char * SAVE_ORIGINAL_TRACK_SWITCH
constexpr const char * SAVE_ALL_ANCESTORS_SWITCH
std::vector< GAncestorData > GAncestorBank
std::vector< GGeneratedParticleData > GGeneratedParticleBank
Declares GPrimaryGeneratorAction, the primary-particle generation action for the GEMC actions module.
Defines error codes used by the GEMC actions module.
#define ERR_GDIGIMAP_NOT_EXISTING
#define ERR_GRUNACTION_NOT_EXISTING
#define ERR_STREAMERMAP_NOT_EXISTING
G4THitsCollection< GHit > GHitsCollection
std::vector< GParticleRecord > GParticleRecordEvent
#define UNINITIALIZEDSTRINGQUANTITY