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_;