diff --git a/configure.ac b/configure.ac
--- a/configure.ac
+++ b/configure.ac
@@ -1,315 +1,315 @@
 ## Process this file with autoconf to produce a configure script.
 
 AC_PREREQ(2.59)
 AC_INIT([Rivet],[2.6.0-pre],[rivet@projects.hepforge.org],[Rivet])
 
 ## Check and block installation into the src/build dir
 if test "$prefix" = "$PWD"; then
   AC_MSG_ERROR([Installation into the build directory is not supported: use a different --prefix argument])
 fi
 ## Force default prefix to have a path value rather than NONE
 if test "$prefix" = "NONE"; then
    prefix=/usr/local
 fi
 
 AC_CONFIG_SRCDIR([src/Core/Analysis.cc])
 AC_CONFIG_HEADERS([include/Rivet/Config/DummyConfig.hh include/Rivet/Config/RivetConfig.hh include/Rivet/Config/BuildOptions.hh])
 AM_INIT_AUTOMAKE([dist-bzip2 -Wall])
 m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
 m4_ifdef([AM_PROG_AR], [AM_PROG_AR])
 AC_CONFIG_MACRO_DIR([m4])
 AC_SUBST(LT_OBJDIR)
 
 ## Package-specific #defines
 AC_DEFINE_UNQUOTED(RIVET_VERSION, "$PACKAGE_VERSION", "Rivet version string")
 AC_DEFINE_UNQUOTED(RIVET_NAME, "$PACKAGE_NAME", "Rivet name string")
 AC_DEFINE_UNQUOTED(RIVET_STRING, "$PACKAGE_STRING", "Rivet name and version string")
 AC_DEFINE_UNQUOTED(RIVET_TARNAME, "$PACKAGE_TARNAME", "Rivet short name string")
 AC_DEFINE_UNQUOTED(RIVET_BUGREPORT, "$PACKAGE_BUGREPORT", "Rivet contact email address")
 
 ## OS X
 AC_CEDAR_OSX
 
 ## Work out the LCG platform tag
 AC_LCG_TAG
 
 ## Set default compiler flags
 if test "x$CXXFLAGS" == "x"; then CXXFLAGS="-O2"; fi
 
 ## Compiler setup
 AC_LANG(C++)
 AC_PROG_CXX
 AX_CXX_COMPILE_STDCXX([11], [noext], [mandatory])
 
 ## Store and propagate the compiler identity and flags
 RIVETCXX="$CXX"
 AC_SUBST(RIVETCXX)
 RIVETCXXFLAGS="$CXXFLAGS"
 AC_SUBST(RIVETCXXFLAGS)
 
 ## Checks for programs.
 AC_PROG_INSTALL
 AC_PROG_LN_S
 AC_DISABLE_STATIC
 AC_LIBTOOL_DLOPEN
 AC_PROG_LIBTOOL
