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::JSONConfig Class Reference

Public Types

using InterpMethod
 

Public Member Functions

 JSONConfig (const marley::JSON &object)
 
 JSONConfig (const std::string &json_filename)
 
marley::Generator create_generator () const
 
InterpMethod get_interpolation_method (const std::string &rule) const
 
const marley::JSONget_json () const
 
int neutrino_pdg (const std::string &nu) const
 
void prepare_direction (marley::Generator &gen) const
 
void prepare_neutrino_source (marley::Generator &gen) const
 
void prepare_reactions (marley::Generator &gen, marley::CoulombCorrector::CoulombMode coulomb_mode, const marley::JSON &ff_config) const
 
void prepare_structure (marley::Generator &gen) const
 
void prepare_target (marley::Generator &gen) const
 
void prepare_weights (marley::Generator &gen) const
 
bool process_extra_source_types (const std::string &type, const marley::JSON &source_spec, int pdg_code, std::unique_ptr< marley::NeutrinoSource > &source) const
 Helper function used to define ROOT-based neutrino source types @detail This function is a no-op when MARLEY is built without ROOT support.
 
void set_json (const marley::JSON &json)
 

Static Public Member Functions

static bool check_for_allowed_approximation (const marley::JSON &ff_config)
 
static void handle_json_error (const std::string &name, const marley::JSON &json)
 

Protected Member Functions

std::string source_get (const char *name, const marley::JSON &source_spec, const char *description, const char *default_str) const
 Helper function for loading strings from the JSON configuration.
 

Protected Attributes

marley::JSON json_
 JSON object describing this configuration.
 

Detailed Description

Definition at line 30 of file JSONConfig.hh.

Member Typedef Documentation

◆ InterpMethod

using marley::JSONConfig::InterpMethod
Initial value:
One-dimensional function y(x) defined using a grid of ordered pairs (x,y) and an interpolation rule.

Definition at line 34 of file JSONConfig.hh.

Constructor & Destructor Documentation

◆ JSONConfig() [1/2]

marley::JSONConfig::JSONConfig ( const marley::JSON & object)
explicit

Definition at line 108 of file JSONConfig.cc.

108 : json_( json )
109{
110}
marley::JSON json_
JSON object describing this configuration.
Definition JSONConfig.hh:83

◆ JSONConfig() [2/2]

marley::JSONConfig::JSONConfig ( const std::string & json_filename)
explicit

Definition at line 112 of file JSONConfig.cc.

113{
114 // Parse the config file
115 json_ = marley::JSON::load_file( json_filename );
116}

Member Function Documentation

◆ check_for_allowed_approximation()

bool marley::JSONConfig::check_for_allowed_approximation ( const marley::JSON & ff_config)
static

Helper function that checks whether an input JSON object representing the form factor configuration corresponds to a valid request for the allowed approximation to be used

Definition at line 1033 of file JSONConfig.cc.

1035{
1036 // The JSON object must be a simple string
1037 if ( !ff_config.is_string() ) return false;
1038
1039 // It must also have one of the values given below
1040 auto cfg_str = ff_config.to_string();
1041 if ( cfg_str == "allowed" || cfg_str == "AA" || cfg_str == "aa" ) {
1042 return true;
1043 }
1044 return false;
1045}

Referenced by marley::NuclearFormFactor::create().

◆ create_generator()

marley::Generator marley::JSONConfig::create_generator ( ) const

Definition at line 140 of file JSONConfig.cc.

