MARLEY (Model of Argon Reaction Low Energy Yields) v2.0.0
A Monte Carlo event generator for tens-of-MeV neutrino interactions
Loading...
Searching...
No Matches
marley::CommandHandler Class Reference

Wrapper for handling commands in the main marley executable. More...

#include <CommandHandler.hh>

Classes

struct  CommandInfo
 

Public Types

using CommandMap = std::map< std::string, CommandInfo >
 

Public Member Functions

 CommandHandler (int argc, char *argv[])
 
bool execute ()
 

Static Public Member Functions

static bool print_top_level_help ()
 

Static Protected Member Functions

static bool cmd_convert (std::deque< std::string > &args)
 Convert MARLEY event files between supported formats.
 
static bool cmd_decay (std::deque< std::string > &args)
 Simulate nuclear de-excitations.
 
static bool cmd_generate (std::deque< std::string > &args)
 Generate Monte Carlo events.
 
static bool cmd_help (std::deque< std::string > &args)
 Display top-level or command-specific help messages.
 
static bool cmd_print (std::deque< std::string > &args)
 Print existing MARLEY events in a human-readable format.
 
static bool cmd_reweight (std::deque< std::string > &args)
 Reweight existing MARLEY events.
 
static bool cmd_summarize (std::deque< std::string > &args)
 Summarize an existing sample of MARLEY events as a ROOT TTree.
 
static bool cmd_xsec (std::deque< std::string > &args)
 Tabulate energy-dependent total cross section values.
 

Protected Attributes

std::deque< std::string > cmds_
 

Static Protected Attributes

static const CommandMap command_map_
 

Detailed Description

Wrapper for handling commands in the main marley executable.

Definition at line 28 of file CommandHandler.hh.


Class Documentation

◆ marley::CommandHandler::CommandInfo

struct marley::CommandHandler::CommandInfo

Definition at line 34 of file CommandHandler.hh.

Class Members
function< bool(deque< string > &) > cmd_ Function to call to execute the command.
function< void() > print_help_ Functiont that prints a detailed help message to stdout.
bool requires_root_ = false

Indicates whether MARLEY needs to be linked against ROOT to execute this command

string summary_ Simple description of the command.

Member Typedef Documentation

◆ CommandMap

using marley::CommandHandler::CommandMap = std::map< std::string, CommandInfo >

Definition at line 49 of file CommandHandler.hh.

Constructor & Destructor Documentation

◆ CommandHandler()

marley::CommandHandler::CommandHandler ( int argc,
char * argv[] )

Definition at line 244 of file CommandHandler.cc.

246 : cmds_( argv, argv + argc )
247{
248 // Strip the executable name off of the front of the deque
249 cmds_.pop_front();
250}
std::deque< std::string > cmds_

Member Function Documentation

◆ cmd_convert()

bool marley::CommandHandler::cmd_convert ( std::deque< std::string > & args)
staticprotected

Convert MARLEY event files between supported formats.

Definition at line 248 of file cmd_convert.cc.

248 {
249
250 std::string output_path;
251 std::string output_format;
252 bool force = false;
253 std::vector< std::string > input_files;
254
255 while ( !args.empty() ) {
256 std::string arg = args.front();
257 args.pop_front();
258
259 if ( arg == "-o" || arg == "--output" ) {
260 if ( args.empty() ) {
261 std::cerr << "marley convert: missing argument after '" << arg
262 << "'\n";
263 return false;
264 }
265 output_path = args.front();
266 args.pop_front();
267 }
268 else if ( arg == "--output-format" ) {
269 if ( args.empty() ) {
270 std::cerr << "marley convert: missing argument after '"
271 << "--output-format'\n";
272 return false;
273 }
274 output_format = args.front();
275 args.pop_front();
276 }
277 else if ( arg == "--force" || arg == "-f" ) {
278 force = true;
279 }
280 else if ( arg == "--help" || arg == "-h" ) {
281 args.clear();
282 args.push_front( "convert" );
284 }
285 else if ( arg.front() == '-' ) {
286 std::cerr << "marley convert: unrecognized option '" << arg << "'\n";
287 return false;
288 }
289 else {
290 input_files.push_back( arg );
291 }
292 }
293
294 if ( output_path.empty() ) {
295 std::cerr << "marley convert: missing required option -o OUTPUT_FILE\n";
296 args.push_front( "convert" );
298 return false;
299 }
300
301 if ( input_files.empty() ) {
302 std::cerr << "marley convert: no input files specified\n";
303 args.push_front( "convert" );
305 return false;
306 }
307
308 if ( output_format.empty() ) {
309 if ( output_path.size() >= 5
310 && output_path.substr( output_path.size() - 5 ) == ".root" )
311 {
312 output_format = "root";
313 }
314 else {
315 output_format = "ascii";
316 }
317 }
318
319 if ( output_format != "ascii" && output_format != "root"
320 && output_format != "legacy" && output_format != "hepevt" )
321 {
322 std::cerr << "marley convert: invalid output format '"
323 << output_format << "'. Supported formats:"
324 " ascii, root, legacy, hepevt\n";
325 return false;
326 }
327
328#ifndef USE_ROOT
329 if ( output_format == "root" ) {
330 std::cerr << "marley convert: ROOT output format requires a"
331 " ROOT-enabled build of MARLEY.\n";
332 return false;
333 }
334#endif
335
336 if ( !force ) {
337 std::ifstream test( output_path );
338 if ( test ) {
339 bool overwrite = marley_utils::prompt_yes_no(
340 "Really overwrite " + output_path + "?" );
341 if ( !overwrite ) {
342 std::cout << "Action aborted.\n";
343 return true;
344 }
345 }
346 }
347
348 if ( output_format == "legacy" ) {
349 convert_to_legacy( input_files, output_path );
350 return true;
351 }
352
353 if ( output_format == "hepevt" ) {
354 convert_to_hepevt( input_files, output_path );
355 return true;
356 }
357
358 std::string out_config_str = "{ format: \"" + output_format
359 + "\", file: \"" + output_path
360 + "\", mode: \"overwrite\", force: true }";
361 auto out_config = marley::JSON::load( out_config_str );
362 auto output_file = marley::OutputFile::make_OutputFile( out_config );
363
364 bool multi_file = (input_files.size() > 1);
365 std::shared_ptr< HepMC3::GenRunInfo > cleaned_run_info;
366 for_each_event( input_files,
367 [ & ]( HepMC3::GenEvent& ev, bool first_event, double,
368 const auto& first_info )
369 {
370 if ( first_event && multi_file ) {
371 cleaned_run_info = make_cleaned_run_info( first_info );
372 }
373 if ( cleaned_run_info ) {
374 ev.set_run_info( cleaned_run_info );
375 }
376 output_file->write_event( &ev );
377 } );
378
379 return true;
380}
void set_run_info(std::shared_ptr< GenRunInfo > run)
Set the GenRunInfo object by smart pointer.
Definition GenEvent.h:148
static bool cmd_help(std::deque< std::string > &args)
Display top-level or command-specific help messages.
Definition cmd_help.cc:24

References cmd_help(), and HepMC3::GenEvent::set_run_info().

◆ cmd_decay()

bool marley::CommandHandler::cmd_decay ( std::deque< std::string > & args)
staticprotected

Simulate nuclear de-excitations.

Definition at line 60 of file cmd_decay.cc.

