diff --git a/cmake/compiler.cmake b/cmake/compiler.cmake index ddf323f4e580d591d034b2416b8c7d3369b388b5..c8edefe1ffd3e19acb5f01c08adeb9a4c0e44fbf 100644 --- a/cmake/compiler.cmake +++ b/cmake/compiler.cmake @@ -95,5 +95,7 @@ endif () ## Linker Settings ################################################################################ if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-no_compact_unwind") + # this line would remove the linker warning on OSX, but unfortunately also + # disables try{}catch{} blocks, and is therefore unused + #set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-no_compact_unwind") endif() diff --git a/program/pythia6m.cc b/program/pythia6m.cc index 9dade4cf05804d2435b4462b2b671f34a60de299..e0da8111d63e2f9634df11bb090cfceb322dc9c8 100644 --- a/program/pythia6m.cc +++ b/program/pythia6m.cc @@ -134,7 +134,7 @@ struct part_buffer { }; part_buffer glb_parts; -void conf_generator(const ptree& settings) { +void conf_generator(const configuration& conf) { LOG_INFO("conf", "Configuring generator"); // User control switch MSEL (manual section 9.2, also 8.3.1) @@ -166,9 +166,8 @@ void conf_generator(const ptree& settings) { // * GAM for real photon beam sprintf(glb_gen.PyModel, "RAD"); } -void conf_mc(const ptree& settings) { +void conf_mc(const configuration& conf) { LOG_INFO("conf", "Configuring MC"); - configuration conf{settings, "mc"}; glb_mc.RunNo = conf.get<int>("run"); glb_mc.NEvents = conf.get<int>("events"); @@ -262,9 +261,8 @@ void init_gmc() { throw exception("Failed to initialized GMC/PYTHIA"); } } -void conf_output(const ptree& settings, const std::string& output) { +void conf_output(const configuration& conf, const std::string& output) { LOG_INFO("conf", "Configuring output"); - configuration conf{settings, "output"}; if (conf.get<bool>("root")) { glb_out.root = new TFile{(output + ".root").c_str(), "recreate"}; @@ -405,12 +403,12 @@ void events() { std::to_string(glb_pyPars.pari[0])); } -int run_pythia(const ptree& settings, const std::string& output) { +int run_pythia(const configuration& conf, const std::string& output) { LOG_INFO("main", "PYTHIA6M"); // configuration - conf_generator(settings); - conf_mc(settings); - conf_output(settings, output); + conf_generator(conf); + conf_mc(conf); + conf_output(conf, output); // initialization init_random(); init_event(); diff --git a/pythia6m/util/configuration.cc b/pythia6m/util/configuration.cc index 85c1c25a5765ed70708c92431a6499be8d3c5308..191ea2ee18a3a6657efea9786fe424afbc8fb29b 100644 --- a/pythia6m/util/configuration.cc +++ b/pythia6m/util/configuration.cc @@ -53,6 +53,15 @@ void configuration::save(ptree& out_conf) const { LOG_INFO(defaults_path_.str(), "Settings saved."); } } +// universal getter +configuration_impl::value_proxy +configuration::value(const string_path& key) const { + return {*this, key}; +} +configuration_impl::value_proxy configuration:: +operator[](const string_path& key) const { + return {*this, key}; +} configuration_path_error configuration::path_error(const string_path& path) const { @@ -66,11 +75,19 @@ configuration::value_error(const string_path& key, const std::string& value) const { return {key, value, settings_path_, defaults_path_}; } +configuration_value_error +configuration::value_error(const string_path& key) const { + return {key, get<std::string>(key), settings_path_, defaults_path_}; +} configuration_translation_error configuration::translation_error(const string_path& key, const std::string& value) const { return {key, value, settings_path_, defaults_path_}; } +configuration_translation_error +configuration::translation_error(const string_path& key) const { + return {key, get<std::string>(key), settings_path_, defaults_path_}; +} } // ns pythia6m // ============================================================================= // exceptions diff --git a/pythia6m/util/configuration.hh b/pythia6m/util/configuration.hh index cf9ea2b45be63cb4b27808e98022a9641e602ee2..c519851c5e4bb04437d09a71defa7282cb49817e 100644 --- a/pythia6m/util/configuration.hh +++ b/pythia6m/util/configuration.hh @@ -1,17 +1,16 @@ #ifndef PYTHIA6M_UTIL_CONFIGURATION_LOADED #define PYTHIA6M_UTIL_CONFIGURATION_LOADED -#include <string> -#include <map> - -#include <boost/property_tree/ptree.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/optional.hpp> #include <boost/property_tree/exceptions.hpp> #include <boost/property_tree/json_parser.hpp> -#include <boost/optional.hpp> -#include <boost/lexical_cast.hpp> - +#include <boost/property_tree/ptree.hpp> +#include <map> #include <pythia6m/util/exception.hh> +#include <pythia6m/util/interval.hh> #include <pythia6m/util/stringify.hh> +#include <string> namespace pythia6m { // necessary type aliases @@ -76,12 +75,19 @@ public: // in the associated settings ptree. // ============================================================================= namespace pythia6m { + +namespace configuration_impl { + class value_proxy; +} + class configuration { public: constexpr static const char* DEFAULTS{"defaults"}; constexpr static const char* TYPE_KEY{"type"}; configuration(const ptree& settings, const string_path& path); + configuration(const configuration& conf, const string_path& path) + : configuration{conf.settings_, path} {} // load the settings from a given ptree void load(const ptree& in_conf); @@ -93,6 +99,17 @@ public: // get the type info std::string type() const { return get<std::string>(TYPE_KEY); } + // add a value to the configuration + template <class T> void set(const string_path& key, const T& value) { + settings_.put(key, value); + } + + // Universal getter + // Use these instead of the get<T>() member functions below for more readible + // code + configuration_impl::value_proxy value(const string_path& key) const; + configuration_impl::value_proxy operator[](const string_path& key) const; + // Three pairs of functions to get a setting by its key. // // In each pair, the translation_map version will lookup the @@ -141,27 +158,29 @@ public: template <class T> T get_bitpattern(const string_path& key) const; template <class T> T get_bitpattern(const string_path& key, const translation_map<T>& tr) const; - // special version to get a std::pair from a "range" vector + // special version to get an interval from a "range" vector // 1. optional versions template <class T> - optional<std::pair<T, T>> get_optional_range(const string_path& key) const; + optional<interval<T>> get_optional_range(const string_path& key) const; template <class T> - optional<std::pair<T, T>> - get_optional_range(const string_path& key, - const translation_map<T>& tr) const; + optional<interval<T>> get_optional_range(const string_path& key, + const translation_map<T>& tr) const; // 2. throwing versions - template <class T> std::pair<T, T> get_range(const string_path& key) const; + template <class T> interval<T> get_range(const string_path& key) const; template <class T> - std::pair<T, T> get_range(const string_path& key, - const translation_map<T>& tr) const; + interval<T> get_range(const string_path& key, + const translation_map<T>& tr) const; // Helper functions to construct exceptions configuration_path_error path_error(const string_path& path) const; configuration_key_error key_error(const string_path& key) const; configuration_value_error value_error(const string_path& key, const std::string& value) const; + configuration_value_error value_error(const string_path& key) const; configuration_translation_error translation_error(const string_path& key, const std::string& value) const; + configuration_translation_error + translation_error(const string_path& key) const; template <class T> configuration_translation_error translation_error(const string_path& key, const std::string& value, @@ -193,8 +212,11 @@ private: namespace pythia6m { class configurable { public: + // ptree-constructor is deprecated, configuration constructor prefered configurable(const ptree& settings, const string_path& path) : conf_{settings, path}, path_{path} {} + configurable(const configuration& conf, const string_path& path) + : conf_{conf, path}, path_{path} {} const string_path& path() const { return path_; } const configuration& conf() const { return conf_; } configuration& conf() { return conf_; } @@ -249,6 +271,29 @@ public: const string_path& defaults_path); }; +// ============================================================================= +// Implementation: configuration value proxies with auto casting +// ============================================================================= +namespace configuration_impl { +class value_proxy { +public: + value_proxy(const configuration& conf, string_path key) + : conf_{conf}, key_{std::move(key)} {} + + template <class T> operator T() const { return conf_.get<T>(key_); } + template <class T> operator std::vector<T>() const { + return conf_.get_vector<T>(key_); + } + template <class T> operator interval<T>() const { + return conf_.get_range<T>(key_); + } + +private: + const configuration& conf_; + const string_path key_; +}; +} + // ============================================================================= // Implementation: configuration getters // ============================================================================= @@ -397,9 +442,9 @@ T configuration::get_bitpattern(const string_path& key, } return *s; } -// and "range" (pair) version +// and "range" (interval) version template <class T> -optional<std::pair<T, T>> +optional<interval<T>> configuration::get_optional_range(const string_path& key) const { auto range = get_optional_vector<T>(key); if (range) { @@ -411,7 +456,7 @@ configuration::get_optional_range(const string_path& key) const { return {}; } template <class T> -optional<std::pair<T, T>> +optional<interval<T>> configuration::get_optional_range(const string_path& key, const translation_map<T>& tr) const { auto range = get_optional_vector(key, tr); @@ -424,7 +469,7 @@ configuration::get_optional_range(const string_path& key, return {}; } template <class T> -std::pair<T, T> configuration::get_range(const string_path& key) const { +interval<T> configuration::get_range(const string_path& key) const { auto range = get_optional_range<T>(key); if (!range) { throw key_error(key); @@ -432,8 +477,8 @@ std::pair<T, T> configuration::get_range(const string_path& key) const { return *range; } template <class T> -std::pair<T, T> configuration::get_range(const string_path& key, - const translation_map<T>& tr) const { +interval<T> configuration::get_range(const string_path& key, + const translation_map<T>& tr) const { auto range = get_optional_range(key, tr); if (!range) { throw key_error(key); diff --git a/pythia6m/util/framework.cc b/pythia6m/util/framework.cc index b8428f7439d601fdd3d1644222c6214c29ce3aec..057b73c00094438c518d8eab5b1a27f6d66b1933 100644 --- a/pythia6m/util/framework.cc +++ b/pythia6m/util/framework.cc @@ -31,28 +31,52 @@ namespace pythia6m { // and provide for error handling // ============================================================================= framework::framework(int argc, char* argv[], - pythia6m_function_type pythia6m_function) - : pythia6m_function_{pythia6m_function} { - try { - LOG_INFO("pythia6m", "Starting pythia6m framework"); + pythia6m_function_type pythia6m_function) try // suppress ROOT signal handler - root_suppress_signals(); + : dummy_{root_suppress_signals()} // parse the command line - args_ = parse_arguments(argc, argv); + , args_{parse_arguments(argc, argv)} // configuration - settings_ = get_settings(); - LOG_INFO("pythia6m", "Configuration file: " + args_["conf"].as<std::string>()); - // run and events - int run = args_["run"].as<int>(); - int events = args_["events"].as<int>(); - settings_.put("mc.run", run); - settings_.put("mc.events", events); - // output root - output_ = args_["out"].as<std::string>(); - char suffix[1024]; - sprintf(suffix, ".run%06i-%i", run, events); - output_ += suffix; - LOG_INFO("pythia6m", "Output files will be written to: " + output_ + ".*"); + , conf_{get_settings(), "mc"} + // framework function + ,pythia6m_function_{pythia6m_function} { + // talk to the user + LOG_INFO("pythia6m", "Starting pythia6m framework"); + LOG_INFO("pythia6m", "Configuration file: " + args_["conf"].as<std::string>()); + + // get our run info and number of events to be generated + const int run = get_option<int>("run"); + const int events = get_option<int>("events"); + + // output file name + + // path + output_ = args_["out"].as<std::string>(); + if (output_.back() != '/') { + output_ += '/'; + } + + // what MC program are we running? + output_ += conf_.get<std::string>("type"); + + // add optional tag + auto tag = conf_.get_optional<std::string>("tag"); + if (tag && tag->size()) { + output_ += "." + *tag; + } + // add the run info and number of generated events + char info[1024]; + sprintf(info, ".run%05i-%i", run, events); + output_ += info; + + // communicate file name to user + LOG_INFO("pythia6m", "Output files will be written to: " + output_ + ".*"); + + // write the a copy of the configuration file to the output + ptree settings; + conf_.save(settings); + write_json(output_ + ".json", settings); + } catch (const framework_help& h) { std::cerr << h.what() << std::endl; exit(0); @@ -71,11 +95,11 @@ framework::framework(int argc, char* argv[], LOG_CRITICAL("std::exception", "Please contact developer for support."); throw pythia6m::exception("Unhandled standard exception", "std::exception"); } -} + int framework::run() const { try{ LOG_INFO("pythia6m", "Starting event generator..."); - int ret = pythia6m_function_(settings_, output_); + int ret = pythia6m_function_(conf_, output_); LOG_INFO("pythia6m", "Finished."); return ret; } catch (const pythia6m::exception& e) { @@ -110,10 +134,9 @@ po::variables_map framework::parse_arguments(int argc, char* argv[]) const { po::options_description opts_visible{"Allowed options"}; opts_visible.add_options()("help,h", "Produce help message")( "conf,c", po::value<std::string>()->required()->notifier(file_exists), - "Configuration JSON file")("run,r", po::value<int>()->required(), + "Configuration JSON file")("run,r", po::value<int>(), "Run number (also the random seed)")( - "events,e", po::value<int>()->required(), - "Number of events to generate")( + "events,e", po::value<int>(), "Number of events to generate")( "verb,v", po::value<unsigned>()->default_value( static_cast<unsigned>(log_level::INFO)), "Verbosity level (0 -> 7; 0: silent, 4: default, 5: debug)")( @@ -164,7 +187,7 @@ ptree framework::get_settings() const { // Suppress the ROOT signal handlers, as they can cause undefined behavior and // interfere with debugging // ============================================================================= -void framework::root_suppress_signals() const { +int framework::root_suppress_signals() const { gSystem->ResetSignal(kSigChild); gSystem->ResetSignal(kSigBus); gSystem->ResetSignal(kSigSegmentationViolation); @@ -175,6 +198,7 @@ void framework::root_suppress_signals() const { gSystem->ResetSignal(kSigUrgent); gSystem->ResetSignal(kSigFloatingException); gSystem->ResetSignal(kSigWindowChanged); + return 0; } } // ns pythia6m diff --git a/pythia6m/util/framework.hh b/pythia6m/util/framework.hh index b04d87e8771640bcadc3eb2161e33f0ed0362426..a832b23a7b91aca7904927f71fe0f47a41a40037 100644 --- a/pythia6m/util/framework.hh +++ b/pythia6m/util/framework.hh @@ -23,7 +23,7 @@ class framework_parse_error; // If you want to use the framework, call MAKE_PYTHIA6M_FRAMEWORK(function) at // the end of your main source file. "function" is the analysis function you // want to call. The analysis function should take the following inputs: -// * settings: ptree made from the input configuration file +// * conf: configuration object made from the input configuration file // * output: the base name for the output files. // Note: // * the framework takes care of all fancy exception handling @@ -43,7 +43,7 @@ namespace pythia6m { class framework { public: using pythia6m_function_type = - std::function<int(const ptree& settings, const std::string& output)>; + std::function<int(const configuration& conf, const std::string& output)>; // setup the analysis framework framework(int argc, char* argv[], pythia6m_function_type pythia6m_function); @@ -53,13 +53,23 @@ public: private: boost::program_options::variables_map parse_arguments(int argc, char* argv[]) const; + + // get our settings ptree used to initialize the configuration ptree get_settings() const; - void root_suppress_signals() const; - pythia6m_function_type pythia6m_function_; + // suppress the ROOT signal handler + int root_suppress_signals() const; + + // get an option from the command line, use the configuration file as fallback + template <class T> T get_option(const std::string& key); + + int dummy_; // dummy used to ensure root_suppress_signals is run before + // everything else boost::program_options::variables_map args_; + configuration conf_; std::string output_; - ptree settings_; + pythia6m_function_type pythia6m_function_; + }; } // ns pythia6m @@ -93,4 +103,27 @@ public: }; } // ns pythia6m + +// ============================================================================= +// Implementation: framework +// ============================================================================= +namespace pythia6m { + // get an option from the command line, use the configuration file as fallback +template <class T> T framework::get_option(const std::string& key) { + T val = 0; + auto val_opt = conf_.get_optional<T>(key); + if (args_.count(key)) { + val = args_[key].as<T>(); + conf_.set(key, val); + } else if (val_opt) { + val = *val_opt; + } else { + throw framework_error( + "Ensure that '" + key + + "' is set on the command line or in the configuration file"); + } + return val; +} +} // ns pythia6m + #endif diff --git a/pythia6m/util/interval.hh b/pythia6m/util/interval.hh new file mode 100644 index 0000000000000000000000000000000000000000..4b306628329df2f1fdccfa2ab12329fb4d7b78e9 --- /dev/null +++ b/pythia6m/util/interval.hh @@ -0,0 +1,31 @@ +#ifndef PYTHIA6M_CORE_INTERVAL_LOADED +#define PYTHIA6M_CORE_INTERVAL_LOADED + +// ============================================================================= +// A simple interval for readible ranges such as cut parameters. +// ============================================================================= +namespace pythia6m { +template <class T> struct interval { + using value_type = T; + value_type min; + value_type max; + constexpr interval() = default; + constexpr interval(const value_type min, const value_type max) + : min{min}, max{max} {} + constexpr interval(const std::pair<value_type, value_type>& rhs) + : min{rhs.first}, max{rhs.second} {} + constexpr bool includes(const value_type value) const { + return value > min && value < max; + } + constexpr bool excludes(const value_type value) const { + return !includes(value); + } + constexpr T width() const { return max - min; } + // Implicitly conversion to std::pair<T,T> when needed. + constexpr operator std::pair<value_type, value_type>() const { + return std::make_pair(min, max); + } +}; +} // ns pythia6m + +#endif diff --git a/pythia6m/util/logger.cc b/pythia6m/util/logger.cc index 3eac63b0cdc18a84875068b203e4996786977e2c..4ceaa52998abaa4449cf6e94aea6028c3046d89a 100644 --- a/pythia6m/util/logger.cc +++ b/pythia6m/util/logger.cc @@ -5,7 +5,7 @@ namespace pythia6m { // Implementation: log_handler // ============================================================================= log_handler::log_handler(const log_level level, std::ostream& sink) - : level_{level}, sink_(sink) {} + : level_{level}, sink_(&sink) {} void log_handler::set_level(const log_level level) { lock_type lock{mutex_}; diff --git a/pythia6m/util/logger.hh b/pythia6m/util/logger.hh index 00f4e5f01ee8f7ba041d2b9a0c01c7fe63cb2697..a05d4887a3c13e30bc61524ce20bccd30969b7f6 100644 --- a/pythia6m/util/logger.hh +++ b/pythia6m/util/logger.hh @@ -104,6 +104,7 @@ public: void set_level(const log_level level); void set_level(unsigned ulevel); + void set_output(std::ostream& sink) { sink_ = &sink; } void operator()(const log_level mlevel, const std::string& mtitle, const std::string& mtext) { @@ -112,14 +113,14 @@ public: return; time_t rt; time(&rt); - sink_ << "[" << rt << ", " << mtitle << ", " - << LOG_LEVEL_NAMES[static_cast<unsigned>(mlevel)] << "] " << mtext - << std::endl; + (*sink_) << "[" << rt << ", " << mtitle << ", " + << LOG_LEVEL_NAMES[static_cast<unsigned>(mlevel)] << "] " << mtext + << std::endl; } private: log_level level_; - std::ostream& sink_; + std::ostream* sink_; mutable mutex_type mutex_; }; } // ns pythia6m diff --git a/pythia6m/util/progress_meter.hh b/pythia6m/util/progress_meter.hh index a87a5fb116c8450430c79798b89e1777c25a47e7..f8b9e8917377f7e668ffbfc1605226f0464af282 100644 --- a/pythia6m/util/progress_meter.hh +++ b/pythia6m/util/progress_meter.hh @@ -11,11 +11,13 @@ namespace pythia6m { class progress_meter { public: - constexpr static const size_t PRECISION = 1000.; // 0.1 percent + constexpr static const size_t PRECISION = 10000.; // 0.01 percent explicit progress_meter(size_t max, size_t start_index = 0, size_t precision = PRECISION) - : max_{max}, index_{start_index}, precision_{precision} { - std::cout << "\nProcessing " << max_ << " events..." << std::endl; + : max_{max} + , index_{start_index} + , precision_{precision > max ? max : precision} { + std::cerr << "\nProcessing " << max_ << " events..." << std::endl; update(); } void update(size_t i = 1) { @@ -27,12 +29,12 @@ public: if (!index_ || !(index_ % (max_ / precision_))) { double cnt = index_ * precision_ / max_; cnt /= (precision_ / 100.); - char msg[10]; + char msg[15]; sprintf(msg, " %3.2f%%\r", cnt); - std::cout << msg << std::flush; + std::cerr << msg << std::flush; } } - ~progress_meter() { std::cout << " Done!" << std::endl; } + ~progress_meter() { std::cerr << " Done!" << std::endl; } private: const size_t max_;