141{
142 uint_fast64_t seed;
143 if ( json_.has_key("seed") ) {
144 bool ok;
145 seed = static_cast< uint_fast64_t >( json_.at("seed").to_long(ok) );
146 if ( !ok ) handle_json_error( "seed", json_.at("seed") );
147 }
148 else seed = std::chrono::system_clock::now().time_since_epoch().count();
149
150 // Start with a default-constructed Generator seeded with either a
151 // user-supplied seed or the current number of seconds since the Unix epoch.
152 marley::Generator gen( seed );
153
154 MARLEY_LOG( DEBUG, "init.config" ) << "Full generator configuration:\n"
155 << json_;
156
157 // Set the method to use for computing Coulomb corrections in all reactions.
158 // If the user gave an explicit setting for this, use that.
159 // Otherwise, interpolate between the Fermi function and the modified
160 // effective momentum approximation.
161 CMode coulomb_mode = CMode::FERMI_AND_MEMA; // Default method
162 if ( json_.has_key("coulomb_mode") ) {
163 const auto& cmode = json_.at( "coulomb_mode" );
164 if ( !cmode.is_string() ) throw marley::Error("Invalid Coulomb mode"
165 " specification " + cmode.dump_string() );
166 std::string my_mode = cmode.to_string();
167 coulomb_mode = marley::CoulombCorrector
168 ::coulomb_mode_from_string( my_mode );
169 }
170
171 // Configure the parameterizations used for nucleon and nuclear form factors
172 marley::JSON ff_config;
173 if ( json_.has_key("form_factors") ) {
174 ff_config = json_.at( "form_factors" );
175 if ( !ff_config.is_object() ) {
176 if ( this->check_for_allowed_approximation(ff_config) ) {
177 MARLEY_LOG( INFO, "init.config" ) << "Using the allowed approximation";
178 }
179 else throw marley::Error( "Invalid form factor configuration "
180 + ff_config.dump_string() );
181 }
182 }
183 else {
184 // Adopt a default form factor configuration if the user did not provide
185 // one
186 ff_config[ "sachs_model" ] = "bbba05";
187 ff_config[ "axial_model" ] = "dipole";
188 ff_config[ "nuclear_model" ] = "klein";
189 }
190
191 // Turn off calls to Generator::normalize_E_pdf() until we
192 // have set up all the needed pieces
193 gen.dont_normalize_E_pdf_ = true;
194
195 // Use the JSON settings to update the generator's parameters
196 prepare_direction( gen );
197 prepare_structure( gen );
198 prepare_neutrino_source( gen );
199 prepare_reactions( gen, coulomb_mode, ff_config );
200 prepare_target( gen );
201 prepare_weights( gen );
202
203 // If the user has disabled nuclear de-excitations, then set the
204 // flag appropriately.
205 if ( json_.has_key("do_deexcitations") ) {
206 const auto& do_deex = json_.at( "do_deexcitations" );
207 if ( do_deex.is_bool() ) {
208 bool deexcite_or_not = do_deex.to_bool();
209 gen.set_do_deexcitations( deexcite_or_not );
210 if ( !deexcite_or_not ) {
211 MARLEY_LOG( INFO, "init.config" ) << "Nuclear de-excitations will not be simulated";
212 }
213 }
214 }
215
216 // Save a copy of the JSON settings used to configure the generator
217 gen.set_json_config( json_ );
218
219 // If specified by the user, set the approach to continuum strength leaking
220 // below the unbound threshold. The default setting is "accumulate."
221 if ( json_.has_key("sub_continuum_mode") ) {
222 const auto& sc_mode_str = json_.at( "sub_continuum_mode" );
223 if ( !sc_mode_str.is_string() ) throw marley::Error( "Invalid sub-continuum"
224 " mode specification " + sc_mode_str.dump_string() );
225 std::string my_mode = sc_mode_str.to_string();
226 SubContinuumMode sc_mode = marley::ContinuumNuclearReaction
227 ::sub_continuum_mode_from_string( my_mode );
228
230 }
231
232 // Skip the rest of initialization if we've disabled all reactions.
233 // This can be used to partially initialize the Generator in unusual
234 // situations.
235 if ( json_.has_key("reactions") ) {
236 const auto& reactions = json_.at( "reactions" );
237 if ( reactions.is_null() ) {
238 MARLEY_LOG( INFO, "init.config" ) << "Null reactions array detected."
239 << " Initialization of reactions will be skipped.";
240 return gen;
241 }
242 }
243
244 // Iterate through all configured reactions. Set flags indicating
245 // whether at least one of them was a CC nuclear reaction and whether at least
246 // one of them is a continuum nuclear reaction. The results will control
247 // whether some logging messages below are printed or skipped due to being
248 // irrelevant.
249 bool found_cc = false;
250 bool found_continuum = false;
251 for ( auto& react : gen.reactions_ ) {
252
253 ProcType pt = react->process_type();
254 if ( pt == ProcType::NeutrinoCC_Discrete
255 || pt == ProcType::AntiNeutrinoCC_Discrete
256 || pt == ProcType::NeutrinoCC_Continuum
257 || pt == ProcType::AntiNeutrinoCC_Continuum)
258 {
259 found_cc = true;
260 }
261
262 if ( pt == ProcType::NeutrinoCC_Continuum
263 || pt == ProcType::AntiNeutrinoCC_Continuum
264 || pt == ProcType::NC_Continuum )
265 {
266 found_continuum = true;
267 }
268 }
269
270 // If a CC reaction is configured, then print a logging message indicating
271 // which Coulomb correction method is active. Otherwise, don't bother.
272 if ( found_cc ) {
273 std::string cmode_str = marley::CoulombCorrector
274 ::string_from_coulomb_mode( coulomb_mode );
275 MARLEY_LOG( INFO, "init.config" ) << "Configured Coulomb correction method: " << cmode_str;
276 }
277
278 // If at least one continuum nuclear reaction is configured, then inform the
279 // user about the active sub-continuum mode
280 if ( found_continuum ) {
281 SubContinuumMode sc_mode
283
284 MARLEY_LOG( INFO, "init.config" ) << "Configured sub-continuum mode: "
285 << marley::ContinuumNuclearReaction
286 ::string_from_sub_continuum_mode( sc_mode );
287 }
288
289 // Now that the reactions and source are both prepared, check that a neutrino
290 // from the source can interact via at least one of the enabled reactions
291 bool found_matching_pdg = false;
292 int source_pdg = gen.get_source().get_pid();
293 for ( const auto& react : gen.get_reactions() ) {
294 if ( source_pdg == react->pdg_a() ) found_matching_pdg = true;
295 }
296 // If neutrinos from the source can never interact, then complain about it
297 if ( !found_matching_pdg ) throw marley::Error( "The neutrino source"
298 " produces " + marley_utils::get_particle_symbol(source_pdg)
299 + ", which cannot participate in any of the configured reactions." );
300
301 // Before returning the newly-created Generator object, print logging
302 // messages describing the reactions that are active.
303 MARLEY_LOG( NOTICE, "init.config" ) << "Generator configuration complete. Active reactions:";
304 for ( const auto& r : gen.get_reactions() ) {
305
306 const marley::TargetAtom ta = r->atomic_target();
307 double atom_frac = gen.get_target().atom_fraction( ta );
308
309 if ( r->pdg_a() == source_pdg && atom_frac > 0. ) {
310
311 std::string proc_type_str;
312 if ( r->process_type() == ProcType::NeutrinoCC_Discrete
313 || r->process_type() == ProcType::AntiNeutrinoCC_Discrete )
314 {
315 proc_type_str = "CC (Discrete)";
316 }
317 else if ( r->process_type() == ProcType::NeutrinoCC_Continuum
318 || r->process_type() == ProcType::AntiNeutrinoCC_Continuum )
319 {
320 proc_type_str = "CC (Continuum)";
321 }
322 else if ( r->process_type() == ProcType::NC_Discrete )
323 {
324 proc_type_str = "NC (Discrete)";
325 }
326 else if ( r->process_type() == ProcType::NC_Continuum )
327 {
328 proc_type_str = "NC (Continuum)";
329 }
330 else if ( r->process_type() == ProcType::NuElectronElastic )
331 {
332 proc_type_str = "ES on " + ta.to_string();
333 }
334 else throw marley::Error( "Unrecognized process type encountered in"
335 " marley::JSONConfig::prepare_reactions()" );
336
337 // Show the threshold in red if it's above the maximum energy
338 // produced by the source
339 std::ostringstream temp_oss;
340 double threshold_KE = r->threshold_kinetic_energy();
341 bool no_flux = ( threshold_KE > gen.get_source().get_Emax() );
342 if ( no_flux ) temp_oss << "\u001b[31m";
343 temp_oss << threshold_KE << " MeV";
344 if ( no_flux ) temp_oss << "\u001b[30m";
345 temp_oss << ')';
346
347 MARLEY_LOG( NOTICE, "init.config" ) << " " << proc_type_str << ": "
348 << r->get_description() << " (KE @ threshold: "
349 << temp_oss.str();
350 if ( no_flux ) MARLEY_LOG( WARN, "init.config" )
351 << "Reaction \"" << r->get_description() << "\" threshold"
352 << " (" << threshold_KE << " MeV) exceeds the maximum source energy ("
353 << gen.get_source().get_Emax() << " MeV). No events will be generated"
354 << " via this reaction.";
355 }
356 }
357
358 // We've prepared the Generator and checked that at least one cross section
359 // should be non-vanishing. Now actually integrate the cross section to check
360 // that there is flux above threshold (and normalize the energy PDF at the
361 // same time). An exception will be thrown if no neutrinos can interact.
362 gen.dont_normalize_E_pdf_ = false;
363 gen.normalize_E_pdf();
364
365 // Now we're all ready to go. Log the flux-averaged total cross section
366 // value before returning the fully-configured generator.
367 double avg_tot_xs = gen.flux_averaged_total_xs(); // MeV^(-2)
368 MARLEY_LOG( INFO, "init.config" ) << "Flux-averaged total cross section per atom: "
369 << marley_utils::hbar_c2 * avg_tot_xs * marley_utils::fm2_to_minus40_cm2
370 << " * 10^(-40) cm^2";
371
372 return gen;
373 }
static SubContinuumMode sub_continuum_mode()
Gets the approach to handling sub-continuum cross-section strength.
static void set_sub_continuum_mode(SubContinuumMode scm)
Sets the approach to handling sub-continuum cross-section strength.
static bool check_for_allowed_approximation(const marley::JSON &ff_config)
std::string to_string() const
Converts the PDG code to a string representation (e.g., "40Ar")
Definition TargetAtom.cc:38

