55#include " G4Event.hh"
66#include " G4Threading.hh"
77
8+ // c++
9+ #include < string>
10+
811// gemc
912#include " event/gEventDataCollection.h"
1013#include " ../generator/gPrimaryGeneratorAction.h"
1114
1215// c++
1316#include < algorithm>
1417#include < cctype>
18+ #include < chrono>
1519
1620namespace {
1721GGeneratedParticleBank make_generated_particle_bank (const GParticleRecordEvent& particles) {
@@ -42,6 +46,18 @@ bool scalar_bool_option_enabled(const std::shared_ptr<GOptions>& goptions, const
4246 [](unsigned char c) { return static_cast <char >(std::tolower (c)); });
4347 return value == " true" || value == " 1" || value == " yes" || value == " on" ;
4448}
49+
50+ // Convert a substring to a non-negative integer, returning false on any malformed input.
51+ bool to_non_negative_int (const std::string& text, int & out) {
52+ if (text.empty ()) return false ;
53+ size_t pos = 0 ;
54+ int value;
55+ try { value = std::stoi (text, &pos); }
56+ catch (...) { return false ; }
57+ if (pos != text.size () || value < 0 ) return false ;
58+ out = value;
59+ return true ;
60+ }
4561}
4662
4763
@@ -51,8 +67,65 @@ GEventAction::GEventAction(const std::shared_ptr<GOptions>& gopt, GRunAction* ru
5167 GBase(gopt, EVENTACTION_LOGGER ),
5268 goptions(gopt),
5369 run_action(run_a) {
54- const auto desc = " GEventAction " + std::to_string (G4Threading::G4GetThreadId ());
70+ const auto thread_id = G4Threading::G4GetThreadId ();
71+ const auto desc = " GEventAction " + std::to_string (thread_id);
5572 log->debug (CONSTRUCTOR , FUNCTION_NAME , desc);
73+
74+ // Parse the log_every option of the form N or N-NTH. Anything malformed disables the
75+ // feature and is reported once (from thread 0) to avoid duplicated warnings across workers.
76+ std::string spec = goptions->getScalarString (LOG_EVERY_OPTION );
77+ if (!spec.empty () && spec != UNINITIALIZEDSTRINGQUANTITY ) {
78+ const auto dash = spec.find (' -' );
79+ std::string n_part = dash == std::string::npos ? spec : spec.substr (0 , dash);
80+ std::string nth_part = dash == std::string::npos ? std::string () : spec.substr (dash + 1 );
81+
82+ // Effective worker-thread count, mirroring gemc::get_nthreads clamping (0 means all cores).
83+ int nthreads = goptions->getScalarInt (" nthreads" );
84+ const int ncores = G4Threading::G4GetNumberOfCores ();
85+ if (nthreads == 0 || nthreads > ncores) nthreads = ncores;
86+
87+ int n = 0 ;
88+ if (!to_non_negative_int (n_part, n) || n == 0 ) {
89+ if (thread_id <= 0 )
90+ log->warning (" Ignoring invalid -" , LOG_EVERY_OPTION , " =" , spec,
91+ " : N must be a positive integer." );
92+ }
93+ else if (dash != std::string::npos) {
94+ int nth = 0 ;
95+ if (!to_non_negative_int (nth_part, nth) || nth >= nthreads) {
96+ if (thread_id <= 0 )
97+ log->warning (" Ignoring invalid -" , LOG_EVERY_OPTION , " =" , spec,
98+ " : thread id must be in [0, " , nthreads - 1 , " ]." );
99+ }
100+ else {
101+ log_every_n = n;
102+ log_every_thread = nth;
103+ }
104+ }
105+ else { log_every_n = n; }
106+ }
107+ }
108+
109+ // Print the periodic "Starting event" line, honoring the log module and optional thread filter.
110+ // The reported event number, count and rate are all per worker thread: each enabled thread logs
111+ // every N events it processes, showing its own 1-based event count and average rate (events / second).
112+ void GEventAction::log_event_start (int thread_id) {
113+ if (log_every_n <= 0 ) return ;
114+ if (log_every_thread >= 0 && log_every_thread != thread_id) return ;
115+
116+ // Anchor this thread's clock on its first counted event, then count this event.
117+ const auto now = std::chrono::steady_clock::now ();
118+ if (log_events_seen == 0 ) { log_start_time = now; }
119+ ++log_events_seen;
120+
121+ if (log_events_seen % log_every_n != 0 ) return ;
122+
123+ const double elapsed_s = std::chrono::duration<double >(now - log_start_time).count ();
124+ const double rate = elapsed_s > 0.0 ? static_cast <double >(log_events_seen) / elapsed_s : 0.0 ;
125+
126+ // log_events_seen is this thread's own 1-based count, not the global Geant4 event id.
127+ log->info (0 , " Starting event n. " , log_events_seen, " in thread " , thread_id,
128+ " . Average rate: " , rate, " events / second" );
56129}
57130
58131// Begin-of-event hook used mainly for tracing event and thread identifiers.
@@ -61,6 +134,8 @@ void GEventAction::BeginOfEventAction([[maybe_unused]] const G4Event* event) {
61134 const auto event_id = event->GetEventID ();
62135
63136 log->debug (NORMAL , FUNCTION_NAME , " event id " , event_id, " in thread " , thread_id);
137+
138+ log_event_start (thread_id);
64139}
65140
66141// Finalize the event by reading hit collections, digitizing them, routing the
0 commit comments