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) {
135 " : N must be a positive integer.");
137 else if (dash != std::string::npos) {
139 if (!to_non_negative_int(nth_part, nth) || nth >= nthreads) {
142 " : thread id must be in [0, ", nthreads - 1,
"].");
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;
171 log->info(0,
"Starting event n. ", log_events_seen,
" in thread ", thread_id,
172 ". Average rate: ", rate,
" events / second");
177 const auto thread_id = G4Threading::G4GetThreadId();
178 const auto event_id =
event->GetEventID();
181 if (track_provenance !=
nullptr) { track_provenance->clear(); }
183 log_event_start(thread_id);
189 if (run_action ==
nullptr) {
191 " run_action is null - cannot access digitization routines or streamers.");
196 run_action->increment_run_events_processed();
199 const auto thread_id = G4Threading::G4GetThreadId();
200 const auto event_id =
event->GetEventID();
202 auto gevent_header = std::make_unique<GEventHeader>(
goptions, event_id, thread_id);
203 auto eventDataCollection = std::make_shared<GEventDataCollection>(
goptions, std::move(gevent_header));
204 eventDataCollection->setGeneratedParticles(
206 eventDataCollection->setGeneratedTrackedParticles(
209 auto*
const hcs_this_event =
event->GetHCofThisEvent();
210 if (hcs_this_event ==
nullptr) {
211 if (!eventDataCollection->getGeneratedParticles().empty() ||
212 !eventDataCollection->getGeneratedTrackedParticles().empty()) {
213 publish_event_data(eventDataCollection);
218 const auto digi_map = run_action->get_digitization_routines_map();
219 if (digi_map ==
nullptr) {
221 " no digitization routines map available in thread ", thread_id);
225 bool has_event_mode_payload =
false;
226 bool has_run_mode_payload =
false;
227 const bool also_reject_true_info = scalar_bool_option_enabled(
goptions,
"also_reject_true_info");
228 std::unordered_set<int> ancestor_track_ids;
232 for (G4int hci = 0; hci < hcs_this_event->GetNumberOfCollections(); ++hci) {
233 auto*
const this_ghc =
static_cast<GHitsCollection*
>(hcs_this_event->GetHC(hci));
234 if (this_ghc ==
nullptr) {
238 const std::string hcSDName = this_ghc->GetSDname();
243 " for event number ", event_id,
244 " for collection number ", hci + 1,
245 " collection name: ", hcSDName);
248 const auto it = digi_map->find(hcSDName);
249 if (it == digi_map->end()) {
251 " no digitization routine registered for collection ", hcSDName,
252 " in thread ", thread_id);
256 const auto& digitization_routine = it->second;
257 if (digitization_routine ==
nullptr) {
259 " digitization routine is null for collection ", hcSDName,
260 " in thread ", thread_id);
264 const auto collection_mode = digitization_routine->collection_mode();
265 size_t accepted_hit_index = 0;
269 for (
size_t hitIndex = 0; hitIndex < this_ghc->GetSize(); ++hitIndex) {
271 auto*
const this_hit =
static_cast<GHit*
>(this_ghc->GetHit(hitIndex));
272 if (this_hit ==
nullptr) {
275 if (save_all_ancestors) {
276 const auto track_ids = this_hit->getTids();
277 ancestor_track_ids.insert(track_ids.begin(), track_ids.end());
280 auto digi_data = no_digitized ? nullptr : digitization_routine->digitizeHit(this_hit, hitIndex);
281 bool hit_accepted = digi_data !=
nullptr;
287 const bool skip_threshold = digitization_routine->apply_thresholds(this_hit, digi_data.get());
288 const bool skip_efficiency = digitization_routine->apply_efficiency(this_hit, digi_data.get());
289 if (skip_threshold || skip_efficiency) {
291 hit_accepted =
false;
295 if (collection_mode == CollectionMode::event) {
297 ++accepted_hit_index;
298 digi_data->includeVariable(
"hitn",
static_cast<int>(accepted_hit_index));
299 run_action->record_analysis_digitized(hcSDName, *digi_data);
300 eventDataCollection->addDetectorDigitizedData(hcSDName, std::move(digi_data));
301 has_event_mode_payload =
true;
304 else if (collection_mode == CollectionMode::run) {
306 run_action->record_analysis_digitized(hcSDName, *digi_data);
307 run_action->collect_event_data_collections(
309 std::move(digi_data));
310 has_run_mode_payload =
true;
316 const bool collect_true = !no_true_info &&
317 (no_digitized || hit_accepted || !also_reject_true_info) &&
318 (collection_mode == CollectionMode::event || run_action->analysis_enabled());
320 const size_t output_hit_index = collection_mode == CollectionMode::event && hit_accepted
321 ? accepted_hit_index : hitIndex + 1;
322 auto true_data = digitization_routine->collectTrueInformation(this_hit, output_hit_index);
323 if (save_original_track && track_provenance !=
nullptr && true_data !=
nullptr) {
324 const int tid = this_hit->getTid();
325 const G4ThreeVector op = track_provenance->originalTrackMomentum(tid);
326 true_data->includeVariable(
"otid", track_provenance->originalTrackId(tid));
327 true_data->includeVariable(
"opid", track_provenance->originalTrackPid(tid));
328 true_data->includeVariable(
"opx", op.getX());
329 true_data->includeVariable(
"opy", op.getY());
330 true_data->includeVariable(
"opz", op.getZ());
332 if (true_data !=
nullptr) { run_action->record_analysis_true(hcSDName, *true_data); }
333 if (collection_mode == CollectionMode::event) {
334 eventDataCollection->addDetectorTrueInfoData(hcSDName, std::move(true_data));
335 has_event_mode_payload =
true;
341 if (save_all_ancestors && track_provenance !=
nullptr) {
342 eventDataCollection->setAncestors(
343 make_ancestor_bank(track_provenance->ancestorsForTracks(ancestor_track_ids)));
347 if (has_run_mode_payload) {
348 run_action->increment_run_events_with_payload();
352 if (has_event_mode_payload ||
353 !eventDataCollection->getAncestors().empty() ||
354 !eventDataCollection->getGeneratedParticles().empty() ||
355 !eventDataCollection->getGeneratedTrackedParticles().empty()) {
356 publish_event_data(eventDataCollection);
360void GEventAction::stream_event(
const G4Event*
event) {
362 if (!sro) {
return; }
363 if (
event->IsAborted()) {
364 sro->interrupt_run();
368 sro->rethrow_if_failed();
369 const auto context = sro->event_context(
static_cast<GSROEventId>(
event->GetEventID()));
370 std::uint64_t sequence = 0;
372 if (sequence == std::numeric_limits<std::uint64_t>::max()) {
373 throw std::overflow_error(
"SRO payload sequence overflow");
375 sro->dispatch_payload_to_crate({crate, context.event_id, sequence++, time, std::move(data)});
377 if (
auto* collections =
event->GetHCofThisEvent()) {
378 const auto routines = run_action->get_digitization_routines_map();
379 if (!routines) {
throw std::runtime_error(
"SRO requires a digitization routine map"); }
380 for (
int index = 0; index < collections->GetNumberOfCollections(); ++index) {
382 if (!hits || detector_is_listed(goptions,
NO_DIGITIZED_OPTION, hits->GetSDname())) {
continue; }
383 const auto& routine = routines->at(hits->GetSDname());
384 if (!routine) {
throw std::runtime_error(
"Null SRO digitization routine"); }
385 for (std::size_t hit = 0; hit < hits->GetSize(); ++hit) {
386 if (
auto* data =
static_cast<GHit*
>(hits->GetHit(hit))) {
387 routine->stream_hit(data, hit, context, emit);
392 sro->complete_event(context.event_id);
395 sro->cancel_run(std::current_exception());
397 if (
auto* manager = G4RunManager::GetRunManager()) { manager->AbortRun(
true); }
402void GEventAction::publish_event_data(
const std::shared_ptr<GEventDataCollection>& event_data)
const {
403 if (run_action ==
nullptr || event_data ==
nullptr) {
407 if (!run_action->has_streamer_threads_map()) {
411 const auto gstreamers_threads_map = run_action->get_streamer_threads_map();
412 if (gstreamers_threads_map ==
nullptr) {
414 " no thread streamer map available - event will not be published.");
418 for (
const auto& [name, gstreamer] : *gstreamers_threads_map) {
419 if (gstreamer ==
nullptr) {
421 " null gstreamer instance for streamer ", name);
425 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