◆ get_interpolation_method()

InterpMethod marley::JSONConfig::get_interpolation_method ( const std::string & rule) const

Definition at line 576 of file JSONConfig.cc.

578{
579 // Try using the ENDF-style numerical codes first
580 static const std::regex rx_nonneg_int( "[0-9]+" );
581
582 if ( std::regex_match(rule, rx_nonneg_int) ) {
583 int endf_interp_code = std::stoi( rule );
584 if ( endf_interp_code == 1 ) return InterpMethod::Constant;
585 else if ( endf_interp_code == 2 ) return InterpMethod::LinearLinear;
586 else if ( endf_interp_code == 3 ) return InterpMethod::LinearLog;
587 else if ( endf_interp_code == 4 ) return InterpMethod::LogLinear;
588 else if ( endf_interp_code == 5 ) return InterpMethod::LogLog;
589 }
590
591 // Interpolation rules may also be given as strings
592 else if ( rule == "const" || rule == "constant" )
593 return InterpMethod::Constant;
594 else if ( rule == "lin" || rule == "linlin" )
595 return InterpMethod::LinearLinear;
596 else if ( rule == "log" || rule == "loglog" )
597 return InterpMethod::LogLog;
598 // linear in energy, logarithmic in probability density
599 else if ( rule == "linlog" )
600 return InterpMethod::LinearLog;
601 // logarithmic in energy, linear in probability density
602 else if ( rule == "loglin" )
603 return InterpMethod::LogLinear;
604 else throw marley::Error( "Invalid interpolation rule '" + rule
605 + "' given in the neutrino source specification" );
606
607 // We shouldn't ever end up here, but return something just in case
608 return InterpMethod::Constant;
609}

◆ get_json()

const marley::JSON & marley::JSONConfig::get_json ( ) const
inline

Definition at line 86 of file JSONConfig.hh.

87 { return json_; }

◆ handle_json_error()

void marley::JSONConfig::handle_json_error ( const std::string & name,
const marley::JSON & json )
static

Definition at line 959 of file JSONConfig.cc.

961{
962 std::ostringstream message;
963 message << "The JSON parameter \"" << name << "\" was set to the"
964 << " invalid value " << json;
965 throw marley::Error( message.str() );
966}

◆ neutrino_pdg()

int marley::JSONConfig::neutrino_pdg ( const std::string & nu) const

Definition at line 118 of file JSONConfig.cc.

118 {
119
120 int pdg = 0;
121
122 bool bad = false;
123
124 // Matches integers
125 static const std::regex rx_int = std::regex( "[-+]?[0-9]+" );
126 if ( std::regex_match(nu, rx_int) ) {
127 pdg = std::stoi( nu );
128 if ( !marley::NeutrinoSource::pdg_is_allowed(pdg) ) bad = true;
129 }
130 else if ( !marley_utils::string_to_neutrino_pdg(nu, pdg) ) {
131 bad = true;
132 }
133
134 if ( bad ) throw marley::Error( "Invalid neutrino type specification '"
135 + nu + "' given for the MARLEY neutrino source." );
136
137 return pdg;
138}
static bool pdg_is_allowed(const int pdg)

◆ prepare_direction()

void marley::JSONConfig::prepare_direction ( marley::Generator & gen) const

Definition at line 376 of file JSONConfig.cc.

