25#include "marley/marley_utils.hh"
26#include "marley/ContinuumNuclearReaction.hh"
27#include "marley/Error.hh"
28#include "marley/FileManager.hh"
29#include "marley/JSONConfig.hh"
30#include "marley/NeutrinoSource.hh"
31#include "marley/NuclearReaction.hh"
32#include "marley/Logger.hh"
33#include "marley/StructureDatabase.hh"
34#include "marley/Weighter.hh"
37#include "marley/marley_root.hh"
48 void source_check_positive(
double x,
const char* description,
49 const char* source_type )
51 if ( x <= 0. )
throw marley::Error( std::string(
"Non-positive ")
52 + description +
" value defined for a " + source_type
53 +
" neutrino source" );
56 void source_check_nonnegative(
double x,
const char* description,
57 const char* source_type)
59 if ( x < 0. )
throw marley::Error( std::string(
"Negative ") + description
60 +
" value defined for a " + source_type +
" neutrino source" );
63 double source_get_double(
const char* name,
const marley::JSON& source_spec,
64 const char* description )
67 std::string(
"Missing source.") + name +
" key for " + description
70 double result = source_spec.at( name ).to_double( ok );
71 if ( !ok )
throw marley::Error( std::string(
"Invalid value given for"
72 " source.") + name +
" key for " + description +
" source" );
76 std::vector<double> get_vector(
const char* name,
const marley::JSON& spec,
77 const char* description )
79 if ( !spec.has_key(name) )
throw marley::Error( std::string(
"The")
80 +
" specification for a " + description +
" source should include"
81 +
" a " + name +
" key." );
85 if ( !vec.is_array() )
throw marley::Error( std::string(
"The")
86 +
" value given for the " + name +
" key for a " + description
87 +
" source should be an array." );
89 std::vector<double> result;
91 auto elements = vec.array_range();
92 if ( elements.begin() != elements.end() ) {
94 for (
const auto& el : elements ) {
95 double dub = el.to_double( ok );
96 if ( ok ) result.push_back( dub );
98 + el.dump_string() +
"' given for the " + name +
" key for a "
99 + description +
" source specification." );
108marley::JSONConfig::JSONConfig(
const marley::JSON& json ) : json_( json )
112marley::JSONConfig::JSONConfig(
const std::string& json_filename )
115 json_ = marley::JSON::load_file( json_filename );
118int marley::JSONConfig::neutrino_pdg(
const std::string& nu )
const {
125 static const std::regex rx_int = std::regex(
"[-+]?[0-9]+" );
126 if ( std::regex_match(nu, rx_int) ) {
127 pdg = std::stoi( nu );
130 else if ( !marley_utils::string_to_neutrino_pdg(nu, pdg) ) {
134 if ( bad )
throw marley::Error(
"Invalid neutrino type specification '"
135 + nu +
"' given for the MARLEY neutrino source." );
140marley::Generator marley::JSONConfig::create_generator()
const
143 if ( json_.has_key(
"seed") ) {
145 seed =
static_cast< uint_fast64_t
>( json_.at(
"seed").to_long(ok) );
146 if ( !ok ) handle_json_error(
"seed", json_.at(
"seed") );
148 else seed = std::chrono::system_clock::now().time_since_epoch().count();
152 marley::Generator gen( seed );
154 MARLEY_LOG( DEBUG,
"init.config" ) <<
"Full generator configuration:\n"
161 CMode coulomb_mode = CMode::FERMI_AND_MEMA;
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 );
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";
179 else throw marley::Error(
"Invalid form factor configuration "
180 + ff_config.dump_string() );
186 ff_config[
"sachs_model" ] =
"bbba05";
187 ff_config[
"axial_model" ] =
"dipole";
188 ff_config[
"nuclear_model" ] =
"klein";
193 gen.dont_normalize_E_pdf_ =
true;
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 );
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";
217 gen.set_json_config( json_ );
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 );
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.";
249 bool found_cc =
false;
250 bool found_continuum =
false;
251 for (
auto& react : gen.reactions_ ) {
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)
262 if ( pt == ProcType::NeutrinoCC_Continuum
263 || pt == ProcType::AntiNeutrinoCC_Continuum
264 || pt == ProcType::NC_Continuum )
266 found_continuum =
true;
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;
280 if ( found_continuum ) {
281 SubContinuumMode sc_mode
284 MARLEY_LOG( INFO,
"init.config" ) <<
"Configured sub-continuum mode: "
285 << marley::ContinuumNuclearReaction
286 ::string_from_sub_continuum_mode( sc_mode );
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;
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." );
303 MARLEY_LOG( NOTICE,
"init.config" ) <<
"Generator configuration complete. Active reactions:";
304 for (
const auto& r : gen.get_reactions() ) {
306 const marley::TargetAtom ta = r->atomic_target();
307 double atom_frac = gen.get_target().atom_fraction( ta );
309 if ( r->pdg_a() == source_pdg && atom_frac > 0. ) {
311 std::string proc_type_str;
312 if ( r->process_type() == ProcType::NeutrinoCC_Discrete
313 || r->process_type() == ProcType::AntiNeutrinoCC_Discrete )
315 proc_type_str =
"CC (Discrete)";
317 else if ( r->process_type() == ProcType::NeutrinoCC_Continuum
318 || r->process_type() == ProcType::AntiNeutrinoCC_Continuum )
320 proc_type_str =
"CC (Continuum)";
322 else if ( r->process_type() == ProcType::NC_Discrete )
324 proc_type_str =
"NC (Discrete)";
326 else if ( r->process_type() == ProcType::NC_Continuum )
328 proc_type_str =
"NC (Continuum)";
330 else if ( r->process_type() == ProcType::NuElectronElastic )
332 proc_type_str =
"ES on " + ta.
to_string();
334 else throw marley::Error(
"Unrecognized process type encountered in"
335 " marley::JSONConfig::prepare_reactions()" );
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";
347 MARLEY_LOG( NOTICE,
"init.config" ) <<
" " << proc_type_str <<
": "
348 << r->get_description() <<
" (KE @ threshold: "
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.";
362 gen.dont_normalize_E_pdf_ =
false;
363 gen.normalize_E_pdf();
367 double avg_tot_xs = gen.flux_averaged_total_xs();
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";
376void marley::JSONConfig::prepare_direction( marley::Generator& gen )
const {
378 if ( json_.has_key(
"direction") ) {
380 const marley::JSON& direction = json_.at(
"direction");
385 if ( direction.is_object() ) {
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") );
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") );
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") );
410 else if ( direction.is_string() && direction.to_string() ==
"isotropic" ) {
411 gen.
get_rotator().set_randomize_directions(
true );
413 MARLEY_LOG( INFO,
"init.config" ) <<
"Projectile directions will be sampled"
417 throw marley::Error(
"Unrecognized value "
418 + direction.dump_string() +
" given for the job configuration file"
419 " key \"direction\"" );
424void marley::JSONConfig::prepare_reactions( marley::Generator& gen,
425 CMode coulomb_mode,
const marley::JSON& ff_config )
const
429 if ( json_.has_key(
"reactions") ) {
431 const marley::JSON& rs = json_.at(
"reactions" );
439 if ( rs.is_array() ) {
441 auto reactions = rs.array_range();
442 if ( reactions.begin() != reactions.end() ) {
447 std::vector< std::pair<marley::TargetAtom, ProcType> >
450 for (
const auto& r : reactions ) {
452 std::string filename = r.to_string();
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." );
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"
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 );
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.";
493 MARLEY_LOG( INFO,
"init.config" ) <<
"Loaded "
494 << marley::Reaction::proc_type_to_string( temp_pt )
495 <<
" reaction data for " << temp_atom <<
" from "
497 loaded_proc_types.push_back( temp_pair );
501 for (
auto& rct : reacts ) gen.
add_reaction( std::move(rct) );
507 throw marley::Error(
"At least one reaction matrix data file must be"
508 " specified using the \"reactions\" parameter" );
512 handle_json_error(
"reactions", rs );
515 throw marley::Error(
"Missing \"reactions\" key in the MARLEY configuration"
519void marley::JSONConfig::prepare_structure( marley::Generator& gen )
const {
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();
530 sdb.load_optical_model_params( &om_config );
539 std::string flmax_key(
"fragment_lmax" );
540 if ( json_.has_key(flmax_key) ) {
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 );
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()" );
550 sdb.set_fragment_l_max( f_lmax );
552 MARLEY_LOG( INFO,
"init.config" ) <<
"Orbital angular momentum cutoff for fragment"
553 <<
" differential decay widths set to l_max = " << f_lmax;
557 std::string glmax_key(
"gamma_lmax" );
558 if ( json_.has_key(glmax_key) ) {
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 );
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()" );
568 sdb.set_gamma_l_max( g_lmax );
570 MARLEY_LOG( INFO,
"init.config" ) <<
"Multipolarity cutoff for gamma-ray"
571 <<
" differential decay widths set to l_max = " << g_lmax;
576InterpMethod marley::JSONConfig::get_interpolation_method(
577 const std::string& rule )
const
580 static const std::regex rx_nonneg_int(
"[0-9]+" );
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;
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;
599 else if ( rule ==
"linlog" )
600 return InterpMethod::LinearLog;
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" );
608 return InterpMethod::Constant;
612void marley::JSONConfig::prepare_neutrino_source( marley::Generator& gen )
const
618 if ( json_.has_key(
"energy_pdf_max") ) {
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 );
624 gen.set_default_E_pdf_max( user_max );
625 MARLEY_LOG( DEBUG,
"init.config.source" ) <<
"User-specified"
626 " energy_pdf_max = " << user_max;
632 if ( !json_.has_key(
"source") )
return;
633 const marley::JSON& source_spec = json_.at(
"source" );
638 MARLEY_LOG( INFO,
"init.config.source" ) <<
"Null source specification detected. Skipping"
639 <<
" neutrino source configuration.";
644 if ( !source_spec.has_key(
"type") ) {
645 throw marley::Error(
"Missing \"type\" key in neutrino source"
652 std::string type = source_spec.at(
"type" ).to_string( ok );
653 if ( !ok ) handle_json_error(
"source.type", source_spec.at(
"type") );
656 if ( !source_spec.has_key(
"neutrino") ) {
657 throw marley::Error(
"Missing \"neutrino\" key in neutrino source"
662 std::string nu = source_spec.at(
"neutrino" ).to_string( ok );
663 if ( !ok ) handle_json_error(
"source.neutrino", source_spec.at(
"neutrino") );
666 int pdg = neutrino_pdg( nu );
668 std::unique_ptr< marley::NeutrinoSource > source;
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";
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";
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,
690 if ( source_spec.has_key(
"eta") ) {
691 eta = source_get_double(
"eta", source_spec,
"Fermi-Dirac" );
694 source_check_nonnegative( Emin,
"Emin",
"Fermi-Dirac" );
695 source_check_positive( temp,
"temperature",
"Fermi-Dirac" );
697 if ( Emax <= Emin )
throw marley::Error(
"Emax <= Emin for a Fermi-Dirac"
698 " neutrino source" );
700 source = std::make_unique< marley::FermiDiracNeutrinoSource >( pdg, Emin,
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;
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" );
715 if ( source_spec.has_key(
"beta") ) {
716 beta = source_get_double(
"beta", source_spec,
"beta-fit" );
719 source_check_nonnegative( Emin,
"Emin",
"beta-fit" );
720 source_check_positive( Emean,
"Emean",
"beta-fit" );
722 if ( Emax <= Emin )
throw marley::Error(
"Emax <= Emin for a beta-fit"
723 " neutrino source" );
725 source = std::make_unique< marley::BetaFitNeutrinoSource >( pdg, Emin,
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;
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" );
740 if ( source_spec.has_key(
"alpha") ) {
741 alpha = source_get_double(
"alpha", source_spec,
"alpha-fit" );
744 source_check_nonnegative( Emin,
"Emin",
"alpha-fit" );
745 source_check_positive( Emean,
"Emean",
"alpha-fit" );
747 if ( Emax <= Emin )
throw marley::Error(
"Emax <= Emin for an alpha-fit"
748 " neutrino source" );
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;
759 else if ( type ==
"hist" || type ==
"histogram" ) {
761 std::vector< double > Es = get_vector(
"E_bin_lefts", source_spec,
763 std::vector< double > weights = get_vector(
"weights", source_spec,
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." );
770 double Emax = source_get_double(
"Emax", source_spec,
"histogram" );
771 source_check_positive( Emax,
"Emax",
"histogram" );
774 Es.push_back( Emax );
778 weights.push_back( 0. );
782 int jmax = Es.size() - 1;
783 for (
int j = 0; j < jmax; ++j ) {
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" );
790 weights.at( j ) /= width;
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";
799 else if ( type ==
"grid" ) {
800 std::vector< double > energies = get_vector(
"energies", source_spec,
802 std::vector< double > PDs = get_vector(
"prob_densities", source_spec,
804 std::string rule = source_get(
"rule", source_spec,
"grid",
"linlin" );
806 InterpMethod method = get_interpolation_method( rule );
808 source = std::make_unique< marley::GridNeutrinoSource >( energies, PDs,
810 MARLEY_LOG( INFO,
"init.config.source" ) <<
"Created grid "
811 << marley_utils::get_particle_symbol( pdg ) <<
" source";
813 else if ( !process_extra_source_types(type, source_spec, pdg, source) ) {
814 throw marley::Error(
"Unrecognized MARLEY neutrino source type '"
821 if ( source_spec.has_key(
"weight_flux") ) {
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") );
827 MARLEY_LOG( DEBUG,
"init.config.source" ) <<
"weight_flux = "
828 << ( should_we_weight ?
"true" :
"false" );
836void marley::JSONConfig::prepare_target( marley::Generator& gen )
const {
840 std::vector< marley::TargetAtom > atoms;
841 std::vector< double > atom_fractions;
846 if ( !json_.has_key(
"target") ) {
852 if ( reactions.empty() )
return;
856 std::set< marley::TargetAtom > temp_atom_set;
857 for (
const auto& react : reactions ) {
858 temp_atom_set.insert( react->atomic_target() );
862 for (
const auto& atom : temp_atom_set ) {
863 atoms.push_back( atom );
865 atom_fractions.push_back( 1. );
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() );
875 if ( !tgt_spec.has_key(
"nuclides") )
throw marley::Error(
"Missing \""
876 "nuclides\" key in the neutrino target specification "
877 + tgt_spec.dump_string() );
879 const auto& n_spec = tgt_spec.at(
"nuclides" );
881 if ( !n_spec.is_array() )
throw marley::Error(
"Invalid \"nuclides\""
882 " array given in the neutrino target specification "
883 + tgt_spec.dump_string() );
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() );
889 const auto& af_spec = tgt_spec.at(
"atom_fractions" );
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() );
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() );
903 if ( num_nuclides < 1 )
throw marley::Error(
"At least one target nuclide"
904 " must be included in the neutrino target specification" );
908 for (
int n = 0; n < num_nuclides; ++n ) {
909 const auto& nuc = n_spec.at( n );
911 int nuc_pdg = nuc.to_long( ok );
912 if ( ok ) atoms.emplace_back( nuc_pdg );
916 else throw marley::Error(
"Invalid target nuclide specifier "
917 + nuc.dump_string() );
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() );
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";
937 MARLEY_LOG( INFO,
"init.config.target" ) <<
"Configured composite neutrino target with the"
938 <<
" following nuclide fractions:\n" << *target;
944 const marley::JSON& source_spec,
const char* description,
945 const char* default_str)
const
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" );
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" );
959void marley::JSONConfig::handle_json_error(
const std::string& name,
962 std::ostringstream message;
963 message <<
"The JSON parameter \"" << name <<
"\" was set to the"
964 <<
" invalid value " << json;
969 const std::string& type,
const marley::JSON& source_spec,
int pdg_code,
970 std::unique_ptr<marley::NeutrinoSource>& source)
const
974 if ( type !=
"th1" && type !=
"tgraph" )
return false;
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(),
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";
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";
1017 if ( json_.has_key(
"weights") ) {
1018 wgt_config = json_.at(
"weights" );
1022 wgt_config = marley::JSON::array();
1026 gen.weighter_ = std::make_shared< marley::Weighter >( wgt_config, gen );
1037 if ( !ff_config.is_string() )
return false;
1040 auto cfg_str = ff_config.to_string();
1041 if ( cfg_str ==
"allowed" || cfg_str ==
"AA" || cfg_str ==
"aa" ) {
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.
CoulombMode
Enumerated type used to set the method for handling Coulomb corrections for CC nuclear reactions.
Base class for all exceptions thrown by MARLEY functions.
static const FileManager & Instance()
Get a const reference to the singleton instance of the FileManager.
The MARLEY Event generator.
const std::array< double, 3 > & neutrino_direction()
Gets the direction of the incident neutrinos that is used when generating events.
void set_source(std::unique_ptr< marley::NeutrinoSource > source)
Take ownership of a new NeutrinoSource, replacing any existing source owned by this Generator.
marley::StructureDatabase & get_structure_db()
Get a reference to the StructureDatabase owned by this Generator.
marley::ProjectileDirectionRotator & get_rotator()
Provides access to the owned ProjectileDirectionRotator.
void set_weight_flux(bool should_we_weight)
Sets the value of the weight_flux flag.
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.
void set_target(std::unique_ptr< marley::Target > target)
Take ownership of a new Target, replacing any existing target owned by this Generator.
void add_reaction(std::unique_ptr< marley::Reaction > reaction)
Take ownership of a new Reaction.
void set_neutrino_direction(const std::array< double, 3 > &dir_vec)
Sets the direction of the incident neutrinos to use when generating events.
InterpolationMethod
Method to use for interpolating between (x,y) grid points.
marley::JSON json_
JSON object describing this configuration.
static bool check_for_allowed_approximation(const marley::JSON &ff_config)
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.
static bool pdg_is_allowed(const int pdg)
ProcessType
Enumerated type describing the kind of scattering process represented by a Reaction.
static std::vector< std::unique_ptr< Reaction > > load_from_file(const std::string &filename, StructureDatabase &db, CoulombCorrector::CoulombMode coulomb_mode, const JSON &ff_config)
std::string to_string() const
Converts the PDG code to a string representation (e.g., "40Ar")