gparticle
Loading...
Searching...
No Matches
gparticle_options.cc
Go to the documentation of this file.
1// gparticle
2#include "gparticle_options.h"
4#include "gparticle_reader.h"
5
6// gemc
7#include "gfactory_options.h"
8#include "gutilities.h"
9
10#include <cmath>
11#include <stdexcept>
12
13// namespace to define options
14namespace gparticle {
15using std::string;
16using std::vector;
17
18// Reads a YAML scalar as a G4-unit-converted double.
19//
20// Accepted forms:
21// "20*deg" — explicit unit, passed directly to getG4Number().
22// "20" — plain number, falls back to fallback_unit with a logged warning.
23// Default units: angles → deg, momentum → MeV, lengths → cm.
24//
25// Returns default_g4val when the key is absent from the node (e.g. optional
26// fields whose schema default was already merged).
27static double parseG4Value(const YAML::Node& node,
28 const string& key,
29 const string& fallback_unit,
30 double default_g4val,
31 const std::shared_ptr<GLogger>& logger) {
32 if (!node[key]) return default_g4val;
33
34 const auto val = node[key].as<string>();
35 if (val.find('*') != string::npos) {
36 return gutilities::getG4Number(val);
37 }
38
39 // Plain number without a unit — apply the fallback and warn.
40 logger->warning("gparticle field <", key, "> value <", val, "> has no unit; assuming ",
41 fallback_unit, ". Use '", val, "*", fallback_unit, "' to suppress this warning.");
42 return gutilities::getG4Number(val + "*" + fallback_unit);
43}
44
45static double parseRequiredG4Value(const YAML::Node& node,
46 const string& key,
47 const string& fallback_unit,
48 const std::shared_ptr<GLogger>& logger) {
49 if (!node[key] || node[key].IsNull()) {
51 "Mandatory gparticle field <", key, "> is missing");
52 }
53 return parseG4Value(node, key, fallback_unit, 0.0, logger);
54}
55
56// Build a vector of configured Gparticle instances from the structured "gparticle" option node.
57// The detailed API contract is documented in gparticle_options.h.
58vector<GparticlePtr> getGParticlesFromOption(const std::shared_ptr<GOptions>& gopts, std::shared_ptr<GLogger>& logger) {
59 // Retrieve the structured option node that contains an array of particle definitions.
60 auto gparticle_node = gopts->getOptionNode("gparticle");
61
62 vector<GparticlePtr> gparticles;
63 gparticles.reserve(gparticle_node.size());
64
65 for (auto gparticle_item : gparticle_node) {
66 gparticles.emplace_back(std::make_shared<Gparticle>(
67 gopts->get_required_variable_in_option<string>(gparticle_item, "name"),
68 gopts->get_variable_in_option<int>(gparticle_item, "multiplicity", 1),
69
70 parseRequiredG4Value(gparticle_item, "p", "MeV", logger),
71 parseG4Value(gparticle_item, "delta_p", "MeV", 0, logger),
72 gopts->get_variable_in_option<string>(gparticle_item, "randomMomentumModel", "uniform"),
73
74 parseG4Value(gparticle_item, "theta", "deg", 0, logger),
75 parseG4Value(gparticle_item, "delta_theta", "deg", 0, logger),
76 gopts->get_variable_in_option<string>(gparticle_item, "randomThetaModel", "uniform"),
77 parseG4Value(gparticle_item, "phi", "deg", 0, logger),
78 parseG4Value(gparticle_item, "delta_phi", "deg", 0, logger),
79
80 parseG4Value(gparticle_item, "vx", "cm", 0, logger),
81 parseG4Value(gparticle_item, "vy", "cm", 0, logger),
82 parseG4Value(gparticle_item, "vz", "cm", 0, logger),
83 parseG4Value(gparticle_item, "delta_vx", "cm", 0, logger),
84 parseG4Value(gparticle_item, "delta_vy", "cm", 0, logger),
85 parseG4Value(gparticle_item, "delta_vz", "cm", 0, logger),
86
87 gopts->get_variable_in_option<string>(gparticle_item, "randomVertexModel", "uniform"),
88 logger
89 ));
90 }
91
92 return gparticles;
93}
94
95vector<GparticlePtr> getGParticles(const std::shared_ptr<GOptions>& gopts, std::shared_ptr<GLogger>& logger) {
96 auto gparticles = getGParticlesFromOption(gopts, logger);
97 auto source_particles = getGParticlesFromSources(gopts, logger);
98 gparticles.insert(gparticles.end(), source_particles.begin(), source_particles.end());
99 return gparticles;
100}
101
102double getEventTimeWidth(const std::shared_ptr<GOptions>& gopts) {
103 if (!gopts) { throw std::invalid_argument("gparticle eventTimeWidth requires GEMC options"); }
104 const double width = gutilities::getG4Number(gopts->getRequiredScalarString("eventTimeWidth"));
105 if (!std::isfinite(width) || width < 0) {
106 throw std::invalid_argument("eventTimeWidth must be a finite non-negative time");
107 }
108 return width;
109}
110
111
112// Define the gparticle option schema and its human-readable help text.
113// The detailed API contract is documented in gparticle_options.h.
116
117 goptions.defineOption(
118 GVariable("eventTimeWidth", "0*ns", "Time assigned to each consecutive generated event"),
119 "Continuous event timeline spacing. Consecutive event IDs begin one eventTimeWidth apart. "
120 "The default 0*ns leaves the timeline disabled; consumers that require continuous time must "
121 "configure it.");
122
123 string help = "Adds a particle to the event generator.\n\n";
124 help += "Kinematic values accept an explicit Geant4 unit (e.g. '4*GeV', '23*deg', '1*mm').\n";
125 help += "A plain number without a unit falls back to the field default: MeV for momentum,\n";
126 help += "deg for angles, cm for vertex coordinates. A warning is logged in that case.\n\n";
127 help += "Examples:\n";
128 help += " - 5 GeV electron along z:\n";
129 help += " -gparticle=\"[{name: e-, p: 5*GeV}]\"\n\n";
130 help += " - one electron and two protons spread in theta:\n";
131 help += " -gparticle=\"[{name: e-, p: 2300*MeV, theta: 23*deg},\n";
132 help += " {name: proton, multiplicity: 2, p: 1200*MeV, theta: 14*deg, delta_theta: 10*deg}]\"\n";
133
134 vector<GVariable> gparticle_v = {
135 {"name", goptions::REQUIRED, "Particle name (mandatory), e.g. \"proton\" or \"e-\""},
136 {"multiplicity", 1, "How many copies of this particle will be generated in each event"},
137 {"p", goptions::REQUIRED, "Particle momentum with unit, e.g. \"4*GeV\" or \"4000*MeV\". "
138 "Plain number falls back to MeV."},
139 {"delta_p", "0*MeV", "Particle momentum spread, centered on p (same unit convention as p)."},
140 {"randomMomentumModel", "uniform", "Momentum randomization. 'gaussian' uses deltas as sigmas."},
141 {"theta", "0*deg", "Particle polar angle, e.g. \"23*deg\" or \"0.4*rad\". "
142 "Plain number falls back to deg."},
143 {"delta_theta", "0*deg", "Particle polar angle spread, centered on theta."},
144 {"randomThetaModel", "uniform",
145 "Distribute cos(theta) or theta. 'cosine': cos(theta) is uniform. 'uniform': theta is uniform."},
146 {"phi", "0*deg", "Particle azimuthal angle, e.g. \"90*deg\". Plain number falls back to deg."},
147 {"delta_phi", "0*deg", "Particle azimuthal angle spread, centered on phi."},
148 {"vx", "0*cm", "Particle vertex x component, e.g. \"1*mm\". Plain number falls back to cm."},
149 {"vy", "0*cm", "Particle vertex y component."},
150 {"vz", "0*cm", "Particle vertex z component."},
151 {"delta_vx", "0*cm", "Particle vertex spread in x."},
152 {"delta_vy", "0*cm", "Particle vertex spread in y."},
153 {"delta_vz", "0*cm", "Particle vertex spread in z."},
154 {"randomVertexModel", "uniform",
155 "Vertex randomization. 'uniform': flat. 'gaussian': deltas are sigmas. 'sphere': uniform in sphere."}
156 };
157
158 goptions.defineOption("gparticle", "define the generator particle(s)", gparticle_v, help);
159
160 string file_help = "Adds particles to the event generator from particle-definition files. \n";
161 file_help += "The option is cumulative and each entry selects a reader by format and filename. \n \n";
162 file_help += "Built-in formats: \n";
163 for (const auto& format : supported_static_reader_formats()) { file_help += " - " + format + "\n"; }
164 file_help += "\n";
165 file_help += "Formats are case-insensitive. Additional formats can be provided by dynamic plugins named ";
166 file_help += "gparticle_<format>_plugin.gplugin exporting GParticleReaderFactory. \n \n";
167 file_help += "Example: \n";
168 file_help += "-gparticlefile=\"[{format: lund, filename: a.lund}]\" \n";
169
170 vector<GVariable> gparticlefile_v = {
171 {"format", goptions::REQUIRED, "Particle file format, for example \"lund\""},
172 {"filename", goptions::REQUIRED, "Input filename containing particle definitions"}
173 };
174
175 goptions.defineOption("gparticlefile", "define generator particles from file(s)", gparticlefile_v, file_help);
176
178
179 return goptions;
180}
181} // namespace gparticle
Conventions and error codes for the gparticle module.
Public API for defining and parsing gparticle-related options.
constexpr const char * GPARTICLE_LOGGER
vector< GparticlePtr > getGParticles(const std::shared_ptr< GOptions > &gopts, std::shared_ptr< GLogger > &logger)
Builds the list of generator particles from structured options.
vector< GparticlePtr > getGParticlesFromOption(const std::shared_ptr< GOptions > &gopts, std::shared_ptr< GLogger > &logger)
Builds inline generator particles from only the -gparticle option.
GOptions defineOptions()
Defines the structured options used by the gparticle module.
GOptions defineOptions()
constexpr int EC__MANDATORY_NOT_FILLED
constexpr RequiredValue REQUIRED
const std::vector< std::string > & supported_static_reader_formats()
Returns built-in file-reader format tokens.
double getEventTimeWidth(const std::shared_ptr< GOptions > &gopts)
Returns the configured width of each consecutive simulation-event window.
std::vector< GparticlePtr > getGParticlesFromSources(const std::shared_ptr< GOptions > &gopts, std::shared_ptr< GLogger > &logger)
Loads propagated particles from all configured file sources.
double getG4Number(const string &v, bool warnIfNotUnit=false)