376 {
377 // Get the incident neutrino direction if the user has specified one
378 if ( json_.has_key("direction") ) {
379
380 const marley::JSON& direction = json_.at("direction");
381 bool ok;
382
383 // The usual use case is for the user to specify the components
384 // of a direction 3-vector using a JSON object
385 if ( direction.is_object() ) {
386
387 std::array<double, 3> dir_vec = gen.neutrino_direction();
388
389 if ( direction.has_key("x") ) {
390 dir_vec.at(0) = direction.at( "x" ).to_double( ok );
391 if ( !ok ) handle_json_error( "direction.x", direction.at("x") );
392 }
393
394 if ( direction.has_key("y") ) {
395 dir_vec.at(1) = direction.at( "y" ).to_double( ok );
396 if ( !ok ) handle_json_error( "direction.y", direction.at("y") );
397 }
398
399 if ( direction.has_key("z") ) {
400 dir_vec.at(2) = direction.at( "z" ).to_double( ok );
401 if ( !ok ) handle_json_error( "direction.z", direction.at("z") );
402 }
403
404 gen.set_neutrino_direction( dir_vec );
405 }
406
407 // The user may also request sampling of an isotropic projectile direction
408 // for every event by associating the string value "isotropic" with the
409 // direction key in the job configuration file
410 else if ( direction.is_string() && direction.to_string() == "isotropic" ) {
411 gen.get_rotator().set_randomize_directions( true );
412
413 MARLEY_LOG( INFO, "init.config" ) << "Projectile directions will be sampled"
414 << " isotropically";
415 }
416 else {
417 throw marley::Error( "Unrecognized value "
418 + direction.dump_string() + " given for the job configuration file"
419 " key \"direction\"" );
420 }
421 }
422}
const std::array< double, 3 > & neutrino_direction()
Gets the direction of the incident neutrinos that is used when generating events.
Definition Generator.hh:470
marley::ProjectileDirectionRotator & get_rotator()
Provides access to the owned ProjectileDirectionRotator.
Definition Generator.hh:287
void set_neutrino_direction(const std::array< double, 3 > &dir_vec)
Sets the direction of the incident neutrinos to use when generating events.
Definition Generator.cc:516

◆ prepare_neutrino_source()

void marley::JSONConfig::prepare_neutrino_source ( marley::Generator & gen) const

Definition at line 612 of file JSONConfig.cc.

