Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-05 08:50:28

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