diff --git a/src/YAMLreader.cc b/src/YAMLreader.cc index 584c58f..798092d 100644 --- a/src/YAMLreader.cc +++ b/src/YAMLreader.cc @@ -1,431 +1,427 @@ #include "RHEJ/YAMLreader.hh" #include <set> #include <string> #include <vector> #include <iostream> #include <stdexcept> namespace RHEJ{ namespace{ //! Get YAML tree of supported options /** * The configuration file is checked against this tree of options * in assert_all_options_known. */ YAML::Node const & get_supported_options(){ const static YAML::Node supported = [](){ YAML::Node supported; static const auto opts = { "trials", "min extparton pt", "max ext soft pt fraction", "FKL", "unordered", "non-FKL", "scales", "scale factors", "max scale ratio", "log correction", "unweight", "event output", "analysis", "RanLux init" }; // add subnodes to "supported" - the assigned value is irrelevant for(auto && opt: opts) supported[opt] = ""; for(auto && jet_opt: {"min pt", "algorithm", "R"}){ supported["resummation jets"][jet_opt] = ""; supported["fixed order jets"][jet_opt] = ""; } for(auto && opt: {"mt", "use impact factors", "include bottom", "mb"}){ supported["Higgs coupling"][opt] = ""; } return supported; }(); return supported; } fastjet::JetAlgorithm to_JetAlgorithm(std::string const & algo){ using namespace fastjet; static const std::map<std::string, fastjet::JetAlgorithm> known = { {"kt", kt_algorithm}, {"cambridge", cambridge_algorithm}, {"antikt", antikt_algorithm}, {"genkt", genkt_algorithm}, {"cambridge for passive", cambridge_for_passive_algorithm}, {"genkt for passive", genkt_for_passive_algorithm}, {"ee kt", ee_kt_algorithm}, {"ee genkt", ee_genkt_algorithm}, {"plugin", plugin_algorithm} }; const auto res = known.find(algo); if(res == known.end()){ throw std::invalid_argument("Unknown jet algorithm " + algo); } return res->second; } EventTreatment to_EventTreatment(std::string const & name){ static const std::map<std::string, EventTreatment> known = { {"reweight", EventTreatment::reweight}, {"keep", EventTreatment::keep}, {"discard", EventTreatment::discard} }; const auto res = known.find(name); if(res == known.end()){ throw std::invalid_argument("Unknown event treatment " + name); } return res->second; } } // namespace anonymous - namespace detail{ - - } // namespace detail - ParticleID to_ParticleID(std::string const & name){ using namespace RHEJ::pid; static const std::map<std::string, ParticleID> known = { {"d", d}, {"down", down}, {"u", u}, {"up", up}, {"s", s}, {"strange", strange}, {"c", c}, {"charm", charm}, {"b", b}, {"bottom", bottom}, {"t", t}, {"top", top}, {"e", e}, {"electron", electron}, {"nu_e", nu_e}, {"electron_neutrino", electron_neutrino}, {"mu", mu}, {"muon", muon}, {"nu_mu", nu_mu}, {"muon_neutrino", muon_neutrino}, {"tau", tau}, {"nu_tau", nu_tau}, {"tau_neutrino", tau_neutrino}, {"d_bar", d_bar}, {"u_bar", u_bar}, {"s_bar", s_bar}, {"c_bar", c_bar}, {"b_bar", b_bar}, {"t_bar", t_bar}, {"e_bar", e_bar}, {"nu_e_bar", nu_e_bar}, {"mu_bar", mu_bar}, {"nu_mu_bar", nu_mu_bar}, {"tau_bar", tau_bar}, {"nu_tau_bar", nu_tau_bar}, {"gluon", gluon}, {"g", g}, {"photon", photon}, {"gamma", gamma}, {"Z", Z}, {"Wp", Wp}, {"Wm", Wm}, {"W+", Wp}, {"W-", Wm}, {"h", h}, {"H", h}, {"Higgs", Higgs}, {"higgs", higgs}, {"p", p}, {"proton", proton}, {"p_bar", p_bar} }; const auto res = known.find(name); if(res == known.end()){ throw std::invalid_argument("Unknown particle " + name); } return res->second; } namespace detail{ void set_from_yaml(fastjet::JetAlgorithm & setting, YAML::Node const & yaml){ setting = to_JetAlgorithm(yaml.as<std::string>()); } void set_from_yaml(EventTreatment & setting, YAML::Node const & yaml){ setting = to_EventTreatment(yaml.as<std::string>()); } void set_from_yaml(ParticleID & setting, YAML::Node const & yaml){ setting = to_ParticleID(yaml.as<std::string>()); } } // namespace detail JetParameters get_jet_parameters( YAML::Node const & node, std::string const & entry ){ assert(node); JetParameters result; fastjet::JetAlgorithm jet_algo = fastjet::antikt_algorithm; double R; set_from_yaml_if_defined(jet_algo, node, entry, "algorithm"); set_from_yaml(R, node, entry, "R"); result.def = fastjet::JetDefinition{jet_algo, R}; set_from_yaml(result.min_pt, node, entry, "min pt"); return result; } HiggsCouplingSettings get_Higgs_coupling( YAML::Node const & node, std::string const & entry ){ assert(node); static constexpr double mt_max = 2e4; #ifndef RHEJ_BUILD_WITH_QCDLOOP if(node[entry]){ throw std::invalid_argument{ "Higgs coupling settings require building Reversed HEJ " "with QCDloop support" }; } #endif HiggsCouplingSettings settings; set_from_yaml_if_defined(settings.mt, node, entry, "mt"); set_from_yaml_if_defined(settings.mb, node, entry, "mb"); set_from_yaml_if_defined(settings.include_bottom, node, entry, "include bottom"); set_from_yaml_if_defined(settings.use_impact_factors, node, entry, "use impact factors"); if(settings.use_impact_factors){ if(settings.mt != std::numeric_limits<double>::infinity()){ throw std::invalid_argument{ "Conflicting settings: " "impact factors may only be used in the infinite top mass limit" }; } } else{ // huge values of the top mass are numerically unstable settings.mt = std::min(settings.mt, mt_max); } return settings; } FileFormat to_FileFormat(std::string const & name){ static const std::map<std::string, FileFormat> known = { {"Les Houches", FileFormat::Les_Houches}, {"HepMC", FileFormat::HepMC} }; const auto res = known.find(name); if(res == known.end()){ throw std::invalid_argument("Unknown file format " + name); } return res->second; } std::string extract_suffix(std::string const & filename){ size_t separator = filename.rfind('.'); if(separator == filename.npos) return {}; return filename.substr(separator + 1); } FileFormat format_from_suffix(std::string const & filename){ const std::string suffix = extract_suffix(filename); if(suffix == "lhe") return FileFormat::Les_Houches; if(suffix == "hepmc") return FileFormat::HepMC; throw std::invalid_argument{ "Can't determine format for output file " + filename }; } void assert_all_options_known( YAML::Node const & conf, YAML::Node const & supported ){ if(!conf.IsMap()) return; if(!supported.IsMap()) throw invalid_type{"must not have sub-entries"}; for(auto const & entry: conf){ const auto name = entry.first.as<std::string>(); if(! supported[name]) throw unknown_option{name}; /* check sub-options, e.g. 'resummation jets: min pt' * we don't check analysis sub-options * those depend on the analysis being used and should be checked there */ if(name != "analysis"){ try{ assert_all_options_known(conf[name], supported[name]); } catch(unknown_option const & ex){ throw unknown_option{name + ": " + ex.what()}; } catch(invalid_type const & ex){ throw invalid_type{name + ": " + ex.what()}; } } } } } // namespace RHEJ namespace YAML { Node convert<RHEJ::OutputFile>::encode(RHEJ::OutputFile const & outfile) { Node node; node[to_string(outfile.format)] = outfile.name; return node; }; bool convert<RHEJ::OutputFile>::decode(Node const & node, RHEJ::OutputFile & out) { switch(node.Type()){ case NodeType::Map: { YAML::const_iterator it = node.begin(); out.format = RHEJ::to_FileFormat(it->first.as<std::string>()); out.name = it->second.as<std::string>(); return true; } case NodeType::Scalar: out.name = node.as<std::string>(); out.format = RHEJ::format_from_suffix(out.name); return true; default: return false; } } } // namespace YAML namespace RHEJ{ namespace detail{ void set_from_yaml(OutputFile & setting, YAML::Node const & yaml){ setting = yaml.as<OutputFile>(); } } namespace{ void update_fixed_order_jet_parameters( JetParameters & fixed_order_jets, YAML::Node const & yaml ){ if(!yaml["fixed order jets"]) return; set_from_yaml_if_defined( fixed_order_jets.min_pt, yaml, "fixed order jets", "min pt" ); fastjet::JetAlgorithm algo = fixed_order_jets.def.jet_algorithm(); set_from_yaml_if_defined(algo, yaml, "fixed order jets", "algorithm"); double R = fixed_order_jets.def.R(); set_from_yaml_if_defined(R, yaml, "fixed order jets", "R"); fixed_order_jets.def = fastjet::JetDefinition{algo, R}; } // like std::stod, but throw if not the whole string can be converted double to_double(std::string const & str){ std::size_t pos; const double result = std::stod(str, &pos); if(pos < str.size()){ throw std::invalid_argument(str + " is not a valid double value"); } return result; } // simple (as in non-composite) scale functions /** * An example for a simple scale function would be H_T, * H_T/2 is then composite (take H_T and then divide by 2) */ ScaleFunction parse_simple_ScaleFunction(std::string const & scale_fun) { assert( scale_fun.empty() || (!std::isspace(scale_fun.front()) && !std::isspace(scale_fun.back())) ); if(scale_fun == "H_T") return H_T; if(scale_fun == "max jet pperp") return max_jet_pt; if(scale_fun == "jet invariant mass") return jet_invariant_mass; if(scale_fun == "m_j1j2") return m_j1j2; try{ const double scale = to_double(scale_fun); return FixedScale{scale}; } catch(std::invalid_argument const &){} throw std::invalid_argument{"Unknown scale choice: " + scale_fun}; } std::string trim_front(std::string const & str){ const auto new_begin = std::find_if( begin(str), end(str), [](char c){ return ! std::isspace(c); } ); return std::string(new_begin, end(str)); } std::string trim_back(std::string str){ size_t pos = str.size() - 1; // use guaranteed wrap-around behaviour to check whether we have // traversed the whole string for(; pos < str.size() && std::isspace(str[pos]); --pos) {} str.resize(pos + 1); // note that pos + 1 can be 0 return str; } ScaleFunction parse_ScaleFunction( std::string const & scale_fun ){ assert( scale_fun.empty() || (!std::isspace(scale_fun.front()) && !std::isspace(scale_fun.back())) ); const size_t delim = scale_fun.find_first_of("*/"); if(delim == scale_fun.npos){ return parse_simple_ScaleFunction(scale_fun); } const std::string first = trim_back(std::string{scale_fun, 0, delim}); const std::string second = trim_front(std::string{scale_fun, delim+1}); double factor; ScaleFunction fun; if(scale_fun[delim] == '/'){ factor = 1/to_double(second); fun = parse_simple_ScaleFunction(first); } else{ assert(scale_fun[delim] == '*'); try{ factor = to_double(second); fun = parse_simple_ScaleFunction(first); } catch(std::invalid_argument const &){ factor = to_double(first); fun = parse_simple_ScaleFunction(second); } } assert(fun != nullptr); return Product{factor, std::move(fun)}; } EventTreatMap get_event_treatment( YAML::Node const & yaml ){ using namespace event_type; EventTreatMap treat { {no_2_jets, EventTreatment::discard}, {bad_final_state, EventTreatment::discard}, {FKL, EventTreatment::reweight}, {unob, EventTreatment::keep}, {unof, EventTreatment::keep}, {nonFKL, EventTreatment::keep} }; set_from_yaml(treat.at(FKL), yaml, "FKL"); set_from_yaml(treat.at(unob), yaml, "unordered"); treat.at(unof) = treat.at(unob); set_from_yaml(treat.at(nonFKL), yaml, "non-FKL"); if(treat[nonFKL] == EventTreatment::reweight){ throw std::invalid_argument{"Cannot reweight non-FKL events"}; } return treat; } Config to_Config(YAML::Node const & yaml){ try{ assert_all_options_known(yaml, get_supported_options()); } catch(unknown_option const & ex){ throw unknown_option{std::string{"Unknown option '"} + ex.what() + "'"}; } Config config; config.resummation_jets = get_jet_parameters(yaml, "resummation jets"); config.fixed_order_jets = config.resummation_jets; update_fixed_order_jet_parameters(config.fixed_order_jets, yaml); set_from_yaml(config.min_extparton_pt, yaml, "min extparton pt"); config.max_ext_soft_pt_fraction = std::numeric_limits<double>::infinity(); set_from_yaml_if_defined( config.max_ext_soft_pt_fraction, yaml, "max ext soft pt fraction" ); set_from_yaml(config.trials, yaml, "trials"); set_from_yaml(config.log_correction, yaml, "log correction"); set_from_yaml(config.unweight, yaml, "unweight"); config.treat = get_event_treatment(yaml); set_from_yaml_if_defined(config.output, yaml, "event output"); set_from_yaml_if_defined(config.RanLux_init, yaml, "RanLux init"); set_from_yaml_if_defined(config.analysis_parameters, yaml, "analysis"); config.scales = to_ScaleConfig(yaml); config.Higgs_coupling = get_Higgs_coupling(yaml, "Higgs coupling"); return config; } } // namespace anonymous ScaleConfig to_ScaleConfig(YAML::Node const & yaml){ ScaleConfig config; std::vector<std::string> scales; set_from_yaml(scales, yaml, "scales"); config.base.reserve(scales.size()); std::transform( begin(scales), end(scales), std::back_inserter(config.base), parse_ScaleFunction ); set_from_yaml_if_defined(config.factors, yaml, "scale factors"); config.max_ratio = std::numeric_limits<double>::infinity(); set_from_yaml_if_defined(config.max_ratio, yaml, "max scale ratio"); return config; } Config load_config(std::string const & config_file){ try{ return to_Config(YAML::LoadFile(config_file)); } catch(...){ std::cerr << "Error reading " << config_file << ":\n "; throw; } } } // namespace RHEJ