6#include "G4Threading.hh"
7#include "G4RunManager.hh"
23#include <unordered_set>
28 bank.reserve(particles.size());
30 for (
const auto& particle : particles) {
35 particle.multiplicity,
48GAncestorBank make_ancestor_bank(
const std::vector<GTrackRecord>& records) {
50 bank.reserve(records.size());
51 for (
const auto& record : records) {
56 record.kinetic_energy,
68bool scalar_bool_option_enabled(
const std::shared_ptr<GOptions>&
goptions,
const std::string& name) {
69 std::string value =
goptions->getOptionalScalarString(name).value_or(
"");
70 std::transform(value.begin(), value.end(), value.begin(),
71 [](
unsigned char c) { return static_cast<char>(std::tolower(c)); });
72 return value ==
"true" || value ==
"1" || value ==
"yes" || value ==
"on";
76bool detector_is_listed(
const std::shared_ptr<GOptions>&
goptions,
const std::string& option,
77 const std::string& detector) {
78 std::string detectors =
goptions->getOptionalScalarString(option).value_or(
"");
79 std::replace(detectors.begin(), detectors.end(),
',',
' ');
81 std::istringstream names(detectors);
83 while (names >> name) {
84 if (name ==
"all" || name == detector)
return true;
90bool to_non_negative_int(
const std::string& text,
int& out) {
91 if (text.empty())
return false;
94 try { value = std::stoi(text, &pos); }
95 catch (...) {
return false; }
96 if (pos != text.size() || value < 0)
return false;
106 std::shared_ptr<GTrackProvenance> provenance) :
110 track_provenance(std::move(provenance)) {
111 const auto thread_id = G4Threading::G4GetThreadId();
112 const auto desc =
"GEventAction " + std::to_string(thread_id);
120 if (spec_option && !spec_option->empty()) {
121 const std::string& spec = *spec_option;
122 const auto dash = spec.find(
'-');
123 std::string n_part = dash == std::string::npos ? spec : spec.substr(0, dash);
124 std::string nth_part = dash == std::string::npos ? std::string() : spec.substr(dash + 1);
127 int nthreads =
goptions->getRequiredScalarInt(
"nthreads");
128 const int ncores = G4Threading::G4GetNumberOfCores();
129 if (nthreads == 0 || nthreads > ncores) nthreads = ncores;
132 if (!to_non_negative_int(n_part, n) || n == 0) {
134 log->warning(
"Ignoring invalid -%1=%2 : N must be a positive integer.",
LOG_EVERY_OPTION, spec);
136 else if (dash != std::string::npos) {
138 if (!to_non_negative_int(nth_part, nth) || nth >= nthreads) {
141 "Ignoring invalid -%1=%2 : thread id must be in [0, %3].",
LOG_EVERY_OPTION, spec,
146 log_every_thread = nth;
149 else { log_every_n = n; }
156void GEventAction::log_event_start(
int thread_id) {
157 if (log_every_n <= 0)
return;
158 if (log_every_thread >= 0 && log_every_thread != thread_id)
return;
161 const auto now = std::chrono::steady_clock::now();
162 if (log_events_seen == 0) { log_start_time = now; }
165 if (log_events_seen % log_every_n != 0)
return;
167 const double elapsed_s = std::chrono::duration<double>(now - log_start_time).count();
168 const double rate = elapsed_s > 0.0 ?
static_cast<double>(log_events_seen) / elapsed_s : 0.0;
172 0,
"Starting event n. %1 in thread %2. Average rate: %3 events / second", log_events_seen, thread_id,
178 const auto thread_id = G4Threading::G4GetThreadId();
179 const auto event_id =
event->GetEventID();
182 if (track_provenance !=
nullptr) { track_provenance->clear(); }
184 log_event_start(thread_id);
190 if (run_action ==
nullptr) {
193 "%1 run_action is null - cannot access digitization routines or streamers.",
FUNCTION_NAME);
198 run_action->increment_run_events_processed();
201 const auto thread_id = G4Threading::G4GetThreadId();
202 const auto event_id =
event->GetEventID();
204 auto gevent_header = std::make_unique<GEventHeader>(
goptions, event_id, thread_id);
205 auto eventDataCollection = std::make_shared<GEventDataCollection>(
goptions, std::move(gevent_header));
206 eventDataCollection->setGeneratedParticles(
208 eventDataCollection->setGeneratedTrackedParticles(
211 auto*
const hcs_this_event =
event->GetHCofThisEvent();
212 if (hcs_this_event ==
nullptr) {
213 if (!eventDataCollection->getGeneratedParticles().empty() ||
214 !eventDataCollection->getGeneratedTrackedParticles().empty()) {
215 publish_event_data(eventDataCollection);
220 const auto digi_map = run_action->get_digitization_routines_map();
221 if (digi_map ==
nullptr) {
228 bool has_event_mode_payload =
false;
229 bool has_run_mode_payload =
false;
230 const bool also_reject_true_info = scalar_bool_option_enabled(
goptions,
"also_reject_true_info");
231 std::unordered_set<int> ancestor_track_ids;
235 for (G4int hci = 0; hci < hcs_this_event->GetNumberOfCollections(); ++hci) {
236 auto*
const this_ghc =
static_cast<GHitsCollection*
>(hcs_this_event->GetHC(hci));
237 if (this_ghc ==
nullptr) {
241 const std::string hcSDName = this_ghc->GetSDname();
246 2,
"%1 worker %2 for event number %3 for collection number %4 collection name: %5",
FUNCTION_NAME,
247 thread_id, event_id, hci + 1, hcSDName);
250 const auto it = digi_map->find(hcSDName);
251 if (it == digi_map->end()) {
254 "%1 no digitization routine registered for collection %2 in thread %3",
FUNCTION_NAME, hcSDName,
259 const auto& digitization_routine = it->second;
260 if (digitization_routine ==
nullptr) {
263 "%1 digitization routine is null for collection %2 in thread %3",
FUNCTION_NAME, hcSDName,
268 const auto collection_mode = digitization_routine->collection_mode();
269 size_t accepted_hit_index = 0;
273 for (
size_t hitIndex = 0; hitIndex < this_ghc->GetSize(); ++hitIndex) {
275 auto*
const this_hit =
static_cast<GHit*
>(this_ghc->GetHit(hitIndex));
276 if (this_hit ==
nullptr) {
279 if (save_all_ancestors) {
280 const auto track_ids = this_hit->getTids();
281 ancestor_track_ids.insert(track_ids.begin(), track_ids.end());
284 auto digi_data = no_digitized ? nullptr : digitization_routine->digitizeHit(this_hit, hitIndex);
285 bool hit_accepted = digi_data !=
nullptr;
296 bool rejected_by_intrinsic_policy =
false;
298 const bool skip_threshold = digitization_routine->apply_thresholds(this_hit, digi_data.get());
299 const bool skip_efficiency = digitization_routine->apply_efficiency(this_hit, digi_data.get());
300 if (skip_threshold || skip_efficiency) {
302 hit_accepted =
false;
303 rejected_by_intrinsic_policy =
304 (skip_threshold && digitization_routine->thresholds_are_intrinsic_impl()) ||
305 (skip_efficiency && digitization_routine->efficiencies_are_intrinsic_impl());
309 if (collection_mode == CollectionMode::event) {
311 ++accepted_hit_index;
312 digi_data->includeVariable(
"hitn",
static_cast<int>(accepted_hit_index));
313 run_action->record_analysis_digitized(hcSDName, *digi_data);
314 eventDataCollection->addDetectorDigitizedData(hcSDName, std::move(digi_data));
315 has_event_mode_payload =
true;
318 else if (collection_mode == CollectionMode::run) {
320 run_action->record_analysis_digitized(hcSDName, *digi_data);
321 run_action->collect_event_data_collections(
323 std::move(digi_data));
324 has_run_mode_payload =
true;
332 const bool drop_true_for_rejected_hit = also_reject_true_info || rejected_by_intrinsic_policy;
333 const bool collect_true = !no_true_info &&
334 (no_digitized || hit_accepted || !drop_true_for_rejected_hit) &&
335 (collection_mode == CollectionMode::event || run_action->analysis_enabled());
337 const size_t output_hit_index = collection_mode == CollectionMode::event && hit_accepted
338 ? accepted_hit_index : hitIndex + 1;
339 auto true_data = digitization_routine->collectTrueInformation(this_hit, output_hit_index);
340 if (save_original_track && track_provenance !=
nullptr && true_data !=
nullptr) {
341 const int tid = this_hit->getTid();
342 const G4ThreeVector op = track_provenance->originalTrackMomentum(tid);
343 true_data->includeVariable(
"otid", track_provenance->originalTrackId(tid));
344 true_data->includeVariable(
"opid", track_provenance->originalTrackPid(tid));
345 true_data->includeVariable(
"opx", op.getX());
346 true_data->includeVariable(
"opy", op.getY());
347 true_data->includeVariable(
"opz", op.getZ());
349 if (true_data !=
nullptr) { run_action->record_analysis_true(hcSDName, *true_data); }
350 if (collection_mode == CollectionMode::event) {
351 eventDataCollection->addDetectorTrueInfoData(hcSDName, std::move(true_data));
352 has_event_mode_payload =
true;
358 if (save_all_ancestors && track_provenance !=
nullptr) {
359 eventDataCollection->setAncestors(
360 make_ancestor_bank(track_provenance->ancestorsForTracks(ancestor_track_ids)));
364 if (has_run_mode_payload) {
365 run_action->increment_run_events_with_payload();
369 if (has_event_mode_payload ||
370 !eventDataCollection->getAncestors().empty() ||
371 !eventDataCollection->getGeneratedParticles().empty() ||
372 !eventDataCollection->getGeneratedTrackedParticles().empty()) {
373 publish_event_data(eventDataCollection);
377void GEventAction::stream_event(
const G4Event*
event) {
379 if (!sro) {
return; }
380 if (
event->IsAborted()) {
381 sro->interrupt_run();
385 sro->rethrow_if_failed();
386 const auto context = sro->event_context(
static_cast<GSROEventId>(
event->GetEventID()));
387 std::uint64_t sequence = 0;
389 if (sequence == std::numeric_limits<std::uint64_t>::max()) {
390 throw std::overflow_error(
"SRO payload sequence overflow");
392 sro->dispatch_payload_to_crate({crate, context.event_id, sequence++, time, std::move(data)});
394 if (
auto* collections =
event->GetHCofThisEvent()) {
395 const auto routines = run_action->get_digitization_routines_map();
396 if (!routines) {
throw std::runtime_error(
"SRO requires a digitization routine map"); }
397 for (
int index = 0; index < collections->GetNumberOfCollections(); ++index) {
399 if (!hits || detector_is_listed(goptions,
NO_DIGITIZED_OPTION, hits->GetSDname())) {
continue; }
400 const auto& routine = routines->at(hits->GetSDname());
401 if (!routine) {
throw std::runtime_error(
"Null SRO digitization routine"); }
402 for (std::size_t hit = 0; hit < hits->GetSize(); ++hit) {
403 if (
auto* data =
static_cast<GHit*
>(hits->GetHit(hit))) {
404 routine->stream_hit(data, hit, context, emit);
409 sro->complete_event(context.event_id);
412 sro->cancel_run(std::current_exception());
414 if (
auto* manager = G4RunManager::GetRunManager()) { manager->AbortRun(
true); }
419void GEventAction::publish_event_data(
const std::shared_ptr<GEventDataCollection>& event_data)
const {
420 if (run_action ==
nullptr || event_data ==
nullptr) {
424 if (!run_action->has_streamer_threads_map()) {
428 const auto gstreamers_threads_map = run_action->get_streamer_threads_map();
429 if (gstreamers_threads_map ==
nullptr) {
432 "%1 no thread streamer map available - event will not be published.", FUNCTION_NAME);
436 for (
const auto& [name, gstreamer] : *gstreamers_threads_map) {
437 if (gstreamer ==
nullptr) {
440 FUNCTION_NAME, name);
444 gstreamer->publishEventData(event_data);
GBase(const std::shared_ptr< GOptions > &gopt, std::string logger_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.
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.
const std::shared_ptr< GSROFactory > & get_sro_factory() 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 * NO_DIGITIZED_OPTION
constexpr const char * NO_TRUE_INFO_OPTION
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.
std::function< void(GSROCrateId, GSROTime, std::unique_ptr< const GSROData >)> GSROEmit
std::chrono::duration< std::int64_t, std::nano > GSROTime
std::uint32_t GSROCrateId
std::uint64_t GSROEventId
Defines error codes used by the GEMC actions module.
G4THitsCollection< GHit > GHitsCollection
std::vector< GParticleRecord > GParticleRecordEvent
constexpr int ERR_GDIGIMAP_NOT_EXISTING
constexpr int ERR_STREAMERMAP_NOT_EXISTING
constexpr int ERR_GRUNACTION_NOT_EXISTING