Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-16 09:07:59

0001 // -*- C++ -*-
0002 #ifndef RIVET_Analysis_HH
0003 #define RIVET_Analysis_HH
0004 
0005 #include "Rivet/Config/RivetCommon.hh"
0006 #include "Rivet/AnalysisInfo.hh"
0007 #include "Rivet/Event.hh"
0008 #include "Rivet/Projection.hh"
0009 #include "Rivet/ProjectionApplier.hh"
0010 #include "Rivet/ProjectionHandler.hh"
0011 #include "Rivet/AnalysisLoader.hh"
0012 #include "Rivet/Tools/Cuts.hh"
0013 #include "Rivet/Tools/Logging.hh"
0014 #include "Rivet/Tools/ParticleUtils.hh"
0015 #ifdef HAVE_H5
0016 #include "Rivet/Tools/RivetHDF5.hh"
0017 #endif
0018 #include "Rivet/Tools/HistoGroup.hh"
0019 #include "Rivet/Tools/RivetMT2.hh"
0020 #include "Rivet/Tools/RivetPaths.hh"
0021 #include "Rivet/Tools/RivetYODA.hh"
0022 #include "Rivet/Tools/Percentile.hh"
0023 #include "Rivet/Tools/Cutflow.hh"
0024 #include "Rivet/Projections/CentralityProjection.hh"
0025 #include "Rivet/Projections/FastJets.hh"
0026 #include <tuple>
0027 
0028 
0029 /// @def vetoEvent
0030 /// Preprocessor define for vetoing events, including the log message and return.
0031 #define vetoEvent                                                       \
0032   do { MSG_DEBUG("Vetoing event on line " << __LINE__ << " of " << __FILE__); return; } while(0)
0033 
0034 
0035 namespace Rivet {
0036 
0037 
0038   // Convenience for analysis writers
0039   using std::cout;
0040   using std::cerr;
0041   using std::endl;
0042   using std::tuple;
0043   using std::stringstream;
0044   using std::swap;
0045   using std::numeric_limits;
0046 
0047 
0048   // Forward declaration
0049   class AnalysisHandler;
0050 
0051 
0052   /// @brief This is the base class of all analysis classes in Rivet.
0053   ///
0054   /// There are
0055   /// three virtual functions which should be implemented in base classes:
0056   ///
0057   /// void init() is called by Rivet before a run is started. Here the
0058   /// analysis class should book necessary histograms. The needed
0059   /// projections should probably rather be constructed in the
0060   /// constructor.
0061   ///
0062   /// void analyze(const Event&) is called once for each event. Here the
0063   /// analysis class should apply the necessary Projections and fill the
0064   /// histograms.
0065   ///
0066   /// void finalize() is called after a run is finished. Here the analysis
0067   /// class should do whatever manipulations are necessary on the
0068   /// histograms. Writing the histograms to a file is, however, done by
0069   /// the AnalysisHandler class.
0070   class Analysis : public ProjectionApplier {
0071   public:
0072 
0073     /// The AnalysisHandler is a friend.
0074     friend class AnalysisHandler;
0075 
0076 
0077     /// Constructor
0078     Analysis(const std::string& name);
0079 
0080     /// The destructor
0081     virtual ~Analysis() {}
0082 
0083     /// The assignment operator is private and must be deleted, so it can never be called.
0084     Analysis& operator = (const Analysis&) = delete;
0085 
0086 
0087   public:
0088 
0089     /// @name Main analysis methods
0090     /// @{
0091 
0092     /// Initialize this analysis object. A concrete class should here
0093     /// book all necessary histograms. An overridden function must make
0094     /// sure it first calls the base class function.
0095     virtual void init() { }
0096 
0097     /// Analyze one event. A concrete class should here apply the
0098     /// necessary projections on the \a event and fill the relevant
0099     /// histograms. An overridden function must make sure it first calls
0100     /// the base class function.
0101     virtual void analyze(const Event& event) = 0;
0102 
0103     /// Finalize this analysis object. A concrete class should here make
0104     /// all necessary operations on the histograms. Writing the
0105     /// histograms to a file is, however, done by the Rivet class. An
0106     /// overridden function must make sure it first calls the base class
0107     /// function.
0108     virtual void finalize() { }
0109 
0110     /// @}
0111 
0112 
0113     /// @name Power-user analysis methods
0114     /// @{
0115 
0116     /// A method called before init(), for cleaner subclassing
0117     virtual void preInit() { }
0118     /// A method called after init(), for cleaner subclassing
0119     virtual void postInit() { }
0120     /// A method called before analyze(), for cleaner subclassing
0121     virtual void preAnalyze(const Event&) { }
0122     /// A method called after analyze(), for cleaner subclassing
0123     virtual void postAnalyze(const Event&) { }
0124     /// A method called before finalize(), for cleaner subclassing
0125     virtual void preFinalize() { }
0126     /// A method called after finalize(), for cleaner subclassing
0127     virtual void postFinalize() { }
0128 
0129     /// Call the projection applier _syncDeclQueue() method.
0130     ///
0131     /// @note It should be hidden for all projection appliers other than analyses.
0132     void syncDeclQueue() {
0133       this->_syncDeclQueue();
0134       this->markAsOwned();
0135     }
0136 
0137     /// @}
0138 
0139 
0140   public:
0141 
0142     /// @name Metadata
0143     ///
0144     /// Metadata is used for querying from the command line and also for
0145     /// building web pages and the analysis pages in the Rivet manual.
0146     /// @{
0147 
0148     /// Get the AnalysisInfo object to parse its info file in which the metadata is stored.
0149     void loadInfo() { info().parseInfoFile(); }
0150 
0151     /// Get the actual AnalysisInfo object in which all this metadata is stored.
0152     const AnalysisInfo& info() const {
0153       if (!_info) throw Error("No AnalysisInfo object :-O");
0154       return *_info;
0155     }
0156 
0157     /// @brief Get the name of the analysis.
0158     ///
0159     /// By default this is computed by combining the results of the
0160     /// experiment, year and Spires ID metadata methods and you should
0161     /// only override it if there's a good reason why those won't
0162     /// work. If options has been set for this instance, a
0163     /// corresponding string is appended at the end.
0164     virtual std::string name() const {
0165       return ( (info().name().empty()) ? _defaultname : info().name() ) + _optstring;
0166     }
0167 
0168     /// @brief Get the path to a data file associated with this analysis
0169     ///
0170     /// The searched-for filename will be \<ANANAME\>.\<extn\> of suffix is empty/unspecified,
0171     /// or \<ANANAME\>-\<suffix\>.\<extn\> if a non-zero suffix is specified.
0172     std::string analysisDataPath(const std::string& extn, const std::string& suffix="") {
0173       string filename = name() + (suffix.empty() ? "" : "-") + suffix + "." + extn;
0174       return findAnalysisDataFile(filename);
0175     }
0176 
0177     /// Get the Inspire ID code for this analysis.
0178     virtual std::string inspireID() const {
0179       return info().inspireID();
0180     }
0181 
0182     /// Get the SPIRES ID code for this analysis (~deprecated).
0183     virtual std::string spiresID() const {
0184       return info().spiresID();
0185     }
0186 
0187     /// @brief Names & emails of paper/analysis authors.
0188     ///
0189     /// Names and email of authors in 'NAME \<EMAIL\>' format. The first
0190     /// name in the list should be the primary contact person.
0191     virtual std::vector<std::string> authors() const {
0192       return info().authors();
0193     }
0194 
0195     /// @brief Get a short description of the analysis.
0196     ///
0197     /// Short (one sentence) description used as an index entry.
0198     /// Use @a description() to provide full descriptive paragraphs
0199     /// of analysis details.
0200     virtual std::string summary() const {
0201       return info().summary();
0202     }
0203 
0204     /// @brief Get a full description of the analysis.
0205     ///
0206     /// Full textual description of this analysis, what it is useful for,
0207     /// what experimental techniques are applied, etc. Should be treated
0208     /// as a chunk of restructuredText (http://docutils.sourceforge.net/rst.html),
0209     /// with equations to be rendered as LaTeX with amsmath operators.
0210     virtual std::string description() const {
0211       return info().description();
0212     }
0213 
0214     /// @brief Information about the events needed as input for this analysis.
0215     ///
0216     /// Event types, energies, kinematic cuts, particles to be considered
0217     /// stable, etc. etc. Should be treated as a restructuredText bullet list
0218     /// (http://docutils.sourceforge.net/rst.html)
0219     virtual std::string runInfo() const {
0220       return info().runInfo();
0221     }
0222 
0223     /// Experiment which performed and published this analysis.
0224     virtual std::string experiment() const {
0225       return info().experiment();
0226     }
0227 
0228     /// Collider on which the experiment ran.
0229     virtual std::string collider() const {
0230       return info().collider();
0231     }
0232 
0233     /// When the original experimental analysis was published.
0234     virtual std::string year() const {
0235       return info().year();
0236     }
0237 
0238     /// The integrated luminosity in inverse femtobarn
0239     virtual double luminosityfb() const {
0240       return info().luminosityfb();
0241     }
0242     /// The integrated luminosity in inverse picobarn
0243     virtual double luminosity() const {
0244       return info().luminosity();
0245     }
0246     /// The integrated luminosity in inverse picobarn
0247     double luminositypb() const { return luminosity(); }
0248 
0249     /// Journal, and preprint references.
0250     virtual std::vector<std::string> references() const {
0251       return info().references();
0252     }
0253 
0254     /// BibTeX citation key for this article.
0255     virtual std::string bibKey() const {
0256       return info().bibKey();
0257     }
0258 
0259     /// BibTeX citation entry for this article.
0260     virtual std::string bibTeX() const {
0261       return info().bibTeX();
0262     }
0263 
0264     /// Whether this analysis is trusted (in any way!)
0265     virtual std::string status() const {
0266       return (info().status().empty()) ? "UNVALIDATED" : info().status();
0267     }
0268 
0269     /// A warning message from the info file, if there is one
0270     virtual std::string warning() const {
0271       return info().warning();
0272     }
0273 
0274     /// Any work to be done on this analysis.
0275     virtual std::vector<std::string> todos() const {
0276       return info().todos();
0277     }
0278 
0279     /// make-style commands for validating this analysis.
0280     virtual std::vector<std::string> validation() const {
0281       return info().validation();
0282     }
0283 
0284     /// Does this analysis have a reentrant finalize()?
0285     virtual bool reentrant() const {
0286       return info().reentrant();
0287     }
0288 
0289     /// Get vector of analysis keywords
0290     virtual const std::vector<std::string>& keywords() const {
0291       return info().keywords();
0292     }
0293 
0294     /// Positive filtering regex for ref-data HepData sync
0295     virtual std::string refMatch() const {
0296       return info().refMatch();
0297     }
0298 
0299     /// Negative filtering regex for ref-data HepData sync
0300     virtual std::string refUnmatch() const {
0301       return info().refUnmatch();
0302     }
0303 
0304     /// Positive filtering regex for setting double precision in Writer
0305     virtual std::string writerDoublePrecision() const {
0306       return info().writerDoublePrecision();
0307     }
0308 
0309     /// Return the allowed pairs of incoming beams required by this analysis.
0310     virtual const std::vector<PdgIdPair>& requiredBeamIDs() const {
0311       return info().beamIDs();
0312     }
0313     /// Declare the allowed pairs of incoming beams required by this analysis.
0314     virtual Analysis& setRequiredBeamIDs(const std::vector<PdgIdPair>& beamids) {
0315       info().setBeamIDs(beamids);
0316       return *this;
0317     }
0318 
0319     /// Sets of valid beam energy pairs, in GeV
0320     virtual const std::vector<std::pair<double, double> >& requiredBeamEnergies() const {
0321       return info().energies();
0322     }
0323     /// Declare the list of valid beam energy pairs, in GeV
0324     virtual Analysis& setRequiredBeamEnergies(const std::vector<std::pair<double, double> >& energies) {
0325       info().setEnergies(energies);
0326       return *this;
0327     }
0328 
0329 
0330     /// Location of reference data YODA file
0331     virtual std::string refFile() const {
0332       return info().refFile();
0333     }
0334     /// Get name of reference data file, which could be different from plugin name
0335     virtual std::string refDataName() const {
0336       return (info().getRefDataName().empty()) ? _defaultname : info().getRefDataName();
0337     }
0338     /// Set name of reference data file, which could be different from plugin name
0339     virtual void setRefDataName(const std::string& ref_data="") {
0340       info().setRefDataName(!ref_data.empty() ? ref_data : name());
0341     }
0342 
0343 
0344     /// @brief Get the actual AnalysisInfo object in which all this metadata is stored (non-const).
0345     ///
0346     /// @note For *internal* use!
0347     AnalysisInfo& info() {
0348       if (!_info) throw Error("No AnalysisInfo object :-O");
0349       return *_info;
0350     }
0351 
0352     /// @}
0353 
0354 
0355     /// @name Run conditions
0356     /// @{
0357 
0358     /// Check if we are running rivet-merge
0359     bool merging() const {
0360       return sqrtS() <= 0.0;
0361     }
0362 
0363     /// Check if the given conditions are compatible with this analysis' declared constraints
0364     bool compatibleWithRun() const;
0365 
0366     /// @}
0367 
0368 
0369     /// @name Analysis / beam compatibility testing
0370     ///
0371     /// @{
0372 
0373     /// Incoming beams for this run
0374     const ParticlePair& beams() const;
0375 
0376     /// Incoming beam IDs for this run
0377     PdgIdPair beamIDs() const;
0378 
0379     /// Incoming beam energies for this run
0380     pair<double,double> beamEnergies() const;
0381 
0382     /// Allowed centre-of-mass energies (in GeV) for this routine
0383     vector<double> allowedEnergies() const;
0384 
0385     /// Centre of mass energy for this run
0386     double sqrtS() const;
0387 
0388     /// Check if analysis is compatible with the provided beam particle IDs and energies
0389     bool beamsMatch(const ParticlePair& beams) const;
0390 
0391     /// Check if analysis is compatible with the provided beam particle IDs and energies
0392     bool beamsMatch(PdgId beam1, PdgId beam2, double e1, double e2) const;
0393 
0394     /// Check if analysis is compatible with the provided beam particle IDs and energies in GeV
0395     bool beamsMatch(const PdgIdPair& beams, const std::pair<double,double>& energies) const;
0396 
0397     /// Check if analysis is compatible with the provided beam particle IDs
0398     bool beamIDsMatch(PdgId beam1, PdgId beam2) const;
0399 
0400     /// Check if analysis is compatible with the provided beam particle IDs
0401     bool beamIDsMatch(const PdgIdPair& beamids) const;
0402 
0403     /// Check if analysis is compatible with the provided beam energies
0404     bool beamEnergiesMatch(double e1, double e2) const;
0405 
0406     /// Check if analysis is compatible with the provided beam energies
0407     bool beamEnergiesMatch(const std::pair<double,double>& energies) const;
0408 
0409     /// Check if analysis is compatible with the provided CoM energy
0410     bool beamEnergyMatch(const std::pair<double,double>& energies) const;
0411 
0412     /// Check if analysis is compatible with the provided CoM energy
0413     bool beamEnergyMatch(double sqrts) const;
0414 
0415     /// Check if sqrtS is compatible with provided value
0416     bool isCompatibleWithSqrtS(double energy, double tolerance=1e-5) const;
0417 
0418     /// @}
0419 
0420 
0421     /// Access the controlling AnalysisHandler object.
0422     AnalysisHandler& handler() const { return *_analysishandler; }
0423 
0424     /// Access the current event
0425     const Event& currentEvent() const {
0426       if (!_currentevent) throw Error("No current event set: did you try to access it in init() or finalize()?");
0427       return *_currentevent;
0428     }
0429 
0430 
0431   protected:
0432 
0433     /// Get a Log object based on the name() property of the calling analysis object.
0434     Log& getLog() const;
0435 
0436     /// Get the process cross-section in pb. Throws if this hasn't been set.
0437     double crossSection() const;
0438 
0439     /// Get the process cross-section per generated event in pb. Throws if this
0440     /// hasn't been set.
0441     double crossSectionPerEvent() const;
0442 
0443     /// Get the process cross-section error in pb. Throws if this hasn't been set.
0444     double crossSectionError() const;
0445 
0446     /// Get the process cross-section error per generated event in
0447     /// pb. Throws if this hasn't been set.
0448     double crossSectionErrorPerEvent() const;
0449 
0450     /// @brief Get the number of events seen (via the analysis handler).
0451     ///
0452     /// @note Use in the finalize phase only.
0453     size_t numEvents() const;
0454 
0455     /// @brief Get the sum of event weights seen (via the analysis handler).
0456     ///
0457     /// @note Use in the finalize phase only.
0458     double sumW() const;
0459     /// Alias
0460     double sumOfWeights() const { return sumW(); }
0461 
0462     /// @brief Get the sum of squared event weights seen (via the analysis handler).
0463     ///
0464     /// @note Use in the finalize phase only.
0465     double sumW2() const;
0466 
0467 
0468   protected:
0469 
0470     /// @defgroup analysis_histopaths Histogram paths
0471     ///
0472     /// @todo Add "tmp" flags to return /ANA/TMP/foo/bar paths
0473     ///
0474     /// @{
0475 
0476     /// Get the canonical histogram "directory" path for this analysis.
0477     const std::string histoDir() const;
0478 
0479     /// Get the canonical histogram path for the named histogram in this analysis.
0480     const std::string histoPath(const std::string& hname) const;
0481 
0482     /// Get the canonical histogram path for the numbered histogram in this analysis.
0483     const std::string histoPath(unsigned int datasetID, unsigned int xAxisID, unsigned int yAxisID) const;
0484 
0485     /// Get the internal histogram name for given d, x and y (cf. HepData)
0486     const std::string mkAxisCode(unsigned int datasetID, unsigned int xAxisID, unsigned int yAxisID) const;
0487 
0488     /// @}
0489 
0490 
0491     #ifdef HAVE_H5
0492     /// @name Auxiliary HDF5 reference data
0493     /// @{
0494 
0495     /// Read in an aux data HDF5 file
0496     H5::File auxFile() const {
0497       return H5::readFile(name()+".h5");
0498     }
0499 
0500     /// Read HDF5 file @a filename
0501     template <typename T>
0502     bool auxData(const string& dsname, T& rtndata) {
0503       /// @todo Cache loading the H5 files? On MPI?
0504       return H5::readData(name()+".h5", dsname, rtndata);
0505     }
0506 
0507     template <typename T>
0508     T auxData(const string& dsname) {
0509       /// @todo Cache loading the H5 files? On MPI?
0510       return H5::readData<T>(name()+".h5", dsname);
0511     }
0512 
0513     /// @}
0514     #endif
0515 
0516 
0517     /// @name Histogram reference data
0518     /// @{
0519 
0520     /// Get all reference data objects for this analysis
0521     const std::map<std::string, YODA::AnalysisObjectPtr>& refData() const {
0522       _cacheRefData();
0523       return _refdata;
0524     }
0525 
0526 
0527     /// Get reference data for a named histo
0528     /// @todo SFINAE to ensure that the type inherits from YODA::AnalysisObject?
0529     template <typename T=YODA::Estimate1D>
0530     const T& refData(const string& hname) const {
0531       _cacheRefData();
0532       MSG_TRACE("Using histo bin edges for " << name() << ":" << hname);
0533       if (!_refdata[hname]) {
0534         MSG_ERROR("Can't find reference histogram " << hname);
0535         throw Exception("Reference data " + hname + " not found.");
0536       }
0537       try {
0538         return dynamic_cast<T&>(*_refdata[hname]);
0539       } catch (...) {
0540         throw Exception("Expected type " + _refdata[hname]->type()+" for reference data \"" + hname + "\".\n");
0541       }
0542     }
0543 
0544 
0545     /// Get reference data for a numbered histo
0546     /// @todo SFINAE to ensure that the type inherits from YODA::AnalysisObject?
0547     template <typename T=YODA::Estimate1D>
0548     const T& refData(unsigned int datasetId, unsigned int xAxisId, unsigned int yAxisId) const {
0549       const string hname = mkAxisCode(datasetId, xAxisId, yAxisId);
0550       return refData<T>(hname);
0551     }
0552 
0553     /// @}
0554 
0555 
0556     /// @defgroup analysis_cbook Counter booking
0557     ///
0558     /// @todo Add "tmp" flags to book in standard temporary paths
0559     ///
0560     /// @{
0561 
0562     /// Book a counter.
0563     CounterPtr& book(CounterPtr&, const std::string& name);
0564 
0565     /// Book a counter, using a path generated from the dataset and axis ID codes
0566     ///
0567     /// The paper, dataset and x/y-axis IDs will be used to build the histo name in the HepData standard way.
0568     CounterPtr& book(CounterPtr&, unsigned int datasetID, unsigned int xAxisID, unsigned int yAxisID);
0569 
0570     /// @}
0571 
0572 
0573     /// @name Estimate booking
0574     /// @{
0575 
0576     /// Book an estimate.
0577     Estimate0DPtr& book(Estimate0DPtr&, const std::string& name);
0578 
0579     /// Book an estimate, using a path generated from the dataset and axis ID codes
0580     ///
0581     /// The paper, dataset and x/y-axis IDs will be used to build the histo name in the HepData standard way.
0582     Estimate0DPtr& book(Estimate0DPtr&, unsigned int datasetID, unsigned int xAxisID, unsigned int yAxisID);
0583 
0584 
0585     /// @}
0586 
0587     /// @name BinnedDbn booking
0588     /// @{
0589 
0590     /// Book a ND histogram with @a nbins uniformly distributed across the range @a lower - @a upper .
0591     template<size_t DbnN, typename... AxisT, typename = YODA::enable_if_all_CAxisT<AxisT...>>
0592     BinnedDbnPtr<DbnN, AxisT...>& book(BinnedDbnPtr<DbnN, AxisT...>& ao,
0593                                        const std::string& name, const std::vector<size_t>& nbins,
0594                                        const std::vector<std::pair<double,double>>& loUpPairs) {
0595       if (nbins.size() != loUpPairs.size()) {
0596         throw RangeError("Vectors should have the same size!");
0597       }
0598       const string path = histoPath(name);
0599 
0600       YODA::BinnedDbn<DbnN, AxisT...> yao(nbins, loUpPairs, path);
0601       _setWriterPrecision(path, yao);
0602 
0603       return ao = registerAO(yao);
0604     }
0605 
0606     // Specialiation for 1D
0607     Histo1DPtr& book(Histo1DPtr& ao, const std::string& name,
0608                                      const size_t nbins, const double lower, const double upper) {
0609       return book(ao, name, vector<size_t>{nbins},
0610                   vector<pair<double,double>>{{lower,upper}});
0611     }
0612     //
0613     Profile1DPtr& book(Profile1DPtr& ao, const std::string& name,
0614                                          const size_t nbins, const double lower, const double upper) {
0615       return book(ao, name, vector<size_t>{nbins},
0616                   vector<pair<double,double>>{{lower,upper}});
0617     }
0618 
0619     // Specialiation for 2D
0620     Histo2DPtr& book(Histo2DPtr& ao, const std::string& name,
0621                                      const size_t nbinsX, const double lowerX, const double upperX,
0622                                      const size_t nbinsY, const double lowerY, const double upperY) {
0623       return book(ao, name, vector<size_t>{nbinsX,nbinsY},
0624                   vector<pair<double,double>>{{lowerX,upperX}, {lowerY,upperY}});
0625     }
0626     //
0627     Profile2DPtr& book(Profile2DPtr& ao, const std::string& name,
0628                                          const size_t nbinsX, const double lowerX, const double upperX,
0629                                          const size_t nbinsY, const double lowerY, const double upperY) {
0630       return book(ao, name, vector<size_t>{nbinsX,nbinsY},
0631                   vector<pair<double,double>>{{lowerX,upperX}, {lowerY,upperY}});
0632     }
0633 
0634     // Specialiation for 3D
0635     Histo3DPtr& book(Histo3DPtr& ao, const std::string& name,
0636                                      const size_t nbinsX, const double lowerX, const double upperX,
0637                                      const size_t nbinsY, const double lowerY, const double upperY,
0638                                      const size_t nbinsZ, const double lowerZ, const double upperZ) {
0639       return book(ao, name, vector<size_t>{nbinsX,nbinsY,nbinsZ},
0640                   vector<pair<double,double>>{{lowerX,upperX}, {lowerY,upperY}, {lowerZ,upperZ}});
0641     }
0642     //
0643     Profile3DPtr& book(Profile3DPtr& ao, const std::string& name,
0644                                          const size_t nbinsX, const double lowerX, const double upperX,
0645                                          const size_t nbinsY, const double lowerY, const double upperY,
0646                                          const size_t nbinsZ, const double lowerZ, const double upperZ) {
0647       return book(ao, name, vector<size_t>{nbinsX,nbinsY,nbinsZ},
0648                   vector<pair<double,double>>{{lowerX,upperX}, {lowerY,upperY}, {lowerZ,upperZ}});
0649     }
0650 
0651     /// Book a ND histogram with non-uniform bins defined by the vector of bin edges @a binedges .
0652     template<size_t DbnN, typename... AxisT>
0653     BinnedDbnPtr<DbnN, AxisT...>& book(BinnedDbnPtr<DbnN, AxisT...>& ao, const std::string& name,
0654                                        const std::vector<AxisT>&... binedges) {
0655       const string path = histoPath(name);
0656       YODA::BinnedDbn<DbnN, AxisT...> yao(binedges..., path);
0657       _setWriterPrecision(path, yao);
0658 
0659       return ao = registerAO(yao);
0660     }
0661 
0662     /// Book a ND histogram with non-uniform bins defined by the vector of bin edges @a binedges .
0663     template<size_t DbnN, typename... AxisT>
0664     BinnedDbnPtr<DbnN, AxisT...>& book(BinnedDbnPtr<DbnN, AxisT...>& ao, const std::string& name,
0665                                        const std::initializer_list<AxisT>&... binedges) {
0666       return book(ao, name, vector<AxisT>{binedges} ...);
0667     }
0668 
0669     /// Book a ND histogram with binning from a reference scatter.
0670     template<size_t DbnN, typename... AxisT>
0671     BinnedDbnPtr<DbnN,  AxisT...>& book(BinnedDbnPtr<DbnN, AxisT...>& ao, const std::string& name,
0672                                         const YODA::BinnedEstimate<AxisT...>& refest) {
0673       const string path = histoPath(name);
0674 
0675       YODA::BinnedDbn<DbnN, AxisT...> yao(refest.binning(), path);
0676       for (const string& a : yao.annotations()) {
0677         if (a != "Path")  yao.rmAnnotation(a);
0678       }
0679       _setWriterPrecision(path, yao);
0680       return ao = registerAO(yao);
0681     }
0682 
0683     /// Book a ND histogram, using the binnings in the reference data histogram.
0684     template<size_t DbnN, typename... AxisT>
0685     BinnedDbnPtr<DbnN, AxisT...>& book(BinnedDbnPtr<DbnN, AxisT...>& ao, const std::string& name) {
0686       return book(ao, name, refData<YODA::BinnedEstimate<AxisT...>>(name));
0687     }
0688 
0689     /// Book a ND histogram, using the binnings in the reference data histogram.
0690     ///
0691     /// The paper, dataset and x/y-axis IDs will be used to build the histo name in the HepData standard way.
0692     template<size_t DbnN, typename... AxisT>
0693     BinnedDbnPtr<DbnN, AxisT...>& book(BinnedDbnPtr<DbnN, AxisT...>& ao, const unsigned int datasetID,
0694                                        const unsigned int xAxisID, const unsigned int yAxisID) {
0695       const string name = mkAxisCode(datasetID, xAxisID, yAxisID);
0696       return book(ao, name);
0697     }
0698 
0699     /// @}
0700 
0701     /// @name HistoGroup booking
0702     /// @{
0703 
0704     template <typename GroupAxisT, typename... AxisT>
0705     HistoGroupPtr<GroupAxisT, AxisT...>& book(HistoGroupPtr<GroupAxisT, AxisT...>& ao,
0706                                               const std::vector<GroupAxisT>& edges,
0707                                               const std::vector<std::string>& names) {
0708       ao = make_shared<HistoGroup<GroupAxisT, AxisT...>>(edges);
0709       if (ao->numBins() != names.size()) {
0710         throw RangeError("Binning and reference-data names don't match!");
0711       }
0712       for (auto& b : ao->bins()) {
0713         const string& refname = names[b.index()-1];
0714         book(b, refname, refData<YODA::BinnedEstimate<AxisT...>>(refname));
0715       }
0716       return ao;
0717     }
0718 
0719     template <typename GroupAxisT, typename... AxisT>
0720     HistoGroupPtr<GroupAxisT, AxisT...>& book(HistoGroupPtr<GroupAxisT, AxisT...>& ao,
0721                                               const std::vector<GroupAxisT>& edges) {
0722       return ao = make_shared<HistoGroup<GroupAxisT, AxisT...>>(edges);
0723     }
0724 
0725     template <typename GroupAxisT, typename... AxisT>
0726     HistoGroupPtr<GroupAxisT, AxisT...>& book(HistoGroupPtr<GroupAxisT, AxisT...>& ao,
0727                                               std::initializer_list<GroupAxisT>&& edges) {
0728       return ao = make_shared<HistoGroup<GroupAxisT, AxisT...>>(std::move(edges));
0729     }
0730 
0731     /// @}
0732 
0733     /// @name BinnedEstimate booking
0734     /// @{
0735 
0736     /// Book a ND estimate with @a nbins uniformly distributed across the range @a lower - @a upper .
0737     template<typename... AxisT, typename = YODA::enable_if_all_CAxisT<AxisT...>>
0738     BinnedEstimatePtr<AxisT...>& book(BinnedEstimatePtr<AxisT...>& ao,
0739                                       const std::string& name, const std::vector<size_t>& nbins,
0740                                       const std::vector<std::pair<double,double>>& loUpPairs) {
0741       if (nbins.size() != loUpPairs.size()) {
0742         throw RangeError("Vectors should have the same size!");
0743       }
0744       const string path = histoPath(name);
0745 
0746       YODA::BinnedEstimate<AxisT...> yao(nbins, loUpPairs, path);
0747       _setWriterPrecision(path, yao);
0748 
0749       return ao = registerAO(yao);
0750     }
0751 
0752 
0753     // Specialiation for 1D
0754     Estimate1DPtr& book(Estimate1DPtr& ao, const std::string& name,
0755                                            const size_t nbins, const double lower, const double upper) {
0756       return book(ao, name, vector<size_t>{nbins},
0757                   vector<pair<double,double>>{{lower,upper}});
0758     }
0759 
0760     // Specialiation for 2D
0761     Estimate2DPtr& book(Estimate2DPtr& ao, const std::string& name,
0762                                            const size_t nbinsX, const double lowerX, const double upperX,
0763                                            const size_t nbinsY, const double lowerY, const double upperY) {
0764       return book(ao, name, vector<size_t>{nbinsX,nbinsY},
0765                   vector<pair<double,double>>{{lowerX,upperX}, {lowerY,upperY}});
0766     }
0767 
0768     // Specialiation for 3D
0769     Estimate3DPtr& book(Estimate3DPtr& ao, const std::string& name,
0770                                            const size_t nbinsX, const double lowerX, const double upperX,
0771                                            const size_t nbinsY, const double lowerY, const double upperY,
0772                                            const size_t nbinsZ, const double lowerZ, const double upperZ) {
0773       return book(ao, name, vector<size_t>{nbinsX,nbinsY,nbinsZ},
0774                   vector<pair<double,double>>{{lowerX,upperX}, {lowerY,upperY}, {lowerZ,upperZ}});
0775     }
0776 
0777     /// Book a ND estimate with non-uniform bins defined by the vector of bin edges @a binedges .
0778     template<typename... AxisT>
0779     BinnedEstimatePtr<AxisT...>& book(BinnedEstimatePtr<AxisT...>& ao, const std::string& name,
0780                                       const std::vector<AxisT>&... binedges) {
0781       const string path = histoPath(name);
0782       YODA::BinnedEstimate<AxisT...> yao(binedges..., path);
0783       _setWriterPrecision(path, yao);
0784 
0785       return ao = registerAO(yao);
0786     }
0787 
0788     /// Book a ND estimate with non-uniform bins defined by the vector of bin edges @a binedges .
0789     template<typename... AxisT>
0790     BinnedEstimatePtr<AxisT...>& book(BinnedEstimatePtr<AxisT...>& ao, const std::string& name,
0791                                       const std::initializer_list<AxisT>&... binedges) {
0792       return book(ao, name, vector<AxisT>{binedges} ...);
0793     }
0794 
0795     /// Book a ND estimate, using the binnings in the reference data histogram.
0796     template<typename... AxisT>
0797     BinnedEstimatePtr<AxisT...>& book(BinnedEstimatePtr<AxisT...>& ao, const std::string& name) {
0798 
0799       const string path = histoPath(name);
0800       YODA::BinnedEstimate<AxisT...> yao;
0801       try {
0802         yao = YODA::BinnedEstimate<AxisT...>(refData<YODA::BinnedEstimate<AxisT...>>(name).binning());
0803       } catch (...) {
0804         MSG_DEBUG("Couldn't retrieve reference binning, continue with nullary AO constructor.");
0805       }
0806       yao.setPath(path);
0807       _setWriterPrecision(path, yao);
0808       return ao = registerAO(yao);
0809     }
0810 
0811     /// Book a ND estimate, using the binnings in the reference data histogram.
0812     ///
0813     /// The paper, dataset and x/y-axis IDs will be used to build the histo name in the HepData standard way.
0814     template<typename... AxisT>
0815     BinnedEstimatePtr<AxisT...>& book(BinnedEstimatePtr<AxisT...>& ao, const unsigned int datasetID,
0816                                       const unsigned int xAxisID, const unsigned int yAxisID) {
0817       const string name = mkAxisCode(datasetID, xAxisID, yAxisID);
0818       return book(ao, name);
0819     }
0820 
0821     /// @}
0822 
0823 
0824     /// @name Scatter booking
0825     /// @{
0826 
0827     /// @brief Book a N-dimensional data point set with the given name.
0828     ///
0829     /// @note Unlike histogram booking, scatter booking by default makes no
0830     /// attempt to use reference data to pre-fill the data object. If you want
0831     /// this, which is sometimes useful e.g. when the x-position is not really
0832     /// meaningful and can't be extracted from the data, then set the @a
0833     /// copy_pts parameter to true. This creates points to match the reference
0834     /// data's x values and errors, but with the y values and errors zeroed...
0835     /// assuming that there is a reference histo with the same name: if there
0836     /// isn't, an exception will be thrown.
0837     template<size_t N>
0838     ScatterNDPtr<N>& book(ScatterNDPtr<N>& snd, const string& name, const bool copy_pts = false) {
0839       const string path = histoPath(name);
0840       YODA::ScatterND<N> scat(path);
0841       if (copy_pts) {
0842         const YODA::ScatterND<N> ref = refData<YODA::EstimateND<N-1>>(name).mkScatter();
0843         for (YODA::PointND<N> p : ref.points()) {
0844           p.setVal(N-1, 0.0);
0845           p.setErr(N-1, 0.0);
0846           scat.addPoint(std::move(p));
0847         }
0848       }
0849       _setWriterPrecision(path, scat);
0850       return snd = registerAO(scat);
0851     }
0852 
0853     /// @brief Book a N-dimensional data point set, using the binnings in the reference data histogram.
0854     ///
0855     /// The paper, dataset and x/y-axis IDs will be used to build the histo name in the HepData standard way.
0856     ///
0857     /// @note Unlike histogram booking, scatter booking by default makes no
0858     /// attempt to use reference data to pre-fill the data object. If you want
0859     /// this, which is sometimes useful e.g. when the x-position is not really
0860     /// meaningful and can't be extracted from the data, then set the @a
0861     /// copy_pts parameter to true. This creates points to match the reference
0862     /// data's x values and errors, but with the y values and errors zeroed.
0863     template<size_t N>
0864     ScatterNDPtr<N>& book(ScatterNDPtr<N>& snd, const unsigned int datasetID, const unsigned int xAxisID,
0865                                                 const unsigned int yAxisID, const bool copy_pts = false) {
0866       const string axisCode = mkAxisCode(datasetID, xAxisID, yAxisID);
0867       return book(snd, axisCode, copy_pts);
0868     }
0869 
0870     /// @brief Book a N-dimensional data point set with equally spaced x-points in a range.
0871     ///
0872     /// The y values and errors will be set to 0.
0873     ///
0874     /// @todo Remove this when we switch to BinnedEstimates
0875     Scatter2DPtr& book(Scatter2DPtr& snd, const string& name,
0876                        const size_t npts, const double lower, const double upper) {
0877       const string path = histoPath(name);
0878 
0879       Scatter2D scat(path);
0880       const double binwidth = (upper-lower)/npts;
0881       for (size_t pt = 0; pt < npts; ++pt) {
0882         const double bincentre = lower + (pt + 0.5) * binwidth;
0883         scat.addPoint(bincentre, 0, binwidth/2.0, 0);
0884       }
0885       _setWriterPrecision(path, scat);
0886 
0887       return snd = registerAO(scat);
0888     }
0889     //
0890     Scatter3DPtr& book(Scatter3DPtr& snd, const string& name,
0891                        const size_t nptsX, const double lowerX, const double upperX,
0892                        const size_t nptsY, const double lowerY, const double upperY) {
0893       const string path = histoPath(name);
0894 
0895       Scatter3D scat(path);
0896       const double xbinwidth = (upperX-lowerX)/nptsX;
0897       const double ybinwidth = (upperY-lowerY)/nptsY;
0898       for (size_t xpt = 0; xpt < nptsX; ++xpt) {
0899         const double xbincentre = lowerX + (xpt + 0.5) * xbinwidth;
0900         for (size_t ypt = 0; ypt < nptsY; ++ypt) {
0901           const double ybincentre = lowerY + (ypt + 0.5) * ybinwidth;
0902           scat.addPoint(xbincentre, ybincentre, 0, 0.5*xbinwidth, 0.5*ybinwidth, 0);
0903         }
0904       }
0905       _setWriterPrecision(path, scat);
0906 
0907       return snd = registerAO(scat);
0908     }
0909 
0910     /// @brief Book a 2-dimensional data point set based on provided contiguous "bin edges".
0911     ///
0912     /// The y values and errors will be set to 0.
0913     ///
0914     /// @todo Remove this when we switch to BinnedEstimates
0915     Scatter2DPtr& book(Scatter2DPtr& snd, const string& name,
0916                        const std::vector<double>& binedges) {
0917       const string path = histoPath(name);
0918 
0919       Scatter2D scat(path);
0920       for (size_t pt = 0; pt < binedges.size()-1; ++pt) {
0921         const double bincentre = (binedges[pt] + binedges[pt+1]) / 2.0;
0922         const double binwidth = binedges[pt+1] - binedges[pt];
0923         scat.addPoint(bincentre, 0, binwidth/2.0, 0);
0924       }
0925       _setWriterPrecision(path, scat);
0926 
0927       return snd = registerAO(scat);
0928     }
0929     //
0930     Scatter3DPtr& book(Scatter3DPtr& snd, const string& name,
0931                        const std::vector<double>& binedgesX,
0932                        const std::vector<double>& binedgesY) {
0933       const string path = histoPath(name);
0934 
0935       Scatter3D scat(path);
0936       for (size_t xpt = 0; xpt < binedgesX.size()-1; ++xpt) {
0937         const double xbincentre = (binedgesX[xpt] + binedgesX[xpt+1]) / 2.0;
0938         const double xbinwidth = binedgesX[xpt+1] - binedgesX[xpt];
0939         for (size_t ypt = 0; ypt < binedgesY.size()-1; ++ypt) {
0940           const double ybincentre = (binedgesY[ypt] + binedgesY[ypt+1]) / 2.0;
0941           const double ybinwidth = binedgesY[ypt+1] - binedgesY[ypt];
0942           scat.addPoint(xbincentre, ybincentre, 0, 0.5*xbinwidth, 0.5*ybinwidth, 0);
0943         }
0944       }
0945       _setWriterPrecision(path, scat);
0946 
0947       return snd = registerAO(scat);
0948     }
0949 
0950     /// Book a 2-dimensional data point set with x-points from an existing scatter and a new path.
0951     template<size_t N>
0952     ScatterNDPtr<N>& book(ScatterNDPtr<N>& snd, const string& name, const YODA::ScatterND<N>& refscatter) {
0953       const string path = histoPath(name);
0954 
0955       YODA::ScatterND<N> scat(refscatter, path);
0956       for (const string& a : scat.annotations()) {
0957         if (a != "Path")  scat.rmAnnotation(a);
0958       }
0959       _setWriterPrecision(path, scat);
0960 
0961       return snd = registerAO(scat);
0962     }
0963 
0964     /// @}
0965 
0966     /// @defgroup Cutflow booking
0967     /// @{
0968 
0969     /// Book a Cutflow object defined by the vector of @a edges
0970     CutflowPtr& book(CutflowPtr& ao, const string& name, const std::vector<std::string>& edges) {
0971       const string path = histoPath(name);
0972       Cutflow yao(edges, path);
0973       _setWriterPrecision(path, yao);
0974       return ao = registerAO(yao);
0975     }
0976 
0977     /// Book a Cutflow object defined by the vector of @a edges
0978     CutflowPtr& book(CutflowPtr& ao, const string& name, const std::initializer_list<std::string>& edges) {
0979       return book(ao, name, vector<std::string>{edges});
0980     }
0981 
0982     /// @}
0983 
0984     /// @name Cutflows booking
0985     /// @{
0986 
0987     CutflowsPtr& book(CutflowsPtr& ao, const std::vector<std::string>& edges,
0988                                            const std::vector<std::vector<std::string>>& innerEdges) {
0989       ao = make_shared<Cutflows>(edges);
0990       if (ao->numBins() !=innerEdges.size()) {
0991         throw RangeError("Outer and Inner edges don't match");
0992       }
0993       for (auto& b : ao->bins()) {
0994         book(b, b.xEdge(), innerEdges[b.index()-1]);
0995       }
0996       return ao;
0997     }
0998 
0999     CutflowsPtr& book(CutflowsPtr& ao, const std::vector<std::string>& edges) {
1000       return ao = make_shared<Cutflows>(edges);
1001     }
1002 
1003     CutflowsPtr& book(CutflowsPtr& ao, std::initializer_list<std::string>&& edges) {
1004       return ao = make_shared<Cutflows>(std::move(edges));
1005     }
1006 
1007     /// @}
1008 
1009 
1010     /// @name Virtual helper function to allow classes deriving
1011     /// from Analysis (e.g. CumulantAnalysis) to load external
1012     /// raw AOs into their local AOs (needed in heavy-ion land).
1013     virtual void rawHookIn(YODA::AnalysisObjectPtr yao) {
1014       (void) yao; // suppress unused variable warning
1015     }
1016 
1017     /// @name Virtual helper function to allow classes deriving from
1018     /// Analysis (e.g. CumulantAnalysis) to fiddle with raw AOs
1019     /// post-finalize/before writing them out (needed in heavy-ion land).
1020     virtual void rawHookOut(const vector<MultiplexAOPtr>& raos, size_t iW) {
1021       (void) raos; // suppress unused variable warning
1022       (void) iW; // suppress unused variable warning
1023     }
1024 
1025 
1026   public:
1027 
1028     /// @name Accessing options for this Analysis instance.
1029     /// @{
1030 
1031     /// Return the map of all options given to this analysis.
1032     const std::map<std::string,std::string>& options() const {
1033       return _options;
1034     }
1035 
1036     /// Get an option for this analysis instance as a string.
1037     std::string getOption(std::string optname, string def="") const {
1038       if ( _options.find(optname) != _options.end() )
1039         return _options.find(optname)->second;
1040       return def;
1041     }
1042 
1043     /// @brief Sane overload for literal character strings (which don't play well with stringstream)
1044     ///
1045     /// Note this isn't a template specialisation, because we can't return a non-static
1046     /// char*, and T-as-return-type is built into the template function definition.
1047     std::string getOption(std::string optname, const char* def) {
1048       return getOption<std::string>(optname, def);
1049     }
1050 
1051     /// @brief Get an option for this analysis instance converted to a specific type
1052     ///
1053     /// The return type is given by the specified @a def value, or by an explicit template
1054     /// type-argument, e.g. getOption<double>("FOO", 3).
1055     ///
1056     /// @warning To avoid accidents, strings not convertible to the requested
1057     /// type will throw a Rivet::ReadError exception.
1058     template<typename T>
1059     T getOption(std::string optname, T def) const {
1060       if (_options.find(optname) == _options.end()) return def;
1061       std::stringstream ss;
1062       ss.exceptions(std::ios::failbit);
1063       T ret;
1064       ss << _options.find(optname)->second;
1065       try {
1066         ss >> ret;
1067       } catch (...) {
1068         throw ReadError("Could not read user-provided option into requested type");
1069       }
1070       return ret;
1071     }
1072 
1073     /// @brief Get an option for this analysis instance converted to a bool
1074     ///
1075     /// Specialisation for bool, to allow use of "yes/no", "true/false"
1076     /// and "on/off" strings, with fallback casting to bool based on int
1077     /// value. An empty value will be treated as false.
1078     ///
1079     /// @warning To avoid accidents, strings not matching one of the above
1080     /// patterns will throw a Rivet::ReadError exception.
1081     ///
1082     /// @todo Make this a template-specialisation... needs to be outside the class body?
1083     // template<>
1084     // bool getOption<bool>(std::string optname, bool def) const {
1085     bool getOption(std::string optname, bool def) const {
1086       if (_options.find(optname) == _options.end()) return def;
1087       const std::string val = getOption(optname);
1088       const std::string lval = toLower(val);
1089       if (lval.empty()) return false;
1090       if (lval == "true" || lval == "yes" || lval == "on") return true;
1091       if (lval == "false" || lval == "no" || lval == "off") return false;
1092       return bool(getOption<int>(optname, 0));
1093     }
1094 
1095     /// @}
1096 
1097 
1098     /// @name Booking heavy ion features
1099     /// @{
1100 
1101     /// @brief Book a CentralityProjection
1102     ///
1103     /// Using a SingleValueProjection, @a proj, giving the value of an
1104     /// experimental observable to be used as a centrality estimator,
1105     /// book a CentralityProjection based on the experimentally
1106     /// measured pecentiles of this observable (as given by the
1107     /// reference data for the @a calHistName histogram in the @a
1108     /// calAnaName analysis. If a preloaded file with the output of a
1109     /// run using the @a calAnaName analysis contains a valid
1110     /// generated @a calHistName histogram, it will be used as an
1111     /// optional percentile binning. Also if this preloaded file
1112     /// contains a histogram with the name @a calHistName with an
1113     /// appended "_IMP" This histogram will be used to add an optional
1114     /// centrality percentile based on the generated impact
1115     /// parameter. If @a increasing is true, a low (high) value of @a proj
1116     /// is assumed to correspond to a more peripheral (central) event.
1117     const CentralityProjection&
1118     declareCentrality(const SingleValueProjection &proj,
1119                       string calAnaName, string calHistName,
1120                       const string projName,
1121               PercentileOrder pctorder=PercentileOrder::DECREASING);
1122 
1123 
1124     /// @brief Book a Percentile Multiplexer around AnalysisObjects.
1125     ///
1126     /// Based on a previously registered CentralityProjection named @a
1127     /// projName book one AnalysisObject for each @a centralityBin and
1128     /// name them according to the corresponding code in the @a ref
1129     /// vector.
1130     template<typename T>
1131     Percentile<T> book(const string& projName,
1132                        const vector<pair<double, double>>& centralityBins,
1133                        const vector<tuple<size_t, size_t, size_t>>& ref) {
1134 
1135       using RefT = typename ReferenceTraits<T>::RefT;
1136       using WrapT = MultiplexPtr<Multiplexer<T>>;
1137 
1138       Percentile<T> pctl(this, projName);
1139 
1140       const size_t nCent = centralityBins.size();
1141       for (size_t iCent = 0; iCent < nCent; ++iCent) {
1142         const string axisCode = mkAxisCode(std::get<0>(ref[iCent]),
1143                                            std::get<1>(ref[iCent]),
1144                                            std::get<2>(ref[iCent]));
1145         const RefT& refscatter = refData<RefT>(axisCode);
1146 
1147         WrapT wtf(_weightNames(), T(refscatter, histoPath(axisCode)));
1148         wtf = addAnalysisObject(wtf);
1149 
1150         CounterPtr cnt(_weightNames(), Counter(histoPath("TMP/COUNTER/" + axisCode)));
1151         cnt = addAnalysisObject(cnt);
1152 
1153         pctl.add(wtf, cnt, centralityBins[iCent]);
1154       }
1155       return pctl;
1156     }
1157 
1158 
1159     // /// @brief Book Percentile Multiplexers around AnalysisObjects.
1160     // ///
1161     // /// Based on a previously registered CentralityProjection named @a
1162     // /// projName book one (or several) AnalysisObject(s) named
1163     // /// according to @a ref where the x-axis will be filled according
1164     // /// to the percentile output(s) of the @projName.
1165     // ///
1166     // /// @todo Convert to just be called book() cf. others
1167     // template <class T>
1168     // PercentileXaxis<T> bookPercentileXaxis(string projName,
1169     //                                        tuple<int, int, int> ref) {
1170 
1171     //   typedef typename ReferenceTraits<T>::RefT RefT;
1172     //   typedef MultiplexPtr<Multiplexer<T>> WrapT;
1173 
1174     //   PercentileXaxis<T> pctl(this, projName);
1175 
1176     //   const string axisCode = mkAxisCode(std::get<0>(ref),
1177     //                                      std::get<1>(ref),
1178     //                                      std::get<2>(ref));
1179     //   const RefT & refscatter = refData<RefT>(axisCode);
1180 
1181     //   WrapT wtf(_weightNames(), T(refscatter, histoPath(axisCode)));
1182     //   wtf = addAnalysisObject(wtf);
1183 
1184     //   CounterPtr cnt(_weightNames(), Counter());
1185     //   cnt = addAnalysisObject(cnt);
1186 
1187     //   pctl.add(wtf, cnt);
1188     //   return pctl;
1189     // }
1190 
1191     /// @}
1192 
1193 
1194   private:
1195 
1196     // Functions that have to be defined in the .cc file to avoid circular #includes
1197 
1198     /// Get the list of weight names from the handler
1199     vector<string> _weightNames() const;
1200 
1201     /// Get the list of weight names from the handler
1202     YODA::AnalysisObjectPtr _getPreload (const string& name) const;
1203 
1204     /// Get an AO from another analysis
1205     MultiplexAOPtr _getOtherAnalysisObject(const std::string & ananame, const std::string& name);
1206 
1207     /// Check that analysis objects aren't being booked/registered outside the init stage
1208     void _checkBookInit() const;
1209 
1210     /// Check if we are in the init stage.
1211     bool _inInit() const;
1212 
1213     /// Check if we are in the finalize stage.
1214     bool _inFinalize() const;
1215 
1216     /// Set DP annotation
1217     template <typename YODAT>
1218     void _setWriterPrecision(const string& path, YODAT& yao) {
1219       const string re = _info->writerDoublePrecision();
1220       if (re != "") {
1221         std::smatch match;
1222         const bool needsDP = std::regex_search(path, match, std::regex(re));
1223         if (needsDP)  yao.setAnnotation("WriterDoublePrecision", "1");
1224       }
1225     }
1226 
1227 
1228   private:
1229 
1230     /// To be used in finalize context only
1231     class CounterAdapter {
1232     public:
1233 
1234       CounterAdapter(double x) : x_(x) {}
1235 
1236       CounterAdapter(const YODA::Counter& c) : x_(c.val()) {}
1237 
1238       CounterAdapter(const YODA::Estimate& e) : x_(e.val()) {}
1239 
1240       CounterAdapter(const YODA::Scatter1D& s) : x_(s.points()[0].x()) {
1241         if (s.numPoints() != 1)  throw RangeError("Can only scale by a single value.");
1242       }
1243 
1244       operator double() const { return x_; }
1245 
1246     private:
1247       double x_;
1248 
1249     };
1250 
1251 
1252   public:
1253 
1254     double dbl(double x) { return x; }
1255     double dbl(const YODA::Counter& c) { return c.val(); }
1256     double dbl(const YODA::Estimate0D& e) { return e.val(); }
1257     double dbl(const YODA::Scatter1D& s) {
1258       if ( s.numPoints() != 1 ) throw RangeError("Only scatter with single value supported.");
1259       return s.points()[0].x();
1260     }
1261 
1262 
1263   protected:
1264 
1265     /// @name Analysis object manipulation
1266     ///
1267     /// @{
1268 
1269     /// Multiplicatively scale the given AnalysisObject, @a ao, by factor @a factor.
1270     template<typename T>
1271     void scale(MultiplexPtr<Multiplexer<T>>& ao, CounterAdapter factor) {
1272       if (!ao) {
1273         MSG_WARNING("Failed to scale AnalysisObject=NULL in analysis "
1274                     << name() << " (scale=" << double(factor) << ")");
1275         return;
1276       }
1277       if (std::isnan(double(factor)) || std::isinf(double(factor))) {
1278         MSG_WARNING("Failed to scale AnalysisObject=" << ao->path() << " in analysis: "
1279                     << name() << " (invalid scale factor = " << double(factor) << ")");
1280         factor = 0;
1281       }
1282       MSG_TRACE("Scaling AnalysisObject " << ao->path() << " by factor " << double(factor));
1283       try {
1284         if constexpr( isFillable<T>::value ) {
1285           ao->scaleW(factor);
1286         }
1287         else {
1288           ao->scale(factor);
1289         }
1290       }
1291       catch (YODA::Exception& we) {
1292         MSG_WARNING("Could not scale AnalysisObject " << ao->path());
1293         return;
1294       }
1295     }
1296 
1297     /// Multiplicatively scale the given histogram group, @a group, by factor @a factor.
1298     template<typename GroupAxisT, typename... AxisT>
1299     void scale(HistoGroupPtr<GroupAxisT, AxisT...>& group, CounterAdapter factor) {
1300       if (!group) {
1301         MSG_WARNING("Failed to scale AnalysisObject=NULL in analysis "
1302                     << name() << " (scale=" << double(factor) << ")");
1303         return;
1304       }
1305       if (std::isnan(double(factor)) || std::isinf(double(factor))) {
1306         MSG_WARNING("Failed to scale histo group in analysis: "
1307                     << name() << " (invalid scale factor = " << double(factor) << ")");
1308         factor = 0;
1309       }
1310       MSG_TRACE("Scaling histo group by factor " << double(factor));
1311       try {
1312         group->scaleW(factor);
1313       }
1314       catch (YODA::Exception& we) {
1315         MSG_WARNING("Could not scale histo group.");
1316         return;
1317       }
1318     }
1319 
1320     /// Multiplicatively scale the given histogram group, @a group, by factors @a factors.
1321     template<typename GroupAxisT, typename... AxisT>
1322     void scale(HistoGroupPtr<GroupAxisT, AxisT...>& group, const vector<double>& factors) {
1323       if (!group) {
1324         MSG_WARNING("Failed to scale AnalysisObject=NULL in analysis " << name());
1325         return;
1326       }
1327       if (group->numBins(true) != factors.size()) {
1328         throw RangeError(name() + ": Number of scale factors does not match group binning");
1329         return;
1330       }
1331       for (auto& b : group->bins(true)) {
1332         if (!b.get())  continue;
1333         double factor = factors[b.index()];
1334         if (std::isnan(factor) || std::isinf(factor)) {
1335           MSG_WARNING("Failed to scale componment of histo group in analysis: "
1336                       << name() << " (invalid scale factor = " << factor << ")");
1337           factor = 0;
1338         }
1339         MSG_TRACE("Scaling histo group element by factor " << factor);
1340         try {
1341           b->scaleW(factor);
1342         }
1343         catch (YODA::Exception& we) {
1344           MSG_WARNING("Could not scale component of histo group.");
1345         }
1346       }
1347     }
1348 
1349     /// Multiplicatively scale the cutflow group, @a group, by factor @a factor.
1350     void scale(CutflowsPtr& group, CounterAdapter factor) {
1351       if (!group) {
1352         MSG_WARNING("Failed to scale AnalysisObject=NULL in analysis "
1353                     << name() << " (scale=" << double(factor) << ")");
1354         return;
1355       }
1356       if (std::isnan(double(factor)) || std::isinf(double(factor))) {
1357         MSG_WARNING("Failed to scale histo group in analysis: "
1358                     << name() << " (invalid scale factor = " << double(factor) << ")");
1359         factor = 0;
1360       }
1361       MSG_TRACE("Scaling histo group by factor " << double(factor));
1362       try {
1363         group->scale(factor);
1364       }
1365       catch (YODA::Exception& we) {
1366         MSG_WARNING("Could not scale histo group.");
1367         return;
1368       }
1369     }
1370 
1371     /// Iteratively scale the AOs in the map @a aos, by factor @a factor.
1372     template<typename T, typename U>
1373     void scale(std::map<T, U>& aos, CounterAdapter factor) {
1374       for (auto& item : aos)  scale(item.second, factor);
1375     }
1376 
1377     /// Iteratively scale the AOs in the iterable @a aos, by factor @a factor.
1378     template <typename AORange, typename = std::enable_if_t<YODA::isIterable<AORange>>>
1379     void scale(AORange& aos, CounterAdapter factor) {
1380       for (auto& ao : aos)  scale(ao, factor);
1381     }
1382 
1383     /// Iteratively scale the AOs in the initialiser list @a aos, by factor @a factor.
1384     template <typename T>
1385     void scale(std::initializer_list<T> aos, CounterAdapter factor) {
1386       for (auto& ao : std::vector<T>{aos})  scale(ao, factor);
1387     }
1388 
1389     /// Iteratively scale the AOs in the map @a aos, by factors @a factors.
1390     template<typename T, typename U>
1391     void scale(std::map<T, U>& aos, const vector<double>& factors) {
1392       for (auto& item : aos)  scale(item.second, factors);
1393     }
1394 
1395     /// Iteratively scale the AOs in the iterable @a aos, by factors @a factors.
1396     template <typename AORange, typename = std::enable_if_t<YODA::isIterable<AORange>>>
1397     void scale(AORange& aos, const vector<double>& factors) {
1398       for (auto& ao : aos)  scale(ao, factors);
1399     }
1400 
1401     /// Iteratively scale the AOs in the initialiser list @a aos, by factors @a factors.
1402     template <typename T>
1403     void scale(std::initializer_list<T> aos, const vector<double>& factors) {
1404       for (auto& ao : std::vector<T>{aos})  scale(ao, factors);
1405     }
1406 
1407     /// Scale the given histogram group, @a group, by the group axis width
1408     template<typename GroupAxisT, typename... AxisT>
1409     void divByGroupWidth(HistoGroupPtr<GroupAxisT, AxisT...>& group) {
1410       if (!group) {
1411         MSG_WARNING("Failed to scale HistoGroup=NULL in analysis "
1412                     << name() << " by group axis width");
1413         return;
1414       }
1415       group->divByGroupWidth();
1416     }
1417 
1418     /// Iteratively scale the HistoGroups in the map @a aos, by the group axis width
1419     template<typename T, typename U>
1420     void divByGroupWidth(std::map<T, U>& aos) {
1421       for (auto& item : aos)  divByGroupWidth(item.second);
1422     }
1423 
1424     /// Iteratively scale the HistoGroups in the iterable @a aos, by the group axis width
1425     template <typename AORange, typename = std::enable_if_t<YODA::isIterable<AORange>>>
1426     void divByGroupWidth(AORange& aos) {
1427       for (auto& ao : aos)  divByGroupWidth(ao);
1428     }
1429 
1430     /// Iteratively scale the HistoGroups in the initialiser list @a aos, by the group axis width
1431     template <typename T>
1432     void divByGroupWidth(std::initializer_list<T> aos) {
1433       for (auto& ao : std::vector<T>{aos})  divByGroupWidth(ao);
1434     }
1435 
1436 
1437     /// Normalize the given analysis object, @a ao to a target @a norm.
1438     template <size_t DbnN, typename... AxisT>
1439     void normalize(BinnedDbnPtr<DbnN, AxisT...> ao, const CounterAdapter norm=1.0, const bool includeoverflows=true) {
1440       if (!ao) {
1441         MSG_WARNING("Failed to normalize histo=NULL in analysis " << name() << " (norm=" << double(norm) << ")");
1442         return;
1443       }
1444       MSG_TRACE("Normalizing histo " << ao->path() << " to " << double(norm));
1445       try {
1446         const double hint = ao->integral(includeoverflows);
1447         if (hint == 0)  MSG_DEBUG("Skipping histo with null area " << ao->path());
1448         else            ao->normalize(norm, includeoverflows);
1449       }
1450       catch (YODA::Exception& we) {
1451         MSG_WARNING("Could not normalize histo " << ao->path());
1452         return;
1453       }
1454     }
1455 
1456     /// Normalize each AO in the given histogram group, @a group to a target @a norm.
1457     template <typename GroupAxisT, typename... AxisT>
1458     void normalize(HistoGroupPtr<GroupAxisT, AxisT...> group, const CounterAdapter norm=1.0, const bool includeoverflows=true) {
1459       if (!group) {
1460         MSG_WARNING("Failed to normalize histo=NULL in analysis " << name() << " (norm=" << double(norm) << ")");
1461         return;
1462       }
1463       MSG_TRACE("Normalizing histo group  to " << double(norm));
1464       try {
1465         const double hint = group->integral(includeoverflows);
1466         if (hint == 0)  MSG_DEBUG("Skipping histo group with null area.");
1467         else            group->normalize(norm, includeoverflows);
1468       }
1469       catch (YODA::Exception& we) {
1470         MSG_WARNING("Could not normalize histo group.");
1471         return;
1472       }
1473     }
1474 
1475 
1476     /// Iteratively normalise the AOs in the iterable @a iter, by factor @a factor.
1477     template <typename AORange, typename = std::enable_if_t<YODA::isIterable<AORange>>>
1478     void normalize(AORange& aos, const CounterAdapter norm=1.0, const bool includeoverflows=true) {
1479       for (auto& ao : aos)  normalize(ao, norm, includeoverflows);
1480     }
1481 
1482     /// Iteratively normalise the AOs in the initialiser list @a iter to a target @a norm.
1483     template<typename T>
1484     void normalize(std::initializer_list<T>&& aos, const CounterAdapter norm=1.0, const bool includeoverflows=true) {
1485       for (auto& ao : aos)  normalize(ao, norm, includeoverflows);
1486     }
1487 
1488     /// Iteratively normalise the AOs in the map @a aos to a target @a norm.
1489     template<typename T, typename U>
1490     void normalize(std::map<T, U>& aos, //BinnedDbnPtr<DbnN, AxisT...>>& aos,
1491                    const CounterAdapter norm=1.0, const bool includeoverflows=true) {
1492       for (auto& item : aos)  normalize(item.second, norm, includeoverflows);
1493     }
1494 
1495     /// Normalize the given histogram group, @a group to a target @a norm.
1496     template <typename GroupAxisT, typename... AxisT>
1497     void normalizeGroup(HistoGroupPtr<GroupAxisT, AxisT...> group, const CounterAdapter norm=1.0, const bool includeoverflows=true) {
1498       if (!group) {
1499         MSG_WARNING("Failed to normalize histo=NULL in analysis " << name() << " (norm=" << double(norm) << ")");
1500         return;
1501       }
1502       MSG_TRACE("Normalizing histo group  to " << double(norm));
1503       try {
1504         const double hint = group->integral(includeoverflows);
1505         if (hint == 0)  MSG_DEBUG("Skipping histo group with null area.");
1506         else            group->normalizeGroup(norm, includeoverflows);
1507       }
1508       catch (YODA::Exception& we) {
1509         MSG_WARNING("Could not normalize histo group.");
1510         return;
1511       }
1512     }
1513 
1514     /// Iteratively normalise the HistoGroups in the iterable @a iter, by factor @a factor.
1515     template <typename AORange, typename = std::enable_if_t<YODA::isIterable<AORange>>>
1516     void normalizeGroup(AORange& aos, const CounterAdapter norm=1.0, const bool includeoverflows=true) {
1517       for (auto& ao : aos)  normalizeGroup(ao, norm, includeoverflows);
1518     }
1519 
1520     /// Iteratively normalise the HistoGroups in the initialiser list @a iter to a target @a norm.
1521     template<typename T>
1522     void normalizeGroup(std::initializer_list<T>&& aos, const CounterAdapter norm=1.0, const bool includeoverflows=true) {
1523       for (auto& ao : aos)  normalizeGroup(ao, norm, includeoverflows);
1524     }
1525 
1526     /// Iteratively normalise the HistoGroups in the map @a aos to a target @a norm.
1527     template<typename T, typename U>
1528     void normalizeGroup(std::map<T, U>& aos, //BinnedDbnPtr<DbnN, AxisT...>>& aos,
1529                    const CounterAdapter norm=1.0, const bool includeoverflows=true) {
1530       for (auto& item : aos)  normalizeGroup(item.second, norm, includeoverflows);
1531     }
1532 
1533 
1534     /// Helper for histogram conversion to an inert estimate type
1535     ///
1536     /// @note Assigns to the (already registered) output estimate, @a est.
1537     /// Preserves the path information of the target.
1538     template<size_t DbnN, typename... AxisT>
1539     void barchart(BinnedDbnPtr<DbnN, AxisT...> ao, BinnedEstimatePtr<AxisT...> est) const {
1540       const string path = est->path();
1541       *est = ao->mkEstimate(path, "stats", false); //< do NOT divide by bin area cf. a differential dsigma/dX histogram
1542     }
1543 
1544     /// Helper for counter division.
1545     ///
1546     /// @note Assigns to the (already registered) output estimate, @a est. Preserves the path information of the target.
1547     void divide(CounterPtr c1, CounterPtr c2, Estimate0DPtr est) const;
1548 
1549     /// Helper for histogram division with raw YODA objects.
1550     ///
1551     /// @note Assigns to the (already registered) output estimate, @a est. Preserves the path information of the target.
1552     void divide(const YODA::Counter& c1, const YODA::Counter& c2, Estimate0DPtr est) const;
1553 
1554     /// Helper for counter division.
1555     ///
1556     /// @note Assigns to the (already registered) output estimate, @a est. Preserves the path information of the target.
1557     void divide(Estimate0DPtr e1, Estimate0DPtr e2, Estimate0DPtr est) const;
1558 
1559     /// Helper for estimate division with raw YODA objects.
1560     ///
1561     /// @note Assigns to the (already registered) output estimate, @a est. Preserves the path information of the target.
1562     void divide(const YODA::Estimate0D& e1, const YODA::Estimate0D& e2, Estimate0DPtr est) const;
1563 
1564 
1565     /// Helper for histogram division.
1566     ///
1567     /// @note Assigns to the (already registered) output estimate, @a est. Preserves the path information of the target.
1568     template<size_t DbnN, typename... AxisT>
1569     void divide(const YODA::BinnedDbn<DbnN, AxisT...>& h1, const YODA::BinnedDbn<DbnN, AxisT...>& h2,
1570                 BinnedEstimatePtr<AxisT...> est) const {
1571       const string path = est->path();
1572       *est = h1 / h2;
1573       est->setPath(path);
1574     }
1575     //
1576     template<size_t DbnN, typename... AxisT>
1577     void divide(BinnedDbnPtr<DbnN, AxisT...> h1, BinnedDbnPtr<DbnN, AxisT...> h2,
1578                              BinnedEstimatePtr<AxisT...> est) const {
1579       return divide(*h1, *h2, est);
1580     }
1581 
1582     /// Helper for binned estimate division.
1583     ///
1584     /// @note Assigns to the (already registered) output estimate, @a est. Preserves the path information of the target.
1585     template<typename... AxisT>
1586     void divide(const YODA::BinnedEstimate<AxisT...>& e1, const YODA::BinnedEstimate<AxisT...>& e2,
1587                 BinnedEstimatePtr<AxisT...> est) const {
1588       const string path = est->path();
1589       *est = e1 / e2;
1590       est->setPath(path);
1591     }
1592     //
1593     template<typename... AxisT>
1594     void divide(BinnedEstimatePtr<AxisT...> e1, BinnedEstimatePtr<AxisT...> e2,
1595                              BinnedEstimatePtr<AxisT...> est) const {
1596       return divide(*e1, *e2, est);
1597     }
1598 
1599 
1600 
1601     /// Helper for counter efficiency calculation.
1602     ///
1603     /// @note Assigns to the (already registered) output estimate, @a est. Preserves the path information of the target.
1604     void efficiency(CounterPtr c1, CounterPtr c2, Estimate0DPtr est) const {
1605       efficiency(*c1, *c2, est);
1606     }
1607 
1608     /// Helper for counter efficiency calculation.
1609     ///
1610     /// @note Assigns to the (already registered) output estimate, @a est. Preserves the path information of the target.
1611     void efficiency(const YODA::Counter& c1, const YODA::Counter& c2, Estimate0DPtr est) const {
1612       const string path = est->path();
1613       *est = YODA::efficiency(c1, c2);
1614       est->setPath(path);
1615     }
1616 
1617 
1618 
1619     /// Helper for histogram efficiency calculation.
1620     ///
1621     /// @note Assigns to the (already registered) output estimate, @a est. Preserves the path information of the target.
1622     template<size_t DbnN, typename... AxisT>
1623     void efficiency(const YODA::BinnedDbn<DbnN, AxisT...>& h1, const YODA::BinnedDbn<DbnN, AxisT...>& h2,
1624                     BinnedEstimatePtr<AxisT...> est) const {
1625       const string path = est->path();
1626       *est = YODA::efficiency(h1, h2);
1627       est->setPath(path);
1628     }
1629     //
1630     template<size_t DbnN, typename... AxisT>
1631     void efficiency(BinnedDbnPtr<DbnN, AxisT...> h1, BinnedDbnPtr<DbnN, AxisT...> h2,
1632                     BinnedEstimatePtr<AxisT...> est) const {
1633       efficiency(*h1, *h2, est);
1634     }
1635 
1636 
1637     /// Helper for estimate efficiency calculation.
1638     ///
1639     /// @note Assigns to the (already registered) output estimate, @a est. Preserves the path information of the target.
1640     template<typename... AxisT>
1641     void efficiency(const YODA::BinnedEstimate<AxisT...>& e1, const YODA::BinnedEstimate<AxisT...>& e2,
1642                     BinnedEstimatePtr<AxisT...> est) const {
1643       const string path = est->path();
1644       *est = YODA::efficiency(e1, e2);
1645       est->setPath(path);
1646     }
1647     //
1648     template<typename... AxisT>
1649     void efficiency(BinnedEstimatePtr<AxisT...> e1, BinnedEstimatePtr<AxisT...> e2,
1650                     BinnedEstimatePtr<AxisT...> est) const {
1651       efficiency(*e1, *e2, est);
1652     }
1653 
1654 
1655     /// Helper for histogram asymmetry calculation.
1656     ///
1657     /// @note Assigns to the (already registered) output estimate, @a est. Preserves the path information of the target.
1658     template<size_t DbnN, typename... AxisT>
1659     void asymm(const YODA::BinnedDbn<DbnN, AxisT...>& h1, const YODA::BinnedDbn<DbnN, AxisT...>& h2,
1660                BinnedEstimatePtr<AxisT...> est) const {
1661       const string path = est->path();
1662       *est = YODA::asymm(h1, h2);
1663       est->setPath(path);
1664     }
1665     //
1666     template<size_t DbnN, typename... AxisT>
1667     void asymm(BinnedDbnPtr<DbnN, AxisT...> h1, BinnedDbnPtr<DbnN, AxisT...> h2,
1668                BinnedEstimatePtr<AxisT...> est) const {
1669       asymm(*h1, *h2, est);
1670     }
1671 
1672     /// Helper for estimate asymmetry calculation.
1673     ///
1674     /// @note Assigns to the (already registered) output estimate, @a est. Preserves the path information of the target.
1675     template<typename... AxisT>
1676     void asymm(const YODA::BinnedEstimate<AxisT...>& e1, const YODA::BinnedEstimate<AxisT...>& e2,
1677                BinnedEstimatePtr<AxisT...> est) const {
1678       const string path = est->path();
1679       *est = YODA::asymm(e1, e2);
1680       est->setPath(path);
1681     }
1682     //
1683     template<typename... AxisT>
1684     void asymm(BinnedEstimatePtr<AxisT...> e1, BinnedEstimatePtr<AxisT...> e2,
1685                BinnedEstimatePtr<AxisT...> est) const {
1686       asymm(*e1, *e2, est);
1687     }
1688 
1689     /// Helper for converting a differential histo to an integral one.
1690     ///
1691     /// @note Assigns to the (already registered) output estimate, @a est. Preserves the path information of the target.
1692     template<size_t DbnN, typename... AxisT>
1693     void integrate(const YODA::BinnedDbn<DbnN, AxisT...>& h, BinnedEstimatePtr<AxisT...> est) const {
1694       const string path = est->path();
1695       *est = mkIntegral(h);
1696       est->setPath(path);
1697     }
1698     //
1699     template<size_t DbnN, typename... AxisT>
1700     void integrate(BinnedDbnPtr<DbnN, AxisT...>& h, BinnedEstimatePtr<AxisT...> est) const {
1701       integrate(*h, est);
1702     }
1703 
1704     /// @}
1705 
1706 
1707   public:
1708 
1709     /// List of registered analysis data objects
1710     const vector<MultiplexAOPtr>& analysisObjects() const {
1711       return _analysisobjects;
1712     }
1713 
1714 
1715   protected:
1716 
1717     /// @name Data object registration, retrieval, and removal
1718     /// @{
1719 
1720     /// Get the default/nominal weight index
1721     size_t defaultWeightIndex() const;
1722 
1723     /// Get a preloaded YODA object.
1724     template <typename YODAT>
1725     shared_ptr<YODAT> getPreload(const string& path) const {
1726       return dynamic_pointer_cast<YODAT>(_getPreload(path));
1727     }
1728 
1729 
1730     /// Register a new data object, optionally read in preloaded data.
1731     template <typename YODAT>
1732     MultiplexPtr< Multiplexer<YODAT> > registerAO(const YODAT& yao) {
1733       using MultiplexerT = Multiplexer<YODAT>;
1734       using YODAPtrT = shared_ptr<YODAT>;
1735       using RAOT = MultiplexPtr<MultiplexerT>;
1736 
1737       if ( !_inInit() && !_inFinalize() ) {
1738         MSG_ERROR("Can't book objects outside of init() or finalize()");
1739         throw UserError(name() + ": Can't book objects outside of init() or finalize().");
1740       }
1741 
1742       // First check that we haven't booked this before.
1743       // This is allowed when booking in finalize: just warn in that case.
1744       // If in init(), throw an exception: it's 99.9% never going to be intentional.
1745       for (auto& waold : analysisObjects()) {
1746         if ( yao.path() == waold.get()->basePath() ) {
1747           const string msg = "Found double-booking of " + yao.path() + " in " + name();
1748           if ( _inInit() ) {
1749             MSG_ERROR(msg);
1750             throw LookupError(msg);
1751           } else {
1752             MSG_WARNING(msg + ". Keeping previous booking");
1753           }
1754           return RAOT(dynamic_pointer_cast<MultiplexerT>(waold.get()));
1755         }
1756       }
1757 
1758       shared_ptr<MultiplexerT> wao = make_shared<MultiplexerT>();
1759       wao->_basePath = yao.path();
1760       YODAPtrT yaop = make_shared<YODAT>(yao);
1761 
1762       for (const string& weightname : _weightNames()) {
1763         // Create two YODA objects for each weight. Copy from
1764         // preloaded YODAs if present. First the finalized yoda:
1765         string finalpath = yao.path();
1766         if ( weightname != "" ) finalpath +=  "[" + weightname + "]";
1767         YODAPtrT preload = getPreload<YODAT>(finalpath);
1768         if ( preload ) {
1769           if ( !bookingCompatible(preload, yaop) ) {
1770             /// @todo What about if/when we want to make the final objects the Scatter or binned persistent type?
1771             MSG_WARNING("Found incompatible pre-existing data object with same base path "
1772                         << finalpath <<  " for " << name());
1773             preload = nullptr;
1774           } else {
1775             MSG_TRACE("Using preloaded " << finalpath << " in " <<name());
1776             wao->_final.push_back(make_shared<YODAT>(*preload));
1777           }
1778         }
1779         else {
1780           wao->_final.push_back(make_shared<YODAT>(yao));
1781           wao->_final.back()->setPath(finalpath);
1782         }
1783 
1784         // Then the raw filling yodas.
1785         string rawpath = "/RAW" + finalpath;
1786         preload = getPreload<YODAT>(rawpath);
1787         if ( preload ) {
1788           if ( !bookingCompatible(preload, yaop) ) {
1789             MSG_WARNING("Found incompatible pre-existing data object with same base path "
1790                         << rawpath <<  " for " << name());
1791             preload = nullptr;
1792           } else {
1793             MSG_TRACE("Using preloaded " << rawpath << " in " <<name());
1794             wao->_persistent.push_back(make_shared<YODAT>(*preload));
1795           }
1796         }
1797         else {
1798           wao->_persistent.push_back(make_shared<YODAT>(yao));
1799           wao->_persistent.back()->setPath(rawpath);
1800         }
1801       }
1802       MultiplexPtr<MultiplexerT> ret(wao);
1803 
1804       ret.get()->unsetActiveWeight();
1805       if ( _inFinalize() ) {
1806         // If booked in finalize() we assume it is the first time
1807         // finalize is run.
1808         ret.get()->pushToFinal();
1809         ret.get()->setActiveFinalWeightIdx(0);
1810       }
1811       _analysisobjects.push_back(ret);
1812 
1813       return ret;
1814     }
1815 
1816 
1817     /// Register a data object in the histogram system
1818     template <typename AO=MultiplexAOPtr>
1819     AO addAnalysisObject(const AO& aonew) {
1820       _checkBookInit();
1821 
1822       for (const MultiplexAOPtr& ao : analysisObjects()) {
1823 
1824         // Check AO base-name first
1825         ao.get()->setActiveWeightIdx(defaultWeightIndex());
1826         aonew.get()->setActiveWeightIdx(defaultWeightIndex());
1827         if (ao->path() != aonew->path()) continue;
1828 
1829         // If base-name matches, check compatibility
1830         // NB. This evil is because dynamic_ptr_cast can't work on MultiplexPtr directly
1831         AO aoold = AO(dynamic_pointer_cast<typename AO::value_type>(ao.get())); //< OMG
1832         if ( !aoold || !bookingCompatible(aonew, aoold) ) {
1833           MSG_WARNING("Found incompatible pre-existing data object with same base path "
1834                       << aonew->path() <<  " for " << name());
1835           throw LookupError("Found incompatible pre-existing data object with same base path during AO booking");
1836         }
1837 
1838         // Finally, check all weight variations
1839         for (size_t weightIdx = 0; weightIdx < _weightNames().size(); ++weightIdx) {
1840           aoold.get()->setActiveWeightIdx(weightIdx);
1841           aonew.get()->setActiveWeightIdx(weightIdx);
1842           if (aoold->path() != aonew->path()) {
1843             MSG_WARNING("Found incompatible pre-existing data object with different weight-path "
1844                         << aonew->path() <<  " for " << name());
1845             throw LookupError("Found incompatible pre-existing data object with same weight-path during AO booking");
1846           }
1847         }
1848 
1849         // They're fully compatible: bind and return
1850         aoold.get()->unsetActiveWeight();
1851         MSG_TRACE("Bound pre-existing data object " << aoold->path() <<  " for " << name());
1852         return aoold;
1853       }
1854 
1855       // No equivalent found
1856       MSG_TRACE("Registered " << aonew->annotation("Type") << " " << aonew->path() <<  " for " << name());
1857       aonew.get()->unsetActiveWeight();
1858 
1859       _analysisobjects.push_back(aonew);
1860       return aonew;
1861     }
1862 
1863     /// Unregister a data object from the histogram system (by name)
1864     void removeAnalysisObject(const std::string& path);
1865 
1866     /// Unregister a data object from the histogram system (by pointer)
1867     void removeAnalysisObject(const MultiplexAOPtr& ao);
1868 
1869     /// Get a Rivet data object from the histogram system
1870     template <typename AO=MultiplexAOPtr>
1871     const AO getAnalysisObject(const std::string& aoname) const {
1872       for (const MultiplexAOPtr& ao : analysisObjects()) {
1873         ao.get()->setActiveWeightIdx(defaultWeightIndex());
1874         if (ao->path() == histoPath(aoname)) {
1875           // return dynamic_pointer_cast<AO>(ao);
1876           return AO(dynamic_pointer_cast<typename AO::value_type>(ao.get()));
1877         }
1878       }
1879       throw LookupError("Data object " + histoPath(aoname) + " not found");
1880     }
1881 
1882 
1883     // /// Get a data object from the histogram system
1884     // template <typename AO=YODA::AnalysisObject>
1885     // const std::shared_ptr<AO> getAnalysisObject(const std::string& name) const {
1886     //   foreach (const AnalysisObjectPtr& ao, analysisObjects()) {
1887     //     if (ao->path() == histoPath(name)) return dynamic_pointer_cast<AO>(ao);
1888     //   }
1889     //   throw LookupError("Data object " + histoPath(name) + " not found");
1890     // }
1891 
1892     // /// Get a data object from the histogram system (non-const)
1893     // template <typename AO=YODA::AnalysisObject>
1894     // std::shared_ptr<AO> getAnalysisObject(const std::string& name) {
1895     //   foreach (const AnalysisObjectPtr& ao, analysisObjects()) {
1896     //     if (ao->path() == histoPath(name)) return dynamic_pointer_cast<AO>(ao);
1897     //   }
1898     //   throw LookupError("Data object " + histoPath(name) + " not found");
1899     // }
1900 
1901 
1902     /// Get a data object from another analysis (e.g. preloaded
1903     /// calibration histogram).
1904     template <typename AO=MultiplexAOPtr>
1905     AO getAnalysisObject(const std::string& ananame,
1906                          const std::string& aoname) {
1907       MultiplexAOPtr ao = _getOtherAnalysisObject(ananame, aoname);
1908       // return dynamic_pointer_cast<AO>(ao);
1909       return AO(dynamic_pointer_cast<typename AO::value_type>(ao.get()));
1910     }
1911 
1912     /// @}
1913 
1914     /// @defgroup Utility functions
1915     /// @{
1916 
1917     /// Avoid `FastJet::` scoping prefix
1918     template <
1919       typename... Args, typename CONTAINER,
1920       typename = std::enable_if_t<
1921         is_citerable_v<CONTAINER>,
1922         Jet
1923       >
1924     >
1925     static CONTAINER reclusterJets(const CONTAINER &jetsIn, Args&&... args){
1926       return FastJets::reclusterJets(jetsIn, std::forward<Args>(args)...);
1927     }
1928 
1929     template <typename T, typename U, typename... Args>
1930     static std::map<T, U> reclusterJets(const std::map<T, U> &jetsMap, Args&&... args){
1931       return FastJets::reclusterJets(jetsMap, std::forward<Args>(args)...);
1932     }
1933 
1934     template <
1935       JetAlg JETALG, typename... Args, typename CONTAINER,
1936       typename = std::enable_if_t<
1937         is_citerable_v<CONTAINER>,
1938         Jet
1939       >
1940     >
1941     static CONTAINER reclusterJets(const CONTAINER &jetsIn, Args&&... args){
1942       return FastJets::reclusterJets<JETALG>(jetsIn, std::forward<Args>(args)...);
1943     }
1944 
1945     template <JetAlg JETALG, typename T, typename U, typename... Args>
1946     static std::map<T, U> reclusterJets(const std::map<T, U> &jetsMap, Args&&... args){
1947       return FastJets::reclusterJets<JETALG>(jetsMap, std::forward<Args>(args)...);
1948     }
1949 
1950     /// @}
1951 
1952 
1953   private:
1954 
1955     /// Name passed to constructor (used to find .info analysis data file, and as a fallback)
1956     string _defaultname;
1957 
1958     /// Pointer to analysis metadata object
1959     unique_ptr<AnalysisInfo> _info;
1960 
1961     /// Storage of all plot objects
1962     /// @todo Make this a map for fast lookup by path?
1963     vector<MultiplexAOPtr> _analysisobjects;
1964 
1965     /// @name Cross-section variables
1966     /// @{
1967     double _crossSection;
1968     bool _gotCrossSection;
1969     /// @}
1970 
1971     /// The controlling AnalysisHandler object.
1972     AnalysisHandler* _analysishandler;
1973 
1974     /// The current event.
1975     const Event* _currentevent = nullptr;
1976 
1977     /// Collection of cached refdata to speed up many autobookings: the
1978     /// reference data file should only be read once.
1979     mutable std::map<std::string, YODA::AnalysisObjectPtr> _refdata;
1980 
1981     /// Options the (this instance of) the analysis
1982     map<string, string> _options;
1983 
1984     /// The string of options.
1985     string _optstring;
1986 
1987 
1988   private:
1989 
1990     /// @name Utility functions
1991     /// @{
1992 
1993     /// Get the reference data for this paper and cache it.
1994     void _cacheRefData() const;
1995 
1996     /// @}
1997 
1998   };
1999 
2000 
2001   // // Template specialisation for literal character strings (which don't play well with stringstream)
2002   // template<>
2003   // inline std::string Analysis::getOption(std::string optname, const char* def) {
2004   //   return getOption<std::string>(optname, def); //.c_str();
2005   // }
2006 
2007 
2008 }
2009 
2010 
2011 // Include definition of analysis plugin system so that analyses automatically see it when including Analysis.hh
2012 #include "Rivet/AnalysisBuilder.hh"
2013 
2014 
2015 /// @defgroup anamacros Analysis macros
2016 /// @{
2017 
2018 /// @def RIVET_DECLARE_PLUGIN
2019 /// Preprocessor define to prettify the global-object plugin hook mechanism
2020 #define RIVET_DECLARE_PLUGIN(clsname) ::Rivet::AnalysisBuilder<clsname> plugin_ ## clsname
2021 
2022 /// @def RIVET_DECLARE_ALIASED_PLUGIN
2023 /// Preprocessor define to prettify the global-object plugin hook mechanism, with an extra alias name for this analysis
2024 #define RIVET_DECLARE_ALIASED_PLUGIN(clsname, alias) RIVET_DECLARE_PLUGIN(clsname)( #alias )
2025 
2026 /// @def RIVET_DEFAULT_ANALYSIS_CTOR
2027 /// Preprocessor define to prettify the awkward constructor, with a name-string argument
2028 #define RIVET_DEFAULT_ANALYSIS_CTOR(clsname) clsname() : Analysis(# clsname) {}
2029 
2030 /// @def RIVET_REGISTER_TYPE
2031 /// Preprocessor define to prettify on-the-fly type registration
2032 #define RIVET_REGISTER_TYPE(...) handler().registerType<__VA_ARGS__>()
2033 
2034 /// @def RIVET_REGISTER_BINNED_SET
2035 /// Preprocessor define to prettify on-the-fly type registration
2036 #define RIVET_REGISTER_BINNED_SET(...) { \
2037     RIVET_REGISTER_TYPE(YODA::BinnedHisto<__VA_ARGS__>); \
2038     RIVET_REGISTER_TYPE(YODA::BinnedProfile<__VA_ARGS__>); \
2039     RIVET_REGISTER_TYPE(YODA::BinnedEstimate<__VA_ARGS__>); }
2040 
2041 /// @}
2042 
2043 
2044 #endif