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
cmd_reweight.cc
1
4//
5// This file is part of MARLEY (Model of Argon Reaction Low Energy Yields)
6//
7// MARLEY is free software: you can redistribute it and/or modify it under the
8// terms of version 3 of the GNU General Public License as published by the
9// Free Software Foundation.
10//
11// For the full text of the license please see COPYING or
12// visit http://opensource.org/licenses/GPL-3.0
13//
14// Please respect the MCnet academic usage guidelines. See GUIDELINES
15// or visit https://www.montecarlonet.org/GUIDELINES for details.
16
17// Standard library includes
18#include <algorithm>
19#include <fstream>
20#include <iostream>
21#include <memory>
22#include <string>
23#include <vector>
24
25// HepMC3 includes
26#include "HepMC3/GenEvent.h"
27#include "HepMC3/GenRunInfo.h"
28
29// MARLEY includes
30#include "cmd_helpers.hh"
31#include "marley/CommandHandler.hh"
32#include "marley/EventFileReader.hh"
33#include "marley/Generator.hh"
34#include "marley/JSONConfig.hh"
35#include "marley/OutputFile.hh"
36#include "marley/WeightCalculator.hh"
37#include "marley/Weighter.hh"
38#include "marley/marley_utils.hh"
39
40bool marley::CommandHandler::cmd_reweight( std::deque< std::string >& args ) {
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}
Stores event-related information.
Definition GenEvent.h:47
const std::vector< double > & weights() const
Get event weight values as a vector.
Definition GenEvent.h:105
void set_run_info(std::shared_ptr< GenRunInfo > run)
Set the GenRunInfo object by smart pointer.
Definition GenEvent.h:148
std::shared_ptr< GenRunInfo > run_info() const
Get a pointer to the the GenRunInfo object.
Definition GenEvent.h:144
Attribute that holds an Integer implemented as an int.
Definition Attribute.h:157
int value() const
get the value associated to this Attribute.
Definition Attribute.h:180
Attribute that holds a string.
Definition Attribute.h:343
static bool cmd_help(std::deque< std::string > &args)
Display top-level or command-specific help messages.
Definition cmd_help.cc:24
static bool cmd_reweight(std::deque< std::string > &args)
Reweight existing MARLEY events.
Base class for all exceptions thrown by MARLEY functions.
Definition Error.hh:26
Object that parses MARLEY output files.
EventProcessor that assigns event weights.
Definition Weighter.hh:32
std::vector< std::shared_ptr< WeightCalculator > > & get_weight_calculators()
Provides non-const access to the owned vector of weight calculators.
Definition Weighter.hh:57
std::vector< std::string > get_weight_names() const
Returns the names of the weights associated with all configured weight calculators.
Definition Weighter.cc:153
void set_use_cv_weight(bool use_it)
Toggles inclusion of the central-value weight.
Definition Weighter.cc:163
virtual void process_event(HepMC3::GenEvent &event, marley::Generator &gen) override
Processes an input GenEvent object.
Definition Weighter.cc:113