60 {
61
62 std::string config_file_name;
63 if ( !args.empty() ) config_file_name = args.front();
64
65 if ( config_file_name.empty() || config_file_name == "-h"
66 || config_file_name == "--help" )
67 {
68 args.clear();
69 args.push_front( "decay" );
71 }
72
73 // -------------------------------------------------------------------------
74 // Load the JSON config from file, then patch in null entries for "reactions"
75 // and "source" if those keys are absent. This allows create_generator() to
76 // take its early-return path and skip cross-section normalisation, which is
77 // irrelevant for standalone de-excitation events.
78 // -------------------------------------------------------------------------
79 marley::JSON json = marley::JSON::load_file( config_file_name );
80
81 if ( !json.has_key("reactions") ) json["reactions"] = marley::JSON( nullptr );
82 if ( !json.has_key("source") ) json["source"] = marley::JSON( nullptr );
83
84 marley::JSONConfig jc( json );
85 marley::Generator gen = jc.create_generator();
86 gen.set_up_run_info();
87
88 bool ok;
89
90 // -------------------------------------------------------------------------
91 // Parse the required top-level "decay" block
92 // -------------------------------------------------------------------------
93 const std::string decay_config_label( "decay" );
94 marley::JSON decay_config;
95 ok = get_from_json< marley::JSON >( decay_config_label, json, decay_config );
96 if ( !ok ) throw marley::Error( "Missing key '" + decay_config_label
97 + "' in job configuration file" );
98
99 long num_events = assign_from_json< long >( "events", decay_config, ok, 1000 );
100
101 // -------------------------------------------------------------------------
102 // Parse the required "nucleus" sub-object
103 // -------------------------------------------------------------------------
104 if ( !decay_config.has_key("nucleus") ) {
105 throw marley::Error( "Missing required \"nucleus\" key in the"
106 " \"decay\" configuration block" );
107 }
108 const marley::JSON& nuc_config = decay_config.at( "nucleus" );
109
110 // Read the nucleus identity. The user may supply either (or both) of:
111 // Option A: Z and A integer keys
112 // Option B: pdg integer key (nuclear PDG code)
113 // If both are supplied they must be consistent.
114
115 bool has_pdg = nuc_config.has_key("pdg");
116 bool has_Z = nuc_config.has_key("Z");
117 bool has_A = nuc_config.has_key("A");
118
119 if ( !has_pdg && !( has_Z && has_A ) ) {
120 throw marley::Error( "The \"nucleus\" block must specify the nuclide"
121 " using either the \"pdg\" key or both the \"Z\" and \"A\" keys" );
122 }
123
124 int nucleus_pdg = 0;
125 int Z = 0, A = 0;
126
127 if ( has_pdg ) {
128 nucleus_pdg = assign_from_json< int >( "pdg", nuc_config, ok );
129 int Z_from_pdg = 0, A_from_pdg = 0;
130 if ( !pdg_to_ZA( nucleus_pdg, Z_from_pdg, A_from_pdg ) ) {
131 throw marley::Error( "The value " + std::to_string(nucleus_pdg)
132 + " given for \"nucleus.pdg\" is not a recognized nuclear"
133 " or nucleon PDG code" );
134 }
135 Z = Z_from_pdg;
136 A = A_from_pdg;
137 }
138
139 if ( has_Z && has_A ) {
140 int Z_cfg = assign_from_json< int >( "Z", nuc_config, ok );
141 int A_cfg = assign_from_json< int >( "A", nuc_config, ok );
142 if ( has_pdg ) {
143 // Both representations present — verify consistency
144 if ( Z_cfg != Z || A_cfg != A ) {
145 throw marley::Error( "Inconsistent nucleus specification: pdg="
146 + std::to_string(nucleus_pdg) + " implies Z=" + std::to_string(Z)
147 + ", A=" + std::to_string(A) + ", but Z=" + std::to_string(Z_cfg)
148 + ", A=" + std::to_string(A_cfg) + " were also given" );
149 }
150 }
151 else {
152 Z = Z_cfg;
153 A = A_cfg;
154 nucleus_pdg = marley_utils::get_nucleus_pid( Z, A );
155 }
156 }
157
158 if ( Z < 0 ) throw marley::Error( "Negative Z encountered" );
159 if ( A < 1 ) throw marley::Error( "A < 1 encountered" );
160
161 // Net ionic charge of the nucleus (protons minus electrons).
162 // Default 0 = neutral atom.
163 int net_charge = assign_from_json< int >( "net_charge", nuc_config, ok, 0 );
164
165 // -------------------------------------------------------------------------
166 // Parse excitation energy: fixed value or uniform sampling interval
167 // -------------------------------------------------------------------------
168 double Ex = 0.;
169 double Ex_min = 0.;
170 double Ex_max = 0.;
171 bool sample_Ex = nuc_config.has_key( "Ex_max" );
172 if ( !sample_Ex ) {
173 Ex = assign_from_json< double >( "Ex", nuc_config, ok, -1.0 );
174 if ( !ok ) throw marley::Error( "Missing \"Ex\" key in \"nucleus\" block"
175 " (or use \"Ex_min\" + \"Ex_max\" for uniform sampling)" );
176 if ( Ex < 0. ) throw marley::Error( "Negative excitation energy"
177 " encountered (Ex = " + std::to_string(Ex) + " MeV)" );
178 }
179 else {
180 Ex_min = assign_from_json< double >( "Ex_min", nuc_config, ok, -1.0 );
181 Ex_max = assign_from_json< double >( "Ex_max", nuc_config, ok, -1.0 );
182 if ( Ex_min < 0. ) throw marley::Error( "Negative lower excitation"
183 " energy bound (Ex_min = " + std::to_string(Ex_min) + " MeV)" );
184 if ( Ex_max < 0. ) throw marley::Error( "Negative upper excitation"
185 " energy bound (Ex_max = " + std::to_string(Ex_max) + " MeV)" );
186 if ( Ex_max < Ex_min ) throw marley::Error( "Upper Ex bound"
187 " (" + std::to_string(Ex_max) + " MeV) is less than lower Ex bound"
188 " (" + std::to_string(Ex_min) + " MeV)" );
189 }
190
191 // -------------------------------------------------------------------------
192 // Parse nuclear spin: required array of 2J values
193 // -------------------------------------------------------------------------
194 std::vector< int > twoJ_vec;
195 if ( !nuc_config.has_key("twoJ") ) {
196 throw marley::Error( "Missing \"twoJ\" key in \"nucleus\" block" );
197 }
198 else {
199 const marley::JSON& twoJ_obj = nuc_config.at( "twoJ" );
200 if ( !twoJ_obj.is_array() ) {
201 throw marley::Error( "The \"twoJ\" key in \"nucleus\" must have"
202 " a value that is a JSON array" );
203 }
204 convert_json< std::vector<int> >( twoJ_obj, twoJ_vec );
205 }
206 if ( twoJ_vec.empty() ) {
207 throw marley::Error( "The \"twoJ\" array in \"nucleus\" must not"
208 " be empty" );
209 }
210 for ( const auto& tJ : twoJ_vec ) {
211 if ( tJ < 0 ) throw marley::Error( "Negative 2J value encountered"
212 " in \"twoJ\" array" );
213 bool even_A = ( A % 2 == 0 );
214 bool even_twoJ = ( tJ % 2 == 0 );
215 if ( even_A != even_twoJ ) throw marley::Error( "Unphysical twoJ = "
216 + std::to_string( tJ ) + " encountered for A = "
217 + std::to_string( A ) );
218 }
219
220 std::vector< double > twoJ_weights( twoJ_vec.size(), 1. );
221 std::discrete_distribution< size_t > twoJ_dist(
222 twoJ_weights.cbegin(), twoJ_weights.cend() );
223
224 // -------------------------------------------------------------------------
225 // Parse parity: optional, default "+"
226 // -------------------------------------------------------------------------
227 auto parity_str = assign_from_json< std::string >( "parity", nuc_config,
228 ok, "+" );
229 if ( parity_str != "+" && parity_str != "-" && parity_str != "random" ) {
230 throw marley::Error( "Invalid \"parity\" setting \""
231 + parity_str + "\" in \"nucleus\" block."
232 " Expected \"+\", \"-\", or \"random\"" );
233 }
234
235 const std::vector< std::string > parity_strings = { "+", "-" };
236 const std::vector< double > parity_weights = { 1., 1. };
237 std::discrete_distribution< size_t > parity_dist(
238 parity_weights.cbegin(), parity_weights.cend() );
239
240 // -------------------------------------------------------------------------
241 // Parse output configuration
242 // -------------------------------------------------------------------------
243 std::vector< std::shared_ptr<marley::OutputFile> > output_files;
244
245 if ( decay_config.has_key("output") ) {
246 marley::JSON output_set = decay_config.at( "output" );
247 if ( !output_set.is_array() ) throw marley::Error( "The"
248 " \"output\" key must have a value that is a JSON array." );
249 for ( const auto& el : output_set.array_range() ) {
250 output_files.push_back( marley::OutputFile::make_OutputFile(el) );
251 }
252 }
253 else {
254 std::string out_cfg_str = "{ format: \"ascii\","
255 " file: \"decay_events.hepmc3\", mode: \"overwrite\" }";
256 auto out_cfg = marley::JSON::load( out_cfg_str );
257 output_files.push_back( marley::OutputFile::make_OutputFile(out_cfg) );
258 }
259
260 for ( const auto& file : output_files ) {
261 if ( file->mode_is_resume() ) {
262 throw marley::Error( "The \"resume\" output mode is not supported"
263 " by the \"marley decay\" command" );
264 }
265 }
266
267 // -------------------------------------------------------------------------
268 // Pre-compute fixed quantities used each event
269 // -------------------------------------------------------------------------
270 const auto& mt = marley::MassTable::Instance();
271
272 double gs_mass = mt.get_atomic_mass( nucleus_pdg )
273 - net_charge * mt.get_particle_mass( marley_utils::ELECTRON );
274
275 double unbound_threshold = mt.unbound_threshold( nucleus_pdg );
276
277 int signal_proc_id = marley_hepmc3::get_nuhepmc_proc_id(
278 ProcType::StandaloneDecay );
279
280 // -------------------------------------------------------------------------
281 // Event loop
282 // -------------------------------------------------------------------------
283 // One-time warning flags for the level-snap behaviour
284 bool warned_snap = false; // suppress after first snap warning
285 bool warned_snap_sample = false; // suppress after first "will continue" note
286
287 for ( long evnum = 0; evnum < num_events; ++evnum ) {
288
289 // --- Sample Ex, twoJ, and parity for this event -----------------------
290 if ( sample_Ex ) {
291 Ex = gen.uniform_random_double( Ex_min, Ex_max, true );
292 }
293
294 size_t twoJ_index = gen.sample_from_distribution( twoJ_dist );
295 int twoJ = twoJ_vec.at( twoJ_index );
296
297 std::string par_str = parity_str;
298 if ( par_str == "random" ) {
299 size_t par_index = gen.sample_from_distribution( parity_dist );
300 par_str = parity_strings.at( par_index );
301 }
302 marley::Parity parity;
303 std::istringstream temp_iss( par_str );
304 temp_iss >> parity;
305
306 // --- Snap to nearest discrete level if below the unbound threshold ----
307 if ( Ex <= unbound_threshold ) {
308 auto* ds = gen.get_structure_db().get_decay_scheme( nucleus_pdg );
309 if ( ds ) {
310 auto* lev = ds->get_pointer_to_closest_level( Ex );
311
312 // Check what changes on the snap
313 double Ex_lev = lev->energy();
314 int twoJ_lev = lev->twoJ();
315 marley::Parity P_lev = lev->parity();
316
317 bool Ex_changed = ( std::abs(Ex_lev - Ex) > 1e-5 );
318 bool twoJ_changed = ( twoJ_lev != twoJ );
319 bool P_changed = ( static_cast<int>(P_lev)
320 != static_cast<int>(parity) );
321
322 if ( ( Ex_changed || twoJ_changed || P_changed ) && !warned_snap ) {
323 std::ostringstream warn_msg;
324 warn_msg << "User-specified nuclear state (Ex=" << Ex
325 << " MeV, 2J=" << twoJ
326 << ", P=" << parity
327 << ") was snapped to nearest discrete level (Ex="
328 << Ex_lev << " MeV, 2J=" << twoJ_lev
329 << ", P=" << P_lev << ").";
330 if ( !Ex_changed ) warn_msg << " Ex unchanged.";
331 if ( !twoJ_changed ) warn_msg << " 2J unchanged.";
332 if ( !P_changed ) warn_msg << " Parity unchanged.";
333 warn_msg << " This warning will not be repeated.";
334 MARLEY_LOG( WARN, "cmd.decay" ) << warn_msg.str();
335 warned_snap = true;
336
337 if ( sample_Ex && !warned_snap_sample ) {
338 MARLEY_LOG( WARN, "cmd.decay" ) << "Discrete level matching"
339 " will continue for subsequent events as Ex values below the"
340 " unbound threshold (" << unbound_threshold << " MeV) are"
341 " sampled. This message will not be repeated.";
342 warned_snap_sample = true;
343 }
344 }
345
346 // Apply the snap — use the level's quantum numbers
347 Ex = Ex_lev;
348 twoJ = twoJ_lev;
349 parity = P_lev;
350 }
351 }
352
353 // --- Build the HepMC3 event object ------------------------------------
354 //
355 // Primary vertex layout:
356 //
357 // IN: dummy projectile (PDG 0, status 4, zero 4-momentum)
358 // target nucleus (status 20, at rest, gs mass)
359 //
360 // OUT: dummy ejectile (PDG 0, status 1, zero 4-momentum)
361 // residue nucleus (status 27, mass = gs + Ex, with Ex/twoJ/parity
362 // attributes for NucleusDecayer)
363
364 auto event = std::make_shared< HepMC3::GenEvent >(
365 HepMC3::Units::MEV, HepMC3::Units::CM );
366
367 event->set_event_number( evnum + 1 );
368
369 event->add_attribute( "signal_process_id",
370 std::make_shared< HepMC3::IntAttribute >( signal_proc_id ) );
371
372 auto prim_vtx = std::make_shared< HepMC3::GenVertex >();
373 prim_vtx->set_status( marley_hepmc3::NUHEPMC_PRIMARY_VERTEX );
374 event->add_vertex( prim_vtx );
375
376 // Dummy projectile — PDG 0, zero 4-momentum
377 auto projectile = marley_hepmc3::make_particle( 0, 0., 0., 0., 0.,
378 marley_hepmc3::NUHEPMC_PROJECTILE_STATUS, 0. );
379
380 // Target nucleus — at rest, ground-state mass
381 auto target = marley_hepmc3::make_particle( nucleus_pdg,
382 marley_hepmc3::NUHEPMC_TARGET_STATUS, gs_mass );
383
384 // Dummy ejectile — clone of projectile (PDG 0, zero 4-momentum), final-state
385 auto ejectile = marley_hepmc3::make_particle( 0, 0., 0., 0., 0.,
386 marley_hepmc3::NUHEPMC_FINAL_STATE_STATUS, 0. );
387
388 // Residue nucleus — excited state
389 double m_residue = gs_mass + Ex;
390 auto residue = marley_hepmc3::make_particle( nucleus_pdg, 0., 0., 0.,
391 m_residue, marley_hepmc3::NUHEPMC_UNDECAYED_RESIDUE_STATUS, m_residue );
392
393 prim_vtx->add_particle_in( projectile );
394 prim_vtx->add_particle_in( target );
395 prim_vtx->add_particle_out( ejectile );
396 prim_vtx->add_particle_out( residue );
397
398 // Set attributes for the target + residue now that these particles
399 // are attached to the event
400 marley_hepmc3::set_particle_charge( *target, net_charge );
401 marley_hepmc3::set_particle_charge( *residue, net_charge );
402
403 residue->add_attribute( "Ex",
404 std::make_shared< HepMC3::DoubleAttribute >( Ex ) );
405 residue->add_attribute( "twoJ",
406 std::make_shared< HepMC3::IntAttribute >( twoJ ) );
407 residue->add_attribute( "parity",
408 std::make_shared< HepMC3::IntAttribute >( static_cast<int>(parity) ) );
409
410 // --- Run de-excitation cascade ----------------------------------------
411 marley::NucleusDecayer nd;
412 nd.process_event( *event, gen );
413
414 gen.finish_event_metadata( *event );
415
416 for ( const auto& file : output_files ) {
417 file->write_event( event.get() );
418 }
419
420 std::cout << "Event " << evnum << "\n";
421 }
422
423 return true;
424}
double uniform_random_double(double min, double max, bool inclusive)
Sample a random number uniformly on either [min, max) or [min, max].
Definition Generator.cc:235
void set_up_run_info()
Initializes the owned GenRunInfo object that will be used to associate run metadata with the output e...
Definition Generator.cc:769
marley::StructureDatabase & get_structure_db()
Get a reference to the StructureDatabase owned by this Generator.
Definition Generator.cc:510
auto sample_from_distribution(RandomNumberDistribution &rnd) -> decltype(std::declval< RandomNumberDistribution & >().operator()(std::declval< std::mt19937_64 & >()))
Sample from an arbitrary probability distribution (defined here as any object that implements an oper...
Definition Generator.hh:193
void finish_event_metadata(HepMC3::GenEvent &ev, bool attach_state=false)
Add final pieces of metadata (e.g., the RNG state) to an otherwise complete event.
Definition Generator.cc:827
static const MassTable & Instance()
Get a const reference to the singleton instance of the MassTable.
Definition MassTable.cc:69
virtual void process_event(HepMC3::GenEvent &event, marley::Generator &gen) override
Processes an input GenEvent object.
marley::DecayScheme * get_decay_scheme(const int particle_id)
Retrieves discrete level data from the database.

References cmd_help(), marley::Generator::finish_event_metadata(), marley::StructureDatabase::get_decay_scheme(), marley::Generator::get_structure_db(), marley::MassTable::Instance(), marley::NucleusDecayer::process_event(), marley::Generator::sample_from_distribution(), marley::Generator::set_up_run_info(), and marley::Generator::uniform_random_double().

◆ cmd_generate()

bool marley::CommandHandler::cmd_generate ( std::deque< std::string > & args)
staticprotected

Generate Monte Carlo events.

Definition at line 340 of file cmd_generate.cc.

340 {
341
342 // Declared here so the catch block can pass it to reset_terminal().
343 // Remains 0 if an exception is thrown before output_files is populated.
344 int num_status_lines = 0;
345
346 // Flag that indicates whether caught exceptions need to call
347 // reset_terminal(). After we set up the status line display, this becomes
348 // important. Before that, it clears logging messages from the screen
349 // unnecessarily.
350 bool exception_needs_reset_terminal = false;
351
352 // Create a pointer to store a buffered event and its corresponding
353 // random number generator state string. We write out the buffered
354 // event only when generation of the following event has successfully
355 // completed. This saves space by allowing the generator state to be
356 // saved only when needed at the end of a run (normal termination or
357 // an early exit in response to an interruption/exception). The state can
358 // be used to restart the MARLEY simulation job where it left off
359 // without a drift in the random number state.
360 std::shared_ptr< HepMC3::GenEvent > buffered_event;
361 std::string cached_generator_state;
362
363 std::vector< std::shared_ptr<marley::OutputFile> > output_files;
364
365 try {
366
367 std::string config_file_name;
368 if ( !args.empty() ) config_file_name = args.front();
369
370 bool cfn_empty = config_file_name.empty();
371
372 if ( cfn_empty || config_file_name.front() == '-' )
373 {
374 if ( !cfn_empty && config_file_name != "-h"
375 && config_file_name != "--help" )
376 {
377 std::cerr << "marley generate: unrecognized option '"
378 << config_file_name << "'\n";
379 }
380 args.clear();
381 args.push_front( "generate" );
383 return false;
384 }
385
386 marley::JSON json = marley::JSON::load_file( config_file_name );
387 marley::JSONConfig jc( json );
388
389 std::chrono::system_clock::time_point start_time_point
390 = std::chrono::system_clock::now();
391
392 std::time_t start_time = std::chrono::system_clock::to_time_t(
393 start_time_point );
394
395 std::cout << "\nMARLEY started on "
396 << put_time( std::localtime(&start_time), "%c %Z" ) << '\n';
397
398 long num_old_events = 0;
399
400 marley::JSON ex_set = json.get_object( "generate", false );
401 long num_events = ex_set.get_long( "events", 1e3 );
402
403 MARLEY_LOG( INFO, "app" ) << "Requested events: " << num_events
404 << ", configuration file: \"" << config_file_name << "\"";
405
406 int status_update_interval = DEFAULT_STATUS_UPDATE_INTERVAL;
407 if ( ex_set.has_key("status_update_interval") ) {
408 const auto& sui = ex_set.at( "status_update_interval" );
409 bool ok;
410 int sui_value = sui.to_long( ok );
411
412 if ( !ok || sui_value < 1 ) {
413 throw marley::Error( "Invalid value " + sui.dump_string()
414 + " given for the \"status_update_interval\" key in the"
415 " job configuration file" );
416 }
417 else status_update_interval = sui_value;
418 }
419
420 if ( ex_set.has_key("output") ) {
421 marley::JSON output_set = ex_set.at( "output" );
422 if ( !output_set.is_array() ) throw marley::Error( "The"
423 " \"output\" key in the \"generate\" section must have a value that"
424 " is a JSON array." );
425 else for ( const auto& el : output_set.array_range() ) {
426 output_files.push_back( marley::OutputFile::make_OutputFile(el) );
427 }
428 }
429 else {
430 std::string out_config_str = "{ format: \"ascii\","
431 " file: \"events.hepmc3\", mode: \"overwrite\", force: false }";
432 auto out_config = marley::JSON::load( out_config_str );
433
434 output_files.push_back( marley::OutputFile::make_OutputFile(
435 out_config ) );
436 }
437
438 // Fixed status line count for this run (computed once; used for scroll
439 // region sizing and fallback threshold throughout).
440 const int file_lines = ( output_files.size() <= MAX_FILE_STATUS_LINES )
441 ? static_cast< int >( output_files.size() ) : 1;
442 num_status_lines = 3 + file_lines;
443
444 // Reset module-level state in case cmd_generate is called more than once.
445 interrupted = false;
446 terminal_resized = false;
447 g_fallback_mode = false;
448 g_status_region_active = false;
449
450 std::signal( SIGINT, signal_handler );
451 std::signal( SIGWINCH, sigwinch_handler );
452
453 // Create the generator before setting up the scroll region so that
454 // initialization logging (including the active-reaction summary printed
455 // at the end of create_generator()) appears before the status zone is
456 // claimed. This avoids truncation of those log messages.
457 std::unique_ptr< marley::Generator > gen;
458
459 bool need_to_resume = false;
460 for ( auto& file : output_files ) {
461 if ( file->mode_is_resume() ) {
462 if ( need_to_resume ) throw marley::Error( "Only one file may be used"
463 " to resume a previous run." );
464 else {
465 need_to_resume = true;
466 bool resume_ok = file->resume( gen, num_old_events );
467 if ( !resume_ok ) throw marley::Error( "Failed to resume previous"
468 " run from the file \"" + file->name() + '\"' );
469 }
470 }
471 }
472
473 if ( !need_to_resume ) gen = std::make_unique<marley::Generator>(
474 jc.create_generator() );
475
476 // Reset the start timestamp after generator creation so that rate and ETA
477 // calculations exclude initialization overhead.
478 start_time_point = std::chrono::system_clock::now();
479 start_time = std::chrono::system_clock::to_time_t( start_time_point );
480
481 exception_needs_reset_terminal = true;
482
483 // Initialize the terminal display now that generator construction is done.
484 // Enter fallback mode if stdout is not a TTY or the terminal is too small.
485 {
486 TermSize ts = get_terminal_size();
487 if ( ts.rows == 0 || ts.rows < num_status_lines + 2 ) {
488 enter_fallback_mode();
489 } else {
490 setup_scroll_region( num_status_lines );
491 }
492 }
493
494 long ev_count = 1 + num_old_events;
495
496 auto last_resize_time = std::chrono::steady_clock::time_point{};
497
498 for (; ev_count <= num_events && !interrupted; ++ev_count) {
499
500 // Handle terminal resize with clock-based debounce. The debounce
501 // prevents thrashing during active window dragging; it is included
502 // unconditionally because some physics configurations (e.g. CEvNS)
503 // run at rates too fast to rely on event-loop latency alone.
504 if ( terminal_resized ) {
505 auto now = std::chrono::steady_clock::now();
506 if ( now - last_resize_time >= RESIZE_DEBOUNCE ) {
507 terminal_resized = false;
508 last_resize_time = now;
509
510 TermSize ts = get_terminal_size();
511 if ( ts.rows == 0 || ts.rows < num_status_lines + 2 ) {
512 enter_fallback_mode();
513 } else if ( g_fallback_mode ) {
514 exit_fallback_mode( num_status_lines );
515 } else {
516 setup_scroll_region( num_status_lines );
517 }
518 }
519 }
520
521 auto event = gen->create_event();
522 event->set_event_number( ev_count );
523
524 // If we have a buffered event from the previous iteration, we will
525 // write it to the output file(s) now. The generator state string
526 // will not be included here to save space. We can use implicit
527 // conversion of the std::shared_ptr here since it default-constructs
528 // to a nullptr and will therefore evaluate to false if we haven't
529 // used it yet.
530 if ( buffered_event ) {
531 for ( const auto& file : output_files ) {
532 file->write_event( buffered_event.get() );
533 }
534 }
535
536 // Replace the buffered event with the current event. Also cache the
537 // generator state string value corresponding to when the current
538 // event was finished.
539 buffered_event = event;
540 cached_generator_state = gen->get_state_string();
541
542 if ( !g_fallback_mode
543 && ( (ev_count - num_old_events) % status_update_interval == 1
544 || ev_count == num_events
545 || status_update_interval == 1 ) )
546 {
547 update_status_bars( ev_count, num_events, num_old_events,
548 start_time_point, output_files, num_status_lines );
549 }
550
551 } // event loop
552
553 // We've exited the event loop, so write out the last completed event
554 // (if any), which will be stored in the buffered_event pointer.
555 // Attach the generator state string this time so that the MARLEY
556 // job can be resumed from where it left off.
557 // NOTE: This call to OutputFile::write_event() handles normal
558 // termination and interruption via the SIGINT signal. The exception
559 // exit path is handled separately below in the catch block.
560 if ( buffered_event ) {
562 cached_generator_state );
563 for ( const auto& file : output_files ) {
564 file->write_event( buffered_event.get() );
565 }
566 }
567
568 reset_terminal( g_fallback_mode ? 0 : num_status_lines );
569
570 for ( const auto& file : output_files ) {
571 std::cout << "Data written to " << file->name() << ' '
572 << marley_utils::num_bytes_to_string( file->bytes_written() ) << '\n';
573 }
574
575 std::chrono::system_clock::time_point end_time_point
576 = std::chrono::system_clock::now();
577 std::time_t end_time
578 = std::chrono::system_clock::to_time_t( end_time_point );
579
580 if ( !interrupted ) {
581 MARLEY_LOG( NOTICE, "app" ) << "Generated " << ( ev_count - 1
582 - num_old_events ) << " event(s) successfully";
583 std::cout << "MARLEY terminated normally on ";
584 }
585 else {
586 MARLEY_LOG( NOTICE, "app" ) << "Generation interrupted after "
587 << ( ev_count - 1 - num_old_events ) << " event(s)";
588 std::cout << "MARLEY was interrupted by the user on ";
589 }
590 std::cout << put_time( std::localtime(&end_time), "%c %Z" ) << '\n';
591
592 return true;
593 }
594
595 catch ( const std::exception& error ) {
596 if ( exception_needs_reset_terminal && g_status_region_active ) {
597 reset_terminal( g_fallback_mode ? 0 : num_status_lines,
598 /*clear_status=*/false );
599 }
600
601 // Write out the buffered event (if any) that was successfully completed
602 // before the exception occurred. Attach the generator state string
603 // so that the MARLEY job can be restarted from the last successful
604 // event for easier debugging.
605 // NOTE: The buffered event was fully created before the exception
606 // occurred, so no exceptions are expected to be thrown in this block.
607 // Just in case, we wrap it with an additional try/catch to inform
608 // the user if writing out the buffered event fails.
609 try {
610 if ( buffered_event ) {
612 cached_generator_state );
613 for ( const auto& file : output_files ) {
614 file->write_event( buffered_event.get() );
615 }
616 }
617 } catch ( const std::exception& except ) {
618 MARLEY_LOG( WARN, "app" ) << std::flush << "Output of buffered"
619 " MARLEY event and its generator state failed";
620 MARLEY_LOG( ERROR, "app" ) << except.what();
621 }
622
623 MARLEY_LOG( ERROR, "app" ) << std::flush << error.what();
624 }
625
626 return false;
627}
void add_state_to_event(HepMC3::GenEvent &ev) const
Attach the current random number generator state to the input event as a string attribute.
Definition Generator.cc:880