613{
614 // Check whether the user provided their own estimate of the source PDF
615 // maximum value. If they did, adopt that before building the source.
616 // This is useful when automatic searches for the maximum don't work.
617 // The user can put in a value manually to get rejection sampling to work.
618 if ( json_.has_key("energy_pdf_max") ) {
619 bool ok;
620 const marley::JSON& max_spec = json_.at( "energy_pdf_max" );
621 double user_max = max_spec.to_double( ok );
622 if ( !ok ) handle_json_error( "energy_pdf_max", max_spec );
623 else {
624 gen.set_default_E_pdf_max( user_max );
625 MARLEY_LOG( DEBUG, "init.config.source" ) << "User-specified"
626 " energy_pdf_max = " << user_max;
627 }
628 }
629
630 // Check whether the JSON configuration includes a neutrino source
631 // specification
632 if ( !json_.has_key("source") ) return;
633 const marley::JSON& source_spec = json_.at( "source" );
634
635 // If the neutrino source key has a null value, just return without doing
636 // anything else
637 if ( source_spec.is_null() ) {
638 MARLEY_LOG( INFO, "init.config.source" ) << "Null source specification detected. Skipping"
639 << " neutrino source configuration.";
640 return;
641 }
642
643 // Complain if the user didn't specify a source type
644 if ( !source_spec.has_key("type") ) {
645 throw marley::Error( "Missing \"type\" key in neutrino source"
646 " specification." );
647 return;
648 }
649
650 // Get the neutrino source type
651 bool ok;
652 std::string type = source_spec.at( "type" ).to_string( ok );
653 if ( !ok ) handle_json_error( "source.type", source_spec.at("type") );
654
655 // Complain if the user didn't specify a neutrino type
656 if ( !source_spec.has_key("neutrino") ) {
657 throw marley::Error( "Missing \"neutrino\" key in neutrino source"
658 " specification." );
659 return;
660 }
661 // Get the neutrino type
662 std::string nu = source_spec.at( "neutrino" ).to_string( ok );
663 if ( !ok ) handle_json_error( "source.neutrino", source_spec.at("neutrino") );
664
665 // Particle Data Group code for the neutrino type produced by this source
666 int pdg = neutrino_pdg( nu );
667
668 std::unique_ptr< marley::NeutrinoSource > source;
669
670 if ( type == "mono" || type == "monoenergetic" ) {
671 double energy = source_get_double( "energy", source_spec, "monoenergetic" );
672 source_check_positive( energy, "energy", "monoenergetic" );
673 source = std::make_unique< marley::MonoNeutrinoSource >( pdg, energy );
674 MARLEY_LOG( INFO, "init.config.source" ) << "Created monoenergetic "
675 << marley_utils::get_particle_symbol( pdg ) << " source with"
676 << " neutrino energy = " << energy << " MeV";
677 }
678 else if ( type == "dar" || type == "decay-at-rest" ) {
679 source = std::make_unique< marley::DecayAtRestNeutrinoSource >( pdg );
680 MARLEY_LOG( INFO, "init.config.source" ) << "Created muon decay-at-rest "
681 << marley_utils::get_particle_symbol( pdg ) << " source";
682 }
683 else if ( type == "fd" || type == "fermi-dirac" || type == "fermi_dirac" ) {
684 double Emin = source_get_double( "Emin", source_spec, "Fermi-Dirac" );
685 double Emax = source_get_double( "Emax", source_spec, "Fermi-Dirac" );
686 double temp = source_get_double( "temperature", source_spec,
687 "Fermi-Dirac" );
688
689 double eta = 0.;
690 if ( source_spec.has_key("eta") ) {
691 eta = source_get_double( "eta", source_spec, "Fermi-Dirac" );
692 }
693
694 source_check_nonnegative( Emin, "Emin", "Fermi-Dirac" );
695 source_check_positive( temp, "temperature", "Fermi-Dirac" );
696
697 if ( Emax <= Emin ) throw marley::Error( "Emax <= Emin for a Fermi-Dirac"
698 " neutrino source" );
699
700 source = std::make_unique< marley::FermiDiracNeutrinoSource >( pdg, Emin,
701 Emax, temp, eta );
702 MARLEY_LOG( INFO, "init.config.source" ) << "Created Fermi-Dirac "
703 << marley_utils::get_particle_symbol( pdg ) << " source with parameters";
704 MARLEY_LOG( INFO, "init.config.source" ) << " Emin = " << Emin << " MeV";
705 MARLEY_LOG( INFO, "init.config.source" ) << " Emax = " << Emax << " MeV";
706 MARLEY_LOG( INFO, "init.config.source" ) << " temperature = " << temp << " MeV";
707 MARLEY_LOG( INFO, "init.config.source" ) << " eta = " << eta;
708 }
709 else if ( type == "bf" || type == "beta" || type == "beta-fit" ) {
710 double Emin = source_get_double( "Emin", source_spec, "beta-fit" );
711 double Emax = source_get_double( "Emax", source_spec, "beta-fit" );
712 double Emean = source_get_double( "Emean", source_spec, "beta-fit" );
713
714 double beta = 4.5;
715 if ( source_spec.has_key("beta") ) {
716 beta = source_get_double( "beta", source_spec, "beta-fit" );
717 }
718
719 source_check_nonnegative( Emin, "Emin", "beta-fit" );
720 source_check_positive( Emean, "Emean", "beta-fit" );
721
722 if ( Emax <= Emin ) throw marley::Error( "Emax <= Emin for a beta-fit"
723 " neutrino source" );
724
725 source = std::make_unique< marley::BetaFitNeutrinoSource >( pdg, Emin,
726 Emax, Emean, beta );
727 MARLEY_LOG( INFO, "init.config.source" ) << "Created beta-fit "
728 << marley_utils::get_particle_symbol( pdg ) << " source with parameters";
729 MARLEY_LOG( INFO, "init.config.source" ) << " Emin = " << Emin << " MeV";
730 MARLEY_LOG( INFO, "init.config.source" ) << " Emax = " << Emax << " MeV";
731 MARLEY_LOG( INFO, "init.config.source" ) << " average energy = " << Emean << " MeV";
732 MARLEY_LOG( INFO, "init.config.source" ) << " beta = " << beta;
733 }
734 else if ( type == "af" || type == "alpha" || type == "alpha-fit" ) {
735 double Emin = source_get_double( "Emin", source_spec, "alpha-fit" );
736 double Emax = source_get_double( "Emax", source_spec, "alpha-fit" );
737 double Emean = source_get_double( "Emean", source_spec, "alpha-fit" );
738
739 double alpha = 2.;
740 if ( source_spec.has_key("alpha") ) {
741 alpha = source_get_double( "alpha", source_spec, "alpha-fit" );
742 }
743
744 source_check_nonnegative( Emin, "Emin", "alpha-fit" );
745 source_check_positive( Emean, "Emean", "alpha-fit" );
746
747 if ( Emax <= Emin ) throw marley::Error( "Emax <= Emin for an alpha-fit"
748 " neutrino source" );
749
750 source = std::make_unique< marley::AlphaFitNeutrinoSource >( pdg, Emin,
751 Emax, Emean, alpha );
752 MARLEY_LOG( INFO, "init.config.source" ) << "Created alpha-fit "
753 << marley_utils::get_particle_symbol( pdg ) << " source with parameters";
754 MARLEY_LOG( INFO, "init.config.source" ) << " Emin = " << Emin << " MeV";
755 MARLEY_LOG( INFO, "init.config.source" ) << " Emax = " << Emax << " MeV";
756 MARLEY_LOG( INFO, "init.config.source" ) << " average energy = " << Emean << " MeV";
757 MARLEY_LOG( INFO, "init.config.source" ) << " alpha = " << alpha;
758 }
759 else if ( type == "hist" || type == "histogram" ) {
760
761 std::vector< double > Es = get_vector( "E_bin_lefts", source_spec,
762 "histogram" );
763 std::vector< double > weights = get_vector( "weights", source_spec,
764 "histogram" );
765
766 if ( Es.size() != weights.size() ) throw marley::Error( "The sizes of the"
767 " arrays of energy bin left edges and weights given for a histogram"
768 " neutrino source are unequal." );
769
770 double Emax = source_get_double( "Emax", source_spec, "histogram" );
771 source_check_positive( Emax, "Emax", "histogram" );
772
773 // Add Emax to the grid
774 Es.push_back( Emax );
775
776 // Set the probability density at E = Emax to be zero (this ensures
777 // that no energies outside of the histogram will be sampled)
778 weights.push_back( 0. );
779
780 // Convert from bin weights to probability densities by dividing by the
781 // width of each bin
782 int jmax = Es.size() - 1;
783 for ( int j = 0; j < jmax; ++j ) {
784
785 double width = Es.at( j + 1 ) - Es.at( j );
786 if ( width <= 0 ) throw marley::Error( "Invalid bin width"
787 + std::to_string(width) + " encountered when creating a histogram"
788 " neutrino source" );
789
790 weights.at( j ) /= width;
791 }
792
793 // Create the source
794 source = std::make_unique< marley::GridNeutrinoSource >( Es, weights, pdg,
795 InterpMethod::Constant );
796 MARLEY_LOG( INFO, "init.config.source" ) << "Created histogram "
797 << marley_utils::get_particle_symbol( pdg ) << " source";
798 }
799 else if ( type == "grid" ) {
800 std::vector< double > energies = get_vector( "energies", source_spec,
801 "grid" );
802 std::vector< double > PDs = get_vector( "prob_densities", source_spec,
803 "grid" );
804 std::string rule = source_get( "rule", source_spec, "grid", "linlin" );
805
806 InterpMethod method = get_interpolation_method( rule );
807
808 source = std::make_unique< marley::GridNeutrinoSource >( energies, PDs,
809 pdg, method );
810 MARLEY_LOG( INFO, "init.config.source" ) << "Created grid "
811 << marley_utils::get_particle_symbol( pdg ) << " source";
812 }
813 else if ( !process_extra_source_types(type, source_spec, pdg, source) ) {
814 throw marley::Error( "Unrecognized MARLEY neutrino source type '"
815 + type + "'" );
816 }
817
818 // If the user has specified whether to weight the incident neutrino spectrum
819 // by the reaction cross section(s), then set the weight_flux_ flag in the
820 // new Generator object accordingly
821 if ( source_spec.has_key("weight_flux") ) {
822 bool ok = false;
823 bool should_we_weight = source_spec.at( "weight_flux" ).to_bool( ok );
824 if ( !ok ) handle_json_error( "source.weight_flux",
825 source_spec.at("weight_flux") );
826 gen.set_weight_flux( should_we_weight );
827 MARLEY_LOG( DEBUG, "init.config.source" ) << "weight_flux = "
828 << ( should_we_weight ? "true" : "false" );
829 }
830
831 // Load the generator with the new source object
832 gen.set_source( std::move(source) );
833
834}
void set_source(std::unique_ptr< marley::NeutrinoSource > source)
Take ownership of a new NeutrinoSource, replacing any existing source owned by this Generator.
Definition Generator.cc:461
void set_weight_flux(bool should_we_weight)
Sets the value of the weight_flux flag.
Definition Generator.cc:532
std::string source_get(const char *name, const marley::JSON &source_spec, const char *description, const char *default_str) const
Helper function for loading strings from the JSON configuration.
bool process_extra_source_types(const std::string &type, const marley::JSON &source_spec, int pdg_code, std::unique_ptr< marley::NeutrinoSource > &source) const
Helper function used to define ROOT-based neutrino source types @detail This function is a no-op when...
bool is_null() const
Functions for getting primitives from the JSON object.
Definition JSON.hh:352

