diff --git a/include/Rivet/Analysis.hh b/include/Rivet/Analysis.hh
--- a/include/Rivet/Analysis.hh
+++ b/include/Rivet/Analysis.hh
@@ -1,1031 +1,1039 @@
 // -*- C++ -*-
 #ifndef RIVET_Analysis_HH
 #define RIVET_Analysis_HH
 
 #include "Rivet/Config/RivetCommon.hh"
 #include "Rivet/AnalysisInfo.hh"
 #include "Rivet/Event.hh"
 #include "Rivet/Projection.hh"
 #include "Rivet/ProjectionApplier.hh"
 #include "Rivet/ProjectionHandler.hh"
 #include "Rivet/AnalysisLoader.hh"
 #include "Rivet/Tools/RivetYODA.hh"
 #include "Rivet/Tools/Logging.hh"
 #include "Rivet/Tools/ParticleUtils.hh"
 #include "Rivet/Tools/Cuts.hh"
 
 
 /// @def vetoEvent
 /// Preprocessor define for vetoing events, including the log message and return.
 #define vetoEvent                                                       \
   do { MSG_DEBUG("Vetoing event on line " << __LINE__ << " of " << __FILE__); return; } while(0)
 
 
 namespace Rivet {
 
 
   // Forward declaration
   class AnalysisHandler;
 
   /// @brief This is the base class of all analysis classes in Rivet.
   ///
   /// There are
   /// three virtual functions which should be implemented in base classes:
   ///
   /// void init() is called by Rivet before a run is started. Here the
   /// analysis class should book necessary histograms. The needed
   /// projections should probably rather be constructed in the
   /// constructor.
   ///
   /// void analyze(const Event&) is called once for each event. Here the
   /// analysis class should apply the necessary Projections and fill the
   /// histograms.
   ///
   /// void finalize() is called after a run is finished. Here the analysis
   /// class should do whatever manipulations are necessary on the
   /// histograms. Writing the histograms to a file is, however, done by
   /// the Rivet class.
   class Analysis : public ProjectionApplier {
 
     /// The AnalysisHandler is a friend.
     friend class AnalysisHandler;
 
 
   public:
 
     /// @name Standard constructors and destructors.
     //@{
 
     // /// The default constructor.
     // Analysis();
 
     /// Constructor
     Analysis(const std::string& name);
 
     /// The destructor.
     virtual ~Analysis() {}
 
     //@}
 
 
   public:
 
     /// @name Main analysis methods
     //@{
 
     /// Initialize this analysis object. A concrete class should here
     /// book all necessary histograms. An overridden function must make
     /// sure it first calls the base class function.
     virtual void init() { }
 
     /// Analyze one event. A concrete class should here apply the
     /// necessary projections on the \a event and fill the relevant
     /// histograms. An overridden function must make sure it first calls
     /// the base class function.
     virtual void analyze(const Event& event) = 0;
 
     /// Finalize this analysis object. A concrete class should here make
     /// all necessary operations on the histograms. Writing the
     /// histograms to a file is, however, done by the Rivet class. An
     /// overridden function must make sure it first calls the base class
     /// function.
     virtual void finalize() { }
 
     //@}
 
 
   public:
 
     /// @name Metadata
     /// Metadata is used for querying from the command line and also for
     /// building web pages and the analysis pages in the Rivet manual.
     //@{
 
     /// Get the actual AnalysisInfo object in which all this metadata is stored.
     const AnalysisInfo& info() const {
       assert(_info && "No AnalysisInfo object :O");
       return *_info;
     }
 
     /// @brief Get the name of the analysis.
     ///
     /// By default this is computed by combining the results of the experiment,
     /// year and Spires ID metadata methods and you should only override it if
     /// there's a good reason why those won't work.
     virtual std::string name() const {
       return (info().name().empty()) ? _defaultname : info().name();
     }
 
     /// Get the Inspire ID code for this analysis.
     virtual std::string inspireId() const {
       return info().inspireId();
     }
 
     /// Get the SPIRES ID code for this analysis (~deprecated).
     virtual std::string spiresId() const {
       return info().spiresId();
     }
 
     /// @brief Names & emails of paper/analysis authors.
     ///
     /// Names and email of authors in 'NAME \<EMAIL\>' format. The first
     /// name in the list should be the primary contact person.
     virtual std::vector<std::string> authors() const {
       return info().authors();
     }
 
     /// @brief Get a short description of the analysis.
     ///
     /// Short (one sentence) description used as an index entry.
     /// Use @a description() to provide full descriptive paragraphs
     /// of analysis details.
     virtual std::string summary() const {
       return info().summary();
     }
 
     /// @brief Get a full description of the analysis.
     ///
     /// Full textual description of this analysis, what it is useful for,
     /// what experimental techniques are applied, etc. Should be treated
     /// as a chunk of restructuredText (http://docutils.sourceforge.net/rst.html),
     /// with equations to be rendered as LaTeX with amsmath operators.
     virtual std::string description() const {
       return info().description();
     }
 
     /// @brief Information about the events needed as input for this analysis.
     ///
     /// Event types, energies, kinematic cuts, particles to be considered
     /// stable, etc. etc. Should be treated as a restructuredText bullet list
     /// (http://docutils.sourceforge.net/rst.html)
     virtual std::string runInfo() const {
       return info().runInfo();
     }
 
     /// Experiment which performed and published this analysis.
     virtual std::string experiment() const {
       return info().experiment();
     }
 
     /// Collider on which the experiment ran.
     virtual std::string collider() const {
       return info().collider();
     }
 
     /// When the original experimental analysis was published.
     virtual std::string year() const {
       return info().year();
     }
 
     /// Journal, and preprint references.
     virtual std::vector<std::string> references() const {
       return info().references();
     }
 
     /// BibTeX citation key for this article.
     virtual std::string bibKey() const {
       return info().bibKey();
     }
 
     /// BibTeX citation entry for this article.
     virtual std::string bibTeX() const {
       return info().bibTeX();
     }
 
     /// Whether this analysis is trusted (in any way!)
     virtual std::string status() const {
       return (info().status().empty()) ? "UNVALIDATED" : info().status();
     }
 
     /// Any work to be done on this analysis.
     virtual std::vector<std::string> todos() const {
       return info().todos();
     }
 
 
     /// Return the allowed pairs of incoming beams required by this analysis.
     virtual const std::vector<PdgIdPair>& requiredBeams() const {
       return info().beams();
     }
     /// Declare the allowed pairs of incoming beams required by this analysis.
     virtual Analysis& setRequiredBeams(const std::vector<PdgIdPair>& requiredBeams) {
       info().setBeams(requiredBeams);
       return *this;
     }
 
 
     /// Sets of valid beam energy pairs, in GeV
     virtual const std::vector<std::pair<double, double> >& requiredEnergies() const {
       return info().energies();
     }
     /// Declare the list of valid beam energy pairs, in GeV
     virtual Analysis& setRequiredEnergies(const std::vector<std::pair<double, double> >& requiredEnergies) {
       info().setEnergies(requiredEnergies);
       return *this;
     }
 
 
     /// Return true if this analysis needs to know the process cross-section.
     /// @todo Remove this and require HepMC >= 2.06
     bool needsCrossSection() const {
       return info().needsCrossSection();
     }
     /// Declare whether this analysis needs to know the process cross-section from the generator.
     /// @todo Remove this and require HepMC >= 2.06
     Analysis& setNeedsCrossSection(bool needed=true) {
       info().setNeedsCrossSection(needed);
       return *this;
     }
 
     //@}
 
 
     /// @name Internal metadata modifying methods
     //@{
 
     /// Get the actual AnalysisInfo object in which all this metadata is stored (non-const).
     AnalysisInfo& info() {
       assert(_info && "No AnalysisInfo object :O");
       return *_info;
     }
 
     //@}
 
 
     /// @name Run conditions
     //@{
 
     /// Incoming beams for this run
     const ParticlePair& beams() const;
 
     /// Incoming beam IDs for this run
     const PdgIdPair beamIds() const;
 
     /// Centre of mass energy for this run
     double sqrtS() const;
 
     //@}
 
 
     /// @name Analysis / beam compatibility testing
     //@{
 
     /// Check if analysis is compatible with the provided beam particle IDs and energies
     bool isCompatible(const ParticlePair& beams) const;
 
     /// Check if analysis is compatible with the provided beam particle IDs and energies
     bool isCompatible(PdgId beam1, PdgId beam2, double e1, double e2) const;
 
     /// Check if analysis is compatible with the provided beam particle IDs and energies
     bool isCompatible(const PdgIdPair& beams, const std::pair<double,double>& energies) const;
 
     //@}
 
 
     /// Set the cross section from the generator
     Analysis& setCrossSection(double xs);
 
     /// Access the controlling AnalysisHandler object.
     AnalysisHandler& handler() const { return *_analysishandler; }
 
 
   protected:
 
     /// Get a Log object based on the name() property of the calling analysis object.
     Log& getLog() const;
 
     /// Get the process cross-section in pb. Throws if this hasn't been set.
     double crossSection() const;
 
     /// Get the process cross-section per generated event in pb. Throws if this
     /// hasn't been set.
     double crossSectionPerEvent() const;
 
     /// Get the number of events seen (via the analysis handler). Use in the
     /// finalize phase only.
     size_t numEvents() const;
 
     /// Get the sum of event weights seen (via the analysis handler). Use in the
     /// finalize phase only.
     double sumOfWeights() const;
 
 
   protected:
 
     /// @name Histogram paths
     //@{
 
     /// Get the canonical histogram "directory" path for this analysis.
     const std::string histoDir() const;
 
     /// Get the canonical histogram path for the named histogram in this analysis.
     const std::string histoPath(const std::string& hname) const;
 
     /// Get the canonical histogram path for the numbered histogram in this analysis.
     const std::string histoPath(unsigned int datasetId, unsigned int xAxisId, unsigned int yAxisId) const;
 
     /// Get the internal histogram name for given d, x and y (cf. HepData)
     const std::string makeAxisCode(unsigned int datasetId, unsigned int xAxisId, unsigned int yAxisId) const;
 
     //@}
 
 
     /// @name Histogram reference data
     //@{
 
     /// Get reference data for a named histo
     /// @todo Move to the templated version when we have C++11 and can have a default fn template type
     const YODA::Scatter2D& refData(const string& hname) const;
 
     /// Get reference data for a numbered histo
     /// @todo Move to the templated version when we have C++11 and can have a default fn template type
     const YODA::Scatter2D& refData(unsigned int datasetId, unsigned int xAxisId, unsigned int yAxisId) const;
 
     /// Get reference data for a named histo
     /// @todo Would be nice to just use these and ditch the S2D no-template version,
     ///   but we need C++11 for default args in function templates
     // template <typename T=Scatter2D>
     /// @todo SFINAE to ensure that the type inherits from YODA::AnalysisObject?
     template <typename T>
     const T& refData(const string& hname) const {
       _cacheRefData();
       MSG_TRACE("Using histo bin edges for " << name() << ":" << hname);
       if (!_refdata[hname]) {
         MSG_ERROR("Can't find reference histogram " << hname);
         throw Exception("Reference data " + hname + " not found.");
       }
       return dynamic_cast<T&>(*_refdata[hname]);
     }
 
     /// Get reference data for a numbered histo
     /// @todo Would be nice to just use these and ditch the S2D no-template version,
     ///   but we need C++11 for default args in function templates
     // template <typename T=Scatter2D>
     /// @todo SFINAE to ensure that the type inherits from YODA::AnalysisObject?
     template <typename T>
     const T& refData(unsigned int datasetId, unsigned int xAxisId, unsigned int yAxisId) const {
       const string hname = makeAxisCode(datasetId, xAxisId, yAxisId);
       return refData(hname);
     }
 
     //@}
 
 
     /// @name Counter booking
     //@{
 
     /// Book a counter.
-    CounterPtr bookCounter(const std::string& name,
+    CounterPtr& bookCounter(const std::string& name,
                            const std::string& title="");
                            // const std::string& valtitle=""
 
     /// Book a counter, using a path generated from the dataset and axis ID codes
     ///
     /// The paper, dataset and x/y-axis IDs will be used to build the histo name in the HepData standard way.
-    CounterPtr bookCounter(unsigned int datasetId, unsigned int xAxisId, unsigned int yAxisId,
+    CounterPtr& bookCounter(unsigned int datasetId, unsigned int xAxisId, unsigned int yAxisId,
                            const std::string& title="");
                            // const std::string& valtitle=""
 
     //@}
 
 
     /// @name 1D histogram booking
     //@{
 
     /// Book a 1D histogram with @a nbins uniformly distributed across the range @a lower - @a upper .
-    Histo1DPtr bookHisto1D(const std::string& name,
+    Histo1DPtr& bookHisto1D(const std::string& name,
                            size_t nbins, double lower, double upper,
                            const std::string& title="",
                            const std::string& xtitle="",
                            const std::string& ytitle="");
 
     /// Book a 1D histogram with non-uniform bins defined by the vector of bin edges @a binedges .
-    Histo1DPtr bookHisto1D(const std::string& name,
+    Histo1DPtr& bookHisto1D(const std::string& name,
                            const std::vector<double>& binedges,
                            const std::string& title="",
                            const std::string& xtitle="",
                            const std::string& ytitle="");
 
     /// Book a 1D histogram with binning from a reference scatter.
-    Histo1DPtr bookHisto1D(const std::string& name,
+    Histo1DPtr& bookHisto1D(const std::string& name,
                            const Scatter2D& refscatter,
                            const std::string& title="",
                            const std::string& xtitle="",
                            const std::string& ytitle="");
 
     /// Book a 1D histogram, using the binnings in the reference data histogram.
-    Histo1DPtr bookHisto1D(const std::string& name,
+    Histo1DPtr& bookHisto1D(const std::string& name,
                            const std::string& title="",
                            const std::string& xtitle="",
                            const std::string& ytitle="");
 
     /// Book a 1D histogram, using the binnings in the reference data histogram.
     ///
     /// The paper, dataset and x/y-axis IDs will be used to build the histo name in the HepData standard way.
-    Histo1DPtr bookHisto1D(unsigned int datasetId, unsigned int xAxisId, unsigned int yAxisId,
+    Histo1DPtr& bookHisto1D(unsigned int datasetId, unsigned int xAxisId, unsigned int yAxisId,
                            const std::string& title="",
                            const std::string& xtitle="",
                            const std::string& ytitle="");
 
     //@}
 
 
     /// @name 2D histogram booking
     //@{
 
     /// Book a 2D histogram with @a nxbins and @a nybins uniformly
     /// distributed across the ranges @a xlower - @a xupper and @a
     /// ylower - @a yupper respectively along the x- and y-axis.
-    Histo2DPtr bookHisto2D(const std::string& name,
+    Histo2DPtr& bookHisto2D(const std::string& name,
                            size_t nxbins, double xlower, double xupper,
                            size_t nybins, double ylower, double yupper,
                            const std::string& title="",
                            const std::string& xtitle="",
                            const std::string& ytitle="",
                            const std::string& ztitle="");
 
     /// Book a 2D histogram with non-uniform bins defined by the
     /// vectorx of bin edges @a xbinedges and @a ybinedges.
-    Histo2DPtr bookHisto2D(const std::string& name,
+    Histo2DPtr& bookHisto2D(const std::string& name,
                            const std::vector<double>& xbinedges,
                            const std::vector<double>& ybinedges,
                            const std::string& title="",
                            const std::string& xtitle="",
                            const std::string& ytitle="",
                            const std::string& ztitle="");
 
     // /// Book a 2D histogram with binning from a reference scatter.
     // Histo2DPtr bookHisto2D(const std::string& name,
     //                        const Scatter3D& refscatter,
     //                        const std::string& title="",
     //                        const std::string& xtitle="",
     //                        const std::string& ytitle="",
     //                        const std::string& ztitle="");
 
     // /// Book a 2D histogram, using the binnings in the reference data histogram.
     // Histo2DPtr bookHisto2D(const std::string& name,
     //                        const std::string& title="",
     //                        const std::string& xtitle="",
     //                        const std::string& ytitle="",
     //                        const std::string& ztitle="");
 
     // /// Book a 2D histogram, using the binnings in the reference data histogram.
     // ///
     // /// The paper, dataset and x/y-axis IDs will be used to build the histo name in the HepData standard way.
     // Histo2DPtr bookHisto2D(unsigned int datasetId, unsigned int xAxisId, unsigned int yAxisId,
     //                        const std::string& title="",
     //                        const std::string& xtitle="",
     //                        const std::string& ytitle="",
     //                        const std::string& ztitle="");
 
     //@}
 
 
     /// @name 1D profile histogram booking
     //@{
 
     /// Book a 1D profile histogram with @a nbins uniformly distributed across the range @a lower - @a upper .
-    Profile1DPtr bookProfile1D(const std::string& name,
+    Profile1DPtr& bookProfile1D(const std::string& name,
                                size_t nbins, double lower, double upper,
                                const std::string& title="",
                                const std::string& xtitle="",
                                const std::string& ytitle="");
 
     /// Book a 1D profile histogram with non-uniform bins defined by the vector of bin edges @a binedges .
-    Profile1DPtr bookProfile1D(const std::string& name,
+    Profile1DPtr& bookProfile1D(const std::string& name,
                                const std::vector<double>& binedges,
                                const std::string& title="",
                                const std::string& xtitle="",
                                const std::string& ytitle="");
 
     /// Book a 1D profile histogram with binning from a reference scatter.
-    Profile1DPtr bookProfile1D(const std::string& name,
+    Profile1DPtr& bookProfile1D(const std::string& name,
                                const Scatter2D& refscatter,
                                const std::string& title="",
                                const std::string& xtitle="",
                                const std::string& ytitle="");
 
     /// Book a 1D profile histogram, using the binnings in the reference data histogram.
-    Profile1DPtr bookProfile1D(const std::string& name,
+    Profile1DPtr& bookProfile1D(const std::string& name,
                                const std::string& title="",
                                const std::string& xtitle="",
                                const std::string& ytitle="");
 
     /// Book a 1D profile histogram, using the binnings in the reference data histogram.
     ///
     /// The paper, dataset and x/y-axis IDs will be used to build the histo name in the HepData standard way.
-    Profile1DPtr bookProfile1D(unsigned int datasetId, unsigned int xAxisId, unsigned int yAxisId,
+    Profile1DPtr& bookProfile1D(unsigned int datasetId, unsigned int xAxisId, unsigned int yAxisId,
                                const std::string& title="",
                                const std::string& xtitle="",
                                const std::string& ytitle="");
 
     //@}
 
 
     /// @name 2D profile histogram booking
     //@{
 
     /// Book a 2D profile histogram with @a nxbins and @a nybins uniformly
     /// distributed across the ranges @a xlower - @a xupper and @a ylower - @a
     /// yupper respectively along the x- and y-axis.
-    Profile2DPtr bookProfile2D(const std::string& name,
+    Profile2DPtr& bookProfile2D(const std::string& name,
                                size_t nxbins, double xlower, double xupper,
                                size_t nybins, double ylower, double yupper,
                                const std::string& title="",
                                const std::string& xtitle="",
                                const std::string& ytitle="",
                                const std::string& ztitle="");
 
     /// Book a 2D profile histogram with non-uniform bins defined by the vectorx
     /// of bin edges @a xbinedges and @a ybinedges.
-    Profile2DPtr bookProfile2D(const std::string& name,
+    Profile2DPtr& bookProfile2D(const std::string& name,
                                const std::vector<double>& xbinedges,
                                const std::vector<double>& ybinedges,
                                const std::string& title="",
                                const std::string& xtitle="",
                                const std::string& ytitle="",
                                const std::string& ztitle="");
 
     /// Book a 2D profile histogram with binning from a reference scatter.
     // Profile2DPtr bookProfile2D(const std::string& name,
     //                            const Scatter3D& refscatter,
     //                            const std::string& title="",
     //                            const std::string& xtitle="",
     //                            const std::string& ytitle="",
     //                            const std::string& ztitle="");
 
     // /// Book a 2D profile histogram, using the binnings in the reference data histogram.
     // Profile2DPtr bookProfile2D(const std::string& name,
     //                            const std::string& title="",
     //                            const std::string& xtitle="",
     //                            const std::string& ytitle="",
     //                            const std::string& ztitle="");
 
     // /// Book a 2D profile histogram, using the binnings in the reference data histogram.
     // ///
     // /// The paper, dataset and x/y-axis IDs will be used to build the histo name in the HepData standard way.
     // Profile2DPtr bookProfile2D(unsigned int datasetId, unsigned int xAxisId, unsigned int yAxisId,
     //                            const std::string& title="",
     //                            const std::string& xtitle="",
     //                            const std::string& ytitle="",
     //                            const std::string& ztitle="");
 
     //@}
 
 
     /// @name 2D scatter booking
     //@{
 
     /// @brief Book a 2-dimensional data point set with the given name.
     ///
     /// @note Unlike histogram booking, scatter booking by default makes no
     /// attempt to use reference data to pre-fill the data object. If you want
     /// this, which is sometimes useful e.g. when the x-position is not really
     /// meaningful and can't be extracted from the data, then set the @a
     /// copy_pts parameter to true. This creates points to match the reference
     /// data's x values and errors, but with the y values and errors zeroed...
     /// assuming that there is a reference histo with the same name: if there
     /// isn't, an exception will be thrown.
 
-    /*
-    Scatter2DPtr bookScatter2D(const std::string& name,
+    Scatter2DPtr& bookScatter2D(const std::string& name,
                                bool copy_pts=false,
                                const std::string& title="",
                                const std::string& xtitle="",
                                const std::string& ytitle="");
 
     /// @brief Book a 2-dimensional data point set, using the binnings in the reference data histogram.
     ///
     /// The paper, dataset and x/y-axis IDs will be used to build the histo name in the HepData standard way.
     ///
     /// @note Unlike histogram booking, scatter booking by default makes no
     /// attempt to use reference data to pre-fill the data object. If you want
     /// this, which is sometimes useful e.g. when the x-position is not really
     /// meaningful and can't be extracted from the data, then set the @a
     /// copy_pts parameter to true. This creates points to match the reference
     /// data's x values and errors, but with the y values and errors zeroed.
-    Scatter2DPtr bookScatter2D(unsigned int datasetId, unsigned int xAxisId, unsigned int yAxisId,
+    Scatter2DPtr& bookScatter2D(unsigned int datasetId, unsigned int xAxisId, unsigned int yAxisId,
                                bool copy_pts=false,
                                const std::string& title="",
                                const std::string& xtitle="",
                                const std::string& ytitle="");
 
     /// @brief Book a 2-dimensional data point set with equally spaced x-points in a range.
     ///
     /// The y values and errors will be set to 0.
-    Scatter2DPtr bookScatter2D(const std::string& name,
+    Scatter2DPtr& bookScatter2D(const std::string& name,
                                size_t npts, double lower, double upper,
                                const std::string& title="",
                                const std::string& xtitle="",
                                const std::string& ytitle="");
 
     /// @brief Book a 2-dimensional data point set based on provided contiguous "bin edges".
     ///
     /// The y values and errors will be set to 0.
-    Scatter2DPtr bookScatter2D(const std::string& hname,
+    Scatter2DPtr& bookScatter2D(const std::string& hname,
                                const std::vector<double>& binedges,
                                const std::string& title,
                                const std::string& xtitle,
                                const std::string& ytitle);
 
     //@}
-    */
 
 
   public:
 
 
     /// @name Analysis object manipulation
     /// @todo Should really be protected: only public to keep BinnedHistogram happy for now...
     //@{
 
     /// Multiplicatively scale the given counter, @a cnt, by factor @s factor.
     void scale(CounterPtr cnt, double factor);
 
     /// Multiplicatively scale the given counters, @a cnts, by factor @s factor.
     /// @note Constness intentional, if weird, to allow passing rvalue refs of smart ptrs (argh)
     /// @todo Use SFINAE for a generic iterable of CounterPtrs
     void scale(const std::vector<CounterPtr>& cnts, double factor) {
       for (auto& c : cnts) scale(c, factor);
     }
     /// @todo YUCK!
     template <std::size_t array_size>
     void scale(const CounterPtr (&cnts)[array_size], double factor) {
       // for (size_t i = 0; i < std::extent<decltype(cnts)>::value; ++i) scale(cnts[i], factor);
       for (auto& c : cnts) scale(c, factor);
     }
 
 
     /// Normalize the given histogram, @a histo, to area = @a norm.
     void normalize(Histo1DPtr histo, double norm=1.0, bool includeoverflows=true);
 
     /// Normalize the given histograms, @a histos, to area = @a norm.
     /// @note Constness intentional, if weird, to allow passing rvalue refs of smart ptrs (argh)
     /// @todo Use SFINAE for a generic iterable of Histo1DPtrs
     void normalize(const std::vector<Histo1DPtr>& histos, double norm=1.0, bool includeoverflows=true) {
       for (auto& h : histos) normalize(h, norm, includeoverflows);
     }
     /// @todo YUCK!
     template <std::size_t array_size>
     void normalize(const Histo1DPtr (&histos)[array_size], double norm=1.0, bool includeoverflows=true) {
       for (auto& h : histos) normalize(h, norm, includeoverflows);
     }
 
     /// Multiplicatively scale the given histogram, @a histo, by factor @s factor.
     void scale(Histo1DPtr histo, double factor);
 
     /// Multiplicatively scale the given histograms, @a histos, by factor @s factor.
     /// @note Constness intentional, if weird, to allow passing rvalue refs of smart ptrs (argh)
     /// @todo Use SFINAE for a generic iterable of Histo1DPtrs
     void scale(const std::vector<Histo1DPtr>& histos, double factor) {
       for (auto& h : histos) scale(h, factor);
     }
     /// @todo YUCK!
     template <std::size_t array_size>
     void scale(const Histo1DPtr (&histos)[array_size], double factor) {
       for (auto& h : histos) scale(h, factor);
     }
 
 
     /// Normalize the given histogram, @a histo, to area = @a norm.
     void normalize(Histo2DPtr histo, double norm=1.0, bool includeoverflows=true);
 
     /// Normalize the given histograms, @a histos, to area = @a norm.
     /// @note Constness intentional, if weird, to allow passing rvalue refs of smart ptrs (argh)
     /// @todo Use SFINAE for a generic iterable of Histo2DPtrs
     void normalize(const std::vector<Histo2DPtr>& histos, double norm=1.0, bool includeoverflows=true) {
       for (auto& h : histos) normalize(h, norm, includeoverflows);
     }
     /// @todo YUCK!
     template <std::size_t array_size>
     void normalize(const Histo2DPtr (&histos)[array_size], double norm=1.0, bool includeoverflows=true) {
       for (auto& h : histos) normalize(h, norm, includeoverflows);
     }
 
     /// Multiplicatively scale the given histogram, @a histo, by factor @s factor.
     void scale(Histo2DPtr histo, double factor);
 
     /// Multiplicatively scale the given histograms, @a histos, by factor @s factor.
     /// @note Constness intentional, if weird, to allow passing rvalue refs of smart ptrs (argh)
     /// @todo Use SFINAE for a generic iterable of Histo2DPtrs
     void scale(const std::vector<Histo2DPtr>& histos, double factor) {
       for (auto& h : histos) scale(h, factor);
     }
     /// @todo YUCK!
     template <std::size_t array_size>
     void scale(const Histo2DPtr (&histos)[array_size], double factor) {
       for (auto& h : histos) scale(h, factor);
     }
 
 
     /// Helper for counter division.
     ///
     /// @note Assigns to the (already registered) output scatter, @a s. Preserves the path information of the target.
     void divide(CounterPtr c1, CounterPtr c2, Scatter1DPtr s) const;
 
     /// Helper for histogram division with raw YODA objects.
     ///
     /// @note Assigns to the (already registered) output scatter, @a s. Preserves the path information of the target.
     void divide(const YODA::Counter& c1, const YODA::Counter& c2, Scatter1DPtr s) const;
 
 
     /// Helper for histogram division.
     ///
     /// @note Assigns to the (already registered) output scatter, @a s. Preserves the path information of the target.
     void divide(Histo1DPtr h1, Histo1DPtr h2, Scatter2DPtr s) const;
 
     /// Helper for histogram division with raw YODA objects.
     ///
     /// @note Assigns to the (already registered) output scatter, @a s. Preserves the path information of the target.
     void divide(const YODA::Histo1D& h1, const YODA::Histo1D& h2, Scatter2DPtr s) const;
 
 
     /// Helper for profile histogram division.
     ///
     /// @note Assigns to the (already registered) output scatter, @a s. Preserves the path information of the target.
     void divide(Profile1DPtr p1, Profile1DPtr p2, Scatter2DPtr s) const;
 
     /// Helper for profile histogram division with raw YODA objects.
     ///
     /// @note Assigns to the (already registered) output scatter, @a s. Preserves the path information of the target.
     void divide(const YODA::Profile1D& p1, const YODA::Profile1D& p2, Scatter2DPtr s) const;
 
 
     /// Helper for 2D histogram division.
     ///
     /// @note Assigns to the (already registered) output scatter, @a s. Preserves the path information of the target.
     void divide(Histo2DPtr h1, Histo2DPtr h2, Scatter3DPtr s) const;
 
     /// Helper for 2D histogram division with raw YODA objects.
     ///
     /// @note Assigns to the (already registered) output scatter, @a s. Preserves the path information of the target.
     void divide(const YODA::Histo2D& h1, const YODA::Histo2D& h2, Scatter3DPtr s) const;
 
 
     /// Helper for 2D profile histogram division.
     ///
     /// @note Assigns to the (already registered) output scatter, @a s. Preserves the path information of the target.
     void divide(Profile2DPtr p1, Profile2DPtr p2, Scatter3DPtr s) const;
 
     /// Helper for 2D profile histogram division with raw YODA objects
     ///
     /// @note Assigns to the (already registered) output scatter, @a s.  Preserves the path information of the target.
     void divide(const YODA::Profile2D& p1, const YODA::Profile2D& p2, Scatter3DPtr s) const;
 
 
     /// Helper for histogram efficiency calculation.
     ///
     /// @note Assigns to the (already registered) output scatter, @a s. Preserves the path information of the target.
     void efficiency(Histo1DPtr h1, Histo1DPtr h2, Scatter2DPtr s) const;
 
     /// Helper for histogram efficiency calculation.
     ///
     /// @note Assigns to the (already registered) output scatter, @a s. Preserves the path information of the target.
     void efficiency(const YODA::Histo1D& h1, const YODA::Histo1D& h2, Scatter2DPtr s) const;
 
 
     /// Helper for histogram asymmetry calculation.
     ///
     /// @note Assigns to the (already registered) output scatter, @a s. Preserves the path information of the target.
     void asymm(Histo1DPtr h1, Histo1DPtr h2, Scatter2DPtr s) const;
 
     /// Helper for histogram asymmetry calculation.
     ///
     /// @note Assigns to the (already registered) output scatter, @a s. Preserves the path information of the target.
     void asymm(const YODA::Histo1D& h1, const YODA::Histo1D& h2, Scatter2DPtr s) const;
 
 
     /// Helper for converting a differential histo to an integral one.
     ///
     /// @note Assigns to the (already registered) output scatter, @a s. Preserves the path information of the target.
     void integrate(Histo1DPtr h, Scatter2DPtr s) const;
 
     /// Helper for converting a differential histo to an integral one.
     ///
     /// @note Assigns to the (already registered) output scatter, @a s. Preserves the path information of the target.
     void integrate(const Histo1D& h, Scatter2DPtr s) const;
 
     //@}
 
 
   public:
 
     /// List of registered analysis data objects
     const vector<shared_ptr<MultiweightAOPtr> >& analysisObjects() const {
       return _analysisobjects;
     }
 
 
   protected:
 
     /// @name Data object registration, retrieval, and removal
     //@{
 
     /// Register a data object in the histogram system
-    void addAnalysisObject(shared_ptr<MultiweightAOPtr> ao);
+    void addAnalysisObject(const shared_ptr<MultiweightAOPtr>& ao);
+
+    /// @todo we need these separately since we *only* want to call this for scatters?
+    void addAnalysisObject(const shared_ptr<Scatter1DPtr>& ao);
+    void addAnalysisObject(const shared_ptr<Scatter2DPtr>& ao);
+    void addAnalysisObject(const shared_ptr<Scatter3DPtr>& ao);
 
     /// Get a data object from the histogram system
     /// @todo Use this default function template arg in C++11
     // template <typename AO=AnalysisObjectPtr>
     template <typename AOPtr>
     const AOPtr& getAnalysisObject(const std::string& name) const {
       for (const shared_ptr<MultiweightAOPtr>& ao : analysisObjects()) {
         if ((*ao)->path() == histoPath(name)) return dynamic_cast<const AOPtr&>(*ao);
       }
       throw Exception("Data object " + histoPath(name) + " not found");
     }
 
     /// Get a data object from the histogram system (non-const)
     /// @todo Use this default function template arg in C++11
     // template <typename AO=AnalysisObjectPtr>
     template <typename AOPtr>
     AOPtr& getAnalysisObject(const std::string& name) {
-      for (shared_ptr<MultiweightAOPtr>& ao : _analysisobjects) {
+      for (const shared_ptr<MultiweightAOPtr>& ao : _analysisobjects) {
         if ((*ao)->path() == histoPath(name)) return dynamic_cast<AOPtr&>(*ao);
       }
 
       throw Exception("Data object " + histoPath(name) + " not found");
     }
 
     /// Unregister a data object from the histogram system (by name)
     void removeAnalysisObject(const std::string& path);
 
     /// Unregister a data object from the histogram system (by pointer)
     void removeAnalysisObject(const MultiweightAOPtr& ao);
 
+    void removeAnalysisObject(const Scatter1DPtr& ao);
+    void removeAnalysisObject(const Scatter2DPtr& ao);
+    void removeAnalysisObject(const Scatter3DPtr& ao);
+
 
     /// Get a named Histo1D object from the histogram system
     const Histo1DPtr getHisto1D(const std::string& name) const {
       return getAnalysisObject<Histo1DPtr>(name);
     }
 
     /// Get a named Histo1D object from the histogram system (non-const)
     Histo1DPtr getHisto1D(const std::string& name) {
       return getAnalysisObject<Histo1DPtr>(name);
     }
 
     /// Get a Histo1D object from the histogram system by axis ID codes (non-const)
     const Histo1DPtr getHisto1D(unsigned int datasetId, unsigned int xAxisId, unsigned int yAxisId) const {
       return getAnalysisObject<Histo1DPtr>(makeAxisCode(datasetId, xAxisId, yAxisId));
     }
 
     /// Get a Histo1D object from the histogram system by axis ID codes (non-const)
     Histo1DPtr getHisto1D(unsigned int datasetId, unsigned int xAxisId, unsigned int yAxisId) {
       return getAnalysisObject<Histo1DPtr>(makeAxisCode(datasetId, xAxisId, yAxisId));
     }
 
 
     // /// Get a named Histo2D object from the histogram system
     // const Histo2DPtr getHisto2D(const std::string& name) const {
     //   return getAnalysisObject<Histo2D>(name);
     // }
 
     // /// Get a named Histo2D object from the histogram system (non-const)
     // Histo2DPtr getHisto2D(const std::string& name) {
     //   return getAnalysisObject<Histo2D>(name);
     // }
 
     // /// Get a Histo2D object from the histogram system by axis ID codes (non-const)
     // const Histo2DPtr getHisto2D(unsigned int datasetId, unsigned int xAxisId, unsigned int yAxisId) const {
     //   return getAnalysisObject<Histo2D>(makeAxisCode(datasetId, xAxisId, yAxisId));
     // }
 
     // /// Get a Histo2D object from the histogram system by axis ID codes (non-const)
     // Histo2DPtr getHisto2D(unsigned int datasetId, unsigned int xAxisId, unsigned int yAxisId) {
     //   return getAnalysisObject<Histo2D>(makeAxisCode(datasetId, xAxisId, yAxisId));
     // }
 
 
     /// Get a named Profile1D object from the histogram system
     const Profile1DPtr getProfile1D(const std::string& name) const {
       return getAnalysisObject<Profile1DPtr>(name);
     }
 
     /// Get a named Profile1D object from the histogram system (non-const)
     Profile1DPtr getProfile1D(const std::string& name) {
       return getAnalysisObject<Profile1DPtr>(name);
     }
 
     /// Get a Profile1D object from the histogram system by axis ID codes (non-const)
     const Profile1DPtr getProfile1D(unsigned int datasetId, unsigned int xAxisId, unsigned int yAxisId) const {
       return getAnalysisObject<Profile1DPtr>(makeAxisCode(datasetId, xAxisId, yAxisId));
     }
 
     /// Get a Profile1D object from the histogram system by axis ID codes (non-const)
     Profile1DPtr getProfile1D(unsigned int datasetId, unsigned int xAxisId, unsigned int yAxisId) {
       return getAnalysisObject<Profile1DPtr>(makeAxisCode(datasetId, xAxisId, yAxisId));
     }
 
 
     // /// Get a named Profile2D object from the histogram system
     // const Profile2DPtr getProfile2D(const std::string& name) const {
     //   return getAnalysisObject<Profile2D>(name);
     // }
 
     // /// Get a named Profile2D object from the histogram system (non-const)
     // Profile2DPtr getProfile2D(const std::string& name) {
     //   return getAnalysisObject<Profile2D>(name);
     // }
 
     // /// Get a Profile2D object from the histogram system by axis ID codes (non-const)
     // const Profile2DPtr getProfile2D(unsigned int datasetId, unsigned int xAxisId, unsigned int yAxisId) const {
     //   return getAnalysisObject<Profile2D>(makeAxisCode(datasetId, xAxisId, yAxisId));
     // }
 
     // /// Get a Profile2D object from the histogram system by axis ID codes (non-const)
     // Profile2DPtr getProfile2D(unsigned int datasetId, unsigned int xAxisId, unsigned int yAxisId) {
     //   return getAnalysisObject<Profile2D>(makeAxisCode(datasetId, xAxisId, yAxisId));
     // }
 
 
     /// Get a named Scatter2D object from the histogram system
     const Scatter2DPtr getScatter2D(const std::string& name) const {
       return getAnalysisObject<Scatter2DPtr>(name);
     }
 
     /// Get a named Scatter2D object from the histogram system (non-const)
     Scatter2DPtr getScatter2D(const std::string& name) {
       return getAnalysisObject<Scatter2DPtr>(name);
     }
 
     /// Get a Scatter2D object from the histogram system by axis ID codes (non-const)
     const Scatter2DPtr getScatter2D(unsigned int datasetId, unsigned int xAxisId, unsigned int yAxisId) const {
       return getAnalysisObject<Scatter2DPtr>(makeAxisCode(datasetId, xAxisId, yAxisId));
     }
 
     /// Get a Scatter2D object from the histogram system by axis ID codes (non-const)
     Scatter2DPtr getScatter2D(unsigned int datasetId, unsigned int xAxisId, unsigned int yAxisId) {
       return getAnalysisObject<Scatter2DPtr>(makeAxisCode(datasetId, xAxisId, yAxisId));
     }
 
     //@}
 
 
   private:
 
     /// Name passed to constructor (used to find .info analysis data file, and as a fallback)
     string _defaultname;
 
     /// Pointer to analysis metadata object
     unique_ptr<AnalysisInfo> _info;
 
     /// Storage of all plot objects
     /// @todo Make this a map for fast lookup by path?
     vector<shared_ptr<MultiweightAOPtr> > _analysisobjects;
+    vector<shared_ptr<AnalysisObjectPtr> > _scatters;
 
     /// @name Cross-section variables
     //@{
     double _crossSection;
     bool _gotCrossSection;
     //@}
 
     /// The controlling AnalysisHandler object.
     AnalysisHandler* _analysishandler;
 
     /// Collection of cached refdata to speed up many autobookings: the
     /// reference data file should only be read once.
     mutable std::map<std::string, YODA::AnalysisObjectPtr> _refdata;
 
 
   private:
 
     /// @name Utility functions
     //@{
 
     /// Get the reference data for this paper and cache it.
     void _cacheRefData() const;
 
     //@}
 
 
     /// The assignment operator is private and must never be called.
     /// In fact, it should not even be implemented.
     Analysis& operator=(const Analysis&);
 
   };
 
 
 }
 
 
 // Include definition of analysis plugin system so that analyses automatically see it when including Analysis.hh
 #include "Rivet/AnalysisBuilder.hh"
 
 /// @def DECLARE_RIVET_PLUGIN
 /// Preprocessor define to prettify the global-object plugin hook mechanism.
 #define DECLARE_RIVET_PLUGIN(clsname) Rivet::AnalysisBuilder<clsname> plugin_ ## clsname
 
 /// @def DECLARE_ALIASED_RIVET_PLUGIN
 /// Preprocessor define to prettify the global-object plugin hook mechanism, with an extra alias name for this analysis.
 // #define DECLARE_ALIASED_RIVET_PLUGIN(clsname, alias) Rivet::AnalysisBuilder<clsname> plugin_ ## clsname ## ( ## #alias ## )
 #define DECLARE_ALIASED_RIVET_PLUGIN(clsname, alias) DECLARE_RIVET_PLUGIN(clsname)( #alias )
 
 /// @def DEFAULT_RIVET_ANA_CONSTRUCTOR
 /// Preprocessor define to prettify the manky constructor with name string argument
 #define DEFAULT_RIVET_ANALYSIS_CTOR(clsname) clsname() : Analysis(# clsname) {}
 
 // DEPRECATED ALIAS
 #define DEFAULT_RIVET_ANA_CONSTRUCTOR(clsname) DEFAULT_RIVET_ANALYSIS_CTOR(clsname)
 
 
 #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,404 +1,405 @@
 #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::Histo1D> Histo1DPtr;
     typedef std::shared_ptr<YODA::Histo2D> Histo2DPtr;
     typedef std::shared_ptr<YODA::Profile1D> Profile1DPtr;
     typedef std::shared_ptr<YODA::Profile2D> Profile2DPtr;
     typedef std::shared_ptr<YODA::Scatter1D> Scatter1DPtr;
     typedef std::shared_ptr<YODA::Scatter2D> Scatter2DPtr;
     typedef std::shared_ptr<YODA::Scatter3D> Scatter3DPtr;
     typedef std::shared_ptr<YODA::Counter> CounterPtr;
 }
 
 
 
 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;
 
             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() :
                 _scatter(YODA::Scatter1DPtr()) { }
 
-            Scatter1DPtr(const YODA::Scatter1DPtr& p) :
-                _scatter(p) { }
+            Scatter1DPtr(const YODA::Scatter1D& p) :
+                _scatter(make_shared<YODA::Scatter1D>(p)) { }
 
             bool operator!() const { return !_scatter; }
             operator bool() const { return bool(_scatter); }
 
             YODA::Scatter1D* operator->() { return _scatter.get(); }
 
             YODA::Scatter1D* operator->() const { return _scatter.get(); }
 
             YODA::Scatter1D & operator*() { return *_scatter; }
 
             const YODA::Scatter1D & operator*() const { return *_scatter; }
 
         protected:
             YODA::Scatter1DPtr _scatter;
     };
 
     class Scatter2DPtr : public AnalysisObjectPtr {
         public:
-            Scatter2DPtr(const YODA::Scatter2DPtr& p) :
-                _scatter(p) { }
+            Scatter2DPtr(const YODA::Scatter2D& p) :
+                _scatter(make_shared<YODA::Scatter2D>(p)) { }
 
             Scatter2DPtr() :
                 _scatter(YODA::Scatter2DPtr()) { }
 
             bool operator!() { return !_scatter; }
             operator bool() { return bool(_scatter); }
 
             YODA::Scatter2D* operator->() { return _scatter.get(); }
 
             YODA::Scatter2D* operator->() const { return _scatter.get(); }
 
             YODA::Scatter2D & operator*() { return *_scatter; }
 
             const YODA::Scatter2D & operator*() const { return *_scatter; }
 
         protected:
             YODA::Scatter2DPtr _scatter;
     };
 
     class Scatter3DPtr : public AnalysisObjectPtr {
         public:
-            Scatter3DPtr(const YODA::Scatter3DPtr& p) :
-                _scatter(p) { }
+            Scatter3DPtr(const YODA::Scatter3D& p) :
+                _scatter(make_shared<YODA::Scatter3D>(p)) { }
 
             Scatter3DPtr() :
                 _scatter(YODA::Scatter3DPtr()) { }
 
             bool operator!() { return !_scatter; }
             operator bool() { return bool(_scatter); }
 
             YODA::Scatter3D* operator->() { return _scatter.get(); }
 
             YODA::Scatter3D* operator->() const { return _scatter.get(); }
 
             YODA::Scatter3D & operator*() { return *_scatter; }
 
             const YODA::Scatter3D & operator*() const { return *_scatter; }
 
         protected:
             YODA::Scatter3DPtr _scatter;
     };
 
 
     class MultiweightAOPtr : public AnalysisObjectPtr {
 
         public:
             virtual void newSubEvent() = 0;
 
             /// @todo 
             /// rename to setActive(Idx)?
             virtual void setActiveWeightIdx(unsigned int iWeight) = 0;
 
             virtual void pushToPersistent(const vector<vector<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
 // 
 // need to 
 // TODO TODO TODO
 
 template <class T>
 class TupleWrapper;
 
 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 ) {
         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 ) {
         fills_.insert( { {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::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::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 ) {
         fills_.insert( {{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 ) {
         fills_.insert( {{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 T>
     class Wrapper : public MultiweightAOPtr {
         public:
 
         /* @todo
          * some things are not really well-defined here
          * for instance: fill() in the finalize() method and integral() in
          * the analyze() method.
          */
 
         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));
         }
 
         Wrapper() : _persistent(), _evgroup(), _active() {}
 
         typename T::Ptr active() const { return _active; }
 
         /* @todo this probably need to loop over all? */
         bool operator!() const { return !active(); }
         operator bool() const { return bool(active()); }
 
         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() {
             typename TupleWrapper<T>::Ptr tmp
                 = make_shared<TupleWrapper<T>>(_persistent[0]->clone());
             tmp->reset();
             _evgroup.push_back( tmp );
             _active = _evgroup.back();
         }
 
         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<vector<double> >& weight);
 
         /* 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;
 
         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 Profile1DPtr = Wrapper<YODA::Profile1D>;
     using Profile2DPtr = Wrapper<YODA::Profile2D>;
     using CounterPtr = Wrapper<YODA::Counter>;
 
     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/src/Core/Analysis.cc b/src/Core/Analysis.cc
--- a/src/Core/Analysis.cc
+++ b/src/Core/Analysis.cc
@@ -1,770 +1,829 @@
 // -*- C++ -*-
 #include "Rivet/Config/RivetCommon.hh"
 #include "Rivet/Analysis.hh"
 #include "Rivet/AnalysisHandler.hh"
 #include "Rivet/AnalysisInfo.hh"
 #include "Rivet/Tools/BeamConstraint.hh"
 
 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());
     }
   }
 
 
   const Scatter2D& Analysis::refData(const string& hname) const {
     _cacheRefData();
     MSG_TRACE("Using histo bin edges for " << name() << ":" << hname);
     if (!_refdata[hname]) {
       MSG_ERROR("Can't find reference histogram " << hname);
       throw Exception("Reference data " + hname + " not found.");
     }
     return dynamic_cast<Scatter2D&>(*_refdata[hname]);
   }
 
 
   const Scatter2D& Analysis::refData(unsigned int datasetId, unsigned int xAxisId, unsigned int yAxisId) const {
     const string hname = makeAxisCode(datasetId, xAxisId, yAxisId);
     return refData(hname);
   }
 
 
-  CounterPtr Analysis::bookCounter(const string& cname,
+  CounterPtr& Analysis::bookCounter(const string& cname,
                                    const string& title) {
     const string path = histoPath(cname);
     shared_ptr<CounterPtr> ctr =
         make_shared<CounterPtr>(handler().numWeights(), Counter(path, title));
     addAnalysisObject(ctr);
     MSG_TRACE("Made counter " << cname << " for " << name());
     return *ctr;
   }
 
 
-  CounterPtr Analysis::bookCounter(unsigned int datasetId, unsigned int xAxisId, unsigned int yAxisId,
+  CounterPtr& Analysis::bookCounter(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 bookCounter(axisCode, title);
   }
 
 
-  Histo1DPtr Analysis::bookHisto1D(const string& hname,
-                                   size_t nbins, double lower, double upper,
-                                   const string& title,
-                                   const string& xtitle,
-                                   const string& ytitle) {
+  Histo1DPtr& Analysis::bookHisto1D(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);
+
     shared_ptr<Histo1DPtr> hp =
-        make_shared<Histo1DPtr>(handler().numWeights(), Histo1D(nbins, lower, upper, path, title));
+        make_shared<Histo1DPtr>(handler().numWeights(), hist);
+
     addAnalysisObject(hp);
-    Histo1DPtr hist = *hp;
     MSG_TRACE("Made histogram " << hname <<  " for " << name());
-    hist->setAnnotation("XLabel", xtitle);
-    hist->setAnnotation("YLabel", ytitle);
-    return hist;
+    return *hp;
   }
 
 
-  Histo1DPtr Analysis::bookHisto1D(const string& hname,
+  Histo1DPtr& Analysis::bookHisto1D(const string& hname,
                                    const vector<double>& binedges,
                                    const string& title,
                                    const string& xtitle,
                                    const string& ytitle) {
     const string path = histoPath(hname);
+
+    Histo1D hist = Histo1D(binedges, path, title);
+
+    hist.setAnnotation("XLabel", xtitle);
+    hist.setAnnotation("YLabel", ytitle);
+
+
     shared_ptr<Histo1DPtr> hp =
-        make_shared<Histo1DPtr>(handler().numWeights(), Histo1D(binedges, path, title));
+        make_shared<Histo1DPtr>(handler().numWeights(), hist);
     addAnalysisObject(hp);
 
-    Histo1DPtr hist = *hp;
     MSG_TRACE("Made histogram " << hname <<  " for " << name());
-    hist->setAnnotation("XLabel", xtitle);
-    hist->setAnnotation("YLabel", ytitle);
-    return hist;
+    return *hp;
   }
 
 
-  Histo1DPtr Analysis::bookHisto1D(const string& hname,
+  Histo1DPtr& Analysis::bookHisto1D(const string& hname,
                                    const Scatter2D& refscatter,
                                    const string& title,
                                    const string& xtitle,
                                    const string& ytitle) {
     const string path = histoPath(hname);
-    shared_ptr<Histo1DPtr> hp =
-        make_shared<Histo1DPtr>(handler().numWeights(), Histo1D(refscatter, path));
+    Histo1D hist = Histo1D(refscatter, path);
+
+    hist.setTitle(title);
+    hist.setAnnotation("XLabel", xtitle);
+    hist.setAnnotation("YLabel", ytitle);
+
+    shared_ptr<Histo1DPtr> hp = 
+        make_shared<Histo1DPtr>(handler().numWeights(), hist);
     addAnalysisObject(hp);
 
-    Histo1DPtr hist = *hp;
     MSG_TRACE("Made histogram " << hname <<  " for " << name());
-    hist->setTitle(title);
-    hist->setAnnotation("XLabel", xtitle);
-    hist->setAnnotation("YLabel", ytitle);
-    return hist;
+    return *hp;
   }
 
 
-  Histo1DPtr Analysis::bookHisto1D(const string& hname,
+  Histo1DPtr& Analysis::bookHisto1D(const string& hname,
                                    const string& title,
                                    const string& xtitle,
                                    const string& ytitle) {
     const Scatter2D& refdata = refData(hname);
     return bookHisto1D(hname, refdata, title, xtitle, ytitle);
   }
 
 
-  Histo1DPtr Analysis::bookHisto1D(unsigned int datasetId, unsigned int xAxisId, unsigned int yAxisId,
+  Histo1DPtr& Analysis::bookHisto1D(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 bookHisto1D(axisCode, title, xtitle, ytitle);
   }
 
 
   /// @todo Add booking methods which take a path, titles and *a reference Scatter from which to book*
 
 
   /////////////////
 
 
-  Histo2DPtr Analysis::bookHisto2D(const string& hname,
+  Histo2DPtr& Analysis::bookHisto2D(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);
+
     shared_ptr<Histo2DPtr> hp =
-        make_shared<Histo2DPtr>(handler().numWeights(), Histo2D(nxbins, xlower, xupper, nybins, ylower, yupper, path, title));
+        make_shared<Histo2DPtr>(handler().numWeights(), hist);
     addAnalysisObject(hp);
 
-    Histo2DPtr hist = *hp;
     MSG_TRACE("Made 2D histogram " << hname <<  " for " << name());
-    hist->setAnnotation("XLabel", xtitle);
-    hist->setAnnotation("YLabel", ytitle);
-    hist->setAnnotation("ZLabel", ztitle);
-    return hist;
+    return *hp;
   }
 
 
-  Histo2DPtr Analysis::bookHisto2D(const string& hname,
+  Histo2DPtr& Analysis::bookHisto2D(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);
+
     shared_ptr<Histo2DPtr> hp =
-        make_shared<Histo2DPtr>(handler().numWeights(), Histo2D(xbinedges, ybinedges, path, title));
+        make_shared<Histo2DPtr>(handler().numWeights(), hist);
     addAnalysisObject(hp);
 
-    Histo2DPtr hist = *hp;
     MSG_TRACE("Made 2D histogram " << hname <<  " for " << name());
-    hist->setAnnotation("XLabel", xtitle);
-    hist->setAnnotation("YLabel", ytitle);
-    hist->setAnnotation("ZLabel", ztitle);
-    return hist;
+    return *hp;
   }
 
 
-  Profile1DPtr Analysis::bookProfile1D(const string& hname,
+  Profile1DPtr& Analysis::bookProfile1D(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);
+
     shared_ptr<Profile1DPtr> pp =
-        make_shared<Profile1DPtr>(handler().numWeights(), Profile1D(nbins, lower, upper, path, title));
+        make_shared<Profile1DPtr>(handler().numWeights(), prof);
     addAnalysisObject(pp);
 
-    Profile1DPtr prof = *pp;
     MSG_TRACE("Made profile histogram " << hname <<  " for " << name());
-    prof->setAnnotation("XLabel", xtitle);
-    prof->setAnnotation("YLabel", ytitle);
-    return prof;
+    return *pp;
   }
 
 
-  Profile1DPtr Analysis::bookProfile1D(const string& hname,
+  Profile1DPtr& Analysis::bookProfile1D(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);
+
     shared_ptr<Profile1DPtr> pp =
-        make_shared<Profile1DPtr>(handler().numWeights(), Profile1D(binedges, path, title));
+        make_shared<Profile1DPtr>(handler().numWeights(), prof);
     addAnalysisObject(pp);
 
-    Profile1DPtr prof = *pp;
     MSG_TRACE("Made profile histogram " << hname <<  " for " << name());
-    prof->setAnnotation("XLabel", xtitle);
-    prof->setAnnotation("YLabel", ytitle);
-    return prof;
+    return *pp;
   }
 
 
-  Profile1DPtr Analysis::bookProfile1D(const string& hname,
+  Profile1DPtr& Analysis::bookProfile1D(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);
+
     shared_ptr<Profile1DPtr> pp =
-        make_shared<Profile1DPtr>(handler().numWeights(), Profile1D(refscatter, path));
+        make_shared<Profile1DPtr>(handler().numWeights(), prof);
     addAnalysisObject(pp);
 
-    Profile1DPtr prof = *pp;
     MSG_TRACE("Made profile histogram " << hname <<  " for " << name());
-    prof->setTitle(title);
-    prof->setAnnotation("XLabel", xtitle);
-    prof->setAnnotation("YLabel", ytitle);
-    return prof;
+    return *pp;
   }
 
 
-  Profile1DPtr Analysis::bookProfile1D(const string& hname,
+  Profile1DPtr& Analysis::bookProfile1D(const string& hname,
                                        const string& title,
                                        const string& xtitle,
                                        const string& ytitle) {
     const Scatter2D& refdata = refData(hname);
     return bookProfile1D(hname, refdata, title, xtitle, ytitle);
   }
 
 
-  Profile1DPtr Analysis::bookProfile1D(unsigned int datasetId, unsigned int xAxisId, unsigned int yAxisId,
+  Profile1DPtr& Analysis::bookProfile1D(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 bookProfile1D(axisCode, title, xtitle, ytitle);
   }
 
 
-  ///////////////////
-
-
-
-  /*
-  Profile2DPtr Analysis::bookProfile2D(const string& hname,
+  Profile2DPtr& Analysis::bookProfile2D(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);
+
     shared_ptr<Profile2DPtr> pp =
-        make_shared<Profile2DPtr>(handler().numWeights(), Profile2D(nxbins, xlower, xupper, nybins, ylower, yupper, path, title));
+        make_shared<Profile2DPtr>(handler().numWeights(), prof);
     addAnalysisObject(pp);
 
-    Profile2DPtr prof = *pp;
     MSG_TRACE("Made 2D profile histogram " << hname <<  " for " << name());
-    prof->setAnnotation("XLabel", xtitle);
-    prof->setAnnotation("YLabel", ytitle);
-    prof->setAnnotation("ZLabel", ztitle);
-    return prof;
+    return *pp;
   }
 
 
-  Profile2DPtr Analysis::bookProfile2D(const string& hname,
+  Profile2DPtr& Analysis::bookProfile2D(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);
+
     shared_ptr<Profile2DPtr> pp =
-        make_shared<Profile2DPtr>(handler().numWeights(), Profile2D(xbinedges, ybinedges, path, title));
+        make_shared<Profile2DPtr>(handler().numWeights(), prof);
     addAnalysisObject(pp);
 
-    Profile2DPtr prof = *pp;
     MSG_TRACE("Made 2D profile histogram " << hname <<  " for " << name());
-    prof->setAnnotation("XLabel", xtitle);
-    prof->setAnnotation("YLabel", ytitle);
-    prof->setAnnotation("ZLabel", ztitle);
-    return prof;
+    return *pp;
   }
 
 
-  Scatter2DPtr Analysis::bookScatter2D(unsigned int datasetId, unsigned int xAxisId, unsigned int yAxisId,
+  Scatter2DPtr& Analysis::bookScatter2D(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 bookScatter2D(axisCode, copy_pts, title, xtitle, ytitle);
   }
 
 
-  Scatter2DPtr Analysis::bookScatter2D(const string& hname,
+  Scatter2DPtr& Analysis::bookScatter2D(const string& hname,
                                        bool copy_pts,
                                        const string& title,
                                        const string& xtitle,
                                        const string& ytitle) {
-    shared_ptr<Scatter2DPtr> sp = make_shared<Scatter2DPtr>(make_shared<Scatter2D>());
+    Scatter2D scat;
     const string path = histoPath(hname);
     if (copy_pts) {
       const Scatter2D& refdata = refData(hname);
-      *sp = Scatter2DPtr(make_shared<Scatter2D>(refdata, path));
-      foreach (Point2D& p, (*sp)->points()) p.setY(0, 0);
+      scat = Scatter2D(refdata, path);
+      foreach (Point2D& p, scat.points()) p.setY(0, 0);
     } else {
-      *sp = Scatter2DPtr(make_shared<Scatter2D>(path));
+      scat = Scatter2D(path);
     }
+
+    scat.setTitle(title);
+    scat.setAnnotation("XLabel", xtitle);
+    scat.setAnnotation("YLabel", ytitle);
+
+    shared_ptr<Scatter2DPtr> sp = make_shared<Scatter2DPtr>(scat);
     addAnalysisObject(sp);
 
-    Scatter2DPtr scat = *sp;
     MSG_TRACE("Made scatter " << hname <<  " for " << name());
-    scat->setTitle(title);
-    scat->setAnnotation("XLabel", xtitle);
-    scat->setAnnotation("YLabel", ytitle);
-    return scat;
+    return *sp;
   }
 
 
-  Scatter2DPtr Analysis::bookScatter2D(const string& hname,
+  Scatter2DPtr& Analysis::bookScatter2D(const string& hname,
                                        size_t npts, double lower, double upper,
                                        const string& title,
                                        const string& xtitle,
                                        const string& ytitle) {
     const string path = histoPath(hname);
-    shared_ptr<Scatter2DPtr> sp = make_shared<Scatter2DPtr>(make_shared<Scatter2D>(path));
+    Scatter2D scat;
     const double binwidth = (upper-lower)/npts;
     for (size_t pt = 0; pt < npts; ++pt) {
       const double bincentre = lower + (pt + 0.5) * binwidth;
-      (*sp)->addPoint(bincentre, 0, binwidth/2.0, 0);
+      scat.addPoint(bincentre, 0, binwidth/2.0, 0);
     }
+    scat.setTitle(title);
+    scat.setAnnotation("XLabel", xtitle);
+    scat.setAnnotation("YLabel", ytitle);
+
+    shared_ptr<Scatter2DPtr> sp = make_shared<Scatter2DPtr>(scat);
     addAnalysisObject(sp);
 
-    Scatter2DPtr scat = *sp;
-
     MSG_TRACE("Made scatter " << hname <<  " for " << name());
-    scat->setTitle(title);
-    scat->setAnnotation("XLabel", xtitle);
-    scat->setAnnotation("YLabel", ytitle);
-    return scat;
+    return *sp;
   }
 
 
-  Scatter2DPtr Analysis::bookScatter2D(const string& hname,
+  Scatter2DPtr& Analysis::bookScatter2D(const string& hname,
                                        const vector<double>& binedges,
                                        const string& title,
                                        const string& xtitle,
                                        const string& ytitle) {
     const string path = histoPath(hname);
-    shared_ptr<Scatter2DPtr> sp =
-        make_shared<Scatter2DPtr>(make_shared<Scatter2D>(path));
+    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];
-      (*sp)->addPoint(bincentre, 0, binwidth/2.0, 0);
+      scat.addPoint(bincentre, 0, binwidth/2.0, 0);
     }
-    addAnalysisObject(sp);
 
-    Scatter2DPtr scat = *sp;
+    scat.setTitle(title);
+    scat.setAnnotation("XLabel", xtitle);
+    scat.setAnnotation("YLabel", ytitle);
+
+    shared_ptr<Scatter2DPtr> sp = make_shared<Scatter2DPtr>(scat);
+
     MSG_TRACE("Made scatter " << hname <<  " for " << name());
-    scat->setTitle(title);
-    scat->setAnnotation("XLabel", xtitle);
-    scat->setAnnotation("YLabel", ytitle);
-    return scat;
+    return *sp;
   }
-  */
-
-
-  /////////////////////
 
 
   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?!?
 
 
   //////////////////////////////////
 
 
-  void Analysis::addAnalysisObject(shared_ptr<MultiweightAOPtr> ao) {
+  // @todo
+  // special handling for scatters
+  void Analysis::addAnalysisObject(const shared_ptr<Scatter1DPtr>& ao) {
+    _scatters.push_back(ao);
+  }
+  void Analysis::addAnalysisObject(const shared_ptr<Scatter2DPtr>& ao) {
+    _scatters.push_back(ao);
+  }
+  void Analysis::addAnalysisObject(const shared_ptr<Scatter3DPtr>& ao) {
+    _scatters.push_back(ao);
+  }
+
+  void Analysis::addAnalysisObject(const shared_ptr<MultiweightAOPtr>& ao) {
     _analysisobjects.push_back(ao);
   }
 
   void Analysis::removeAnalysisObject(const string& path) {
     for (vector<shared_ptr<MultiweightAOPtr> >::iterator it = _analysisobjects.begin();  it != _analysisobjects.end(); ++it) {
       if ((**it)->path() == path) {
         _analysisobjects.erase(it);
         break;
       }
     }
+
+    for (vector<shared_ptr<AnalysisObjectPtr> >::iterator 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 (vector<shared_ptr<AnalysisObjectPtr> >::iterator it = _scatters.begin();  it != _scatters.end(); ++it) {
+      if (**it == ao) {
+        _scatters.erase(it);
+        break;
+      }
+    }
+  }
+
+  void Analysis::removeAnalysisObject(const Scatter2DPtr& ao) {
+    for (vector<shared_ptr<AnalysisObjectPtr> >::iterator it = _scatters.begin();  it != _scatters.end(); ++it) {
+      if (**it == ao) {
+        _scatters.erase(it);
+        break;
+      }
+    }
+  }
+
+  void Analysis::removeAnalysisObject(const Scatter3DPtr& ao) {
+    for (vector<shared_ptr<AnalysisObjectPtr> >::iterator it = _scatters.begin();  it != _scatters.end(); ++it) {
+      if (**it == ao) {
+        _scatters.erase(it);
+        break;
+      }
+    }
   }
 
   void Analysis::removeAnalysisObject(const MultiweightAOPtr& ao) {
     for (vector<shared_ptr<MultiweightAOPtr> >::iterator it = _analysisobjects.begin();  it != _analysisobjects.end(); ++it) {
       if (**it == ao) {
         _analysisobjects.erase(it);
         break;
       }
     }
- }
-
+  }
 
 }