actions
Loading...
Searching...
No Matches
sro_runtime_example.cc
Go to the documentation of this file.
1#include "gaction.h"
2#include <gemc/gfactory/gfactory.h>
3
4#include "G4Box.hh"
5#include "G4LogicalVolume.hh"
6#include "G4NistManager.hh"
7#include "G4PVPlacement.hh"
8#include "G4RunManagerFactory.hh"
9#include "G4MTRunManager.hh"
10#include "G4SDManager.hh"
11#include "G4VSensitiveDetector.hh"
12#include "G4VUserDetectorConstruction.hh"
13#include "QBBC.hh"
14
15#include <filesystem>
16#include <fstream>
17#include <iostream>
18#include <set>
19#include <tuple>
20#include <unistd.h>
21
22namespace {
23void require(bool condition, const char* message) {
24 if (!condition) { throw std::runtime_error(message); }
25}
26
27class Sensitive final : public G4VSensitiveDetector {
28public:
29 Sensitive() : G4VSensitiveDetector("test_sro") { collectionName.insert("hits"); }
30 void Initialize(G4HCofThisEvent* collections) override {
31 auto* hits = new GHitsCollection("test_sro", "hits");
32 collections->AddHitsCollection(GetCollectionID(0), hits);
33 const auto* event = G4RunManager::GetRunManager()->GetCurrentEvent();
34 if (event->GetEventID() % 4) {
35 hits->insert(new GHit(nullptr));
36 hits->insert(new GHit(nullptr));
37 }
38 }
39 void EndOfEvent(G4HCofThisEvent*) override {
40 auto* manager = G4RunManager::GetRunManager();
41 if (manager->GetCurrentRun()->GetRunID() == 1 && manager->GetCurrentEvent()->GetEventID() == 0) {
42 const_cast<G4Event*>(manager->GetCurrentEvent())->SetEventAborted();
43 }
44 }
45 G4bool ProcessHits(G4Step*, G4TouchableHistory*) override { return false; }
46};
47
48class Detector final : public G4VUserDetectorConstruction {
49public:
50 explicit Detector(bool no_hits) : empty(no_hits) {}
51 G4VPhysicalVolume* Construct() override {
52 auto* material = G4NistManager::Instance()->FindOrBuildMaterial("G4_Galactic");
53 auto* solid = new G4Box("world", 1 * CLHEP::m, 1 * CLHEP::m, 1 * CLHEP::m);
54 auto* logical = new G4LogicalVolume(solid, material, "world");
55 return new G4PVPlacement(nullptr, {}, logical, "world", nullptr, false, 0);
56 }
57 void ConstructSDandField() override {
58 if (!empty) { G4SDManager::GetSDMpointer()->AddNewDetector(new Sensitive); }
59 }
60private:
61 bool empty;
62};
63
64void check_output(const std::filesystem::path& base, int run, int count, bool serial) {
65 std::set<std::size_t> workers;
66 for (int crate : {1, 2}) {
67 std::ifstream file(base.string() + "_r" + std::to_string(run) + "_c" + std::to_string(crate) + ".txt");
68 require(file.is_open(), "Missing crate output file");
69 std::vector<std::tuple<int, int, int>> actual;
70 std::string tag;
71 while (file >> tag && tag == "F") {
72 int event, sequence, time;
73 std::size_t worker;
74 require(static_cast<bool>(file >> event >> sequence >> time >> worker), "Malformed SRO frame");
75 actual.emplace_back(event, sequence, time);
76 workers.insert(worker);
77 }
78 std::string reason;
79 int safe_time;
80 require(tag == "E" && static_cast<bool>(file >> reason >> safe_time), "Missing SRO finalization");
81 std::vector<std::tuple<int, int, int>> expected;
82 if (run != 1) {
83 for (int event = 0; event < count; ++event) {
84 if (event % 4) {
85 expected.emplace_back(event, crate - 1, event * 10);
86 expected.emplace_back(event, crate + 1, event * 10);
87 }
88 }
89 }
90 require(actual == expected, "Missing, duplicate, misrouted, or unordered SRO payloads");
91 require(reason == (run == 1 ? "interrupted" : "completed"), "Incorrect SRO end reason");
92 require(safe_time == (run == 1 ? 0 : count * 10), "Empty/aborted events produced incorrect safe time");
93 require(!(file >> tag), "Unexpected records after SRO finalization");
94 }
95 if (run == 0) { require(serial ? workers.size() == 1 : workers.size() > 1, "Worker coverage missing"); }
96}
97} // namespace
98
99int main(int argc, char* argv[]) {
100 try {
101 const auto scratch = std::filesystem::temp_directory_path() / ("gemc-sro-" + std::to_string(getpid()));
102 require(std::filesystem::create_directory(scratch), "Could not create unique SRO test directory");
103 const auto base = scratch / "output";
104 auto definitions = gaction::defineOptions();
105 definitions += gstreamer::defineOptions();
106 definitions += gparticle::defineOptions();
107 definitions.defineOption(GVariable("nthreads", 4, "test workers"), "test workers");
108 definitions.defineOption(GVariable("also_reject_true_info", "false", "true information policy"),
109 "true information policy");
110 definitions.defineSwitch("sro_serial", "Exercise sequential Geant4 execution");
111 definitions.defineSwitch("sro_empty", "Exercise events without hit collections");
112 definitions.defineSwitch("sro_test_worker_failure", "Inject a worker SRO failure");
113 std::vector<std::string> args(argv, argv + argc);
114 args.push_back("-gparticle=[{name: geantino, p: 1, theta: 0}]");
115 args.push_back("-gstreamer=[{format: sro, filename: '" + base.string() +
116 "', implementation: test_sro_implementation}]");
117 std::vector<char*> pointers;
118 for (auto& argument : args) { pointers.push_back(argument.data()); }
119 auto options = std::make_shared<GOptions>(
120 static_cast<int>(pointers.size()), pointers.data(), definitions);
121 const bool serial = options->getSwitch("sro_serial");
122 const bool empty = options->getSwitch("sro_empty");
123 require(gstreamer::gstreamersMapPtr(options)->empty(), "SRO leaked into worker streamer maps");
124 GManager plugins(options);
125 auto digitizers = std::make_shared<gdynamicdigitization::dRoutinesMap>();
126 (*digitizers)["test_sro"] = plugins.LoadAndRegisterObjectFromLibrary<GDynamicDigitization>(
127 "test_sro_implementation", options);
128 auto manager = std::unique_ptr<G4RunManager>(G4RunManagerFactory::CreateRunManager(
129 serial ? G4RunManagerType::SerialOnly : G4RunManagerType::MTOnly, true, 4));
130 if (auto* mt = dynamic_cast<G4MTRunManager*>(manager.get())) { mt->SetEventModulo(1); }
131 manager->SetUserInitialization(new Detector(empty));
132 manager->SetUserInitialization(new QBBC);
133 manager->SetUserInitialization(new GAction(options, digitizers));
134 manager->Initialize();
135 const std::vector<int> counts{64, 16, 8};
136 for (int run = 0; run < static_cast<int>(counts.size()); ++run) {
137 manager->BeamOn(counts[run]);
138 if (!empty) { check_output(base, run, counts[run], serial); }
139 }
140 manager.reset();
141 require(std::distance(std::filesystem::directory_iterator(scratch),
142 std::filesystem::directory_iterator{})
143 == (empty ? 0 : 6), "Expected exactly one output file per crate and run");
144 std::filesystem::remove_all(scratch);
145 std::cout << "SRO runtime routing, empty events, interruption, and repeated runs passed\n";
146 return 0;
147 }
148 catch (const std::exception& error) {
149 std::cerr << error.what() << '\n';
150 return 1;
151 }
152}
Registers GEMC user actions for worker threads, sequential execution, and the master thread.
Definition gaction.h:95
std::shared_ptr< T > LoadAndRegisterObjectFromLibrary(std::string_view name, const std::shared_ptr< GOptions > &gopts)
Declares GAction, the Geant4 action-initialization entry point for the GEMC actions module.
run
event
G4THitsCollection< GHit > GHitsCollection
GOptions defineOptions()
std::shared_ptr< const gstreamersMap > gstreamersMapPtr(const std::shared_ptr< GOptions > &gopts, int thread_id=-1)
GOptions defineOptions()
GOptions defineOptions()
Builds the aggregate option set required by the actions subsystem.
Definition gaction.h:54
int main(int argc, char *argv[])