◆ prepare_reactions()

void marley::JSONConfig::prepare_reactions ( marley::Generator & gen,
marley::CoulombCorrector::CoulombMode coulomb_mode,
const marley::JSON & ff_config ) const

Definition at line 424 of file JSONConfig.cc.

426{
427 const auto& fm = marley::FileManager::Instance();
428
429 if ( json_.has_key("reactions") ) {
430
431 const marley::JSON& rs = json_.at( "reactions" );
432
433 // If the reactions key has a null value, skip trying
434 // to load any reaction data. This can be used in unusual
435 // situations when we don't actually want to simulate any
436 // reactions.
437 if ( rs.is_null() ) return;
438
439 if ( rs.is_array() ) {
440
441 auto reactions = rs.array_range();
442 if ( reactions.begin() != reactions.end() ) {
443
444 // Create a temporary vector to cache the (TargetAtom, ProcessType)
445 // pairs for which reactions have already been loaded. Complain if
446 // there is duplication.
447 std::vector< std::pair<marley::TargetAtom, ProcType> >
448 loaded_proc_types;
449
450 for ( const auto& r : reactions ) {
451
452 std::string filename = r.to_string();
453
454 // Find the reaction data file using the MARLEY search path
455 std::string full_file_name = fm.find_file( filename );
456 if ( full_file_name.empty() ) {
457 throw marley::Error( "Could not locate the reaction data file "
458 + filename + ". Please check that the file name is spelled"
459 " correctly and that the file is in a folder"
460 " on the MARLEY search path." );
461 }
462
464 full_file_name, gen.get_structure_db(), coulomb_mode,
465 ff_config );
466
467 if ( reacts.empty() ) throw marley::Error( "Failed to load"
468 " any reactions from the file " + full_file_name + ". Please"
469 " check that it is readable and conforms to the correct input"
470 " format." );
471
472 // All of the Reaction objects loaded from a single file will have
473 // the same process type and atomic target, so just save this
474 // information from the first one
475 auto temp_atom = reacts.front()->atomic_target();
476 auto temp_pt = reacts.front()->process_type();
477 std::pair< marley::TargetAtom, ProcType >
478 temp_pair( temp_atom, temp_pt );
479
480 // If we have a duplicate, warn the user that we'll ignore it
481 auto begin = loaded_proc_types.cbegin();
482 auto end = loaded_proc_types.cend();
483 if ( std::find(begin, end, temp_pair) != end ) {
484 MARLEY_LOG( WARN, "init.config" ) << "Reaction settings for the "
485 << marley::Reaction::proc_type_to_string( temp_pt )
486 << " process on " << temp_atom << " were already loaded."
487 << " To avoid duplication, those in " << full_file_name
488 << " will be ignored.";
489 continue;
490 }
491 // Otherwise, save the process type for later checks of this kind
492 else {
493 MARLEY_LOG( INFO, "init.config" ) << "Loaded "
494 << marley::Reaction::proc_type_to_string( temp_pt )
495 << " reaction data for " << temp_atom << " from "
496 << full_file_name;
497 loaded_proc_types.push_back( temp_pair );
498 }
499
500 // Transfer ownership of the new reactions to the generator
501 for ( auto& rct : reacts ) gen.add_reaction( std::move(rct) );
502 }
503
504 return;
505 }
506 else {
507 throw marley::Error( "At least one reaction matrix data file must be"
508 " specified using the \"reactions\" parameter" );
509 }
510 }
511
512 handle_json_error( "reactions", rs );
513 }
514
515 throw marley::Error( "Missing \"reactions\" key in the MARLEY configuration"
516 " file." );
517}
static const FileManager & Instance()
Get a const reference to the singleton instance of the FileManager.
marley::StructureDatabase & get_structure_db()
Get a reference to the StructureDatabase owned by this Generator.
Definition Generator.cc:510
void add_reaction(std::unique_ptr< marley::Reaction > reaction)
Take ownership of a new Reaction.
Definition Generator.cc:479
static std::vector< std::unique_ptr< Reaction > > load_from_file(const std::string &filename, StructureDatabase &db, CoulombCorrector::CoulombMode coulomb_mode, const JSON &ff_config)
Definition Reaction.cc:366

◆ prepare_structure()

void marley::JSONConfig::prepare_structure ( marley::Generator & gen) const

