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
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
0035 Cutflow() : BaseT(), icurr(0) {}
0036
0037
0038 Cutflow(const BinningT& binning) : BaseT(binning), icurr(0) { }
0039
0040
0041 Cutflow(BinningT&& binning) : BaseT(std::move(binning)), icurr(0) { }
0042
0043
0044
0045
0046 Cutflow(const std::vector<std::string>& edges, const std::string& path = "")
0047 : BaseT(std::vector<std::string>{""} + edges, path), icurr(0) { }
0048
0049
0050
0051
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;
0055 return pos;
0056 }
0057
0058
0059
0060
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;
0064 return pos;
0065 }
0066
0067
0068 virtual int fillinit(const double weight=1.0, const double fraction=1.0) {
0069 return fill(""s, weight, fraction);
0070 }
0071
0072
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
0080 virtual int fillnext(const double weight = 1.0, const double fraction = 1.0) {
0081 return fillnext(true, weight, fraction);
0082 }
0083
0084
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);
0092 }
0093 return (int)icurr;
0094 }
0095
0096
0097 size_t currentIndex() const { return icurr; }
0098
0099
0100 void scale(const double factor) {
0101 BaseT::scaleW(factor);
0102 }
0103
0104
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
0115 void normalizeFirst(const double norm) {
0116 normalizeStep(""s, norm);
0117 }
0118
0119
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
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
0166 void print(std::ostream& os) const {
0167 os << str() << std::flush;
0168 }
0169
0170 protected:
0171
0172
0173 Log& getLog() const {
0174 return Rivet::Log::getLog("Rivet.Cutflow");
0175 }
0176
0177 size_t icurr;
0178
0179 };
0180
0181
0182
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
0194
0195
0196
0197
0198
0199
0200
0201 FillCollector(typename YAO::Ptr yao) : YAO(yao->binning()) {
0202 YAO::setPath(yao->path());
0203 }
0204
0205
0206
0207
0208
0209 int fill(typename YAO::FillType&& fillCoords,
0210 const double weight=1.0, const double fraction=1.0) {
0211 (void)fraction;
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
0246 void reset() noexcept { _fills.clear(); }
0247
0248
0249 const Fills<YAO>& fills() const { return _fills; }
0250
0251 private:
0252
0253 Fills<YAO> _fills;
0254
0255 };
0256
0257
0258
0259
0260
0261 using CutflowPtr = MultiplexPtr<Multiplexer<Cutflow>>;
0262
0263
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
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
0286
0287
0288
0289 Cutflows() : BaseT(groupAdapter<FillDim{}, BinContentT, std::string>) { }
0290
0291
0292 Cutflows(const std::vector<std::string>& edges)
0293 : BaseT(YODA::Axis<std::string>(edges), groupAdapter<FillDim{}, BinContentT, std::string>) { }
0294
0295
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
0302
0303
0304
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;
0308 return cfl->fill({edge}, weight, fraction);
0309 }
0310
0311
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
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
0333 int groupfillnext(const double weight = 1.0, const double fraction = 1.0) {
0334 return groupfillnext(true, weight, fraction);
0335 }
0336
0337
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
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
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
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
0366
0367
0368
0369
0370
0371 void reset() noexcept { BaseT::reset(); }
0372
0373
0374
0375
0376
0377
0378
0379 size_t fillDim() const noexcept { return FillDim{}; }
0380
0381
0382 size_t dim() const noexcept { return fillDim()+1; }
0383
0384
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
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
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
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
0425 double integral(const bool includeOverflows=true) const noexcept {
0426 return sumW(includeOverflows);
0427 }
0428
0429
0430 double integralError(const bool includeOverflows=true) const noexcept {
0431 return sqrt(sumW2(includeOverflows));
0432 }
0433
0434
0435
0436
0437
0438
0439
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
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
0456
0457
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
0466 void normalizeFirst(const double norm) {
0467 normalizeStep(""s, norm);
0468 }
0469
0470
0471
0472
0473
0474
0475
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
0486 void print(std::ostream& os) const {
0487 os << str() << std::flush;
0488 }
0489
0490
0491
0492 };
0493
0494
0495
0496
0497
0498 using CutflowsPtr = std::shared_ptr<Cutflows>;
0499
0500
0501 inline std::ostream& operator << (std::ostream& os, const Cutflows& cfs) {
0502 return os << cfs.str();
0503 }
0504
0505
0506 inline std::ostream& operator << (std::ostream& os, const CutflowsPtr& cfs) {
0507 return os << cfs->str();
0508 }
0509
0510
0511
0512 }
0513
0514 #endif