References marley::Generator::add_state_to_event(), and cmd_help().

Referenced by execute().

◆ cmd_help()

bool marley::CommandHandler::cmd_help ( std::deque< std::string > & args)
staticprotected

Display top-level or command-specific help messages.

Definition at line 24 of file cmd_help.cc.

24 {
25 if ( args.empty() ) {
27 return true;
28 }
29
30 if ( args.size() > 1u ) {
31 std::cerr << "marley help: too many arguments\n";
32 return false;
33 }
34
35 // If we've made it here, then the help command has exactly one argument
36 // which corresponds to a command name or the -h/--help options
37 std::string command_name = args.front();
38
39 // These options are considered synonyms of the help command itself
40 if ( command_name == "-h" || command_name == "--help" ) {
41 command_name = "help";
42 }
43
44 auto cmd_iter = command_map_.find( command_name );
45 if ( cmd_iter != command_map_.end() ) {
46 cmd_iter->second.print_help_();
47 return true;
48 }
49
50 std::cerr << "marley: unknown command '" << command_name << "'\n";
51 std::cerr << "Run 'marley help' for a list of available commands.\n";
52 return false;
53}
static bool print_top_level_help()
static const CommandMap command_map_

References command_map_, and print_top_level_help().

Referenced by cmd_convert(), cmd_decay(), cmd_generate(), cmd_print(), cmd_reweight(), and cmd_xsec().