Definition at line 519 of file JSONConfig.cc.

519 {
520 auto& sdb = gen.get_structure_db();
521
522 // Check for a custom configuration of nuclear optical model parameters.
523 // If the user asked for one, use it instead of the default settings.
524 const std::string om_key = "opt_mod";
525 if ( json_.has_key(om_key) ) {
526 const marley::JSON om_config = json_.at( om_key );
527 MARLEY_LOG( INFO, "init.config" ) << "Loading custom optical model configuration";
528 MARLEY_LOG( DEBUG, "init.config" ) << om_config.dump_string();
529
530 sdb.load_optical_model_params( &om_config );
531 }
532
533 // If the user specified a non-default value of either the
534 // maximum orbital angular momentum or the maximum multipolarity
535 // to consider when simulating decays to the continuum, set
536 // the appropriate member variable of the StructureDatabase
537 // object owned by the Generator
538
539 std::string flmax_key( "fragment_lmax" );
540 if ( json_.has_key(flmax_key) ) {
541 bool ok;
542 const marley::JSON& flmax_json = json_.at( flmax_key );
543 int f_lmax = flmax_json.to_long( ok );
544 if ( !ok ) handle_json_error( flmax_key.c_str(), flmax_json );
545
546 if ( f_lmax < 0 ) throw marley::Error( "Negative value of "
547 + flmax_key + " = " + std::to_string(f_lmax) + " encountered in"
548 " marley::JSONConfig::prepare_structure()" );
549
550 sdb.set_fragment_l_max( f_lmax );
551
552 MARLEY_LOG( INFO, "init.config" ) << "Orbital angular momentum cutoff for fragment"
553 << " differential decay widths set to l_max = " << f_lmax;
554 }
555
556 // TODO: reduce code duplication here
557 std::string glmax_key( "gamma_lmax" );
558 if ( json_.has_key(glmax_key) ) {
559 bool ok;
560 const marley::JSON& glmax_json = json_.at( glmax_key );
561 int g_lmax = glmax_json.to_long( ok );
562 if ( !ok ) handle_json_error( glmax_key.c_str(), glmax_json );
563
564 if ( g_lmax < 1 ) throw marley::Error( "Nonpositive value of "
565 + glmax_key + " = " + std::to_string(g_lmax) + " encountered in"
566 " marley::JSONConfig::prepare_structure()" );
567
568 sdb.set_gamma_l_max( g_lmax );
569
570 MARLEY_LOG( INFO, "init.config" ) << "Multipolarity cutoff for gamma-ray"
571 << " differential decay widths set to l_max = " << g_lmax;
572 }
573}

◆ prepare_target()

void marley::JSONConfig::prepare_target ( marley::Generator & gen) const

Definition at line 836 of file JSONConfig.cc.

836 {
837
838 // Temporary storage for the list of target atoms and their atom fractions
839 // in the possibly-composite neutrino target
840 std::vector< marley::TargetAtom > atoms;
841 std::vector< double > atom_fractions;
842
843 // In the absence of a user-specified target, create one automatically from
844 // the configured reactions. Each unique target atom involved in at least
845 // one reaction will be included with equal weight.
846 if ( !json_.has_key("target") ) {
847
848 // If there aren't any configured reactions, just return without
849 // configuring the target at all. This only happens in unusual
850 // situations.
851 const auto& reactions = gen.get_reactions();
852 if ( reactions.empty() ) return;
853
854 // Otherwise, store the set of target atoms involved in at least
855 // one reaction
856 std::set< marley::TargetAtom > temp_atom_set;
857 for ( const auto& react : reactions ) {
858 temp_atom_set.insert( react->atomic_target() );
859 }
860
861 // Add each target atom with equal weight to the target configuration
862 for ( const auto& atom : temp_atom_set ) {
863 atoms.push_back( atom );
864 // This will be renormalized appropriately by the Target object itself
865 atom_fractions.push_back( 1. );
866 }
867 }
868 else {
869 // If the user has specified a target composition explicitly, parse the
870 // JSON object used to define it.
871 const auto& tgt_spec = json_.at( "target" );
872 if ( !tgt_spec.is_object() ) throw marley::Error( "Invalid neutrino target"
873 " specification " + tgt_spec.dump_string() );
874
875 if ( !tgt_spec.has_key("nuclides") ) throw marley::Error( "Missing \""
876 "nuclides\" key in the neutrino target specification "
877 + tgt_spec.dump_string() );
878
879 const auto& n_spec = tgt_spec.at( "nuclides" );
880
881 if ( !n_spec.is_array() ) throw marley::Error( "Invalid \"nuclides\""
882 " array given in the neutrino target specification "
883 + tgt_spec.dump_string() );
884
885 if ( !tgt_spec.has_key("atom_fractions") ) throw marley::Error( "Missing \""
886 "atom_fractions\" key in the neutrino target specification "
887 + tgt_spec.dump_string() );
888
889 const auto& af_spec = tgt_spec.at( "atom_fractions" );
890
891 if ( !af_spec.is_array() ) throw marley::Error( "Invalid"
892 " \"atom_fractions\" array given in the neutrino target specification "
893 + tgt_spec.dump_string() );
894
895 // Check that the two arrays used to specify the target are of equal length
896 int num_nuclides = n_spec.length();
897 if ( num_nuclides != af_spec.length() ) throw marley::Error(
898 "Arrays of unequal length specified for the \"nuclides\" and \"atom"
899 "_fractions\" keys in the neutrino target specification "
900 + tgt_spec.dump_string() );
901
902 // Check that at least one target atom is listed
903 if ( num_nuclides < 1 ) throw marley::Error( "At least one target nuclide"
904 " must be included in the neutrino target specification" );
905
906 // Loop over each of the target atoms. Parse their information and add them
907 // to the vectors that will be used to initialize the Target object.
908 for ( int n = 0; n < num_nuclides; ++n ) {
909 const auto& nuc = n_spec.at( n );
910 bool ok = false;
911 int nuc_pdg = nuc.to_long( ok );
912 if ( ok ) atoms.emplace_back( nuc_pdg );
913 // TODO: add support for string parsing
914 //else if ( nuc.is_string() ) {
915 //}
916 else throw marley::Error( "Invalid target nuclide specifier "
917 + nuc.dump_string() );
918
919 // Parse and store the atom fraction. We already check for sane values
920 // while initializing the Target object itself, so just make sure that the
921 // conversion to a double worked out all right.
922 const auto& frac_spec = af_spec.at( n );
923 double frac = frac_spec.to_double( ok );
924 if ( ok ) atom_fractions.push_back( frac );
925 else throw marley::Error( "Invalid atom fraction "
926 + frac_spec.dump_string() );
927 }
928 }
929
930 // We're done. Create the new Target object and move it into the Generator.
931 auto target = std::make_unique< marley::Target >( atoms, atom_fractions );
932 if ( target->has_single_nuclide() ) {
933 const marley::TargetAtom& ta = target->atom_fraction_map().cbegin()->first;
934 MARLEY_LOG( INFO, "init.config.target" ) << "Configured pure " << ta << " neutrino target";
935 }
936 else {
937 MARLEY_LOG( INFO, "init.config.target" ) << "Configured composite neutrino target with the"
938 << " following nuclide fractions:\n" << *target;
939 }
940 gen.set_target( std::move(target) );
941}
const std::vector< std::unique_ptr< marley::Reaction > > & get_reactions() const
Get a const reference to the vector of Reaction objects owned by this Generator.
Definition Generator.hh:468
void set_target(std::unique_ptr< marley::Target > target)
Take ownership of a new Target, replacing any existing target owned by this Generator.
Definition Generator.cc:612