-
+AX_EXECINFO
 
 ## YODA histogramming library
 AC_CEDAR_LIBRARYANDHEADERS([YODA], , , [AC_MSG_ERROR([YODA is required])])
 YODABINPATH=$YODALIBPATH/../bin
 AC_SUBST(YODABINPATH)
 AC_PATH_PROG(YODACONFIG, yoda-config, [], [$YODALIBPATH/../bin:$PATH])
 YODA_PYTHONPATH=""
 if test -f "$YODACONFIG"; then
   AC_MSG_CHECKING([YODA version using yoda-config])
   YODA_VERSION=`$YODACONFIG --version`
   AC_MSG_RESULT([$YODA_VERSION])
   YODA_VERSION1=[`echo $YODA_VERSION | cut -d. -f1 | sed -e 's/\([0-9]*\).*/\1/g'`]
   YODA_VERSION2=[`echo $YODA_VERSION | cut -d. -f2 | sed -e 's/\([0-9]*\).*/\1/g'`]
   YODA_VERSION3=[`echo $YODA_VERSION | cut -d. -f3 | sed -e 's/\([0-9]*\).*/\1/g'`]
   let YODA_VERSION_INT=YODA_VERSION1*10000+YODA_VERSION2*100+YODA_VERSION3
   if test $YODA_VERSION_INT -lt 10500; then
     AC_MSG_ERROR([YODA version isn't sufficient: at least version 1.5.0 required])
   fi
   AC_MSG_CHECKING([YODA Python path using yoda-config])
   YODA_PYTHONPATH=`$YODACONFIG --pythonpath`
   AC_MSG_RESULT([$YODA_PYTHONPATH])
 fi
 AC_SUBST(YODA_PYTHONPATH)
 
 
 ## HepMC event record library
 AC_CEDAR_LIBRARYANDHEADERS([HepMC], , , [AC_MSG_ERROR([HepMC is required])])
 oldCPPFLAGS=$CPPFLAGS
 CPPFLAGS="$CPPFLAGS -I$HEPMCINCPATH"
 if test -e "$HEPMCINCPATH/HepMC/HepMCDefs.h"; then
   AC_LANG_CONFTEST([AC_LANG_SOURCE([#include <iostream>
 #include "HepMC/HepMCDefs.h"
 int main() { std::cout << HEPMC_VERSION << std::endl; return 0; }])])
 else
   AC_LANG_CONFTEST([AC_LANG_SOURCE([#include <iostream>
 #include "HepMC/defs.h"
 int main() { std::cout << VERSION << std::endl; return 0; }])])
 fi
 if test -f conftest.cc; then
   $CXX $CPPFLAGS conftest.cc -o conftest 2>&1 1>&5
 elif test -f conftest.C; then
   $CXX $CPPFLAGS conftest.C -o conftest 2>&1 1>&5
 else
   $CXX $CPPFLAGS conftest.cpp -o conftest 2>&1 1>&5
 fi
 hepmc_version=`./conftest`
 if test x$hepmc_version != x; then
   let hepmc_major=`echo "$hepmc_version" | cut -d. -f1`
   let hepmc_minor=`echo "$hepmc_version" | cut -d. -f2`
 fi
 rm -f conftest conftest.cpp conftest.cc conftest.C
 HEPMC_VERSION=$hepmc_major$hepmc_minor
 AC_MSG_NOTICE([HepMC version is $hepmc_version -> $HEPMC_VERSION])
 AC_SUBST(HEPMC_VERSION)
 CPPFLAGS=$oldCPPFLAGS
 
 
 ## FastJet clustering library
 AC_CEDAR_LIBRARYANDHEADERS([fastjet], , , [AC_MSG_ERROR([FastJet is required])])
 AC_PATH_PROG(FJCONFIG, fastjet-config, [], $FASTJETPATH/bin:$PATH)
 if test -f "$FJCONFIG"; then
   AC_MSG_CHECKING([FastJet version using fastjet-config])
   fjversion=`$FJCONFIG --version`
   AC_MSG_RESULT([$fjversion])
   fjmajor=$(echo $fjversion | cut -f1 -d.)
   fjminor=$(echo $fjversion | cut -f2 -d.)
   fjmicro=$(echo $fjversion | cut -f3 -d.)
   if test "$fjmajor" -lt 3; then
     AC_MSG_ERROR([FastJet version 3.0.0 or later is required])
   fi
   FASTJETCONFIGLIBADD="$($FJCONFIG --plugins --shared --libs)"
 else
   FASTJETCONFIGLIBADD="-L$FASTJETLIBPATH -l$FASTJETLIBNAME"
   FASTJETCONFIGLIBADD="$FASTJETCONFIGLIBADD -lSISConePlugin -lsiscone -lsiscone_spherical"
   FASTJETCONFIGLIBADD="$FASTJETCONFIGLIBADD -lCDFConesPlugin -lD0RunIIConePlugin -lNestedDefsPlugin"
   FASTJETCONFIGLIBADD="$FASTJETCONFIGLIBADD -lTrackJetPlugin -lATLASConePlugin -lCMSIterativeConePlugin"
   FASTJETCONFIGLIBADD="$FASTJETCONFIGLIBADD -lEECambridgePlugin -lJadePlugin"
 fi;
 AC_MSG_NOTICE([FastJet LIBADD = $FASTJETCONFIGLIBADD])
 AC_SUBST(FASTJETCONFIGLIBADD)
 # Check for FastJet headers that require the --enable-all(cxx)plugins option
 FASTJET_ERRMSG="Required FastJet plugin headers were not found: did you build FastJet with the --enable-allcxxplugins option?"
 oldCPPFLAGS=$CPPFLAGS
 CPPFLAGS="$CPPFLAGS -I$FASTJETINCPATH"
 AC_CHECK_HEADER([fastjet/D0RunIIConePlugin.hh], [], [AC_MSG_ERROR([$FASTJET_ERRMSG])])
 AC_CHECK_HEADER([fastjet/TrackJetPlugin.hh], [], [AC_MSG_ERROR([$FASTJET_ERRMSG])])
 CPPFLAGS=$oldCPPFLAGS
 
 
 # ## GNU Scientific Library
 # AC_SEARCH_GSL
 # AC_CEDAR_HEADERS([gsl], , , [AC_MSG_ERROR([GSL (GNU Scientific Library) is required])])
 # oldCPPFLAGS=$CPPFLAGS
 # CPPFLAGS="$CPPFLAGS -I$GSLINCPATH"
 # AC_CHECK_HEADER([gsl/gsl_vector.h], [], [AC_MSG_ERROR([GSL vectors not found.])])
 # CPPFLAGS=$oldCPPFLAGS
 
 
 ## Disable build/install of standard analyses
 AC_ARG_ENABLE([analyses],
   [AC_HELP_STRING(--disable-analyses, [don't try to build or install standard analyses])],
   [], [enable_analyses=yes])
 if test x$enable_analyses != xyes; then
    AC_MSG_WARN([Not building standard Rivet analyses, by request])
 fi
 AM_CONDITIONAL(ENABLE_ANALYSES, [test x$enable_analyses = xyes])
 
 
 ## Build LaTeX docs if possible...
 AC_PATH_PROG(PDFLATEX, pdflatex)
 AM_CONDITIONAL(WITH_PDFLATEX, [test x$PDFLATEX != x])
 ## ... unless told otherwise!
 AC_ARG_ENABLE([pdfmanual],
   [AC_HELP_STRING(--enable-pdfmanual, [build and install the manual])],
   [], [enable_pdfmanual=no])
 if test x$enable_pdfmanual = xyes; then
    AC_MSG_WARN([Building Rivet manual, by request])
 fi
 AM_CONDITIONAL(ENABLE_PDFMANUAL, [test x$enable_pdfmanual = xyes])
 
 ## Build Doxygen documentation if possible
 AC_ARG_ENABLE([doxygen],
   [AC_HELP_STRING(--disable-doxygen, [don't try to make Doxygen documentation])],
   [], [enable_doxygen=yes])
 if test x$enable_doxygen = xyes; then
    AC_PATH_PROG(DOXYGEN, doxygen)
 fi
 AM_CONDITIONAL(WITH_DOXYGEN, [test x$DOXYGEN != x])
 
 ## Build asciidoc docs if possible
 AC_PATH_PROG(ASCIIDOC, asciidoc)
 AM_CONDITIONAL(WITH_ASCIIDOC, [test x$ASCIIDOC != x])
 
 
 ## Python extension
 AC_ARG_ENABLE(pyext, [AC_HELP_STRING(--disable-pyext,
   [don't build Python module (default=build)])],
   [], [enable_pyext=yes])
 ## Basic Python checks
 if test x$enable_pyext == xyes; then
   AX_PYTHON_DEVEL([>= '2.6'])
   AC_SUBST(PYTHON_VERSION)
   RIVET_PYTHONPATH=`$PYTHON -c "import distutils.sysconfig; print distutils.sysconfig.get_python_lib(prefix='$prefix', plat_specific=True);"`
   AC_SUBST(RIVET_PYTHONPATH)
   if test -z "$PYTHON"; then
     AC_MSG_ERROR([Can't build Python extension since python can't be found])
     enable_pyext=no
   fi
   if test -z "$PYTHON_CPPFLAGS"; then
     AC_MSG_ERROR([Can't build Python extension since Python.h header file cannot be found])
     enable_pyext=no
   fi
 fi
 AM_CONDITIONAL(ENABLE_PYEXT, [test x$enable_pyext == xyes])
 
 
 ## Cython checks
 if test x$enable_pyext == xyes; then
   AM_CHECK_CYTHON([0.24.0], [:], [:])
   if test x$CYTHON_FOUND = xyes; then
     AC_MSG_NOTICE([Cython >= 0.24 found: Python extension source can be rebuilt (for developers)])
   fi
 
   AC_CHECK_FILE([pyext/rivet/core.cpp],
                 [],
                 [if test "x$CYTHON_FOUND" != "xyes"; then
                   AC_MSG_ERROR([Cython is required for --enable-pyext, no pre-built core.cpp was found.])
                 fi])
 
   cython_compiler=$CXX
   ## Set extra Python extension build flags (to cope with Cython output code oddities)
   PYEXT_CXXFLAGS="$CXXFLAGS"
   AC_CEDAR_CHECKCXXFLAG([-Wno-unused-but-set-variable], [PYEXT_CXXFLAGS="$PYEXT_CXXFLAGS -Wno-unused-but-set-variable"])
   AC_CEDAR_CHECKCXXFLAG([-Wno-sign-compare], [PYEXT_CXXFLAGS="$PYEXT_CXXFLAGS -Wno-sign-compare"])
   AC_SUBST(PYEXT_CXXFLAGS)
   AC_MSG_NOTICE([All Python build checks successful: 'rivet' Python extension will be built])
 fi
 AM_CONDITIONAL(WITH_CYTHON, [test x$CYTHON_FOUND = xyes])
 
 
 ## Set default build flags
 AM_CPPFLAGS="-I\$(top_srcdir)/include -I\$(top_builddir)/include"
 #AM_CPPFLAGS="$AM_CPPFLAGS -I\$(top_srcdir)/include/eigen3"
 #AM_CPPFLAGS="$AM_CPPFLAGS \$(GSL_CPPFLAGS)"
 dnl AM_CPPFLAGS="$AM_CPPFLAGS \$(BOOST_CPPFLAGS)"
 AM_CPPFLAGS="$AM_CPPFLAGS -I\$(YODAINCPATH)"
 AM_CPPFLAGS="$AM_CPPFLAGS -I\$(HEPMCINCPATH)"
 AM_CPPFLAGS="$AM_CPPFLAGS -I\$(FASTJETINCPATH)"
 AC_CEDAR_CHECKCXXFLAG([-pedantic], [AM_CXXFLAGS="$AM_CXXFLAGS -pedantic"])
 AC_CEDAR_CHECKCXXFLAG([-Wall], [AM_CXXFLAGS="$AM_CXXFLAGS -Wall"])
 AC_CEDAR_CHECKCXXFLAG([-Wno-long-long], [AM_CXXFLAGS="$AM_CXXFLAGS -Wno-long-long"])
 AC_CEDAR_CHECKCXXFLAG([-Wno-format], [AM_CXXFLAGS="$AM_CXXFLAGS -Wno-format"])
 dnl AC_CEDAR_CHECKCXXFLAG([-Wno-unused-variable], [AM_CXXFLAGS="$AM_CXXFLAGS -Wno-unused-variable"])
 AC_CEDAR_CHECKCXXFLAG([-Werror=uninitialized], [AM_CXXFLAGS="$AM_CXXFLAGS -Werror=uninitialized"])
 AC_CEDAR_CHECKCXXFLAG([-Werror=delete-non-virtual-dtor], [AM_CXXFLAGS="$AM_CXXFLAGS -Werror=delete-non-virtual-dtor"])
 
 ## Add OpenMP-enabling flags if possible
 AX_OPENMP([AM_CXXFLAGS="$AM_CXXFLAGS $OPENMP_CXXFLAGS"])
 
 ## Debug flag (default=-DNDEBUG, enabled=-g)
 AC_ARG_ENABLE([debug], [AC_HELP_STRING(--enable-debug,
   [build with debugging symbols  @<:@default=no@:>@])], [], [enable_debug=no])
 if test x$enable_debug == xyes; then
   AM_CXXFLAGS="$AM_CXXFLAGS -g"
 fi
 
 
 ## Extra warnings flag (default=none)
 AC_ARG_ENABLE([extra-warnings], [AC_HELP_STRING(--enable-extra-warnings,
   [build with extra compiler warnings (recommended for developers)  @<:@default=no@:>@])], [], [enable_extra_warnings=no])
 if test x$enable_extra_warnings == xyes; then
    AC_CEDAR_CHECKCXXFLAG([-Wextra], [AM_CXXFLAGS="$AM_CXXFLAGS -Wextra "])
 fi
 
 
 AC_SUBST(AM_CPPFLAGS)
 AC_SUBST(AM_CXXFLAGS)
 
 AC_EMPTY_SUBST
 AC_CONFIG_FILES(Makefile Doxyfile)
 AC_CONFIG_FILES(include/Makefile include/Rivet/Makefile)
 AC_CONFIG_FILES(src/Makefile)
 AC_CONFIG_FILES(src/Core/Makefile src/Core/yamlcpp/Makefile)
 AC_CONFIG_FILES(src/Tools/Makefile)
 AC_CONFIG_FILES(src/Projections/Makefile)
 AC_CONFIG_FILES(src/AnalysisTools/Makefile)
 AC_CONFIG_FILES(analyses/Makefile)
 AC_CONFIG_FILES(test/Makefile)
 AC_CONFIG_FILES(pyext/Makefile pyext/rivet/Makefile pyext/setup.py)
 AC_CONFIG_FILES(data/Makefile data/texmf/Makefile)
 AC_CONFIG_FILES(doc/Makefile)
 AC_CONFIG_FILES(doc/rivetversion.sty doc/diffanas)
 AC_CONFIG_FILES(bin/Makefile bin/rivet-config bin/rivet-buildplugin)
 AC_CONFIG_FILES(rivetenv.sh rivetenv.csh rivet.pc)
 
 AC_OUTPUT
 
 if test x$enable_pyrivet == xyes; then
    cat <<EOF
 
 ************************************************************
 RIVET CONFIGURED!
 
 Now build and install (to the $prefix tree) with e.g.
 make -j2 && make -j2 install
 
 To use Rivet, we recommend reading HepMC files from a file
 or pipe (the latter may be made with mkfifo) using the
 'rivet' executable.
 
 For a more pleasant command line experience, you can include
 the data/rivet-completion file into your .bashrc file,
 or your bash_completion.d directory if you have one.
 
 The rivetenv.*sh files will not be installed, but can help you
 to set up a Rivet runtime environment in future.
 ************************************************************
 EOF
 fi
diff --git a/include/Rivet/Analyses/MC_JetAnalysis.hh b/include/Rivet/Analyses/MC_JetAnalysis.hh
--- a/include/Rivet/Analyses/MC_JetAnalysis.hh
+++ b/include/Rivet/Analyses/MC_JetAnalysis.hh
@@ -1,66 +1,67 @@
 // -*- C++ -*-
 #ifndef RIVET_MC_JetAnalysis_HH
 #define RIVET_MC_JetAnalysis_HH
 
 #include "Rivet/Analysis.hh"
 #include "Rivet/Projections/FinalState.hh"
 
 namespace Rivet {
 
 
   /// @brief Base class providing common functionality for MC jet validation analyses
   /// @todo Could reduce duplication by inheriting this from MC_ParticleAnalysis, with minor tweaking
   class MC_JetAnalysis : public Analysis {
   public:
 
     /// Default constructor.
     MC_JetAnalysis(const string& name,
                    size_t njet,
                    const string& jetpro_name,
                    double jetptcut=20*GeV);
 
 
     /// @name Analysis methods
     //@{
     virtual void init();
     virtual void analyze(const Event& event);
     virtual void finalize();
     //@}
 
 
   protected:
 
     /// The number of jets for which histograms are to be initialised
     size_t _njet;
 
     /// The name of the jet projection to be used for this analysis
     /// (this projection has to be registered by the derived analysis!)
     const std::string _jetpro_name;
 
     /// Jet pT cutoff
     double _jetptcut;
 
     /// @todo Add jet masses and d(rap)
 
     /// @name Histograms
     //@{
     std::vector<Histo1DPtr> _h_pT_jet;
     std::vector<Histo1DPtr> _h_eta_jet;
     std::vector<Histo1DPtr> _h_eta_jet_plus, _h_eta_jet_minus;
     std::vector<Histo1DPtr> _h_rap_jet;
     std::vector<Histo1DPtr> _h_rap_jet_plus, _h_rap_jet_minus;
     std::vector<Histo1DPtr> _h_mass_jet;
+    std::vector<Scatter2DPtr> tmpeta, tmprap;
     std::map<std::pair<size_t, size_t>, Histo1DPtr> _h_deta_jets;
     std::map<std::pair<size_t, size_t>, Histo1DPtr> _h_dphi_jets;
     std::map<std::pair<size_t, size_t>, Histo1DPtr> _h_dR_jets;
     Histo1DPtr _h_jet_multi_exclusive;
     Histo1DPtr _h_jet_multi_inclusive;
     Scatter2DPtr _h_jet_multi_ratio;
     Histo1DPtr _h_jet_HT, _h_mjj_jets;
     //@}
 
   };
 
 }
 
 #endif
diff --git a/include/Rivet/AnalysisHandler.hh b/include/Rivet/AnalysisHandler.hh
--- a/include/Rivet/AnalysisHandler.hh
+++ b/include/Rivet/AnalysisHandler.hh
@@ -1,263 +1,271 @@
 // -*- C++ -*-
 #ifndef RIVET_RivetHandler_HH
 #define RIVET_RivetHandler_HH
 
 #include "Rivet/Config/RivetCommon.hh"
 #include "Rivet/Particle.hh"
 #include "Rivet/AnalysisLoader.hh"
 #include "Rivet/Tools/RivetYODA.hh"
 
 namespace Rivet {
 
 
   // Forward declaration and smart pointer for Analysis
   class Analysis;
   typedef std::shared_ptr<Analysis> AnaHandle;
 
 
   // Needed to make smart pointers compare equivalent in the STL set
   struct CmpAnaHandle {
     bool operator() (const AnaHandle& a, const AnaHandle& b) {
       return a.get() < b.get();
     }
   };
 
 
   /// A class which handles a number of analysis objects to be applied to
   /// generated events. An {@link Analysis}' AnalysisHandler is also responsible
   /// for handling the final writing-out of histograms.
   class AnalysisHandler {
   public:
 
     /// @name Constructors and destructors. */
     //@{
 
     /// Preferred constructor, with optional run name.
     AnalysisHandler(const string& runname="");
 
     /// @brief Destructor
     /// The destructor is not virtual, as this class should not be inherited from.
     ~AnalysisHandler();
 
     //@}
 
 
   private:
 
     /// Get a logger object.
     Log& getLog() const;
 
 
   public:
 
     /// @name Run properties
     //@{
 
     /// Get the name of this run.
     string runName() const;
 
 
     /// Get the number of events seen. Should only really be used by external
     /// steering code or analyses in the finalize phase.
     size_t numEvents() const;
 
     /// Get the sum of the event weights seen - the weighted equivalent of the
     /// number of events. Should only really be used by external steering code
     /// or analyses in the finalize phase.
     double sumOfWeights() const {
         return _eventCounter->sumW();
     }
 
     size_t numWeights() const {
         return _weightNames.size();
     }
 
 
     /// Set the active weight.
     void setActiveWeight(unsigned int iWeight);
 
     /// Is cross-section information required by at least one child analysis?
     bool needCrossSection() const;
 
     /// Set the cross-section for the process being generated.
     AnalysisHandler& setCrossSection(double xs);
 
     /// Get the cross-section known to the handler.
     double crossSection() const {
       return _xs;
     }
 
     /// Whether the handler knows about a cross-section.
     bool hasCrossSection() const;
 
 
     /// Set the beam particles for this run
     AnalysisHandler& setRunBeams(const ParticlePair& beams) {
       _beams = beams;
       MSG_DEBUG("Setting run beams = " << beams << " @ " << sqrtS()/GeV << " GeV");
       return *this;
     }
 
     /// Get the beam particles for this run, usually determined from the first event.
     const ParticlePair& beams() const { return _beams; }
 
     /// Get beam IDs for this run, usually determined from the first event.
     /// @deprecated Use standalone beamIds(ah.beams()), to clean AH interface
     PdgIdPair beamIds() const;
 
     /// Get energy for this run, usually determined from the first event.
     /// @deprecated Use standalone sqrtS(ah.beams()), to clean AH interface
     double sqrtS() const;
 
     /// Setter for _ignoreBeams
     void setIgnoreBeams(bool ignore=true);
 
     //@}
 
 
     /// @name Handle analyses
     //@{
 
     /// Get a list of the currently registered analyses' names.
     std::vector<std::string> analysisNames() const;
 
     /// Get the collection of currently registered analyses.
     const std::set<AnaHandle, CmpAnaHandle>& analyses() const {
       return _analyses;
     }
 
     /// Get a registered analysis by name.
     const AnaHandle analysis(const std::string& analysisname) const;
 
 
     /// Add an analysis to the run list by object
     AnalysisHandler& addAnalysis(Analysis* analysis);
 
     /// @brief Add an analysis to the run list using its name.
     ///
     /// The actual Analysis to be used will be obtained via
     /// AnalysisLoader::getAnalysis(string).  If no matching analysis is found,
     /// no analysis is added (i.e. the null pointer is checked and discarded.
     AnalysisHandler& addAnalysis(const std::string& analysisname);
 
     /// @brief Add analyses to the run list using their names.
     ///
     /// The actual {@link Analysis}' to be used will be obtained via
     /// AnalysisHandler::addAnalysis(string), which in turn uses
     /// AnalysisLoader::getAnalysis(string). If no matching analysis is found
     /// for a given name, no analysis is added, but also no error is thrown.
     AnalysisHandler& addAnalyses(const std::vector<std::string>& analysisnames);
 
 
     /// Remove an analysis from the run list using its name.
     AnalysisHandler& removeAnalysis(const std::string& analysisname);
 
     /// Remove analyses from the run list using their names.
     AnalysisHandler& removeAnalyses(const std::vector<std::string>& analysisnames);
 
     //@}
 
 
     /// @name Main init/execute/finalise
     //@{
 
     /// Initialize a run, with the run beams taken from the example event.
     void init(const GenEvent& event);
 
     /// @brief Analyze the given \a event by reference.
     ///
     /// This function will call the AnalysisBase::analyze() function of all
     /// included analysis objects.
     void analyze(const GenEvent& event);
 
     /// @brief Analyze the given \a event by pointer.
     ///
     /// This function will call the AnalysisBase::analyze() function of all
     /// included analysis objects, after checking the event pointer validity.
     void analyze(const GenEvent* event);
 
     /// Finalize a run. This function calls the AnalysisBase::finalize()
     /// functions of all included analysis objects.
     void finalize();
 
     //@}
 
 
     /// @name Histogram / data object access
     //@{
 
     /// Add a vector of analysis objects to the current state.
     void addData(const std::vector<YODA::AnalysisObjectPtr>& aos);
 
     /// Read analysis plots into the histo collection (via addData) from the named file.
     void readData(const std::string& filename);
 
     /// Get all analyses' plots as a vector of analysis objects.
     std::vector<YODA::AnalysisObjectPtr> getData() const;
     std::vector<reference_wrapper<MultiweightAOPtr> > getRivetAOs() const;
     std::vector<YODA::AnalysisObjectPtr> getYodaAOs() const;
 
     /// Get all analyses' plots as a vector of analysis objects.
     void setWeightNames(const GenEvent& ge); 
 
     /// Do we have named weights?
     bool haveNamedWeights();
 
 
     /// Write all analyses' plots (via getData) to the named file.
     void writeData(const std::string& filename) const;
 
     //@}
 
+    /// Indicate which Rivet stage we're in. 
+    /// At the moment, only INIT is used to enable booking.
+    enum class Stage { OTHER, INIT };
+
+    /// Which stage are we in?
+    Stage stage() const { return _stage; }
 
   private:
+    /// Current handler stage
+    Stage _stage = Stage::OTHER;
 
     /// The collection of Analysis objects to be used.
     set<AnaHandle, CmpAnaHandle> _analyses;
 
     /// @name Run properties
     //@{
 
     /// Weight names
     std::vector<std::string> _weightNames;
     std::vector<std::valarray<double> > _subEventWeights;
     size_t _numWeightTypes; // always == WeightVector.size()
 
     /// Run name
     std::string _runname;
 
     mutable CounterPtr _eventCounter;
 
     /// Cross-section known to AH
     double _xs, _xserr;
 
     /// Beams used by this run.
     ParticlePair _beams;
 
     /// Flag to check if init has been called
     bool _initialised;
 
     /// Flag whether input event beams should be ignored in compatibility check
     bool _ignoreBeams;
 
     /// Current event number
     int _eventNumber;
     //@}
 
 
   private:
 
     /// The assignment operator is private and must never be called.
     /// In fact, it should not even be implemented.
     AnalysisHandler& operator=(const AnalysisHandler&);
 
     /// The copy constructor is private and must never be called.  In
     /// fact, it should not even be implemented.
     AnalysisHandler(const AnalysisHandler&);
 
   };
 
 
 }
 
 #endif
diff --git a/include/Rivet/Tools/RivetYODA.hh b/include/Rivet/Tools/RivetYODA.hh
--- a/include/Rivet/Tools/RivetYODA.hh
+++ b/include/Rivet/Tools/RivetYODA.hh
@@ -1,419 +1,438 @@
 #ifndef RIVET_RIVETYODA_HH
 #define RIVET_RIVETYODA_HH
 
 /// @author Andy Buckley
 /// @date   2009-01-30
 /// @author David Grellscheid
 /// @date   2011-07-18
 /// @author David Grellscheid
 /// @date   2016-09-27
 
 #include "Rivet/Config/RivetCommon.hh"
 
 #include "YODA/AnalysisObject.h"
 #include "YODA/Counter.h"
 #include "YODA/Histo1D.h"
 #include "YODA/Histo2D.h"
 #include "YODA/Profile1D.h"
 #include "YODA/Profile2D.h"
 #include "YODA/Scatter1D.h"
 #include "YODA/Scatter2D.h"
 #include "YODA/Scatter3D.h"
 
 #include <map>
 
 namespace YODA {
     typedef std::shared_ptr<YODA::AnalysisObject> AnalysisObjectPtr;
     // typedef std::shared_ptr<YODA::Scatter1D> Scatter1DPtr;
     // typedef std::shared_ptr<YODA::Scatter2D> Scatter2DPtr;
     // typedef std::shared_ptr<YODA::Scatter3D> Scatter3DPtr;
 }
 
 
 
 namespace Rivet {
 
 
     class AnalysisObjectPtr {
         public:
             virtual ~AnalysisObjectPtr() {}
 
             virtual YODA::AnalysisObject* operator->() = 0;
             virtual YODA::AnalysisObject* operator->() const = 0;
             virtual const YODA::AnalysisObject & operator*() const = 0;
 
             /// @todo 
             /// rename to setActive(Idx)?
             virtual void setActiveWeightIdx(unsigned int iWeight) = 0;
 
+            virtual void blockDestructor(bool) = 0;
+
             bool operator ==(const AnalysisObjectPtr& p) { return (this == &p); }
 
         protected:
             /// @todo do we need this?
             // virtual void reset() = 0;
     };
 
     /// @todo
     /// implement scatter1dptr and scatter2dptr here
     /// these need to be multi-weighted eventually.
     /*
     class Scatter1DPtr : public AnalysisObjectPtr {
         public:
             Scatter1DPtr() : _persistent() { }
 
             Scatter1DPtr(size_t len_of_weightvec, const YODA::Scatter1D& p) {
               for (size_t m = 0; m < len_of_weightvec; ++m)
                 _persistent.push_back(make_shared<YODA::Scatter1D>(p));
             }
 
             bool operator!() const { return !_persistent; }
             operator bool() const { return bool(_persistent); }
 
             YODA::Scatter1D* operator->() { return _persistent.get(); }
 
             YODA::Scatter1D* operator->() const { return _persistent.get(); }
 
             YODA::Scatter1D & operator*() { return *_persistent; }
 
             const YODA::Scatter1D & operator*() const { return *_persistent; }
 
         protected:
             vector<YODA::Scatter1DPtr> _persistent;
     };
 
     class Scatter2DPtr : public AnalysisObjectPtr {
         public:
             Scatter2DPtr(size_t len_of_weightvec, const YODA::Scatter2D& p) {
               for (size_t m = 0; m < len_of_weightvec; ++m)
                 _persistent.push_back(make_shared<YODA::Scatter2D>(p));
             }
 
             Scatter2DPtr() : _persistent() { }
 
             bool operator!() { return !_persistent; }
             operator bool() { return bool(_persistent); }
 
             YODA::Scatter2D* operator->() { return _persistent.get(); }
 
             YODA::Scatter2D* operator->() const { return _persistent.get(); }
 
             YODA::Scatter2D & operator*() { return *_persistent; }
 
             const YODA::Scatter2D & operator*() const { return *_persistent; }
 
         protected:
             vector<YODA::Scatter2DPtr> _persistent;
     };
 
     class Scatter3DPtr : public AnalysisObjectPtr {
         public:
             Scatter3DPtr(size_t len_of_weightvec, const YODA::Scatter3D& p) {
               for (size_t m = 0; m < len_of_weightvec; ++m)
                 _persistent.push_back(make_shared<YODA::Scatter3D>(p));
             }
 
             Scatter3DPtr() : _persistent() { }
 
             bool operator!() { return !_persistent; }
             operator bool() { return bool(_persistent); }
 
             YODA::Scatter3D* operator->() { return _persistent.get(); }
 
             YODA::Scatter3D* operator->() const { return _persistent.get(); }
 
             YODA::Scatter3D & operator*() { return *_persistent; }
 
             const YODA::Scatter3D & operator*() const { return *_persistent; }
 
         protected:
             vector<YODA::Scatter3DPtr> _persistent;
     };
     */
 
 
     class MultiweightAOPtr : public AnalysisObjectPtr {
 
         public:
             virtual void newSubEvent() = 0;
 
             virtual void pushToPersistent(const vector<valarray<double> >& weight) = 0;
 
             virtual YODA::AnalysisObjectPtr activeYODAPtr() const = 0;
     };
 
 
 using Weight = double;
 
 template <class T>
     using Fill = pair<typename T::FillType, Weight>;
 
 template <class T>
     using Fills = multiset<Fill<T>>;
 
 
 // TODO TODO TODO 
 // need to override the old fill method too!
 // otherwise we can't intercept existing fill calls in analysis code
 // TODO TODO TODO
 
 template <class T>
 class TupleWrapper;
 
 template<>
 class TupleWrapper<YODA::Counter> : public YODA::Counter {
 public:
     typedef shared_ptr<TupleWrapper<YODA::Counter>> Ptr;
     TupleWrapper(const YODA::Counter & h) : YODA::Counter(h) {}
     // todo: do we need to deal with users using fractions directly?
     void fill( double weight=1.0, double fraction=1.0 ) {
         fills_.insert( {YODA::Counter::FillType(),weight} );
     }
     void reset() { fills_.clear(); }
     const Fills<YODA::Counter> & fills() const { return fills_; }
 private:
     // x / weight pairs 
     Fills<YODA::Counter> fills_;
 };
 
 template<>
 class TupleWrapper<YODA::Histo1D> : public YODA::Histo1D {
 public:
     typedef shared_ptr<TupleWrapper<YODA::Histo1D>> Ptr;
     TupleWrapper(const YODA::Histo1D & h) : YODA::Histo1D(h) {}
     // todo: do we need to deal with users using fractions directly?
     void fill( double x, double weight=1.0, double fraction=1.0 ) {
         if ( std::isnan(x) ) throw YODA::RangeError("X is NaN");
         fills_.insert( { x , weight } );
     }
     void reset() { fills_.clear(); }
     const Fills<YODA::Histo1D> & fills() const { return fills_; }
 private:
     // x / weight pairs 
     Fills<YODA::Histo1D> fills_;
 };
 
 template<>
 class TupleWrapper<YODA::Profile1D> : public YODA::Profile1D {
 public:
     typedef shared_ptr<TupleWrapper<YODA::Profile1D>> Ptr;
     TupleWrapper(const YODA::Profile1D & h) : YODA::Profile1D(h) {}
     // todo: do we need to deal with users using fractions directly?
     void fill( double x, double y, double weight=1.0, double fraction=1.0 ) {
         if ( std::isnan(x) ) throw YODA::RangeError("X is NaN");
         if ( std::isnan(y) ) throw YODA::RangeError("Y is NaN");
         fills_.insert( { YODA::Profile1D::FillType{x,y}, weight } );
     }
     void reset() { fills_.clear(); }
     const Fills<YODA::Profile1D> & fills() const { return fills_; }
 private:
     // x / weight pairs 
     Fills<YODA::Profile1D> fills_;
 };
 
 
 template<>
 class TupleWrapper<YODA::Histo2D> : public YODA::Histo2D {
 public:
     typedef shared_ptr<TupleWrapper<YODA::Histo2D>> Ptr;
     TupleWrapper(const YODA::Histo2D & h) : YODA::Histo2D(h) {}
     // todo: do we need to deal with users using fractions directly?
     void fill( double x, double y, double weight=1.0, double fraction=1.0 ) {
         if ( std::isnan(x) ) throw YODA::RangeError("X is NaN");
         if ( std::isnan(y) ) throw YODA::RangeError("Y is NaN");
         fills_.insert( { YODA::Histo2D::FillType{x,y}, weight } );
     }
     void reset() { fills_.clear(); }
     const Fills<YODA::Histo2D> & fills() const { return fills_; }
 private:
     // x / weight pairs 
     Fills<YODA::Histo2D> fills_;
 };
 
 template<>
 class TupleWrapper<YODA::Profile2D> : public YODA::Profile2D {
 public:
     typedef shared_ptr<TupleWrapper<YODA::Profile2D>> Ptr;
     TupleWrapper(const YODA::Profile2D & h) : YODA::Profile2D(h) {}
     // todo: do we need to deal with users using fractions directly?
     void fill( double x, double y, double z, double weight=1.0, double fraction=1.0 ) {
         if ( std::isnan(x) ) throw YODA::RangeError("X is NaN");
         if ( std::isnan(y) ) throw YODA::RangeError("Y is NaN");
         if ( std::isnan(z) ) throw YODA::RangeError("Z is NaN");
         fills_.insert( { YODA::Profile2D::FillType{x,y,z}, weight } );
     }
     void reset() { fills_.clear(); }
     const Fills<YODA::Profile2D> & fills() const { return fills_; }
 private:
     // x / weight pairs 
     Fills<YODA::Profile2D> fills_;
 };
 
 template<>
 class TupleWrapper<YODA::Scatter1D> : public YODA::Scatter1D {
 public:
     typedef shared_ptr<TupleWrapper<YODA::Scatter1D>> Ptr;
     TupleWrapper(const YODA::Scatter1D & h) : YODA::Scatter1D(h) {}
 };
 
 template<>
 class TupleWrapper<YODA::Scatter2D> : public YODA::Scatter2D {
 public:
     typedef shared_ptr<TupleWrapper<YODA::Scatter2D>> Ptr;
     TupleWrapper(const YODA::Scatter2D & h) : YODA::Scatter2D(h) {}
 };
 
 template<>
 class TupleWrapper<YODA::Scatter3D> : public YODA::Scatter3D {
 public:
     typedef shared_ptr<TupleWrapper<YODA::Scatter3D>> Ptr;
     TupleWrapper(const YODA::Scatter3D & h) : YODA::Scatter3D(h) {}
 };
 
 
     template <class T>
     class Wrapper : public MultiweightAOPtr {
+        friend class Analysis;
+
         public:
 
         /* @todo
          * some things are not really well-defined here
          * for instance: fill() in the finalize() method and integral() in
          * the analyze() method.
          */
 
         Wrapper() = default;
 
         Wrapper(size_t len_of_weightvec, const T & p);
 
+        ~Wrapper();
+
         typename T::Ptr active() const;
 
         /* @todo this probably need to loop over all? */
         bool operator!() const { return !_active; } // Don't use active() here, assert will catch
 
         operator bool() const { return static_cast<bool>(_active); } // Don't use active() here, assert will catch
 
         T * operator->() { return active().get(); }
         
         T * operator->() const { return active().get(); }
 
         T & operator*() { return *active(); }
 
         const T & operator*() const { return *active(); }
         /* @todo
          * these need to be re-thought out.
 
          void reset() { active()->reset(); }
          */
 
         /* @todo
          * these probably need to loop over all?
          * do we even want to provide equality?
          */
         /* @todo
          * how about no.
         friend bool operator==(Wrapper a, Wrapper b){
             if (a._persistent.size() != b._persistent.size())
                 return false;
 
             for (size_t i = 0; i < a._persistent.size(); i++) {
                 if (a._persistent.at(i) != b._persistent.at(i)) {
                     return false;
                 }
             }
 
             return true;
         }
 
         friend bool operator!=(Wrapper a, Wrapper b){
             return !(a == b);
         }
 
 
         friend bool operator<(Wrapper a, Wrapper b){
             if (a._persistent.size() >= b._persistent.size())
                 return false;
 
             for (size_t i = 0; i < a._persistent.size(); i++) {
                 if (*(a._persistent.at(i)) >= *(b._persistent.at(i))) {
                     return false;
                 }
             }
 
             return true;
         }
         */
 
 
         private:
         void setActiveWeightIdx(unsigned int iWeight) {
             _active = _persistent.at(iWeight);
         }
 
         /* this is for dev only---we shouldn't need this in real runs. */
         void unsetActiveWeight() { _active.reset(); }
 
         void newSubEvent();
 
         virtual YODA::AnalysisObjectPtr activeYODAPtr() const { return _active; }
 
         const vector<typename T::Ptr> & persistent() const { return _persistent; }
 
         /* to be implemented for each type */
         void pushToPersistent(const vector<valarray<double> >& weight);
 
+        /// Set destructor blocking flag.
+        /// Once booked in an analysis, we need to block the destructor 
+        /// from being called until we're done with finalize.
+        /// This gives a clearer indication to the users that they're booking with
+        /// temporary objects.
+        void blockDestructor(bool b) { _blockDestructor = b; }
+
         /* M of these, one for each weight */
         vector<typename T::Ptr> _persistent;
 
         /* N of these, one for each event in evgroup */
         vector<typename TupleWrapper<T>::Ptr> _evgroup;
 
         typename T::Ptr _active;
 
+        /// Destructor blocking flag.
+        /// Once booked in an analysis, we need to block the destructor 
+        /// from being called until we're done with finalize.
+        /// 
+        bool _blockDestructor = false;
+
         // do we need implicit cast?
         // operator typename T::Ptr () {
         //     return _active;
         // }
 
         friend class AnalysisHandler;
     };
 
 
     // every object listed here needs a virtual fill method in YODA,
     // otherwise the Tuple fakery won't work.
 
-    using Histo1DPtr = Wrapper<YODA::Histo1D>;
-    using Histo2DPtr = Wrapper<YODA::Histo2D>;
+    using Histo1DPtr   = Wrapper<YODA::Histo1D>;
+    using Histo2DPtr   = Wrapper<YODA::Histo2D>;
     using Profile1DPtr = Wrapper<YODA::Profile1D>;
     using Profile2DPtr = Wrapper<YODA::Profile2D>;
-    using CounterPtr = Wrapper<YODA::Counter>;
+    using CounterPtr   = Wrapper<YODA::Counter>;
     using Scatter1DPtr = Wrapper<YODA::Scatter1D>;
     using Scatter2DPtr = Wrapper<YODA::Scatter2D>;
     using Scatter3DPtr = Wrapper<YODA::Scatter3D>;
 
     using YODA::Counter;
     using YODA::Histo1D;
     using YODA::HistoBin1D;
     using YODA::Histo2D;
     using YODA::HistoBin2D;
     using YODA::Profile1D;
     using YODA::ProfileBin1D;
     using YODA::Profile2D;
     using YODA::ProfileBin2D;
     using YODA::Scatter1D;
     using YODA::Point1D;
     using YODA::Scatter2D;
     using YODA::Point2D;
     using YODA::Scatter3D;
     using YODA::Point3D;
 
     /// Function to get a map of all the refdata in a paper with the
     /// given @a papername.
     map<string, YODA::AnalysisObjectPtr> getRefData(const string& papername);
 
     /// @todo Also provide a Scatter3D getRefData() version?
 
     /// Get the file system path to the reference file for this paper.
     string getDatafilePath(const string& papername);
 
 }
 
 #endif
diff --git a/m4/ax_execinfo.m4 b/m4/ax_execinfo.m4
new file mode 100644
--- /dev/null
+++ b/m4/ax_execinfo.m4
@@ -0,0 +1,67 @@
+# ===========================================================================
+#       https://www.gnu.org/software/autoconf-archive/ax_execinfo.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_EXECINFO([ACTION-IF-EXECINFO-H-IS-FOUND], [ACTION-IF-EXECINFO-H-IS-NOT-FOUND], [ADDITIONAL-TYPES-LIST])
+#
+# DESCRIPTION
+#
+#   Checks for execinfo.h header and if the len parameter/return type can be
+#   found from a list, also define backtrace_size_t to that type.
+#
+#   By default the list of types to try contains int and size_t, but should
+#   some yet undiscovered system use e.g. unsigned, the 3rd argument can be
+#   used for extensions. I'd like to hear of further suggestions.
+#
+#   Executes ACTION-IF-EXECINFO-H-IS-FOUND when present and the execinfo.h
+#   header is found or ACTION-IF-EXECINFO-H-IS-NOT-FOUND in case the header
+#   seems unavailable.
+#
+#   Also adds -lexecinfo to LIBS on BSD if needed.
+#
+# LICENSE
+#
+#   Copyright (c) 2014 Thomas Jahns <jahns@dkrz.de>
+#
+#   Copying and distribution of this file, with or without modification, are
+#   permitted in any medium without royalty provided the copyright notice
+#   and this notice are preserved. This file is offered as-is, without any
+#   warranty.
+
+#serial 2
+
+AC_DEFUN([AX_EXECINFO],
+  [AC_CHECK_HEADERS([execinfo.h])
+   AS_IF([test x"$ac_cv_header_execinfo_h" = xyes],
+     [AC_CACHE_CHECK([size parameter type for backtrace()],
+	[ax_cv_proto_backtrace_type],
+	[AC_LANG_PUSH([C])
+	 for ax_cv_proto_backtrace_type in size_t int m4_ifnblank([$3],[$3 ])none; do
+	   AS_IF([test "${ax_cv_proto_backtrace_type}" = none],
+	     [ax_cv_proto_backtrace_type= ; break])
+	   AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
+#include <execinfo.h>
+extern
+${ax_cv_proto_backtrace_type} backtrace(void **addrlist, ${ax_cv_proto_backtrace_type} len);
+char **backtrace_symbols(void *const *buffer, ${ax_cv_proto_backtrace_type} size);
+])],
+	   [break])
+	 done
+	 AC_LANG_POP([C])])])
+   AS_IF([test x${ax_cv_proto_backtrace_type} != x],
+     [AC_DEFINE_UNQUOTED([backtrace_size_t], [$ax_cv_proto_backtrace_type],
+        [Defined to return type of backtrace().])])
+   AC_SEARCH_LIBS([backtrace],[execinfo])
+   AS_IF([test x"${ax_cv_proto_backtrace_type}" != x -a x"$ac_cv_header_execinfo_h" = xyes -a x"$ac_cv_search_backtrace" != xno],
+     [AC_DEFINE([HAVE_BACKTRACE],[1],
+        [Defined if backtrace() could be fully identified.])
+     ]m4_ifnblank([$1],[$1
+]),m4_ifnblank([$2],[$2
+]))])
+dnl
+dnl Local Variables:
+dnl mode: autoconf
+dnl End:
+dnl
diff --git a/src/AnalysisTools/MC_JetAnalysis.cc b/src/AnalysisTools/MC_JetAnalysis.cc
--- a/src/AnalysisTools/MC_JetAnalysis.cc
+++ b/src/AnalysisTools/MC_JetAnalysis.cc
@@ -1,190 +1,185 @@
 // -*- C++ -*-
 #include "Rivet/Analyses/MC_JetAnalysis.hh"
 #include "Rivet/Projections/FastJets.hh"
 
 namespace Rivet {
 
   
 
 
   MC_JetAnalysis::MC_JetAnalysis(const string& name,
                                  size_t njet,
                                  const string& jetpro_name,
                                  double jetptcut)
     : Analysis(name), _njet(njet), _jetpro_name(jetpro_name), _jetptcut(jetptcut),
       _h_pT_jet(njet),
       _h_eta_jet(njet), _h_eta_jet_plus(njet), _h_eta_jet_minus(njet),
       _h_rap_jet(njet), _h_rap_jet_plus(njet), _h_rap_jet_minus(njet),
-      _h_mass_jet(njet)
+      _h_mass_jet(njet), tmpeta(njet), tmprap(njet)
   {
     setNeedsCrossSection(true); // legitimate use, since a base class has no .info file!
   }
 
 
 
   // Book histograms
   void MC_JetAnalysis::init() {
     const double sqrts = sqrtS() ? sqrtS() : 14000.*GeV;
 
     for (size_t i = 0; i < _njet; ++i) {
       const string pTname = "jet_pT_" + to_str(i+1);
       const double pTmax = 1.0/(double(i)+2.0) * sqrts/GeV/2.0;
       const int nbins_pT = 100/(i+1);
       if (pTmax > 10) { // Protection aginst logspace exception, needed for LEP
         book(_h_pT_jet[i] ,pTname, logspace(nbins_pT, 10.0, pTmax));
       }
 
       const string massname = "jet_mass_" + to_str(i+1);
       const double mmax = 100.0;
       const int nbins_m = 100/(i+1);
       book(_h_mass_jet[i] ,massname, logspace(nbins_m, 1.0, mmax));
 
       const string etaname = "jet_eta_" + to_str(i+1);
       book(_h_eta_jet[i] ,etaname, i > 1 ? 25 : 50, -5.0, 5.0);
       book(_h_eta_jet_plus[i], "_" + etaname + "_plus", i > 1 ? 15 : 25, 0, 5);
       book(_h_eta_jet_minus[i], "_" + etaname + "_minus", i > 1 ? 15 : 25, 0, 5);
 
       const string rapname = "jet_y_" + to_str(i+1);
       book(_h_rap_jet[i] ,rapname, i>1 ? 25 : 50, -5.0, 5.0);
       book(_h_rap_jet_plus[i], "_" + rapname + "_plus", i > 1 ? 15 : 25, 0, 5);
       book(_h_rap_jet_minus[i], "_" + rapname + "_minus", i > 1 ? 15 : 25, 0, 5);
 
+      book(tmpeta[i], "jet_eta_pmratio_" + to_str(i+1));
+      book(tmprap[i], "jet_y_pmratio_" + to_str(i+1));
+
       for (size_t j = i+1; j < min(size_t(3), _njet); ++j) {
         const std::pair<size_t, size_t> ij = std::make_pair(i, j);
+        const string ijstr = to_str(i+1) + to_str(j+1);
 
-        string detaname = "jets_deta_" + to_str(i+1) + to_str(j+1);
-        Histo1DPtr tmpeta;
-        book(tmpeta, detaname, 25, -5.0, 5.0);
-        _h_deta_jets.insert(make_pair(ij, tmpeta));
+        string detaname = "jets_deta_" + ijstr;
+        book(_h_deta_jets[ij], detaname, 25, -5.0, 5.0);
 
-        string dphiname = "jets_dphi_" + to_str(i+1) + to_str(j+1);
-        Histo1DPtr tmpphi;
-        book(tmpphi, dphiname, 25, 0.0, M_PI);
-        _h_dphi_jets.insert(make_pair(ij, tmpphi));
+        string dphiname = "jets_dphi_" + ijstr;
+        book(_h_dphi_jets[ij], dphiname, 25, 0.0, M_PI);
 
-        string dRname = "jets_dR_" + to_str(i+1) + to_str(j+1);
-        Histo1DPtr tmpR;
-        book(tmpR, dRname, 25, 0.0, 5.0);
-        _h_dR_jets.insert(make_pair(ij, tmpR));
+        string dRname = "jets_dR_" + ijstr;
+        book(_h_dR_jets[ij], dRname, 25, 0.0, 5.0);
       }
     }
 
     book(_h_jet_multi_exclusive ,"jet_multi_exclusive", _njet+3, -0.5, _njet+3-0.5);
     book(_h_jet_multi_inclusive ,"jet_multi_inclusive", _njet+3, -0.5, _njet+3-0.5);
     book(_h_jet_multi_ratio, "jet_multi_ratio");
     book(_h_jet_HT ,"jet_HT", logspace(50, _jetptcut, sqrts/GeV/2.0));
     book(_h_mjj_jets, "jets_mjj", 40, 0.0, sqrts/GeV/2.0);
   }
 
 
   // Do the analysis
   void MC_JetAnalysis::analyze(const Event & e) {
 
     const Jets& jets = apply<FastJets>(e, _jetpro_name).jetsByPt(_jetptcut);
 
     for (size_t i = 0; i < _njet; ++i) {
       if (jets.size() < i+1) continue;
       _h_pT_jet[i]->fill(jets[i].pT()/GeV);
       // Check for numerical precision issues with jet masses
       double m2_i = jets[i].mass2();
       if (m2_i < 0) {
         if (m2_i < -1e-4) {
           MSG_WARNING("Jet mass2 is negative: " << m2_i << " GeV^2. "
                       << "Truncating to 0.0, assuming numerical precision is to blame.");
         }
         m2_i = 0.0;
       }
 
       // Jet mass
       _h_mass_jet[i]->fill(sqrt(m2_i)/GeV);
 
       // Jet eta
       const double eta_i = jets[i].eta();
       _h_eta_jet[i]->fill(eta_i);
       (eta_i > 0.0 ? _h_eta_jet_plus : _h_eta_jet_minus)[i]->fill(fabs(eta_i));
 
       // Jet rapidity
       const double rap_i = jets[i].rapidity();
       _h_rap_jet[i]->fill(rap_i);
       (rap_i > 0.0 ? _h_rap_jet_plus : _h_rap_jet_minus)[i]->fill(fabs(rap_i));
 
       // Inter-jet properties
       for (size_t j = i+1; j < min(size_t(3),_njet); ++j) {
         if (jets.size() < j+1) continue;
         std::pair<size_t, size_t> ij = std::make_pair(i, j);
         double deta = jets[i].eta()-jets[j].eta();
         double dphi = deltaPhi(jets[i].momentum(),jets[j].momentum());
         double dR = deltaR(jets[i].momentum(), jets[j].momentum());
         _h_deta_jets[ij]->fill(deta);
         _h_dphi_jets[ij]->fill(dphi);
         _h_dR_jets[ij]->fill(dR);
       }
     }
 
     // Multiplicities
     _h_jet_multi_exclusive->fill(jets.size());
     for (size_t i = 0; i < _njet+2; ++i) {
       if (jets.size() >= i) {
         _h_jet_multi_inclusive->fill(i);
       }
     }
 
     // HT
     double HT = 0.0;
     foreach (const Jet& jet, jets) {
       HT += jet.pT();
     }
     _h_jet_HT->fill(HT);
 
     // mjj
     if (jets.size() > 1) {
       double mjj = (jets[0].momentum() + jets[1].momentum()).mass();
       _h_mjj_jets->fill(mjj);
     }
   }
 
 
   // Finalize
   void MC_JetAnalysis::finalize() {
     const double scaling = crossSection()/sumOfWeights();
     for (size_t i = 0; i < _njet; ++i) {
       scale(_h_pT_jet[i], scaling);
       scale(_h_mass_jet[i], scaling);
       scale(_h_eta_jet[i], scaling);
       scale(_h_rap_jet[i], scaling);
 
       // Create eta/rapidity ratio plots
-      Scatter2DPtr tmpeta, tmprap;
-      book(tmpeta, "jet_eta_pmratio_" + to_str(i+1));
-      book(tmprap, "jet_y_pmratio_" + to_str(i+1));
-      divide(*_h_eta_jet_plus[i], *_h_eta_jet_minus[i], tmpeta);
-      divide(*_h_rap_jet_plus[i], *_h_rap_jet_minus[i], tmprap);
+      divide(*_h_eta_jet_plus[i], *_h_eta_jet_minus[i], tmpeta[i]);
+      divide(*_h_rap_jet_plus[i], *_h_rap_jet_minus[i], tmprap[i]);
     }
 
     // Scale the d{eta,phi,R} histograms
     typedef map<pair<size_t, size_t>, Histo1DPtr> HistMap;
     for (HistMap::value_type& it : _h_deta_jets) scale(it.second, scaling);
     for (HistMap::value_type& it : _h_dphi_jets) scale(it.second, scaling);
     for (HistMap::value_type& it : _h_dR_jets) scale(it.second, scaling);
 
     // Fill inclusive jet multi ratio
     int Nbins = _h_jet_multi_inclusive->numBins();
     for (int i = 0; i < Nbins-1; ++i) {
       _h_jet_multi_ratio->addPoint(i+1, 0, 0.5, 0);
       if (_h_jet_multi_inclusive->bin(i).sumW() > 0.0) {
         const double ratio = _h_jet_multi_inclusive->bin(i+1).sumW()/_h_jet_multi_inclusive->bin(i).sumW();
         const double relerr_i = _h_jet_multi_inclusive->bin(i).relErr();
         const double relerr_j = _h_jet_multi_inclusive->bin(i+1).relErr();
         const double err = ratio * (relerr_i + relerr_j);
         _h_jet_multi_ratio->point(i).setY(ratio, err);
       }
     }
 
     scale(_h_jet_multi_exclusive, scaling);
     scale(_h_jet_multi_inclusive, scaling);
     scale(_h_jet_HT, scaling);
     scale(_h_mjj_jets, scaling);
   }
 
 
 }
diff --git a/src/Core/Analysis.cc b/src/Core/Analysis.cc
--- a/src/Core/Analysis.cc
+++ b/src/Core/Analysis.cc
@@ -1,885 +1,926 @@
 // -*- C++ -*-
 #include "Rivet/Config/RivetCommon.hh"
 #include "Rivet/Analysis.hh"
 #include "Rivet/AnalysisHandler.hh"
 #include "Rivet/AnalysisInfo.hh"
 #include "Rivet/Tools/BeamConstraint.hh"
 
+#include "DummyConfig.hh"
+#ifdef HAVE_EXECINFO_H
+#include <execinfo.h>
+#endif
+
 namespace Rivet {
 
 
   Analysis::Analysis(const string& name)
     : _crossSection(-1.0),
       _gotCrossSection(false),
       _analysishandler(NULL)
   {
     ProjectionApplier::_allowProjReg = false;
     _defaultname = name;
 
     unique_ptr<AnalysisInfo> ai = AnalysisInfo::make(name);
     assert(ai);
     _info = move(ai);
     assert(_info);
   }
 
   double Analysis::sqrtS() const {
     return handler().sqrtS();
   }
 
   const ParticlePair& Analysis::beams() const {
     return handler().beams();
   }
 
   const PdgIdPair Analysis::beamIds() const {
     return handler().beamIds();
   }
 
 
   const string Analysis::histoDir() const {
     /// @todo Cache in a member variable
     string _histoDir;
     if (_histoDir.empty()) {
       _histoDir = "/" + name();
       if (handler().runName().length() > 0) {
         _histoDir = "/" + handler().runName() + _histoDir;
       }
       replace_all(_histoDir, "//", "/"); //< iterates until none
     }
     return _histoDir;
   }
 
 
   const string Analysis::histoPath(const string& hname) const {
     const string path = histoDir() + "/" + hname;
     return path;
   }
 
 
   const string Analysis::histoPath(unsigned int datasetId, unsigned int xAxisId, unsigned int yAxisId) const {
     return histoDir() + "/" + makeAxisCode(datasetId, xAxisId, yAxisId);
   }
 
 
   const string Analysis::makeAxisCode(unsigned int datasetId, unsigned int xAxisId, unsigned int yAxisId) const {
     stringstream axisCode;
     axisCode << "d";
     if (datasetId < 10) axisCode << 0;
     axisCode << datasetId;
     axisCode << "-x";
     if (xAxisId < 10) axisCode << 0;
     axisCode << xAxisId;
     axisCode << "-y";
     if (yAxisId < 10) axisCode << 0;
     axisCode << yAxisId;
     return axisCode.str();
   }
 
 
   Log& Analysis::getLog() const {
     string logname = "Rivet.Analysis." + name();
     return Log::getLog(logname);
   }
 
 
   size_t Analysis::numEvents() const {
     return handler().numEvents();
   }
 
 
   double Analysis::sumOfWeights() const {
     return handler().sumOfWeights();
   }
 
 
   ///////////////////////////////////////////
 
 
   bool Analysis::isCompatible(const ParticlePair& beams) const {
     return isCompatible(beams.first.pid(),  beams.second.pid(),
                         beams.first.energy(), beams.second.energy());
   }
 
 
   bool Analysis::isCompatible(PdgId beam1, PdgId beam2, double e1, double e2) const {
     PdgIdPair beams(beam1, beam2);
     pair<double,double> energies(e1, e2);
     return isCompatible(beams, energies);
   }
 
 
   bool Analysis::isCompatible(const PdgIdPair& beams, const pair<double,double>& energies) const {
     // First check the beam IDs
     bool beamIdsOk = false;
     foreach (const PdgIdPair& bp, requiredBeams()) {
       if (compatible(beams, bp)) {
         beamIdsOk =  true;
         break;
       }
     }
     if (!beamIdsOk) return false;
 
     // Next check that the energies are compatible (within 1% or 1 GeV, whichever is larger, for a bit of UI forgiveness)
 
     /// @todo Use some sort of standard ordering to improve comparisons, esp. when the two beams are different particles
     bool beamEnergiesOk = requiredEnergies().size() > 0 ? false : true;
     typedef pair<double,double> DoublePair;
     foreach (const DoublePair& ep, requiredEnergies()) {
       if ((fuzzyEquals(ep.first, energies.first, 0.01) && fuzzyEquals(ep.second, energies.second, 0.01)) ||
           (fuzzyEquals(ep.first, energies.second, 0.01) && fuzzyEquals(ep.second, energies.first, 0.01)) ||
           (abs(ep.first - energies.first) < 1*GeV && abs(ep.second - energies.second) < 1*GeV) ||
           (abs(ep.first - energies.second) < 1*GeV && abs(ep.second - energies.first) < 1*GeV)) {
         beamEnergiesOk =  true;
         break;
       }
     }
     return beamEnergiesOk;
 
     /// @todo Need to also check internal consistency of the analysis'
     /// beam requirements with those of the projections it uses.
   }
 
 
   ///////////////////////////////////////////
 
 
   Analysis& Analysis::setCrossSection(double xs) {
     _crossSection = xs;
     _gotCrossSection = true;
     return *this;
   }
 
   double Analysis::crossSection() const {
     if (!_gotCrossSection || std::isnan(_crossSection)) {
       string errMsg = "You did not set the cross section for the analysis " + name();
       throw Error(errMsg);
     }
     return _crossSection;
   }
 
   double Analysis::crossSectionPerEvent() const {
     return _crossSection/sumOfWeights();
   }
 
 
 
   ////////////////////////////////////////////////////////////
   // Histogramming
 
 
   void Analysis::_cacheRefData() const {
     if (_refdata.empty()) {
       MSG_TRACE("Getting refdata cache for paper " << name());
       _refdata = getRefData(name());
     }
   }
 
 
   CounterPtr & Analysis::book(CounterPtr & ctr,
                       const string& cname,
                       const string& title) {
     const string path = histoPath(cname);
     ctr = CounterPtr(handler().numWeights(), Counter(path, title));
     addAnalysisObject(ctr);
     MSG_TRACE("Made counter " << cname << " for " << name());
     return ctr;
   }
 
 
   CounterPtr & Analysis::book(CounterPtr & ctr, unsigned int datasetId, unsigned int xAxisId, unsigned int yAxisId,
                                    const string& title) {
                                    // const string& xtitle,
                                    // const string& ytitle) {
     const string axisCode = makeAxisCode(datasetId, xAxisId, yAxisId);
     return book(ctr, axisCode, title);
   }
 
 
   Histo1DPtr & Analysis::book(Histo1DPtr & histo, const string& hname,
                                     size_t nbins, double lower, double upper,
                                     const string& title,
                                     const string& xtitle,
                                     const string& ytitle) {
     const string path = histoPath(hname);
 
     Histo1D hist = Histo1D(nbins, lower, upper, path, title);
 
     hist.setAnnotation("XLabel", xtitle);
     hist.setAnnotation("YLabel", ytitle);
 
     histo = Histo1DPtr(handler().numWeights(), hist);
 
     addAnalysisObject(histo);
     MSG_TRACE("Made histogram " << hname <<  " for " << name());
     return histo;
 //    Histo1DPtr hist;
 //    try { // try to bind to pre-existing
 //      // AnalysisObjectPtr ao = getAnalysisObject(path);
 //      // hist = dynamic_pointer_cast<Histo1D>(ao);
 //      hist = getHisto1D(hname);
 //      /// @todo Test that cast worked
 //      /// @todo Also test that binning is as expected?
 //      MSG_TRACE("Bound pre-existing histogram " << hname <<  " for " << name());
 //    } catch (...) { // binding failed; make it from scratch
 //      hist = make_shared<Histo1D>(nbins, lower, upper, histoPath(hname), title);
 //      addAnalysisObject(hist);
 //      MSG_TRACE("Made histogram " << hname <<  " for " << name());
 //    }
   }
 
   Histo1DPtr & Analysis::book(Histo1DPtr & histo, const string& hname,
                                    const initializer_list<double>& binedges,
                                    const string& title,
                                    const string& xtitle,
                                    const string& ytitle) {
   	return book(histo, hname, vector<double>{binedges}, title, xtitle, ytitle);
   }
 
   Histo1DPtr & Analysis::book(Histo1DPtr & histo, const string& hname,
                                    const vector<double>& binedges,
                                    const string& title,
                                    const string& xtitle,
                                    const string& ytitle) {
     const string path = histoPath(hname);
 //    Histo1DPtr hist;
 //    try { // try to bind to pre-existing
 //      // AnalysisObjectPtr ao = getAnalysisObject(path);
 //      // hist = dynamic_pointer_cast<Histo1D>(ao);
 //      hist = getHisto1D(hname);
 //      /// @todo Test that cast worked
 //      /// @todo Also test that binning is as expected?
 //      MSG_TRACE("Bound pre-existing histogram " << hname <<  " for " << name());
 //    } catch (...) { // binding failed; make it from scratch
 //      hist = make_shared<Histo1D>(binedges, histoPath(hname), title);
 //      addAnalysisObject(hist);
 //      MSG_TRACE("Made histogram " << hname <<  " for " << name());
 //    }
     Histo1D hist = Histo1D(binedges, path, title);
 
     hist.setAnnotation("XLabel", xtitle);
     hist.setAnnotation("YLabel", ytitle);
 
 
     histo = Histo1DPtr(handler().numWeights(), hist);
     addAnalysisObject(histo);
 
     MSG_TRACE("Made histogram " << hname <<  " for " << name());
     return histo;
   }
 
 
   Histo1DPtr & Analysis::book(Histo1DPtr & histo, const string& hname,
                                    const Scatter2D& refscatter,
                                    const string& title,
                                    const string& xtitle,
                                    const string& ytitle) {
     const string path = histoPath(hname);
 //    Histo1DPtr hist;
 //    try { // try to bind to pre-existing
 //      // AnalysisObjectPtr ao = getAnalysisObject(path);
 //     // hist = dynamic_pointer_cast<Histo1D>(ao);
 //      hist = getHisto1D(hname);
 //      /// @todo Test that cast worked
 //      /// @todo Also test that binning is as expected?
 //      MSG_TRACE("Bound pre-existing histogram " << hname <<  " for " << name());
 //    } catch (...) { // binding failed; make it from scratch
 //      hist = make_shared<Histo1D>(refscatter, histoPath(hname));
 //      if (hist->hasAnnotation("IsRef")) hist->rmAnnotation("IsRef");
 //      addAnalysisObject(hist);
 //	MSG_TRACE("Made histogram " << hname <<  " for " << name());
 //    }
     Histo1D hist = Histo1D(refscatter, path);
 
     hist.setTitle(title);
     hist.setAnnotation("XLabel", xtitle);
     hist.setAnnotation("YLabel", ytitle);
 
     histo = Histo1DPtr(handler().numWeights(), hist);
     addAnalysisObject(histo);
 
     MSG_TRACE("Made histogram " << hname <<  " for " << name());
     return histo;
   }
 
   Histo1DPtr & Analysis::book(Histo1DPtr & histo, const string& hname,
                                    const string& title,
                                    const string& xtitle,
                                    const string& ytitle) {
     const Scatter2D& refdata = refData(hname);
     return book(histo, hname, refdata, title, xtitle, ytitle);
   }
 
 
   Histo1DPtr & Analysis::book(Histo1DPtr & histo, unsigned int datasetId, unsigned int xAxisId, unsigned int yAxisId,
                                    const string& title,
                                    const string& xtitle,
                                    const string& ytitle) {
     const string axisCode = makeAxisCode(datasetId, xAxisId, yAxisId);
     return book(histo, axisCode, title, xtitle, ytitle);
   }
 
 
   /// @todo Add booking methods which take a path, titles and *a reference Scatter from which to book*
 
 
   /////////////////
 
 
   Histo2DPtr & Analysis::book(Histo2DPtr & h2d,const string& hname,
                                    size_t nxbins, double xlower, double xupper,
                                    size_t nybins, double ylower, double yupper,
                                    const string& title,
                                    const string& xtitle,
                                    const string& ytitle,
                                    const string& ztitle)
   {
     const string path = histoPath(hname);
 
     Histo2D hist(nxbins, xlower, xupper, nybins, ylower, yupper, path, title);
     hist.setAnnotation("XLabel", xtitle);
     hist.setAnnotation("YLabel", ytitle);
     hist.setAnnotation("ZLabel", ztitle);
 
     h2d = Histo2DPtr(handler().numWeights(), hist);
     addAnalysisObject(h2d);
 
     MSG_TRACE("Made 2D histogram " << hname <<  " for " << name());
     return h2d;
   }
 
 
   Histo2DPtr & Analysis::book(Histo2DPtr & h2d,const string& hname,
                                    const initializer_list<double>& xbinedges,
                                    const initializer_list<double>& ybinedges,
                                    const string& title,
                                    const string& xtitle,
                                    const string& ytitle,
                                    const string& ztitle)
   {
   	return book(h2d, hname, vector<double>{xbinedges}, vector<double>{ybinedges}, title, xtitle, ytitle, ztitle);
   }
 
   Histo2DPtr & Analysis::book(Histo2DPtr & h2d,const string& hname,
                                    const vector<double>& xbinedges,
                                    const vector<double>& ybinedges,
                                    const string& title,
                                    const string& xtitle,
                                    const string& ytitle,
                                    const string& ztitle)
   {
     const string path = histoPath(hname);
 
     Histo2D hist(xbinedges, ybinedges, path, title);
     hist.setAnnotation("XLabel", xtitle);
     hist.setAnnotation("YLabel", ytitle);
     hist.setAnnotation("ZLabel", ztitle);
 
     h2d = Histo2DPtr(handler().numWeights(), hist);
     addAnalysisObject(h2d);
 
     MSG_TRACE("Made 2D histogram " << hname <<  " for " << name());
     return h2d;
   }
 
 
   Profile1DPtr & Analysis::book(Profile1DPtr & p1d,const string& hname,
                                        size_t nbins, double lower, double upper,
                                        const string& title,
                                        const string& xtitle,
                                        const string& ytitle) {
     const string path = histoPath(hname);
 
     Profile1D prof(nbins, lower, upper, path, title);
     prof.setAnnotation("XLabel", xtitle);
     prof.setAnnotation("YLabel", ytitle);
 
     p1d = Profile1DPtr(handler().numWeights(), prof);
     addAnalysisObject(p1d);
 
     MSG_TRACE("Made profile histogram " << hname <<  " for " << name());
     return p1d;
   }
 
 
   Profile1DPtr & Analysis::book(Profile1DPtr & p1d,const string& hname,
                                        const initializer_list<double>& binedges,
                                        const string& title,
                                        const string& xtitle,
                                        const string& ytitle) {
   	return book(p1d, hname, vector<double>{binedges}, title, xtitle, ytitle);
   }
 
   Profile1DPtr & Analysis::book(Profile1DPtr & p1d,const string& hname,
                                        const vector<double>& binedges,
                                        const string& title,
                                        const string& xtitle,
                                        const string& ytitle) {
     const string path = histoPath(hname);
     Profile1D prof(binedges, path, title);
     prof.setAnnotation("XLabel", xtitle);
     prof.setAnnotation("YLabel", ytitle);
 
     p1d = Profile1DPtr(handler().numWeights(), prof);
     addAnalysisObject(p1d);
 
     MSG_TRACE("Made profile histogram " << hname <<  " for " << name());
     return p1d;
   }
 
 
   Profile1DPtr & Analysis::book(Profile1DPtr & p1d,const string& hname,
                                        const Scatter2D& refscatter,
                                        const string& title,
                                        const string& xtitle,
                                        const string& ytitle) {
     const string path = histoPath(hname);
     Profile1D prof(refscatter, path);
     prof.setTitle(title);
     prof.setAnnotation("XLabel", xtitle);
     prof.setAnnotation("YLabel", ytitle);
 
     p1d = Profile1DPtr(handler().numWeights(), prof);
     addAnalysisObject(p1d);
 
     MSG_TRACE("Made profile histogram " << hname <<  " for " << name());
     return p1d;
 //    if (prof.hasAnnotation("IsRef")) prof.rmAnnotation("IsRef");
   }
 
 
   Profile1DPtr & Analysis::book(Profile1DPtr & p1d,const string& hname,
                                        const string& title,
                                        const string& xtitle,
                                        const string& ytitle) {
     const Scatter2D& refdata = refData(hname);
     book(p1d, hname, refdata, title, xtitle, ytitle);
     return p1d;
   }
 
 
   Profile1DPtr & Analysis::book(Profile1DPtr & p1d,unsigned int datasetId, unsigned int xAxisId, unsigned int yAxisId,
                                        const string& title,
                                        const string& xtitle,
                                        const string& ytitle) {
     const string axisCode = makeAxisCode(datasetId, xAxisId, yAxisId);
     return book(p1d, axisCode, title, xtitle, ytitle);
   }
 
 
   Profile2DPtr & Analysis::book(Profile2DPtr & p2d, const string& hname,
                                    size_t nxbins, double xlower, double xupper,
                                    size_t nybins, double ylower, double yupper,
                                    const string& title,
                                    const string& xtitle,
                                    const string& ytitle,
                                    const string& ztitle)
   {
     const string path = histoPath(hname);
     Profile2D prof(nxbins, xlower, xupper, nybins, ylower, yupper, path, title);
     prof.setAnnotation("XLabel", xtitle);
     prof.setAnnotation("YLabel", ytitle);
     prof.setAnnotation("ZLabel", ztitle);
 
     p2d = Profile2DPtr(handler().numWeights(), prof);
     addAnalysisObject(p2d);
 
     MSG_TRACE("Made 2D profile histogram " << hname <<  " for " << name());
     return p2d;
   }
 
 
   Profile2DPtr & Analysis::book(Profile2DPtr & p2d, const string& hname,
                                    const initializer_list<double>& xbinedges,
                                    const initializer_list<double>& ybinedges,
                                    const string& title,
                                    const string& xtitle,
                                    const string& ytitle,
                                    const string& ztitle)
   {
   	return book(p2d, hname, vector<double>{xbinedges}, vector<double>{ybinedges}, title, xtitle, ytitle, ztitle);
   }
 
   Profile2DPtr & Analysis::book(Profile2DPtr & p2d, const string& hname,
                                    const vector<double>& xbinedges,
                                    const vector<double>& ybinedges,
                                    const string& title,
                                    const string& xtitle,
                                    const string& ytitle,
                                    const string& ztitle)
   {
     const string path = histoPath(hname);
     Profile2D prof(xbinedges, ybinedges, path, title);
     prof.setAnnotation("XLabel", xtitle);
     prof.setAnnotation("YLabel", ytitle);
     prof.setAnnotation("ZLabel", ztitle);
 
     p2d = Profile2DPtr(handler().numWeights(), prof);
     addAnalysisObject(p2d);
 
     MSG_TRACE("Made 2D profile histogram " << hname <<  " for " << name());
     return p2d;
   }
 
 
   Scatter2DPtr & Analysis::book(Scatter2DPtr & s2d, unsigned int datasetId, unsigned int xAxisId, unsigned int yAxisId,
                                        bool copy_pts,
                                        const string& title,
                                        const string& xtitle,
                                        const string& ytitle) {
     const string axisCode = makeAxisCode(datasetId, xAxisId, yAxisId);
     return book(s2d, axisCode, copy_pts, title, xtitle, ytitle);
   }
 
 
   Scatter2DPtr & Analysis::book(Scatter2DPtr & s2d, const string& hname,
                                        bool copy_pts,
                                        const string& title,
                                        const string& xtitle,
                                        const string& ytitle) {
     Scatter2D scat;
     const string path = histoPath(hname);
     if (copy_pts) {
       const Scatter2D& refdata = refData(hname);
       scat = Scatter2D(refdata, path);
       for (Point2D& p : scat.points()) p.setY(0, 0);
     } else {
       scat = Scatter2D(path);
     }
 
     scat.setTitle(title);
     scat.setAnnotation("XLabel", xtitle);
     scat.setAnnotation("YLabel", ytitle);
 
     s2d = Scatter2DPtr(handler().numWeights(), scat);
     addAnalysisObject(s2d);
 
     MSG_TRACE("Made scatter " << hname <<  " for " << name());
 //    if (scat.hasAnnotation("IsRef")) scat.rmAnnotation("IsRef");
     return s2d;
   }
 
 
   Scatter2DPtr & Analysis::book(Scatter2DPtr & s2d, const string& hname,
                                        size_t npts, double lower, double upper,
                                        const string& title,
                                        const string& xtitle,
                                        const string& ytitle) {
     const string path = histoPath(hname);
     Scatter2D scat;
     const double binwidth = (upper-lower)/npts;
     for (size_t pt = 0; pt < npts; ++pt) {
       const double bincentre = lower + (pt + 0.5) * binwidth;
       scat.addPoint(bincentre, 0, binwidth/2.0, 0);
     }
     scat.setTitle(title);
     scat.setAnnotation("XLabel", xtitle);
     scat.setAnnotation("YLabel", ytitle);
 
     s2d = Scatter2DPtr(handler().numWeights(), scat);
     addAnalysisObject(s2d);
 
     MSG_TRACE("Made scatter " << hname <<  " for " << name());
     return s2d;
   }
 
 
   Scatter2DPtr & Analysis::book(Scatter2DPtr & s2d, const string& hname,
                                        const vector<double>& binedges,
                                        const string& title,
                                        const string& xtitle,
                                        const string& ytitle) {
     const string path = histoPath(hname);
     Scatter2D scat;
     for (size_t pt = 0; pt < binedges.size()-1; ++pt) {
       const double bincentre = (binedges[pt] + binedges[pt+1]) / 2.0;
       const double binwidth = binedges[pt+1] - binedges[pt];
       scat.addPoint(bincentre, 0, binwidth/2.0, 0);
     }
 
     scat.setTitle(title);
     scat.setAnnotation("XLabel", xtitle);
     scat.setAnnotation("YLabel", ytitle);
 
     s2d = Scatter2DPtr(handler().numWeights(), scat);
     addAnalysisObject(s2d);
 
 
     MSG_TRACE("Made scatter " << hname <<  " for " << name());
     return s2d;
   }
 
 
   void Analysis::divide(CounterPtr c1, CounterPtr c2, Scatter1DPtr s) const {
     const string path = s->path();
     *s = *c1 / *c2;
     s->setPath(path);
   }
 
   void Analysis::divide(const Counter& c1, const Counter& c2, Scatter1DPtr s) const {
     const string path = s->path();
     *s = c1 / c2;
     s->setPath(path);
   }
 
 
   void Analysis::divide(Histo1DPtr h1, Histo1DPtr h2, Scatter2DPtr s) const {
     const string path = s->path();
     *s = *h1 / *h2;
     s->setPath(path);
   }
 
   void Analysis::divide(const Histo1D& h1, const Histo1D& h2, Scatter2DPtr s) const {
     const string path = s->path();
     *s = h1 / h2;
     s->setPath(path);
   }
 
 
   void Analysis::divide(Profile1DPtr p1, Profile1DPtr p2, Scatter2DPtr s) const {
     const string path = s->path();
     *s = *p1 / *p2;
     s->setPath(path);
   }
 
   void Analysis::divide(const Profile1D& p1, const Profile1D& p2, Scatter2DPtr s) const {
     const string path = s->path();
     *s = p1 / p2;
     s->setPath(path);
   }
 
 
   void Analysis::divide(Histo2DPtr h1, Histo2DPtr h2, Scatter3DPtr s) const {
     const string path = s->path();
     *s = *h1 / *h2;
     s->setPath(path);
   }
 
   void Analysis::divide(const Histo2D& h1, const Histo2D& h2, Scatter3DPtr s) const {
     const string path = s->path();
     *s = h1 / h2;
     s->setPath(path);
   }
 
 
   void Analysis::divide(Profile2DPtr p1, Profile2DPtr p2, Scatter3DPtr s) const {
     const string path = s->path();
     *s = *p1 / *p2;
     s->setPath(path);
   }
 
   void Analysis::divide(const Profile2D& p1, const Profile2D& p2, Scatter3DPtr s) const {
     const string path = s->path();
     *s = p1 / p2;
     s->setPath(path);
   }
 
 
   /// @todo Counter and Histo2D efficiencies and asymms
 
 
   void Analysis::efficiency(Histo1DPtr h1, Histo1DPtr h2, Scatter2DPtr s) const {
     const string path = s->path();
     *s = YODA::efficiency(*h1, *h2);
     s->setPath(path);
   }
 
   void Analysis::efficiency(const Histo1D& h1, const Histo1D& h2, Scatter2DPtr s) const {
     const string path = s->path();
     *s = YODA::efficiency(h1, h2);
     s->setPath(path);
   }
 
 
   void Analysis::asymm(Histo1DPtr h1, Histo1DPtr h2, Scatter2DPtr s) const {
     const string path = s->path();
     *s = YODA::asymm(*h1, *h2);
     s->setPath(path);
   }
 
   void Analysis::asymm(const Histo1D& h1, const Histo1D& h2, Scatter2DPtr s) const {
     const string path = s->path();
     *s = YODA::asymm(h1, h2);
     s->setPath(path);
   }
 
 
   void Analysis::scale(CounterPtr cnt, double factor) {
     if (!cnt) {
       MSG_WARNING("Failed to scale counter=NULL in analysis " << name() << " (scale=" << factor << ")");
       return;
     }
     if (std::isnan(factor) || std::isinf(factor)) {
       MSG_WARNING("Failed to scale counter=" << cnt->path() << " in analysis: " << name() << " (invalid scale factor = " << factor << ")");
       factor = 0;
     }
     MSG_TRACE("Scaling counter " << cnt->path() << " by factor " << factor);
     try {
       cnt->scaleW(factor);
     } catch (YODA::Exception& we) {
       MSG_WARNING("Could not scale counter " << cnt->path());
       return;
     }
   }
 
 
   void Analysis::normalize(Histo1DPtr histo, double norm, bool includeoverflows) {
     if (!histo) {
       MSG_WARNING("Failed to normalize histo=NULL in analysis " << name() << " (norm=" << norm << ")");
       return;
     }
     MSG_TRACE("Normalizing histo " << histo->path() << " to " << norm);
     try {
       histo->normalize(norm, includeoverflows);
     } catch (YODA::Exception& we) {
       MSG_WARNING("Could not normalize histo " << histo->path());
       return;
     }
   }
 
 
   void Analysis::scale(Histo1DPtr histo, double factor) {
     if (!histo) {
       MSG_WARNING("Failed to scale histo=NULL in analysis " << name() << " (scale=" << factor << ")");
       return;
     }
     if (std::isnan(factor) || std::isinf(factor)) {
       MSG_WARNING("Failed to scale histo=" << histo->path() << " in analysis: " << name() << " (invalid scale factor = " << factor << ")");
       factor = 0;
     }
     MSG_TRACE("Scaling histo " << histo->path() << " by factor " << factor);
     try {
       histo->scaleW(factor);
     } catch (YODA::Exception& we) {
       MSG_WARNING("Could not scale histo " << histo->path());
       return;
     }
   }
 
 
   void Analysis::normalize(Histo2DPtr histo, double norm, bool includeoverflows) {
     if (!histo) {
       MSG_ERROR("Failed to normalize histo=NULL in analysis " << name() << " (norm=" << norm << ")");
       return;
     }
     MSG_TRACE("Normalizing histo " << histo->path() << " to " << norm);
     try {
       histo->normalize(norm, includeoverflows);
     } catch (YODA::Exception& we) {
       MSG_WARNING("Could not normalize histo " << histo->path());
       return;
     }
   }
 
 
   void Analysis::scale(Histo2DPtr histo, double factor) {
     if (!histo) {
       MSG_ERROR("Failed to scale histo=NULL in analysis " << name() << " (scale=" << factor << ")");
       return;
     }
     if (std::isnan(factor) || std::isinf(factor)) {
       MSG_ERROR("Failed to scale histo=" << histo->path() << " in analysis: " << name() << " (invalid scale factor = " << factor << ")");
       factor = 0;
     }
     MSG_TRACE("Scaling histo " << histo->path() << " by factor " << factor);
     try {
       histo->scaleW(factor);
     } catch (YODA::Exception& we) {
       MSG_WARNING("Could not scale histo " << histo->path());
       return;
     }
   }
 
 
   void Analysis::integrate(Histo1DPtr h, Scatter2DPtr s) const {
     // preserve the path info
     const string path = s->path();
     *s = toIntegralHisto(*h);
     s->setPath(path);
   }
 
   void Analysis::integrate(const Histo1D& h, Scatter2DPtr s) const {
     // preserve the path info
     const string path = s->path();
     *s = toIntegralHisto(h);
     s->setPath(path);
   }
 
-
+}
   /// @todo 2D versions of integrate... defined how, exactly?!?
 
 
   //////////////////////////////////
 
+namespace {
+  void errormsg(std::string name) {
+#ifdef HAVE_BACKTRACE
+     void * buffer[4];
+     backtrace(buffer, 4);
+     backtrace_symbols_fd(buffer, 4 , 1);
+#endif
+    std::cerr << name << ": Can't book objects outside of init().\n";
+    assert(false);
+  }
+}
 
+namespace Rivet {
   // @todo
   // special handling for scatters
   void Analysis::addAnalysisObject(const shared_ptr<Scatter1DPtr>& ao) {
-    _scatters.push_back(ao);
+    if (handler().stage() == AnalysisHandler::Stage::INIT) {
+      _scatters.push_back(ao);    
+      ao.get()->blockDestructor(true);
+    } 
+    else {
+      errormsg(name());
+    }
   }
   void Analysis::addAnalysisObject(const shared_ptr<Scatter2DPtr>& ao) {
-    _scatters.push_back(ao);
+    if (handler().stage() == AnalysisHandler::Stage::INIT) {
+      _scatters.push_back(ao);    
+      ao.get()->blockDestructor(true);
+    }
+    else {
+      errormsg(name());
+    }
   }
   void Analysis::addAnalysisObject(const shared_ptr<Scatter3DPtr>& ao) {
-    _scatters.push_back(ao);
+    if (handler().stage() == AnalysisHandler::Stage::INIT) {
+      _scatters.push_back(ao);
+      ao.get()->blockDestructor(true);
+    } 
+    else {
+      errormsg(name());
+    }
   }
 
   void Analysis::addAnalysisObject(MultiweightAOPtr & ao) {
-    _analysisobjects.push_back(ao);
+    if (handler().stage() == AnalysisHandler::Stage::INIT) {
+      _analysisobjects.push_back(ao);
+      ao.blockDestructor(true);
+    } 
+    else {
+      errormsg(name());
+    }
   }
 
 
   void Analysis::removeAnalysisObject(const string& path) {
     for (auto it = _analysisobjects.begin();
          it != _analysisobjects.end(); ++it) {
       if ((*it).get()->path() == path) {
         _analysisobjects.erase(it);
         break;
       }
     }
 
     for (auto it = _scatters.begin();  it != _scatters.end(); ++it) {
       if ((**it)->path() == path) {
         _scatters.erase(it);
         break;
       }
     }
   }
 
 
   /// @todo can we really remove (multiweighted) analysis objects by == operator??
   void Analysis::removeAnalysisObject(const Scatter1DPtr& ao) {
     for (auto it = _scatters.begin();  it != _scatters.end(); ++it) {
       if (**it == ao) {
         _scatters.erase(it);
         break;
       }
     }
   }
 
   void Analysis::removeAnalysisObject(const Scatter2DPtr& ao) {
     for (auto it = _scatters.begin();  it != _scatters.end(); ++it) {
       if (**it == ao) {
         _scatters.erase(it);
         break;
       }
     }
   }
 
   void Analysis::removeAnalysisObject(const Scatter3DPtr& ao) {
     for (auto it = _scatters.begin();  it != _scatters.end(); ++it) {
       if (**it == ao) {
         _scatters.erase(it);
         break;
       }
     }
   }
 
   void Analysis::removeAnalysisObject(const MultiweightAOPtr& ao) {
     for (auto it = _analysisobjects.begin();  it != _analysisobjects.end(); ++it) {
       if ((*it).get() == ao) {
         _analysisobjects.erase(it);
         break;
       }
     }
   }
 
 }
diff --git a/src/Core/AnalysisHandler.cc b/src/Core/AnalysisHandler.cc
--- a/src/Core/AnalysisHandler.cc
+++ b/src/Core/AnalysisHandler.cc
@@ -1,519 +1,524 @@
 // -*- C++ -*-
 #include "Rivet/Config/RivetCommon.hh"
 #include "Rivet/AnalysisHandler.hh"
 #include "Rivet/Analysis.hh"
 #include "Rivet/Tools/ParticleName.hh"
 #include "Rivet/Tools/BeamConstraint.hh"
 #include "Rivet/Tools/Logging.hh"
 #include "Rivet/Projections/Beam.hh"
 #include "YODA/ReaderYODA.h"
 #include "YODA/WriterYODA.h"
 #include <regex>
 
 namespace {
     inline std::vector<std::string> split(const std::string& input, const std::string& regex) {
         // passing -1 as the submatch index parameter performs splitting
         std::regex re(regex);
         std::sregex_token_iterator
             first{input.begin(), input.end(), re, -1},
             last;
         return {first, last};
     }
 }
 
 
 namespace Rivet {
 
 
   AnalysisHandler::AnalysisHandler(const string& runname)
     : _runname(runname),
       _eventCounter(0, Counter()), _xs(NAN),
       _initialised(false), _ignoreBeams(false)
   {}
 
 
   AnalysisHandler::~AnalysisHandler()
   {}
 
 
   Log& AnalysisHandler::getLog() const {
     return Log::getLog("Rivet.Analysis.Handler");
   }
 
 
   /// http://stackoverflow.com/questions/4654636/how-to-determine-if-a-string-is-a-number-with-c
   bool is_number(const std::string& s)
   {
     std::string::const_iterator it = s.begin();
     while (it != s.end() && std::isdigit(*it)) ++it;
     return !s.empty() && it == s.end();
   }
 
   /// Check if any of the weightnames is not a number
   bool AnalysisHandler::haveNamedWeights() {
     bool dec=false;
     for (unsigned int i=0;i<_weightNames.size();++i) {
       string s = _weightNames[i];
       if (!is_number(s)) {
         dec=true;
         break;
       }
     }
     return dec;
   }
 
 
   void AnalysisHandler::init(const GenEvent& ge) {
     if (_initialised)
       throw UserError("AnalysisHandler::init has already been called: cannot re-initialize!");
 
     setRunBeams(Rivet::beams(ge));
     MSG_DEBUG("Initialising the analysis handler");
     _eventNumber = ge.event_number();
 
     setWeightNames(ge);
     if (haveNamedWeights())
         MSG_INFO("Using named weights");
     else
         MSG_INFO("NOT using named weights. Using first weight as nominal weight");
 
     _numWeightTypes = _weightNames.size();
     _eventCounter = CounterPtr(_numWeightTypes, Counter("_EVTCOUNT"));
 
     // Check that analyses are beam-compatible, and remove those that aren't
     const size_t num_anas_requested = analysisNames().size();
     vector<string> anamestodelete;
     for (const AnaHandle a : _analyses) {
       if (!_ignoreBeams && !a->isCompatible(beams())) {
         //MSG_DEBUG(a->name() << " requires beams " << a->requiredBeams() << " @ " << a->requiredEnergies() << " GeV");
         anamestodelete.push_back(a->name());
       }
     }
     for (const string& aname : anamestodelete) {
       MSG_WARNING("Analysis '" << aname << "' is incompatible with the provided beams: removing");
       removeAnalysis(aname);
     }
     if (num_anas_requested > 0 && analysisNames().empty()) {
       cerr << "All analyses were incompatible with the first event's beams\n"
            << "Exiting, since this probably wasn't intentional!" << endl;
       exit(1);
     }
 
     // Warn if any analysis' status is not unblemished
     for (const AnaHandle a : analyses()) {
       if (toUpper(a->status()) == "PRELIMINARY") {
         MSG_WARNING("Analysis '" << a->name() << "' is preliminary: be careful, it may change and/or be renamed!");
       } else if (toUpper(a->status()) == "OBSOLETE") {
         MSG_WARNING("Analysis '" << a->name() << "' is obsolete: please update!");
       } else if (toUpper(a->status()).find("UNVALIDATED") != string::npos) {
         MSG_WARNING("Analysis '" << a->name() << "' is unvalidated: be careful, it may be broken!");
       }
     }
 
     // Initialize the remaining analyses
+    _stage = Stage::INIT;
     for (AnaHandle a : _analyses) {
       MSG_DEBUG("Initialising analysis: " << a->name());
       try {
         // Allow projection registration in the init phase onwards
         a->_allowProjReg = true;
         a->init();
         //MSG_DEBUG("Checking consistency of analysis: " << a->name());
         //a->checkConsistency();
       } catch (const Error& err) {
         cerr << "Error in " << a->name() << "::init method: " << err.what() << endl;
         exit(1);
       }
       MSG_DEBUG("Done initialising analysis: " << a->name());
     }
+    _stage = Stage::OTHER;
     _initialised = true;
     MSG_DEBUG("Analysis handler initialised");
   }
 
   void AnalysisHandler::setWeightNames(const GenEvent& ge) {
     /// reroute the print output to a stringstream and process
     /// The iteration is done over a map in hepmc2 so this is safe
     ostringstream stream;
     ge.weights().print(stream);  // Super lame, I know
     string str =  stream.str();
 
     std::regex re("(([^()]+))"); // Regex for stuff enclosed by parentheses ()
     for(std::sregex_iterator i = std::sregex_iterator(str.begin(), str.end(), re);
                           i != std::sregex_iterator(); ++i ) {
       std::smatch m = *i;
       vector<string> temp = ::split(m.str(), "[,]");
       if (temp.size() ==2) {
         MSG_DEBUG("Name of weight #" << _weightNames.size() << ": " << temp[0]);
         _weightNames.push_back(temp[0]);
       }
     }
   }
 
 
   void AnalysisHandler::analyze(const GenEvent& ge) {
     // Call init with event as template if not already initialised
     if (!_initialised) init(ge);
     assert(_initialised);
 
     // Ensure that beam details match those from the first event (if we're checking beams)
     if ( !_ignoreBeams ) {
       const PdgIdPair beams = Rivet::beamIds(ge);
       const double sqrts = Rivet::sqrtS(ge);
       if (!compatible(beams, _beams) || !fuzzyEquals(sqrts, sqrtS())) {
         cerr << "Event beams mismatch: "
              << PID::toBeamsString(beams) << " @ " << sqrts/GeV << " GeV" << " vs. first beams "
              << this->beams() << " @ " << this->sqrtS()/GeV << " GeV" << endl;
         exit(1);
       }
     }
 
 
     // Create the Rivet event wrapper
     /// @todo Filter/normalize the event here
     Event event(ge);
 
     // won't happen for first event because _eventNumber is set in
     // init()
     if (_eventNumber != ge.event_number()) {
         /// @todo
         /// can we get away with not passing a matrix?
 
         MSG_TRACE("AnalysisHandler::analyze(): Pushing _eventCounter to persistent.");
         _eventCounter.pushToPersistent(_subEventWeights);
         // if this is indeed a new event, push the temporary
         // histograms and reset
         for (const AnaHandle& a : _analyses) {
             for (MultiweightAOPtr & ao : a->analysisObjects()) {
                 MSG_TRACE("AnalysisHandler::analyze(): Pushing " << a->name() << "'s " << ao->name() << " to persistent.");
                 ao.pushToPersistent(_subEventWeights);
             }
             MSG_TRACE("AnalysisHandler::analyze(): finished pushing " << a->name() << "'s objects to persistent.");
         }
 
         _eventNumber = ge.event_number();
 
         MSG_DEBUG("nominal event # " << _eventCounter._persistent[0]->numEntries());
         MSG_DEBUG("nominal sum of weights: " << _eventCounter._persistent[0]->sumW());
         MSG_DEBUG("Event has " << _subEventWeights.size() << " sub events.");
         _subEventWeights.clear();
     }
 
 
     _eventCounter.newSubEvent();
 
     for (const AnaHandle& a : _analyses) {
         for (MultiweightAOPtr & ao : a->analysisObjects()) {
             ao.newSubEvent();
         }
     }
 
     _subEventWeights.push_back(event.weights());
     MSG_DEBUG("Analyzing subevent #" << _subEventWeights.size() - 1 << ".");
 
 
     // Cross-section
     #ifdef HEPMC_HAS_CROSS_SECTION
     if (ge.cross_section()) {
       _xs = ge.cross_section()->cross_section();
       _xserr = ge.cross_section()->cross_section_error();
     }
     #endif
 
     _eventCounter->fill();
     // Run the analyses
     for (AnaHandle a : _analyses) {
       MSG_TRACE("About to run analysis " << a->name());
       try {
         a->analyze(event);
       } catch (const Error& err) {
         cerr << "Error in " << a->name() << "::analyze method: " << err.what() << endl;
         exit(1);
       }
       MSG_TRACE("Finished running analysis " << a->name());
     }
   }
 
 
   void AnalysisHandler::analyze(const GenEvent* ge) {
     if (ge == NULL) {
       MSG_ERROR("AnalysisHandler received null pointer to GenEvent");
       //throw Error("AnalysisHandler received null pointer to GenEvent");
     }
     analyze(*ge);
   }
 
 
   void AnalysisHandler::finalize() {
       if (!_initialised) return;
       MSG_INFO("Finalising analyses");
 
       MSG_TRACE("AnalysisHandler::finalize(): Pushing analysis objects to persistent.");
       _eventCounter.pushToPersistent(_subEventWeights);
       for (const AnaHandle& a : _analyses) {
           for (MultiweightAOPtr & ao : a->analysisObjects())
               ao.pushToPersistent(_subEventWeights);
       }
 
       for (const AnaHandle& a : _analyses) {
           a->setCrossSection(_xs);
           for (size_t iW = 0; iW < numWeights(); iW++) {
               _eventCounter.setActiveWeightIdx(iW);
               for (MultiweightAOPtr & ao : a->analysisObjects())
                   ao.setActiveWeightIdx(iW);
 
               MSG_TRACE("running " << a->name() << "::finalize() for weight " << iW << ".");
 
               try {
                   a->finalize();
               } catch (const Error& err) {
                   cerr << "Error in " << a->name() << "::finalize method: " << err.what() << endl;
                   exit(1);
               }
           }
+          // allow AO destruction again
+          for (MultiweightAOPtr & ao : a->analysisObjects())
+            ao.blockDestructor(false);
       }
 
     // Print out number of events processed
     MSG_INFO("Processed " << numEvents() << " event" << (numEvents() == 1 ? "" : "s"));
 
     // // Delete analyses
     // MSG_DEBUG("Deleting analyses");
     // _analyses.clear();
 
     // Print out MCnet boilerplate
     cout << endl;
     cout << "The MCnet usage guidelines apply to Rivet: see http://www.montecarlonet.org/GUIDELINES" << endl;
     cout << "Please acknowledge plots made with Rivet analyses, and cite arXiv:1003.0694 (http://arxiv.org/abs/1003.0694)" << endl;
   }
 
 
   AnalysisHandler& AnalysisHandler::addAnalysis(const string& analysisname) {
     // Check for a duplicate analysis
     /// @todo Might we want to be able to run an analysis twice, with different params?
     ///       Requires avoiding histo tree clashes, i.e. storing the histos on the analysis objects.
     for (const AnaHandle& a : _analyses) {
       if (a->name() == analysisname) {
         MSG_WARNING("Analysis '" << analysisname << "' already registered: skipping duplicate");
         return *this;
       }
     }
     AnaHandle analysis( AnalysisLoader::getAnalysis(analysisname) );
     if (analysis.get() != 0) { // < Check for null analysis.
       MSG_DEBUG("Adding analysis '" << analysisname << "'");
       analysis->_analysishandler = this;
       _analyses.insert(analysis);
     } else {
       MSG_WARNING("Analysis '" << analysisname << "' not found.");
     }
     // MSG_WARNING(_analyses.size());
     // for (const AnaHandle& a : _analyses) MSG_WARNING(a->name());
     return *this;
   }
 
 
   AnalysisHandler& AnalysisHandler::removeAnalysis(const string& analysisname) {
     std::shared_ptr<Analysis> toremove;
     for (const AnaHandle a : _analyses) {
       if (a->name() == analysisname) {
         toremove = a;
         break;
       }
     }
     if (toremove.get() != 0) {
       MSG_DEBUG("Removing analysis '" << analysisname << "'");
       _analyses.erase(toremove);
     }
     return *this;
   }
 
 
 
 
 
   void AnalysisHandler::addData(const std::vector<YODA::AnalysisObjectPtr>& aos) {
     for (const YODA::AnalysisObjectPtr ao : aos) {
       const string path = ao->path();
       if (path.size() > 1) { // path > "/"
         try {
           const string ananame =  ::split(path, "/")[0];
           AnaHandle a = analysis(ananame);
           //MultiweightAOPtr mao = ????; /// @todo generate right Multiweight object from ao 
           //a->addAnalysisObject(mao); /// @todo Need to statistically merge...
         } catch (const Error& e) {
           MSG_WARNING(e.what());
         }
       }
     }
   }
 
   void AnalysisHandler::readData(const string& filename) {
     vector<YODA::AnalysisObjectPtr> aos;
     try {
       /// @todo Use new YODA SFINAE to fill the smart ptr vector directly
       vector<YODA::AnalysisObject*> aos_raw;
       YODA::ReaderYODA::read(filename, aos_raw);
       for (YODA::AnalysisObject* aor : aos_raw) aos.push_back(YODA::AnalysisObjectPtr(aor));
     } catch (const YODA::ReadError & e) {
       throw UserError("Unexpected error in reading file: " + filename);
     }
     if (!aos.empty()) addData(aos);
   }
 
 
 
 
 
 
   vector<reference_wrapper<MultiweightAOPtr> > AnalysisHandler::getRivetAOs() const {
       vector<reference_wrapper<MultiweightAOPtr> > rtn;
 
       for (AnaHandle a : _analyses) {
           for (const auto & ao : a->analysisObjects()) {
               rtn.push_back(ao);
           }
       }
 
       // Should event counter be included here?
       rtn.push_back(_eventCounter);
 
       return rtn;
   }
 
   vector<YODA::AnalysisObjectPtr> AnalysisHandler::getYodaAOs() const {
       vector<YODA::AnalysisObjectPtr> rtn;
 
       for (MultiweightAOPtr & rao : getRivetAOs()) {
           // need to set the index
           // before we can search the PATH
           rao.setActiveWeightIdx(0);
           if (rao->path().find("/TMP/") != string::npos)
               continue;
 
           for (size_t iW = 0; iW < numWeights(); iW++) {
               rao.setActiveWeightIdx(iW);
 
               // add the weight name in brackets unless we recognize a
               // nominal weight
               if (_weightNames[iW] != "Weight"
                   && _weightNames[iW] != "0"
                   && _weightNames[iW] != "Default")
                   rao->setPath(rao->path() + "[" + _weightNames[iW] + "]");
 
               rtn.push_back(rao.activeYODAPtr());
           }
       }
 
       YODA::Scatter1D::Points pts; pts.insert(YODA::Point1D(_xs, _xserr));
       rtn.push_back( make_shared<Scatter1D>(pts, "/_XSEC") );
 
       sort(rtn.begin(), rtn.end(),
            [](YODA::AnalysisObjectPtr a, YODA::AnalysisObjectPtr b) {
                 return a->path() < b->path();
             }
           );
 
       return rtn;
   }
 
   vector<YODA::AnalysisObjectPtr> AnalysisHandler::getData() const {
 
       return getYodaAOs();
   }
 
 
   void AnalysisHandler::writeData(const string& filename) const {
     const vector<YODA::AnalysisObjectPtr> aos = getData();
     try {
       YODA::WriterYODA::write(filename, aos.begin(), aos.end());
     } catch ( YODA::WriteError ) {
       throw UserError("Unexpected error in writing file: " + filename);
     }
   }
 
 
   string AnalysisHandler::runName() const { return _runname; }
   size_t AnalysisHandler::numEvents() const { return _eventCounter->numEntries(); }
 
 
   /*
    * why is this here?
   void AnalysisHandler::setSumOfWeights(const double& sum) {
     sumOfWeights() = sum;
   }
   */
 
 
   std::vector<std::string> AnalysisHandler::analysisNames() const {
     std::vector<std::string> rtn;
     for (AnaHandle a : _analyses) {
       rtn.push_back(a->name());
     }
     return rtn;
   }
 
 
   const AnaHandle AnalysisHandler::analysis(const std::string& analysisname) const {
     for (const AnaHandle a : analyses())
       if (a->name() == analysisname) return a;
     throw Error("No analysis named '" + analysisname + "' registered in AnalysisHandler");
   }
 
 
   AnalysisHandler& AnalysisHandler::addAnalyses(const std::vector<std::string>& analysisnames) {
     for (const string& aname : analysisnames) {
       //MSG_DEBUG("Adding analysis '" << aname << "'");
       addAnalysis(aname);
     }
     return *this;
   }
 
 
   AnalysisHandler& AnalysisHandler::removeAnalyses(const std::vector<std::string>& analysisnames) {
     for (const string& aname : analysisnames) {
       removeAnalysis(aname);
     }
     return *this;
   }
 
 
   bool AnalysisHandler::needCrossSection() const {
     bool rtn = false;
     for (const AnaHandle a : _analyses) {
       if (!rtn) rtn = a->needsCrossSection();
       if (rtn) break;
     }
     return rtn;
   }
 
 
   AnalysisHandler& AnalysisHandler::setCrossSection(double xs) {
     _xs = xs;
     return *this;
   }
 
 
   bool AnalysisHandler::hasCrossSection() const {
     return (!std::isnan(crossSection()));
   }
 
 
   AnalysisHandler& AnalysisHandler::addAnalysis(Analysis* analysis) {
     analysis->_analysishandler = this;
     _analyses.insert(AnaHandle(analysis));
     return *this;
   }
 
 
   PdgIdPair AnalysisHandler::beamIds() const {
     return Rivet::beamIds(beams());
   }
 
 
   double AnalysisHandler::sqrtS() const {
     return Rivet::sqrtS(beams());
   }
 
   void AnalysisHandler::setIgnoreBeams(bool ignore) {
     _ignoreBeams=ignore;
   }
 
 
 }
diff --git a/src/Tools/RivetYODA.cc b/src/Tools/RivetYODA.cc
--- a/src/Tools/RivetYODA.cc
+++ b/src/Tools/RivetYODA.cc
@@ -1,368 +1,392 @@
 #include "Rivet/Config/RivetCommon.hh"
 #include "Rivet/Tools/RivetYODA.hh"
 #include "Rivet/Tools/RivetPaths.hh"
 #include "YODA/ReaderYODA.h"
 #include "YODA/ReaderAIDA.h"
 
+#include "DummyConfig.hh"
+#ifdef HAVE_EXECINFO_H
+#include <execinfo.h>
+#endif
+
 using namespace std;
 
 namespace Rivet {
 
+template <class T>
+Wrapper<T>::~Wrapper() {
+  // checking the use_count, because we only want to prevent destruction of the last one,
+  // which seems to have a count of 2 for some reason
+  if (   _blockDestructor  // we're a registered AO
+      && !_persistent.empty() // with some entries
+      && _persistent[0]  // that are non-null
+      && _persistent[0].use_count() <= 2 ) { // and have no refs left => can't destruct
+#ifdef HAVE_BACKTRACE
+     void * buffer[4];
+     backtrace(buffer, 4);
+     backtrace_symbols_fd(buffer, 4 , 1);
+#endif
+     cerr << "***\n"
+          << "* Cannot destruct temporary AO before finalize.\n"
+          << "* All booked AOs must be class members.\n"
+          << "***\n";
+     assert(false);
+   }
+}
 
 template <class T>
-Wrapper<T>::Wrapper(size_t len_of_weightvec, const T & p) {
+Wrapper<T>::Wrapper(size_t len_of_weightvec, const T & p) 
+{
   for (size_t m = 0; m < len_of_weightvec; ++m)
     _persistent.push_back(make_shared<T>(p));
 }
 
 
 template <class T>
 typename T::Ptr Wrapper<T>::active() const {
-    assert(_active);
+    if ( !_active ) {
+      assert(false && "No active pointer set. Was this object booked in init()?");
+    }
     return _active;
 }
 
 
 template <class T>
 void Wrapper<T>::newSubEvent() {
   typename TupleWrapper<T>::Ptr tmp
       = make_shared<TupleWrapper<T>>(_persistent[0]->clone());
   tmp->reset();
   _evgroup.push_back( tmp );
   _active = _evgroup.back();
   assert(_active);
 }
 
 
 
   string getDatafilePath(const string& papername) {
     /// Try to find YODA otherwise fall back to try AIDA
     const string path1 = findAnalysisRefFile(papername + ".yoda");
     if (!path1.empty()) return path1;
     const string path2 = findAnalysisRefFile(papername + ".aida");
     if (!path2.empty()) return path2;
     throw Rivet::Error("Couldn't find ref data file '" + papername + ".yoda" +
                        " in data path, '" + getRivetDataPath() + "', or '.'");
   }
 
 
   map<string, YODA::AnalysisObjectPtr> getRefData(const string& papername) {
     const string datafile = getDatafilePath(papername);
 
     // Make an appropriate data file reader and read the data objects
     /// @todo Remove AIDA support some day...
     YODA::Reader& reader = (datafile.find(".yoda") != string::npos) ?   \
       YODA::ReaderYODA::create() : YODA::ReaderAIDA::create();
     vector<YODA::AnalysisObject *> aovec;
     reader.read(datafile, aovec);
 
     // Return value, to be populated
     map<string, YODA::AnalysisObjectPtr> rtn;
     foreach ( YODA::AnalysisObject* ao, aovec ) {
       YODA::AnalysisObjectPtr refdata(ao);
       if (!refdata) continue;
       const string plotpath = refdata->path();
       // Split path at "/" and only return the last field, i.e. the histogram ID
       const size_t slashpos = plotpath.rfind("/");
       const string plotname = (slashpos+1 < plotpath.size()) ? plotpath.substr(slashpos+1) : "";
       rtn[plotname] = refdata;
     }
     return rtn;
   }
 }
 
 namespace {
 
   using Rivet::Fill;
   using Rivet::Fills;
   using Rivet::TupleWrapper;
 
     template <class T>
     double get_window_size(const typename T::Ptr & histo,
                            typename T::BinType x) {
         // the bin index we fall in
         const auto binidx = histo->binIndexAt(x);
         // gaps, overflow, underflow don't contribute
         if ( binidx == -1 )
             return 0;
 
 
         const auto & b = histo->bin(binidx);
 
         // if we don't have a valid neighbouring bin,
         // we use infinite width
         typename T::Bin b1(-1.0/0.0, 1.0/0.0);
 
         // points in the top half compare to the upper neighbour
         if ( x > b.xMid() ) {
             size_t nextidx = binidx + 1;
             if ( nextidx < histo->bins().size() )
                 b1 = histo->bin(nextidx);
         }
         else { // compare to the lower neighbour
             int nextidx = binidx - 1;
             if ( nextidx >= 0 )
                 b1 = histo->bin(nextidx);
         }
         // the factor 2 is arbitrary, could poss. be smaller
         return min( b.width(), b1.width() ) / 2.0;
     }
 
     template <class T>
      typename T::BinType
      fillT2binT(typename T::FillType a) {
        return a;
     }
 
     template <>
     YODA::Profile1D::BinType
     fillT2binT<YODA::Profile1D>(YODA::Profile1D::FillType a) {
       return get<0>(a);
     }
 
     template <>
     YODA::Profile2D::BinType
     fillT2binT<YODA::Profile2D>(YODA::Profile2D::FillType a) {
       return YODA::Profile2D::BinType{ get<0>(a), get<1>(a) };
     }
 
 
 
   template <class T>
   void commit(vector<typename T::Ptr> & persistent,
               const vector< vector<Fill<T>> > & tuple,
               const vector<valarray<double>> & weights ) {
 
     // TODO check if all the xs are in the same bin anyway!
     // Then no windowing needed
 
     assert(persistent.size() == weights[0].size());
 
     for ( const auto & x : tuple ) {
       double maxwindow = 0.0;
       for ( const auto & xi : x ) {
         // TODO check for NOFILL here
         // persistent[0] has the same binning as all the other persistent objects
         double window = get_window_size<T>(persistent[0], fillT2binT<T>(xi.first));
         if ( window > maxwindow )
           maxwindow = window;
       }
       const double wsize = maxwindow;
       // all windows have same size
 
       set<double> edgeset;
       // bin edges need to be in here!
       for ( const auto & xi : x ) {
         edgeset.insert(fillT2binT<T>(xi.first) - wsize);
         edgeset.insert(fillT2binT<T>(xi.first) + wsize);
       }
 
           vector< std::tuple<double,valarray<double>,double> > hfill;
           double sumf = 0.0;
           auto edgit = edgeset.begin();
           double ehi = *edgit;
           while ( ++edgit != edgeset.end() ) {
             double elo = ehi;
             ehi = *edgit;
             valarray<double> sumw(0.0, persistent.size()); // need m copies of this
             bool gap = true; // Check for gaps between the sub-windows.
             for ( size_t i = 0; i < x.size(); ++i  ) {
               // check equals comparisons here!
               if ( fillT2binT<T>(x[i].first) + wsize >= ehi
                    &&
                    fillT2binT<T>(x[i].first) - wsize <= elo ) {
                 sumw += x[i].second * weights[i];
                 gap = false;
               }
             }
             if ( gap ) continue;
             hfill.push_back( make_tuple( (ehi + elo)/2.0, sumw, (ehi - elo) ) );
             sumf += ehi - elo;
           }
 
           for ( auto f : hfill )
             for ( size_t m = 0; m < persistent.size(); ++m )
               persistent[m]->fill( get<0>(f), get<1>(f)[m], get<2>(f)/sumf );
             // Note the scaling to one single fill
 
     }
   }
 
   template<>
   void commit<YODA::Histo2D>(vector<YODA::Histo2D::Ptr> & persistent,
               const vector< vector<Fill<YODA::Histo2D>> > & tuple,
               const vector<valarray<double>> & weights)
   {}
 
   template<>
   void commit<YODA::Profile2D>(vector<YODA::Profile2D::Ptr> & persistent,
               const vector< vector<Fill<YODA::Profile2D>> > & tuple,
               const vector<valarray<double>> & weights)
   {}
 
     template <class T>
     double distance(T a, T b) {
       return abs(a - b);
     }
 
     template <>
     double distance<tuple<double,double> >(tuple<double,double> a, tuple<double,double> b) {
       return Rivet::sqr(get<0>(a) - get<0>(b)) + Rivet::sqr(get<1>(a) - get<1>(b));
     }
 
 
 
 }
 
 
 
 
 /// fills is a vector of sub-event with an ordered set of x-values of
 /// the fills in each sub-event. NOFILL should be an "impossible"
 /// value for this histogram. Returns a vector of sub-events with
 /// an ordered vector of fills (including NOFILLs) for each sub-event.
 template <class T>
 vector< vector<Fill<T> > >
 match_fills(const vector<typename TupleWrapper<T>::Ptr> & evgroup, const Fill<T> & NOFILL)
 {
   vector< vector<Fill<T> > > matched;
   // First just copy subevents into vectors and find the longest vector.
   unsigned int maxfill = 0; // length of biggest vector
   int imax = 0; // index position of biggest vector
   for ( const auto & it : evgroup ) {
     const auto & subev = it->fills();
     if ( subev.size() > maxfill ) {
       maxfill = subev.size();
       imax = matched.size();
     }
     matched.push_back(vector<Fill<T> >(subev.begin(), subev.end()));
   }
   // Now, go through all subevents with missing fills.
   const vector<Fill<T>> & full = matched[imax]; // the longest one
   for ( auto & subev : matched ) {
     if ( subev.size() == maxfill ) continue;
 
     // Add NOFILLs to the end;
     while ( subev.size() < maxfill ) subev.push_back(NOFILL);
 
     // Iterate from the back and shift all fill values backwards by
     // swapping with NOFILLs so that they better match the full
     // subevent.
     for ( int i = maxfill - 1; i >= 0; --i ) {
       if ( subev[i] == NOFILL ) continue;
       size_t j = i;
       while ( j + 1 < maxfill && subev[j + 1] == NOFILL &&
               distance(fillT2binT<T>(subev[j].first),
                        fillT2binT<T>(full[j].first))
               >
               distance(fillT2binT<T>(subev[j].first),
                        fillT2binT<T>(full[j + 1].first)) )
       {
             swap(subev[j], subev[j + 1]);
             ++j;
       }
     }
   }
   // transpose
   vector<vector<Fill<T>>> result(maxfill,vector<Fill<T>>(matched.size()));
   for (size_t i = 0; i < matched.size(); ++i)
       for (size_t j = 0; j < maxfill; ++j)
           result.at(j).at(i) = matched.at(i).at(j);
   return result;
 }
 
 
 
 
 
 
 namespace Rivet {
 
   template <class T>
   void Wrapper<T>::pushToPersistent(const vector<valarray<double> >& weight) {
-
       assert( _evgroup.size() == weight.size() );
 
       // have we had subevents at all?
       const bool have_subevents = _evgroup.size() > 1;
       if ( ! have_subevents ) {
 
 
 
         // simple replay of all tuple entries
         // each recorded fill is inserted into all persistent weightname histos
         for ( size_t m = 0; m < _persistent.size(); ++m )
             for ( const auto & f : _evgroup[0]->fills() )
                 _persistent[m]->fill( f.first, f.second * weight[0][m] );
 
 
 
       } else {
 
 
 
         // outer index is subevent, inner index is jets in the event
         vector<vector<Fill<T>>> linedUpXs
             = match_fills<T>(_evgroup, {typename T::FillType(), 0.0});
         commit<T>( _persistent, linedUpXs, weight );
 
 
 
       }
       _evgroup.clear();
       _active.reset();
   }
 
   template <>
   void Wrapper<YODA::Counter>::pushToPersistent(const vector<valarray<double> >& weight) {
     for ( size_t m = 0; m < _persistent.size(); ++m ) {
       for ( size_t n = 0; n < _evgroup.size(); ++n ) {
         for ( const auto & f : _evgroup[n]->fills() ) {
           _persistent[m]->fill( f.second * weight[n][m] );
         }
       }
     }
 
     _evgroup.clear();
     _active.reset();
   }
 
   template <>
   void Wrapper<YODA::Scatter1D>::pushToPersistent(const vector<valarray<double> >& weight) {
 
     cout << ("WARNING: filling scatters in the event loop is not a well-defined behavior!!") << endl;
-
     _evgroup.clear();
     _active.reset();
   }
 
   template <>
   void Wrapper<YODA::Scatter2D>::pushToPersistent(const vector<valarray<double> >& weight) {
 
     cout << ("WARNING: filling scatters in the event loop is not a well-defined behavior!!") << endl;
-
     _evgroup.clear();
     _active.reset();
   }
 
   template <>
   void Wrapper<YODA::Scatter3D>::pushToPersistent(const vector<valarray<double> >& weight) {
 
     cout << ("WARNING: filling scatters in the event loop is not a well-defined behavior!!") << endl;
-
     _evgroup.clear();
     _active.reset();
   }
 
   // explicitly instantiate all wrappers
 
   template class Wrapper<YODA::Histo1D>;
   template class Wrapper<YODA::Histo2D>;
   template class Wrapper<YODA::Profile1D>;
   template class Wrapper<YODA::Profile2D>;
   template class Wrapper<YODA::Counter>;
   template class Wrapper<YODA::Scatter1D>;
   template class Wrapper<YODA::Scatter2D>;
   template class Wrapper<YODA::Scatter3D>;
 
 }
diff --git a/test/testApi.cc b/test/testApi.cc
--- a/test/testApi.cc
+++ b/test/testApi.cc
@@ -1,34 +1,34 @@
 #include "Rivet/AnalysisHandler.hh"
 #include "HepMC/GenEvent.h"
 #include "HepMC/IO_GenEvent.h"
 
 using namespace std;
 
 int main() {
   Rivet::AnalysisHandler ah;
-  Rivet::Log::setLevel("Rivet", Rivet::Log::TRACE);
+  Rivet::Log::setLevel("Rivet", Rivet::Log::DEBUG);
 
   // Specify the analyses to be used
   ah.addAnalysis("EXAMPLE");
   ah.addAnalyses({{ "MC_JETS", "EXAMPLE_CUTS", "EXAMPLE_SMEAR" }});
 
   std::ifstream file("testApi.hepmc");
   HepMC::IO_GenEvent hepmcio(file);
   HepMC::GenEvent* evt = hepmcio.read_next_event();
   while (evt) {
     // Analyse current event
     ah.analyze(*evt);
 
     // Clean up and get next event
     delete evt; evt = nullptr;
     hepmcio >> evt;
   }
   file.close();
 
   ah.setCrossSection(1.0);
   
   ah.finalize();
   ah.writeData("out.yoda");
 
   return 0;
 }