◆ cmd_print()

bool marley::CommandHandler::cmd_print ( std::deque< std::string > & args)
staticprotected

Print existing MARLEY events in a human-readable format.

Definition at line 105 of file cmd_print.cc.

105 {
106
107 std::string first_arg;
108 if ( !args.empty() ) first_arg = args.front();
109
110 if ( first_arg.empty() || first_arg == "-h" || first_arg == "--help" ) {
111 args.clear();
112 args.push_front( "print" );
114 }
115
116 PrintFormat format = PrintFormat::Pretty;
117 if ( first_arg == "pretty" ) {
118 // Pretty is the default format (set above), so just
119 // drop the format specifier from the input arguments
120 args.pop_front();
121 }
122 else if ( first_arg == "hepmc3" ) {
123 format = PrintFormat::HepMC3;
124 args.pop_front();
125 }
126 else if ( first_arg == "legacy" ) {
127 format = PrintFormat::Legacy;
128 args.pop_front();
129 }
130
131 // If there are no input files listed, print the help message
132 // and signal that an error condition was encountered
133 if ( args.empty() ) {
134 args.push_front( "print" );
136 return false;
137 }
138
139 // Now do the actual printing by iterating over the events in each
140 // input file specified in the remaining command-line arguments
141 for ( const auto& file_name : args ) {
142 marley::EventFileReader reader( file_name );
143 HepMC3::GenEvent ev;
144 int event_number = 0;
145 while ( reader >> ev ) {
146 if ( format == PrintFormat::Pretty ) {
147 marley_hepmc3::print_event( ev );
148 }
149 else if ( format == PrintFormat::HepMC3 ) {
150 std::cout << ev;
151 }
152 else {
153 // Legacy format
154 print_event_info( ev, event_number );
155 ++event_number;
156 }
157 }
158 }
159 return true;
160}

