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
OutputFileAscii.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 <filesystem>
19#include <limits>
20
21// HepMC3 includes
22#include "HepMC3/Attribute.h"
23#include "HepMC3/GenEvent.h"
24#include "HepMC3/GenRunInfo.h"
25#include "HepMC3/ReaderAscii.h"
26#include "HepMC3/WriterAscii.h"
27
28// MARLEY includes
29#include "marley/OutputFileAscii.hh"
30#include "marley/Error.hh"
31#include "marley/Generator.hh"
32#include "marley/JSON.hh"
33#include "marley/JSONConfig.hh"
34
35namespace {
36
37 constexpr char DUMMY_CHAR = 'a';
38
39 // Moves to the end of an ASCII-format file containing HepMC3 events and
40 // opened as the input std::fstream. Backs up until the start of the last
41 // HepMC3 event stored in the file. The stream is left in a state that is
42 // ready for reading in the last event using, e.g., HepMC3::ReaderAscii.
43 // Returns the stream position of the 'E' that begins the last event,
44 // or std::streampos(-1) if no event is found.
45 std::streampos seek_to_last_genevent( std::fstream& stream ) {
46 char c = DUMMY_CHAR;
47 char old_c = DUMMY_CHAR;
48 stream.seekg( 0, std::ios::end );
49 std::streampos size = stream.tellg();
50 for ( int i = 1; i <= size; ++i ) {
51 stream.seekg( -i, std::ios::end );
52 old_c = c;
53 stream.get( c );
54 if ( c == '\n' && old_c == 'E' ) {
55 stream.seekg( -i + 1, std::ios::end );
56 stream.clear();
57 return stream.tellg();
58 }
59 }
60 return std::streampos( -1 );
61 }
62
63}
64
65marley::OutputFileAscii::OutputFileAscii( const marley::JSON& output_config )
66 : marley::OutputFile( output_config )
67{
68 format_ = Format::ASCII;
69
70 this->open();
71 reader_ = std::make_shared< HepMC3::ReaderAscii >( stream_ );
72 writer_ = std::make_shared< HepMC3::WriterAscii >( stream_ );
73
74 // Ensure that all floating-point values are output with full precision
75 writer_->set_precision( std::numeric_limits<double>::max_digits10 );
76}
77
79
81
82 auto open_mode_flag = std::ios::in | std::ios::out | std::ios::trunc;
83
84 if ( mode_ == Mode::RESUME ) {
86 throw marley::Error( "Cannot resume run. Could"
87 " not open the file \"" + name_ + '\"' );
88 else open_mode_flag = std::ios::in | std::ios::out;
89 }
90 else if ( mode_ != Mode::OVERWRITE ) {
91 throw marley::Error( "Unrecognized file mode encountered in"
92 " OutputFileAscii::open()" );
93 }
94
95 stream_.open( name_, open_mode_flag );
96
97}
98
99bool marley::OutputFileAscii::resume( std::unique_ptr<marley::Generator>& gen,
100 long& num_previous_events )
101{
102 if ( mode_ != Mode::RESUME ) {
103 throw marley::Error( "Cannot call OutputFileAscii::resume() for an output"
104 " mode other than \"resume\"" );
105 return false;
106 }
107
108 MARLEY_LOG( INFO, "io" ) << "Continuing previous run from the file " << name_;
109
110 // Create a temporary event to use for storage while parsing the file
111 auto evt = std::make_shared< HepMC3::GenEvent >();
112
113 // Read back the first event from the file so that we can retrieve the run
114 // information
115 stream_.seekg( 0, std::ios::beg );
116 bool read_ok = reader_->read_event( *evt );
117
118 auto run_info = evt->run_info();
119
120 if ( !read_ok || !run_info ) {
121 throw marley::Error( "Failed to retrieve run information from the file "
122 + name_ );
123 return false;
124 }
125
126 // Extract the prior generator JSON configuration from the RunInfo
127 auto config_str = run_info->attribute< HepMC3::StringAttribute >(
128 "MARLEY.JSONconfig" );
129
130 if ( !config_str ) {
131 throw marley::Error( "Failed to retrieve generator configuration from the"
132 " file " + name_ );
133 return false;
134 }
135
136 auto seed_str = run_info->attribute< HepMC3::StringAttribute >(
137 "MARLEY.RNGseed" );
138
139 if ( !seed_str ) {
140 throw marley::Error( "Failed to load previous random number"
141 " generator seed from the file " + name_ );
142 return false;
143 }
144
145 // Parse the prior generator configuration into a JSON object
146 auto json_config = marley::JSON::load( config_str->value() );
147
148 // Move to the beginning of the last HepMC3 event in the file and
149 // save its stream position for truncation
150 std::streampos last_event_pos = seek_to_last_genevent( stream_ );
151
152 if ( last_event_pos == std::streampos( -1 ) ) {
153 throw marley::Error( "Failed to find the last event in the file "
154 + name_ );
155 return false;
156 }
157
158 // Parse the last HepMC3 event so that we can retrieve the generator state
159 read_ok = reader_->read_event( *evt );
160
161 auto state_str = evt->attribute< HepMC3::StringAttribute >(
162 "MARLEY.GeneratorState" );
163
164 if ( !read_ok || !state_str ) {
165 throw marley::Error( "Failed to retrieve generator state from the file "
166 + name_ );
167 return false;
168 }
169
170 // We assume that the events in the file were originally written out using
171 // the convention in the marley executable: the event number of the last
172 // event in the file is equal to the total number of events.
173 num_previous_events = evt->event_number();
174
175 MARLEY_LOG( INFO, "io" ) << "Resuming run from \"" << name_ << "\":"
176 << " found " << num_previous_events << " previous event(s)";
177
178 // We're done retrieving the information. Restore the Generator to its
179 // previous state
180 gen = this->restore_generator( json_config );
181 gen->seed_using_state_string( state_str->value() );
182
183 MARLEY_LOG( INFO, "io" ) << "The previous run was initialized using"
184 << " the random number generator seed " << seed_str->value();
185
186 // If the file has reweight provenance, merge the accumulated weight
187 // calculator configurations into the Generator's Weighter before
188 // setting up run info. This ensures new events have the correct
189 // number of weight slots to match the existing events in the file.
190 merge_reweight_provenance_weights( *run_info, *gen );
191
192 // Initialize the Generator's run info now so that it is available for
193 // use below (it is normally initialized lazily inside create_event()).
194 gen->set_up_run_info();
195
196 // Defer truncation: save the event and truncation position so that
197 // the stale GeneratorState and HepMC3 footer are removed only on the
198 // first write_event() call. This ensures the GeneratorState remains in
199 // the file should an exception occur before any new event is written.
200 // Also reassign the event's run_info to the Generator's so that the
201 // pointer-identity comparison in WriterAscii::write_event() does not
202 // produce spurious "different GenRunInfo" warnings.
203 evt->set_run_info( gen->run_info() );
204 evt->remove_attribute( "MARLEY.GeneratorState" );
205 pending_truncate_pos_ = last_event_pos;
207
208 return true;
209}
210
212 // If the stream is open, then update the byte count. Otherwise, just
213 // use the saved value.
214 if ( stream_.is_open() ) {
215 stream_.flush();
216 byte_count_ = static_cast<int_fast64_t>( stream_.tellp() );
217 }
218 return byte_count_;
219}
220
222
223 if ( !event ) throw marley::Error( "Null pointer passed to"
224 " OutputFileAscii::write_event()" );
225
226 if ( pending_flush_event_ ) {
227 stream_.flush();
228 std::filesystem::resize_file( name_,
229 static_cast<std::uintmax_t>( pending_truncate_pos_ ) );
230 stream_.clear();
233 writer_->set_run_info( pending_flush_event_->run_info() );
234 writer_->write_event( *pending_flush_event_ );
235 stream_.flush();
236 pending_flush_event_.reset();
237 pending_truncate_pos_ = std::streampos( -1 );
238 }
239
240 writer_->write_event( *event );
241}
Stores event-related information.
Definition GenEvent.h:47
Attribute that holds a string.
Definition Attribute.h:343
Base class for all exceptions thrown by MARLEY functions.
Definition Error.hh:26
virtual void open()
Opens the owned std::fstream with the correct settings.
std::fstream stream_
Stream used to read and write from the output file as needed.
std::shared_ptr< HepMC3::ReaderAscii > reader_
std::shared_ptr< HepMC3::GenEvent > pending_flush_event_
virtual void write_event(HepMC3::GenEvent *event) override
Write a new HepMC3::GenEvent to this output file.
virtual bool resume(std::unique_ptr< marley::Generator > &gen, long &num_previous_events) override
Load a marley::Generator object whose configuration and state were saved to the metadata in the outpu...
int_fast64_t byte_count_
Storage for the number of bytes written to disk.
std::streampos pending_truncate_pos_
std::shared_ptr< HepMC3::WriterAscii > writer_
Helper object used to produce standard ASCII HepMC3 output.
virtual int_fast64_t bytes_written() override
The number of bytes that have been written during the current MARLEY session to this file.
std::unique_ptr< marley::Generator > restore_generator(const marley::JSON &config)
Helper function for resume() that instantiates a marley::Generator object given the previous JSON con...
Definition OutputFile.cc:55
void prompt_before_overwrite()
If mode_ is OVERWRITE and the output file already exists and force_ is false, prompt the user before ...
Definition OutputFile.cc:63
static void merge_reweight_provenance_weights(HepMC3::GenRunInfo &run_info, marley::Generator &gen)
When resuming from a file that has been through one or more reweight passes, merge the reweight prove...
Definition OutputFile.cc:80
bool check_if_file_exists(const std::string &filename)
Checks that a given file exists (and is readable)
Definition OutputFile.hh:85
std::string name_
Name of the file to receive output.