aegir-genie
GENIE neutrino event generation for the SHiP experiment's aegir framework.
Loading...
Searching...
No Matches
genie_config.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2026 CERN for the benefit of the SHiP Collaboration
2//
3// SPDX-License-Identifier: LGPL-3.0-or-later
4
5// genie_config.hpp — configuration for the GENIE driver assembly
6//
7// Shared by the genie_source plugin and the standalone gevgen_ship app (via
8// genie_driver_setup.hpp). Kept as a plain struct with a validate() member so
9// the checks are unit testable without loading the phlex plugin (see
10// tests/flux_driver_test.cpp).
11// Validation runs before any GENIE singleton is touched: several GENIE init
12// helpers call exit() on bad input rather than throwing, so failing early
13// with a clear message is the only way to surface configuration errors.
14
15#pragma once
16
17#include <cstdlib>
18#include <filesystem>
19#include <stdexcept>
20#include <string>
21
22namespace aegir {
23
24// Resolve a GeoModel geometry db path: an existing path is used as-is; a
25// bare filename is looked up under $SHIPGEOMETRY_ROOT/share/geometry (the
26// flat install layout of the shipgeometry package, as in aegir's
27// geometry_geomodel_provider). A missing path *with* directory components
28// is an error — silently substituting the central geometry for a mistyped
29// path would run the simulation on the wrong detector.
30inline std::string resolve_geometry_file(std::string const& path,
31 std::string const& context) {
32 namespace fs = std::filesystem;
33 if (fs::exists(path)) return path;
34 auto const p = fs::path(path);
35 if (p == p.filename()) {
36 if (auto const* root = std::getenv("SHIPGEOMETRY_ROOT")) {
37 auto const resolved = fs::path(root) / "share" / "geometry" / p;
38 if (fs::exists(resolved)) return resolved.string();
39 }
40 }
41 throw std::runtime_error(context + ": cannot locate geometry db '" + path +
42 "'; set SHIPGEOMETRY_ROOT or provide an absolute "
43 "path");
44}
45
47 std::string tune = "G18_02a_00_000";
48 std::string spline_file; // GENIE cross-section splines (gmkspl XML output)
49 std::string flux_file; // neutrino flux file (format per flux_format)
50 // 'ship': SHiP flux ntuple, schema v1 (ShipFluxDriver);
51 // 'gsimple': GENIE GSimple flux (genie::flux::GSimpleNtpFlux) — the format
52 // the SHiP neutrino group produces for gevgen_fnal.
53 std::string flux_format = "ship";
54 // Detector geometry: GeoModel SQLite db (resolved via
55 // resolve_geometry_file), the same db the Geant4 side tracks through.
56 std::string geometry_file;
57 // Optional: restrict GENIE to the named logical volume ('cave' in
58 // production); empty scans the entire world.
59 std::string top_volume;
60 long seed = 20260706; // base seed; each event derives its own via Philox
61 // Optional cache for the (expensive) max-path-lengths geometry scan: if the
62 // file exists it is loaded, otherwise it is computed and saved there.
64
65 // `context` prefixes the error messages ("genie_source", "gevgen_ship").
66 void validate(std::string const& context = "genie_source") const {
67 namespace fs = std::filesystem;
68 if (tune.empty())
69 throw std::runtime_error(context + ": 'tune' must not be empty");
70 if (seed <= 0)
71 throw std::runtime_error(
72 context +
73 ": 'seed' must be > 0 (GENIE ignores non-positive seeds), got " +
74 std::to_string(seed));
75 // The per-event Philox key uses the seed as a 32-bit word (see
76 // reseed_event); wider seeds would silently alias mod 2^32 while GENIE's
77 // own RandGen and the GHEP metadata got the full value. (Event numbers
78 // are likewise taken mod 2^32 — unreachable in practice.)
79 if (seed > 4294967295L)
80 throw std::runtime_error(
81 context + ": 'seed' must fit in 32 bits (max 4294967295), got " +
82 std::to_string(seed));
83 auto require_file = [&context](std::string const& key,
84 std::string const& path) {
85 if (path.empty())
86 throw std::runtime_error(context + ": config key '" + key +
87 "' is required");
88 if (!fs::exists(path))
89 throw std::runtime_error(context + ": " + key + " '" + path +
90 "' does not exist");
91 };
92 if (flux_format != "ship" && flux_format != "gsimple")
93 throw std::runtime_error(context +
94 ": flux_format must be 'ship' or 'gsimple', "
95 "got '" +
96 flux_format + "'");
97 require_file("splines", spline_file);
98 if (flux_file.empty())
99 throw std::runtime_error(context +
100 ": config key 'flux_file' is required");
101 // Remote URLs (root://... via xrootd) cannot be checked on the local
102 // filesystem; leave those to the flux driver's own error handling.
103 if (!flux_file.contains("://")) require_file("flux_file", flux_file);
104 if (geometry_file.empty())
105 throw std::runtime_error(context +
106 ": config key 'geometry_file' is required");
107 resolve_geometry_file(geometry_file, context); // throws if unlocatable
108 if (!max_path_lengths_file.empty() && !fs::exists(max_path_lengths_file)) {
109 // Will be created after the geometry scan — its directory must exist.
110 auto const dir = fs::path{max_path_lengths_file}.parent_path();
111 if (!dir.empty() && !fs::is_directory(dir))
112 throw std::runtime_error(context +
113 ": cannot create max_path_lengths_file '" +
114 max_path_lengths_file + "': directory '" +
115 dir.string() + "' does not exist");
116 }
117 }
118};
119
120} // namespace aegir
Definition genie_config.hpp:22
std::string resolve_geometry_file(std::string const &path, std::string const &context)
Definition genie_config.hpp:30
Definition genie_config.hpp:46
void validate(std::string const &context="genie_source") const
Definition genie_config.hpp:66
std::string geometry_file
Definition genie_config.hpp:56
std::string max_path_lengths_file
Definition genie_config.hpp:63
std::string flux_format
Definition genie_config.hpp:53
std::string top_volume
Definition genie_config.hpp:59
std::string flux_file
Definition genie_config.hpp:49
std::string tune
Definition genie_config.hpp:47
long seed
Definition genie_config.hpp:60
std::string spline_file
Definition genie_config.hpp:48