References cmd_help().

◆ cmd_reweight()

bool marley::CommandHandler::cmd_reweight ( std::deque< std::string > & args)
staticprotected

Reweight existing MARLEY events.

Definition at line 40 of file cmd_reweight.cc.

40 {
41
42 // If we have fewer than two arguments, decide whether the
43 // user intended to request help with this command
44 if ( args.size() < 2u ) {
45 std::string first_arg;
46 if ( !args.empty() ) first_arg = args.front();
47
48 // Print the help message either way
49 args.clear();
50 args.push_front( "reweight" );
52
53 // Return a boolean status based on whether the help message was
54 // explicitly requested (normal behavior) or not (an error condition)
55 if ( first_arg == "-h" || first_arg == "--help" ) return true;
56 return false;
57 }
58
59 // Extract the configuration file name and collect all input files
60 std::string config_file_name( args.front() );
61 args.pop_front();
62 std::vector< std::string > input_files( args.begin(), args.end() );
63
64 // Load the reweight configuration
65 marley::JSON rw_config = marley::JSON::load_file( config_file_name );
66 if ( !rw_config.has_key("weights") ) throw marley::Error( "Missing"
67 " \"weights\" key in marley reweight configuration file \""
68 + config_file_name + "\"" );
69
70 const auto& json_weights = rw_config.at( "weights" );
71
72 // Read the first event from the first input file to extract the run
73 // information needed to reconstruct the Generator and check weight names.
74 marley::EventFileReader first_reader( input_files[0] );
75 HepMC3::GenEvent first_ev;
76 if ( !(first_reader >> first_ev) ) {
77 throw marley::Error( "Failed to read the first event from input file \""
78 + input_files[0] + "\". The file may be empty or corrupt." );
79 }
80
81 auto first_run_info = first_ev.run_info();
82 const std::vector< std::string > wgt_names = first_run_info->weight_names();
83
84 // Reconstruct the original Generator from the saved configuration
85 auto prior_config_str = first_run_info->attribute< HepMC3::StringAttribute >(
86 "MARLEY.JSONconfig" );
87
88 if ( !prior_config_str ) {
89 throw marley::Error( "Failed to retrieve previous generator"
90 " configuration from the input file \"" + input_files[0] + "\"" );
91 }
92
93 auto prior_json_config = marley::JSON::load( prior_config_str->value() );
94 marley::JSONConfig jc( prior_json_config );
95 auto gen = std::make_unique< marley::Generator >( jc.create_generator() );
96
97 // Create the Weighter and check for name conflicts
98 marley::Weighter weighter( json_weights, *gen );
99 weighter.set_use_cv_weight( false );
100
101 auto& calc_vec = weighter.get_weight_calculators();
102
103 for ( const auto& wc : calc_vec ) {
104 if ( std::find( wgt_names.cbegin(), wgt_names.cend(), wc->name() )
105 != wgt_names.cend() )
106 {
107 throw marley::Error( "Weight name \"" + wc->name()
108 + "\" from the reweight configuration file \"" + config_file_name
109 + "\" conflicts with an existing weight in the input file" );
110 }
111 }
112
113 // Prepend TrivialWeightCalculators for the existing weight names so that
114 // the Weighter preserves them in the output. Iterate in reverse order
115 // and insert at the beginning to maintain the original ordering.
116 for ( auto riter = wgt_names.crbegin();
117 riter != wgt_names.crend(); ++riter )
118 {
119 const auto& w_name = *riter;
120 marley::JSON temp_json;
121 temp_json[ "name" ] = w_name;
122 auto w_calc = std::make_shared< marley
123 ::TrivialWeightCalculator >( temp_json );
124 calc_vec.insert( calc_vec.begin(), w_calc );
125 }
126
127 auto full_name_vec = weighter.get_weight_names();
128
129 // Read output settings from the optional "reweight" section (if present)
130 marley::JSON rw_section;
131 bool has_rw_section = false;
132 if ( rw_config.has_key("reweight") ) {
133 const marley::JSON& rw_section_ref = rw_config.at( "reweight" );
134 if ( !rw_section_ref.is_object() ) throw marley::Error(
135 "The \"reweight\" section in the marley reweight configuration"
136 " file \"" + config_file_name + "\" must be a JSON object" );
137 rw_section = rw_section_ref;
138 has_rw_section = true;
139 }
140
141 std::vector< std::shared_ptr<marley::OutputFile> > output_files;
142
143 if ( has_rw_section && rw_section.has_key("output") ) {
144 marley::JSON output_set = rw_section.at( "output" );
145 if ( !output_set.is_array() ) throw marley::Error( "The"
146 " \"output\" key in the reweighting configuration must have a value"
147 " that is a JSON array." );
148 else for ( const auto& el : output_set.array_range() ) {
149 if ( el.has_key("mode") ) {
150 std::string mode_str = el.at( "mode" ).to_string();
151 if ( mode_str != "overwrite" ) throw marley::Error( "Only the"
152 " \"overwrite\" output file mode is allowed for a reweighting"
153 " job." );
154 }
155 output_files.push_back( marley::OutputFile::make_OutputFile(el) );
156 }
157 }
158 else {
159 std::string out_config_str = "{ format: \"ascii\","
160 " file: \"reweighted_events.hepmc3\", mode: \"overwrite\" }";
161 auto out_config = marley::JSON::load( out_config_str );
162
163 output_files.push_back( marley::OutputFile::make_OutputFile(out_config) );
164 }
165
166 // Build the reweighted GenRunInfo from a copy of the first file's run info
167 bool multi_file = ( input_files.size() > 1 );
168 auto reweighted_run_info = std::make_shared< HepMC3::GenRunInfo >(
169 *first_run_info );
170 reweighted_run_info->set_weight_names( full_name_vec );
171
172 // For multi-file reweight, strip the RNG seed to prevent unsafe resume.
173 // Single-file reweight preserves the seed so that resume remains possible
174 // (the accumulated Weighter will be reconstructed from the saved reweight
175 // provenance attributes when needed).
176 if ( multi_file ) reweighted_run_info->remove_attribute(
177 "MARLEY.RNGseed" );
178
179 // Save the reweight configuration as run info provenance attributes
180 {
181 int rw_index = 0;
182 auto count_attr = first_run_info->attribute< HepMC3::IntAttribute >(
183 "MARLEY.ReweightConfig.count" );
184 if ( count_attr ) rw_index = count_attr->value();
185
186 // Build a combined provenance object with the weights array and
187 // (if present) the reweight section containing output settings
188 marley::JSON prov_obj = marley::JSON::object();
189 prov_obj["weights"] = json_weights;
190 if ( has_rw_section ) {
191 prov_obj["reweight"] = rw_section;
192 }
193
194 reweighted_run_info->add_attribute(
195 "MARLEY.ReweightConfig." + std::to_string( rw_index ),
196 std::make_shared< HepMC3::StringAttribute >(
197 prov_obj.dump_string() ) );
198
199 reweighted_run_info->add_attribute(
200 "MARLEY.ReweightConfig.count",
201 std::make_shared< HepMC3::IntAttribute >( rw_index + 1 ) );
202 }
203
204 // Process all events across all input files
205 int event_count = 0;
206 for_each_event( input_files,
207 [ & ]( HepMC3::GenEvent& ev, bool /*first_event*/,
208 double /*flux_avg_xsec*/, const auto& /*first_info*/ )
209 {
210 std::cout << "Event " << event_count << '\n';
211
212 // Save the original weight values before set_run_info resizes
213 // the event's weight vector to match the reweighted run info.
214 auto orig_weights = ev.weights();
215
216 // Apply the reweighted GenRunInfo. HepMC3's set_run_info resizes
217 // m_weights to match the weight_names count, filling with 1.0.
218 ev.set_run_info( reweighted_run_info );
219
220 // Restore the original weight values into the first slots
221 for ( size_t i = 0; i < orig_weights.size(); ++i )
222 ev.weights()[ i ] = orig_weights[ i ];
223
224 // Compute the new weight values
225 weighter.process_event( ev, *gen );
226
227 // Write the event to all output files
228 for ( const auto& file : output_files )
229 file->write_event( &ev );
230
231 ++event_count;
232 } );
233
234 return true;
235}
const std::vector< double > & weights() const
Get event weight values as a vector.
Definition GenEvent.h:105
std::shared_ptr< GenRunInfo > run_info() const
Get a pointer to the the GenRunInfo object.
Definition GenEvent.h:144