◆ prepare_weights()

void marley::JSONConfig::prepare_weights ( marley::Generator & gen) const

Definition at line 1012 of file JSONConfig.cc.

1012 {
1013
1014 // If the user has specified settings under the "weights" key in the
1015 // configuration file, then use those
1016 marley::JSON wgt_config;
1017 if ( json_.has_key("weights") ) {
1018 wgt_config = json_.at( "weights" );
1019 }
1020 // Otherwise, default to an empty array (no configured weight calculators)
1021 else {
1022 wgt_config = marley::JSON::array();
1023 }
1024
1025 // Initialize the Weighter object owned by the generator
1026 gen.weighter_ = std::make_shared< marley::Weighter >( wgt_config, gen );
1027
1028}

◆ process_extra_source_types()

bool marley::JSONConfig::process_extra_source_types ( const std::string & type,
const marley::JSON & source_spec,
int pdg_code,
std::unique_ptr< marley::NeutrinoSource > & source ) const

Helper function used to define ROOT-based neutrino source types @detail This function is a no-op when MARLEY is built without ROOT support.

Definition at line 968 of file JSONConfig.cc.

971{
972
973#ifdef USE_ROOT
974 if ( type != "th1" && type != "tgraph" ) return false;
975
976 std::string tfile = source_get( "tfile", source_spec, type.c_str(), nullptr );
977 std::string namecycle = source_get( "namecycle", source_spec, type.c_str(),
978 nullptr );
979
980 if ( type == "th1" ) {
981 auto th1 = marley_root::get_root_object< TH1 >( tfile, namecycle );
982 source = marley_root::make_root_neutrino_source( pdg_code, th1 );
983 MARLEY_LOG( INFO, "init.config.source" ) << "Created a TH1 "
984 << marley_utils::neutrino_pdg_to_string( pdg_code )
985 << " source with parameters";
986 MARLEY_LOG( INFO, "init.config.source" ) << " Emin = " << source->get_Emin() << " MeV";
987 MARLEY_LOG( INFO, "init.config.source" ) << " Emax = " << source->get_Emax() << " MeV";
988 return true;
989 }
990
991 else if ( type == "tgraph" ) {
992 auto tg = marley_root::get_root_object<TGraph>( tfile, namecycle );
993 source = marley_root::make_root_neutrino_source( pdg_code, tg );
994 MARLEY_LOG( INFO, "init.config.source" ) << "Created a TGraph "
995 << marley_utils::neutrino_pdg_to_string( pdg_code )
996 << " source with parameters";
997 MARLEY_LOG( INFO, "init.config.source" ) << " Emin = " << source->get_Emin() << " MeV";
998 MARLEY_LOG( INFO, "init.config.source" ) << " Emax = " << source->get_Emax() << " MeV";
999 return true;
1000 }
1001#else
1002 // Avoid unused parameter warnings via these casts to void
1003 (void)type;
1004 (void)source_spec;
1005 (void)pdg_code;
1006 (void)source;
1007#endif
1008
1009 return false;
1010}
virtual double get_Emax() const =0
Get the maximum neutrino energy (MeV) that can be sampled by this source.
virtual double get_Emin() const =0
Get the minimum neutrino energy (MeV) that can be sampled by this source.

References source_get().

◆ set_json()

void marley::JSONConfig::set_json ( const marley::JSON & json)
inline

Definition at line 89 of file JSONConfig.hh.

90 { json_ = json; }

◆ source_get()

std::string marley::JSONConfig::source_get ( const char * name,
const marley::JSON & source_spec,
const char * description,
const char * default_str ) const
protected

Helper function for loading strings from the JSON configuration.

Definition at line 943 of file JSONConfig.cc.

946{
947 if ( !source_spec.has_key(name) ) {
948 if ( default_str ) return default_str;
949 else throw marley::Error( std::string("Missing source.") + name
950 + " key for " + description + " source" );
951 }
952 bool ok;
953 std::string result = source_spec.at( name ).to_string( ok );
954 if ( !ok ) throw marley::Error( std::string("Invalid value given for source.")
955 + name + " key for " + description + " source" );
956 return result;
957}

Referenced by process_extra_source_types().

Member Data Documentation

◆ json_

marley::JSON marley::JSONConfig::json_
protected

JSON object describing this configuration.

Definition at line 83 of file JSONConfig.hh.


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