Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-04-19 09:06:51

0001 #ifndef RIVET_Cutflow_HH
0002 #define RIVET_Cutflow_HH
0003 
0004 #include "Rivet/Tools/RivetYODA.hh"
0005 #include "Rivet/Tools/Utils.hh"
0006 
0007 #include <iomanip>
0008 #include <iostream>
0009 #include <vector>
0010 #include <sstream>
0011 #include <string>
0012 
0013 namespace Rivet {
0014 
0015   namespace {
0016     template<typename T>
0017     std::vector<T> operator+(std::vector<T>&& res, const std::vector<T>& vadd) {
0018       res.insert(std::end(res), std::begin(vadd), std::end(vadd));
0019       return std::move(res);
0020     }
0021   }
0022 
0023   /// A tracker of numbers & fractions of events passing sequential cuts
0024   class Cutflow : public YODA::BinnedHisto<std::string> {
0025   public:
0026 
0027     using BaseT = YODA::BinnedHisto<std::string>;
0028     using BinningT = BaseT::BinningT;
0029     using FillType = BaseT::FillType;
0030     using BinType = BaseT::BinT;
0031     using Ptr = std::shared_ptr<Cutflow>;
0032     using AnalysisObject::operator =;
0033 
0034     /// @brief Nullary constructor
0035     Cutflow() : BaseT(), icurr(0) {}
0036 
0037     /// @brief Constructor using binning object
0038     Cutflow(const BinningT& binning) : BaseT(binning), icurr(0) { }
0039 
0040     /// @brief Constructor using rvalue binning object
0041     Cutflow(BinningT&& binning) : BaseT(std::move(binning)), icurr(0) { }
0042 
0043     /// Proper constructor using a cutflow name and a list of edges
0044     ///
0045     /// @note the constructor will preprend the empty string as a pre-cut step.
0046     Cutflow(const std::vector<std::string>& edges, const std::string& path = "")
0047       : BaseT(std::vector<std::string>{""} + edges, path), icurr(0) { }
0048 
0049     /// @brief Fill function with FillType
0050     ///
0051     /// @note This method will set the current fill position icurr.
0052     virtual int fill(FillType&& coords, const double weight = 1.0, const double fraction = 1.0) {
0053       int pos = BaseT::fill(std::move(coords), weight, fraction);
0054       icurr = (size_t)pos; // cannot be negative for discrete AOs
0055       return pos;
0056     }
0057 
0058     /// @brief Fill function using an explicit coordinate
0059     //
0060     /// @note This method will set the current fill position icurr.
0061     virtual int fill(std::string val, const double weight = 1.0, const double fraction = 1.0) {
0062       int pos = BaseT::fill({val}, weight, fraction);
0063       icurr = (size_t)pos; // cannot be negative for discrete AOs
0064       return pos;
0065     }
0066 
0067     /// @brief Convenience method to fill the pre-cut step
0068     virtual int fillinit(const double weight=1.0, const double fraction=1.0) {
0069       return fill(""s, weight, fraction);
0070     }
0071 
0072     /// @brief Convenience method to fill the next cut step if @a cutresult is true
0073     virtual int fillnext(const bool cutresult, const double weight = 1.0, const double fraction = 1.0) {
0074       if (!cutresult)  return 0;
0075       const std::string edge = BaseT::bin(++icurr).xEdge();
0076       return fill(edge, weight, fraction);
0077     }
0078 
0079     /// @brief Convenience method to fill the next cut step
0080     virtual int fillnext(const double weight = 1.0, const double fraction = 1.0) {
0081       return fillnext(true, weight, fraction);
0082     }
0083 
0084     /// @brief Convenience method to fill the next cut steps from an n-element results vector @a cutresult
0085     virtual int fillnext(const std::vector<bool>& cutresults, const double weight = 1.0, const double fraction = 1.0) {
0086       if (icurr+cutresults.size() > BaseT::numBins()+1) {
0087         throw RangeError("Number of filled cut results needs to match the Cutflow construction (in cutflow '"+BaseT::path()+"')");
0088       }
0089       for (size_t i = 0; i < cutresults.size(); ++i) {
0090         if (!cutresults[i])  return 0;
0091         fillnext(weight, fraction); // also incremenrs icurr
0092       }
0093       return (int)icurr;
0094     }
0095 
0096     /// Returns current cutflow index for debugging
0097     size_t currentIndex() const {  return icurr; }
0098 
0099     /// Scale the cutflow weights by the given factor
0100     void scale(const double factor) {
0101       BaseT::scaleW(factor);
0102     }
0103 
0104     /// Scale the cutflow weights so that the weight count after cut @a edge is @a norm
0105     void normalizeStep(const std::string& edge, const double norm) {
0106       const auto& b = BaseT::binAt(edge);
0107       if (b.sumW() == 0) {
0108         MSG_WARNING("Failed to scale Cutflow " << BaseT::path() << " as bin " << edge << " is empty");
0109         return;
0110       }
0111       scale(norm/b.sumW());
0112     }
0113 
0114     /// Alias to scale the cutflow weights so that the weight after the pre-init cut is @a norm
0115     void normalizeFirst(const double norm) {
0116       normalizeStep(""s, norm);
0117     }
0118 
0119     /// Create a string representation
0120     string str() const {
0121       using namespace std;
0122       stringstream ss;
0123       ss << fixed << std::setprecision(1) << BaseT::bin(1).sumW();
0124       const size_t weight0len = ss.str().length();
0125       ss << fixed << std::setprecision(1) << BaseT::bin(1).effNumEntries();
0126       const size_t count0len = ss.str().length();
0127       ss.str("");
0128       ss << BaseT::path() << " cut-flow:\n";
0129       size_t maxnamelen = 0;
0130       for (const string& edge : BaseT::xEdges()) {
0131         maxnamelen = std::max(edge.length(), maxnamelen);
0132       }
0133       ss << setw(maxnamelen+5) << "" << "   "
0134          << setw(weight0len) << right << "Weight" << "   "
0135          << setw(count0len) << right << "Count" << "    "
0136          << setw(6) << right << "A_cumu" << "    "
0137          << setw(6) << right << "A_incr";
0138       //for (size_t i = 0; i <= ncuts; ++i) {
0139       const double wtot = BaseT::bin(1).sumW();
0140       double wlast = wtot;
0141       for (const auto& bin : BaseT::bins()) {
0142         const size_t i = bin.index();
0143         const int pcttot = (wtot == 0) ? -1 : round(100*bin.sumW()/wtot);
0144         const int pctinc = (i == 1 || wlast == 0) ? -1 : round(100*bin.sumW()/wlast);
0145         wlast = bin.sumW();
0146         stringstream ss2;
0147         ss2 << fixed << setprecision(1) << bin.sumW();
0148         const string weightstr = ss2.str(); ss2.str("");
0149         ss2 << fixed << setprecision(1) << bin.effNumEntries();
0150         const string countstr = ss2.str(); ss2.str("");
0151         ss2 << fixed << setprecision(3) << pcttot << "%";
0152         const string pcttotstr = ss2.str(); ss2.str("");
0153         ss2 << fixed << setprecision(3) << pctinc << "%";
0154         const string pctincstr = ss2.str();
0155         ss << "\n"
0156            << setw(maxnamelen+5) << left << (i == 1 ? "" : "Pass "+BaseT::bin(i).xEdge()) << "   "
0157            << setw(weight0len) << right << weightstr << "    "
0158            << setw(count0len) << right << countstr << "    "
0159            << setw(6) << right << (pcttot < 0 ? "- " : pcttotstr) << "    "
0160            << setw(6) << right << (pctinc < 0 ? "- " : pctincstr);
0161       }
0162       return ss.str();
0163     }
0164 
0165     /// Print string representation to a stream
0166     void print(std::ostream& os) const {
0167       os << str() << std::flush;
0168     }
0169 
0170   protected:
0171 
0172     /// Get a logger object.
0173     Log& getLog() const {
0174       return Rivet::Log::getLog("Rivet.Cutflow");
0175     }
0176 
0177     size_t icurr;
0178 
0179   };
0180 
0181 
0182   /// FillCollector specialisation for all Cutflow-like AO
0183   template <>
0184   class FillCollector<Cutflow> : public Cutflow {
0185   public:
0186 
0187     using YAO = Cutflow;
0188     using Ptr = shared_ptr<FillCollector<YAO>>;
0189     using YAO::operator =;
0190 
0191     FillCollector() : YAO() { }
0192 
0193     /// Constructor
0194     ///
0195     /// We call the cheaper constructor based on the
0196     /// binning to avoid copying of the bin content.
0197     /// The underlying binning object is still used
0198     /// in analize() by many routines, e.g. to query
0199     /// numBins() or to loop over bins() with
0200     /// subsequent calls to bin xEdge() etc.
0201     FillCollector(typename YAO::Ptr yao) : YAO(yao->binning()) {
0202       YAO::setPath(yao->path());
0203     }
0204 
0205     /// Overloaded fill method, which stores Fill info
0206     /// until Multiplexer<T>::collapseEventGroup() is called.
0207     ///
0208     /// @todo Do we need to deal with users using fractions directly?
0209     int fill(typename YAO::FillType&& fillCoords,
0210              const double weight=1.0, const double fraction=1.0) {
0211       (void)fraction; // suppress unused variable warning
0212       YAO::icurr = YAO::_binning.globalIndexAt(fillCoords);
0213       _fills.insert(_fills.end(), { std::move(fillCoords), weight } );
0214       return (int)YAO::icurr;
0215     }
0216 
0217     int fill(const std::string val, const double weight = 1.0, const double fraction = 1.0) {
0218       return fill(typename YAO::FillType{val}, weight, fraction);
0219     }
0220 
0221     int fillnext(const bool cutresult, const double weight = 1.0, const double fraction = 1.0) {
0222       if (!cutresult)  return 0;
0223       return fill(YAO::bin(++YAO::icurr).xEdge(), weight, fraction);
0224     }
0225 
0226     int fillnext(const double weight = 1.0, const double fraction = 1.0) {
0227       return fillnext(true, weight, fraction);
0228     }
0229 
0230     int fillnext(const std::vector<bool>& cutresults, const double weight = 1.0, const double fraction = 1.0) {
0231       if (YAO::icurr+cutresults.size() > YAO::numBins()+1) {
0232         throw RangeError("Number of filled cut results needs to match the Cutflow construction (in cutflow '"+YAO::path()+"')");
0233       }
0234       for (size_t i = 0; i < cutresults.size(); ++i) {
0235         if (!cutresults[i])  return 0;
0236         fillnext(weight, fraction);
0237       }
0238       return (int)YAO::icurr;
0239     }
0240 
0241     int fillinit(const double weight=1.0, const double fraction=1.0) {
0242       return fill(""s, weight, fraction);
0243     }
0244 
0245     /// Empty the subevent stack (for start of new event group).
0246     void reset() noexcept { _fills.clear(); }
0247 
0248     /// Access the fill info subevent stack.
0249     const Fills<YAO>& fills() const { return _fills; }
0250 
0251   private:
0252 
0253     Fills<YAO> _fills;
0254 
0255   };
0256 
0257   /// @name Cutflow utilities
0258   /// @{
0259 
0260   /// Convenience alias
0261   using CutflowPtr = MultiplexPtr<Multiplexer<Cutflow>>;
0262 
0263   /// Print a Cutflow to a stream
0264   inline std::ostream& operator << (std::ostream& os, const Cutflow& cf) {
0265     return os << cf.str();
0266   }
0267 
0268   inline std::ostream& operator << (std::ostream& os, const CutflowPtr& cf) {
0269     return os << cf->str();
0270   }
0271 
0272   /// @}
0273 
0274 
0275   /// A container for several Cutflow objects, with some convenient batch access
0276   class Cutflows : public YODA::FillableStorage<2, CutflowPtr, std::string>,
0277                      public YODA::Fillable {
0278   public:
0279 
0280     using BaseT = YODA::FillableStorage<2, CutflowPtr, std::string>;
0281     using BinContentT = CutflowPtr;
0282     using BinT = YODA::Bin<1, BinContentT, BaseT::BinningT>;
0283     using FillDim = std::integral_constant<size_t, 2>;
0284 
0285     /// @name Constructors
0286     /// @{
0287 
0288     /// Nullary constructor
0289     Cutflows() : BaseT(groupAdapter<FillDim{}, BinContentT, std::string>) { }
0290 
0291     /// Constructor using a vector of (outer) edges
0292     Cutflows(const std::vector<std::string>& edges)
0293          : BaseT(YODA::Axis<std::string>(edges), groupAdapter<FillDim{}, BinContentT, std::string>) { }
0294 
0295     /// Constructor using an initializer_set of (outer) edges
0296     Cutflows(std::initializer_list<std::string>&& edges)
0297          : BaseT(YODA::Axis<std::string>(std::move(edges)), groupAdapter<FillDim{}, BinContentT, std::string>) { }
0298 
0299     /// @}
0300 
0301     /// @name Fill methods
0302     /// @{
0303 
0304     /// Fill method using the FillType of the underlying FIllableStorage
0305     int fill(const std::string& grpCoord, const std::string& edge, const double weight = 1.0, const double fraction = 1.0) {
0306       auto& cfl = BaseT::binAt({grpCoord});
0307       if (!cfl.raw())  return -1; // nullptr if bin not booked
0308       return cfl->fill({edge}, weight, fraction);
0309     }
0310 
0311     /// Method to fill the pre-cut steps
0312     int groupfillinit(const double weight=1.0, const double fraction=1.0) {
0313       bool pass = true;
0314       for (auto& cfl : BaseT::bins()) {
0315         if (!cfl.get())  continue;
0316         pass &= cfl->fillinit(weight, fraction);
0317       }
0318       return (int)pass;
0319     }
0320 
0321     /// Method to fill the next step if @a cutresult is true
0322     int groupfillnext(const bool cutresult, const double weight = 1.0, const double fraction = 1.0) {
0323       if (!cutresult)  return 0;
0324       bool pass = true;
0325       for (auto& cfl : BaseT::bins()) {
0326         if (!cfl.get())  continue;
0327         pass &= cfl->fillnext(cutresult, weight, fraction);
0328       }
0329       return (int)pass;
0330     }
0331 
0332     /// Short-hand method that assumes the cut result is true
0333     int groupfillnext(const double weight = 1.0, const double fraction = 1.0) {
0334       return groupfillnext(true, weight, fraction);
0335     }
0336 
0337     /// Method using a vector of @a cutresults
0338     int groupfillnext(const std::vector<bool>& cutresults, const double weight = 1.0, const double fraction = 1.0) {
0339       bool pass = true;
0340       for (auto& cfl : BaseT::bins()) {
0341         if (!cfl.get())  continue;
0342         pass &= cfl->fillnext(cutresults, weight, fraction);
0343       }
0344       return (int)pass;
0345     }
0346 
0347     /// Method that only fills the next step at coordinate @a grpCoord
0348     int fillnext(const std::string& grpCoord, const double weight=1.0, const double fraction=1.0) {
0349       return BaseT::binAt(grpCoord)->fillnext(weight, fraction);
0350     }
0351 
0352     /// Method that only fills the next step at coordinate @a grpCoord if @a cutresult is true
0353     int fillnext(const std::string& grpCoord, const bool cutresult, const double weight=1.0, const double fraction=1.0) {
0354       return BaseT::binAt(grpCoord)->fillnext(cutresult, weight, fraction);
0355     }
0356 
0357     /// Method that only fills the next step at coordinate @a grpCoord using a vector of @a cutresults
0358     int fillnext(const std::string& grpCoord, const std::vector<bool>& cutresults,
0359                  const double weight = 1.0, const double fraction = 1.0) {
0360       return BaseT::binAt(grpCoord)->fillnext(cutresults, weight, fraction);
0361     }
0362 
0363     /// @}
0364 
0365     /// @name Reset methods
0366     /// @{
0367 
0368     /// @brief Reset the histogram.
0369     ///
0370     /// Keep the binning but set all bin contents and related quantities to zero
0371     void reset() noexcept { BaseT::reset(); }
0372 
0373     /// @}
0374 
0375     /// @name Whole histo data
0376     /// @{
0377 
0378     /// @brief Fill dimension of the object (number of conent axes + temprary axis)
0379     size_t fillDim() const noexcept { return FillDim{}; }
0380 
0381     /// @brief Total dimension of the object (number of fill axes + filled value)
0382     size_t dim() const noexcept { return fillDim()+1; }
0383 
0384     /// @brief Get the number of fills (fractional fills are possible).
0385     double numEntries(const bool includeOverflows=true) const noexcept {
0386       double n = 0;
0387       for (const auto& cfl : BaseT::bins(includeOverflows)) {
0388         if (!cfl.get())  continue;
0389         n += cfl->numEntries(includeOverflows);
0390       }
0391       return n;
0392     }
0393 
0394     /// @brief Get the effective number of fills.
0395     double effNumEntries(const bool includeOverflows=true) const noexcept {
0396       double n = 0;
0397       for (const auto& cfl : BaseT::bins(includeOverflows)) {
0398         if (!cfl.get())  continue;
0399         n += cfl->effNumEntries(includeOverflows);
0400       }
0401       return n;
0402     }
0403 
0404     /// @brief Calculates sum of weights in histo group.
0405     double sumW(const bool includeOverflows=true) const noexcept {
0406       double sumw = 0;
0407       for (const auto& cfl : BaseT::bins(includeOverflows)) {
0408         if (!cfl.get())  continue;
0409         sumw += cfl->sumW(includeOverflows);
0410       }
0411       return sumw;
0412     }
0413 
0414     /// @brief Calculates sum of squared weights in histo group.
0415     double sumW2(const bool includeOverflows=true) const noexcept {
0416       double sumw2 = 0;
0417       for (const auto& cfl : BaseT::bins(includeOverflows)) {
0418         if (!cfl.get())  continue;
0419         sumw2 += cfl->sumW2(includeOverflows);
0420       }
0421       return sumw2;
0422     }
0423 
0424     /// @brief Get the total volume of the histogram group.
0425     double integral(const bool includeOverflows=true) const noexcept {
0426       return sumW(includeOverflows);
0427     }
0428 
0429     /// @brief Get the total volume error of the histogram group.
0430     double integralError(const bool includeOverflows=true) const noexcept {
0431       return sqrt(sumW2(includeOverflows));
0432     }
0433 
0434     /// @}
0435 
0436     /// @name Transformations
0437     /// @{
0438 
0439     /// @brief Rescale as if all fill weights had been different by factor @a scalefactor.
0440     void scaleW(const double scalefactor) noexcept {
0441       for (auto& cfl : BaseT::bins(true, true)) {
0442         if (!cfl.get())  continue;
0443         cfl->scaleW(scalefactor);
0444       }
0445     }
0446 
0447     /// @brief Rescale as if all fill weights had been different by factor @a scalefactor along dimension @a i.
0448     void scale(const double scalefactor) noexcept {
0449       for (auto& cfl : BaseT::bins(true, true)) {
0450         if (!cfl.get())  continue;
0451         cfl->scale(scalefactor);
0452       }
0453     }
0454 
0455     /// Scale the cutflow weights so that the weight count after cut @a edge is @a norm
0456     ///
0457     /// @note Consider each sub-histogram separately when calculating the integral.
0458     void normalizeStep(const std::string& edge, const double norm) {
0459       for (auto& cfl : BaseT::bins(true, true)) {
0460         if (!cfl.get())  continue;
0461         if (cfl->binAt(edge).sumW() != 0.)  cfl->normalizeStep(edge, norm);
0462       }
0463     }
0464 
0465     /// Alias to scale the cutflow weights so that the weight after the pre-init cut is @a norm
0466     void normalizeFirst(const double norm) {
0467       normalizeStep(""s, norm);
0468     }
0469 
0470     /// @}
0471 
0472     /// @name Streaming utilities
0473     /// @{
0474 
0475     /// Create a string representation
0476     string str() const {
0477       std::stringstream ss;
0478       for (auto& cf : BaseT::bins(true, true)) {
0479         if (!cf.get())  continue;
0480         ss << cf << "\n\n";
0481       }
0482       return ss.str();
0483     }
0484 
0485     /// Print string representation to a stream
0486     void print(std::ostream& os) const {
0487       os << str() << std::flush;
0488     }
0489 
0490     /// @}
0491 
0492   };
0493 
0494   /// @name Cutflows utilities
0495   /// @{
0496 
0497   /// Convenience alias
0498   using CutflowsPtr = std::shared_ptr<Cutflows>;
0499 
0500   /// Print a Cutflows to a stream
0501   inline std::ostream& operator << (std::ostream& os, const Cutflows& cfs) {
0502     return os << cfs.str();
0503   }
0504 
0505   /// Print a Cutflows to a stream
0506   inline std::ostream& operator << (std::ostream& os, const CutflowsPtr& cfs) {
0507     return os << cfs->str();
0508   }
0509 
0510   /// @}
0511 
0512 }
0513 
0514 #endif