References cmd_help(), marley::Weighter::get_weight_calculators(), marley::Weighter::get_weight_names(), marley::Weighter::process_event(), HepMC3::GenEvent::run_info(), HepMC3::GenEvent::set_run_info(), marley::Weighter::set_use_cv_weight(), HepMC3::IntAttribute::value(), and HepMC3::GenEvent::weights().

◆ cmd_summarize()

bool marley::CommandHandler::cmd_summarize ( std::deque< std::string > & args)
staticprotected

Summarize an existing sample of MARLEY events as a ROOT TTree.

Definition at line 46 of file cmd_summarize.cc.

48{
49 std::cerr << "marley: the 'summarize' command requires linking to ROOT.";
50 std::cerr << "Please rebuild MARLEY against ROOT and try again.\n";
51 return false;
52}

◆ cmd_xsec()

bool marley::CommandHandler::cmd_xsec ( std::deque< std::string > & args)
staticprotected

Tabulate energy-dependent total cross section values.

Definition at line 39 of file cmd_xsec.cc.

39 {
40
41 std::string output_path;
42 std::string config_file_path;
43 bool force = false;
44
45 while ( !args.empty() ) {
46 std::string arg = args.front();
47 args.pop_front();
48
49 if ( arg == "-o" || arg == "--output" ) {
50 if ( args.empty() ) {
51 std::cerr << "marley xsec: missing argument after '" << arg << "'\n";
52 return false;
53 }
54 output_path = args.front();
55 args.pop_front();
56 }
57 else if ( arg == "-f" || arg == "--force" ) {
58 force = true;
59 }
60 else if ( arg == "-h" || arg == "--help" ) {
61 args.clear();
62 args.push_front( "xsec" );
64 }
65 else if ( arg.front() == '-' ) {
66 std::cerr << "marley xsec: unrecognized option '" << arg << "'\n";
67 return false;
68 }
69 else if ( config_file_path.empty() ) {
70 config_file_path = arg;
71 }
72 else {
73 std::cerr << "marley xsec: unexpected extra argument '"
74 << arg << "'\n";
75 return false;
76 }
77 }
78
79 if ( output_path.empty() ) {
80 std::cerr << "marley xsec: missing required output file\n";
81 args.push_front( "xsec" );
83 return false;
84 }
85
86 if ( config_file_path.empty() ) {
87 std::cerr << "marley xsec: missing required configuration file\n";
88 args.push_front( "xsec" );
90 return false;
91 }
92
93 if ( !force ) {
94 std::ifstream temp_stream( output_path );
95 if ( temp_stream ) {
96 bool overwrite = marley_utils::prompt_yes_no(
97 "Really overwrite " + output_path + '?');
98 if ( !overwrite ) {
99 std::cout << "Total cross section dump aborted.\n";
100 return true;
101 }
102 }
103 }
104
105 std::ofstream out_file( output_path );
106
107 marley::JSONConfig config( config_file_path );
108 marley::Generator gen = config.create_generator();
109
110 double KEmin = DEFAULT_KE_MIN;
111 double KEmax = DEFAULT_KE_MAX;
112 int num_steps = DEFAULT_NUM_STEPS;
113 int projectile_pdg = DEFAULT_PDG;
114
115 const marley::JSON& json = config.get_json();
116
117 if ( json.has_key("xsec") ) {
118 const marley::JSON& xsec_settings = json.at( "xsec" );
119
120 if ( xsec_settings.has_key("KEmin") ) {
121 bool ok = false;
122 KEmin = xsec_settings.at("KEmin").to_double( ok );
123 if ( !ok ) throw marley::Error("Unrecognized KEmin value "
124 + xsec_settings.at("KEmin").to_string() + " encountered in the"
125 " \"xsec\" section of the job configuration file.");
126 }
127
128 if ( xsec_settings.has_key("KEmax") ) {
129 bool ok = false;
130 KEmax = xsec_settings.at("KEmax").to_double( ok );
131 if ( !ok ) throw marley::Error("Unrecognized KEmax value "
132 + xsec_settings.at("KEmax").to_string() + " encountered in the"
133 " \"xsec\" section of the job configuration file.");
134 }
135
136 if ( xsec_settings.has_key("steps") ) {
137 bool ok = false;
138 num_steps = xsec_settings.at("steps").to_long( ok );
139 if ( !ok ) throw marley::Error("Unrecognized steps value "
140 + xsec_settings.at("steps").to_string() + " encountered in the"
141 " \"xsec\" section of the job configuration file.");
142 }
143
144 if ( xsec_settings.has_key("pdg") ) {
145 bool ok = false;
146 projectile_pdg = xsec_settings.at("pdg").to_long( ok );
147 if ( !ok ) throw marley::Error("Unrecognized pdg value "
148 + xsec_settings.at("pdg").to_string() + " encountered in the"
149 " \"xsec\" section of the job configuration file.");
150 }
151 }
152
153 double KE = KEmin;
154 int steps = num_steps;
155
156 if ( steps <= 1 ) {
157 double xsec = gen.total_xs( projectile_pdg, KE );
158 xsec *= marley_utils::hbar_c2 * marley_utils::fm2_to_minus40_cm2 * 1e2;
159
160 out_file << KE << ' ' << xsec << '\n';
161
162 MARLEY_LOG( INFO, "app" ) << "KE = " << KE
163 << " MeV, abundance-weighted total xsec = "
164 << xsec << " × 10^{-42} cm^2 / atom";
165 }
166 else {
167 double delta = ( KEmax - KEmin ) / ( steps - 1 );
168 for ( int s = 0; s < steps; ++s ) {
169 KE = KEmin + s * delta;
170 double xsec = gen.total_xs( projectile_pdg, KE );
171 xsec *= marley_utils::hbar_c2 * marley_utils::fm2_to_minus40_cm2 * 1e2;
172
173 out_file << KE << ' ' << xsec << '\n';
174
175 MARLEY_LOG( INFO, "app" ) << "KE = " << KE
176 << " MeV, abundance-weighted total xsec = "
177 << xsec << " × 10^{-42} cm^2 / atom";
178 }
179 }
180
181 return true;
182}
double total_xs(int pdg_a, double KEa, int pdg_atom) const
Computes the total cross section at fixed energy for all configured reactions involving a particular ...
Definition Generator.cc:629

References cmd_help(), and marley::Generator::total_xs().

◆ execute()

bool marley::CommandHandler::execute ( )

Execute the requested command based on the command-line arguments

Returns
true if the command was successful, false otherwise

Definition at line 188 of file CommandHandler.cc.

188 {
189
190 // If we don't have any command-line arguments, then just print the
191 // standard top-level help message
192 if ( cmds_.empty() ) {
193 return this->print_top_level_help();
194 }
195
196 // Otherwise, get the first argument which should correspond to a command
197 // or one of the top-level options
198 std::string subcmd = cmds_.front();
199
200 // Reassign some synonyms to their corresponding commands that appear in the
201 // map of accepted values
202 if ( subcmd == "--version" || subcmd == "-v" ) {
203 subcmd = "version";
204 }
205 else if ( subcmd == "--help" || subcmd == "-h" ) {
206 subcmd = "help";
207 }
208
209 auto cmd_iter = command_map_.find( subcmd );
210 if ( cmd_iter != command_map_.end() ) {
211 // The user provided an explicit command, so drop it from the
212 // deque before delegating to the appropriate function. We no longer need to
213 // resolve the command name.
214 cmds_.pop_front();
215 // Call the function corresponding to the chosen command
216 try {
217 return cmd_iter->second.cmd_( cmds_ );
218 } catch ( const std::exception& e ) {
219 // If we encountered an uncaught exception, log the error message
220 MARLEY_LOG( ERROR, "app" ) << std::flush << e.what();
221 return false;
222 }
223 }
224
225 // The 'marley' command is an easter egg that doesn't appear in the official
226 // list
227 if ( subcmd == "marley" ) {
228 std::cout << marley_utils::marley_pic;
229 return true;
230 }
231
232 // The default command is 'generate', so if the user didn't explicitly
233 // provide it and didn't use an option prefix, then assume that the user
234 // intended 'generate'
235 if ( !subcmd.empty() && subcmd[0] != '-' ) {
236 return this->cmd_generate( cmds_ );
237 }
238
239 std::cerr << "marley: unknown option '" << subcmd << "'\n";
240 std::cerr << "Run 'marley help' for a list of available commands.\n";
241 return false;
242}
static bool cmd_generate(std::deque< std::string > &args)
Generate Monte Carlo events.

References cmd_generate(), cmds_, command_map_, and print_top_level_help().

◆ print_top_level_help()

bool marley::CommandHandler::print_top_level_help ( )
static

Print the top-level help message by aggregating the command summaries from the map

Definition at line 168 of file CommandHandler.cc.

168 {
169 std::cout << "Usage: marley <command> [options]\n\n"
170 << "Commands:\n";
171
172 for ( const auto& [name, info] : command_map_ ) {
173 std::cout << " " << std::left << std::setw(12) << name
174 << info.summary_;
175 if ( info.requires_root_ ) std::cout << " [requires ROOT]";
176 std::cout << '\n';
177 }
178
179 std::cout << "\nOptions:\n"
180 << " -h, --help Show top-level help\n"
181 << " -v, --version Print version information\n";
182 std::cout << "\nRun 'marley help <command>' or 'marley <command> --help' for"
183 << " details.\n"
184 << "MARLEY home page: <https://www.marleygen.org>\n";
185 return true;
186}

References command_map_.

Referenced by cmd_help(), and execute().

Member Data Documentation

◆ cmds_

std::deque< std::string > marley::CommandHandler::cmds_
protected

Stores the command-line arguments with the executable name removed from the front

Definition at line 67 of file CommandHandler.hh.

Referenced by execute().

◆ command_map_

const marley::CommandHandler::CommandMap marley::CommandHandler::command_map_
staticprotected

Map containing information about recognized commands handled by the marley executable

Definition at line 136 of file CommandHandler.hh.

Referenced by cmd_help(), execute(), and print_top_level_help().


The documentation for this class was generated from the following files: