File indexing completed on 2025-04-19 09:06:50
0001
0002 #ifndef RIVET_CENTRALITYBINNER_HH
0003 #define RIVET_CENTRALITYBINNER_HH
0004 #include <tuple>
0005 #include "Rivet/Config/RivetCommon.hh"
0006 #include "Rivet/Tools/RivetYODA.hh"
0007 #include "Rivet/Projections/HepMCHeavyIon.hh"
0008
0009 namespace Rivet {
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034 class CentralityEstimator : public Projection {
0035 public:
0036
0037
0038 CentralityEstimator(): _estimate(-1.0) {
0039 setName("CentralityEstimator");
0040 declare(HepMCHeavyIon(), "HepMC");
0041 }
0042
0043
0044 RIVET_DEFAULT_PROJ_CLONE(CentralityEstimator);
0045
0046 protected:
0047
0048
0049 void project(const Event& e) {
0050 _estimate = -1.0;
0051 double imp = apply<HepMCHeavyIon>(e, "HepMC").impact_parameter();
0052 if ( imp < 0.0 ) return;
0053 _estimate = imp > 0.0? 1.0/imp: numeric_limits<double>::max();
0054 }
0055
0056
0057 CmpState compare(const Projection& p) const {
0058 return mkNamedPCmp(p, "HepMC");
0059 }
0060
0061
0062 public:
0063
0064
0065 double estimate() const { return _estimate; }
0066
0067
0068 protected:
0069
0070
0071 double _estimate;
0072
0073 };
0074
0075
0076
0077
0078
0079 template <typename T>
0080 struct CentralityBinTraits {
0081
0082
0083 static T clone(const T & t) {
0084 return T(t->newclone());
0085 }
0086
0087
0088 static void add(T & t, const T & o) {
0089 *t += *o;
0090 }
0091
0092
0093 static void scale(T & t, double f) {
0094 t->scaleW(f);
0095 }
0096
0097
0098
0099 static void normalize(T & t, double sumw) {
0100 if ( t->sumW() > 0.0 ) t->normalize(t->sumW()/sumw);
0101 }
0102
0103
0104 static string path(T t) {
0105 return t->path();
0106 }
0107
0108 };
0109
0110
0111
0112
0113 struct MergeDistance {
0114
0115
0116
0117
0118
0119
0120
0121
0122
0123
0124 static double dist(double cestLo, double cestHi, double weight,
0125 double clo, double chi, double, double) {
0126 return (cestHi - cestLo)*weight/(cestHi*(chi - clo));
0127 }
0128 };
0129
0130
0131
0132
0133
0134
0135
0136
0137
0138 template <typename T = Histo1DPtr, typename MDist = MergeDistance>
0139 class CentralityBinner: public ProjectionApplier {
0140 public:
0141
0142
0143
0144
0145
0146 CentralityBinner(int maxbins = 200, double wlim = 0.02)
0147 : _currentCEst(-1.0), _maxBins(maxbins), _warnlimit(wlim), _weightsum(0.0) {
0148 _percentiles.insert(0.0);
0149 _percentiles.insert(1.0);
0150 }
0151
0152
0153
0154 void setProjection(const CentralityEstimator & p, string pname) {
0155 declare(p, pname);
0156 _estimator = pname;
0157 }
0158
0159
0160 virtual std::string name() const {
0161 return "Rivet::CentralityBinner";
0162 }
0163
0164
0165
0166
0167
0168
0169
0170
0171
0172
0173
0174
0175
0176 void add(T t, double cmin, double cmax,
0177 double cestmin = -1.0, double cestmax = -1.0 ) {
0178 _percentiles.insert(max(1.0 - cmax/100.0, 0.0));
0179 _percentiles.insert(min(1.0 - cmin/100.0, 1.0));
0180 if ( _unfilled.empty() && _ready.empty() )
0181 _devnull = CentralityBinTraits<T>::clone(t);
0182 if ( cestmin < 0.0 )
0183 _unfilled.push_back(Bin(t, 1.0 - cmax/100.0, 1.0 - cmin/100.0));
0184 else
0185 _ready[t] = Bin(t, 1.0 - cmax/100.0, 1.0 - cmin/100.0, cestmin, cestmax);
0186 }
0187
0188
0189
0190
0191
0192
0193
0194
0195
0196 T select(const Event & event, double weight = 1.0) {
0197 return select(applyProjection<CentralityEstimator>
0198 (event, _estimator).estimate(), weight);
0199 }
0200
0201
0202
0203
0204
0205
0206
0207 T select(double cest, double weight = 1.0);
0208
0209
0210
0211
0212
0213
0214
0215 void finalize();
0216
0217
0218
0219 void normalizePerEvent() {
0220 for ( auto & b : _ready ) b.second.normalizePerEvent();
0221 }
0222
0223
0224
0225 map<double,double> edges() const {
0226 map<double,double> ret;
0227 for ( auto & b : _ready ) {
0228 ret[1.0 - b.second._centLo] = b.second._cestLo;
0229 ret[1.0 - b.second._centHi] = b.second._cestHi;
0230 }
0231 return ret;
0232 }
0233
0234
0235 const T & current() const {
0236 return _currenT;
0237 }
0238
0239
0240
0241 double estimator() const {
0242 return _currentCEst;
0243 }
0244
0245 vector<T> allObjects() {
0246 vector<T> ret;
0247 for ( auto & fb : _flexiBins ) ret.push_back(fb._t);
0248 if ( !ret.empty() ) return ret;
0249 for ( auto b : _ready ) ret.push_back(b.second._t);
0250 return ret;
0251 }
0252
0253 private:
0254
0255
0256 struct FlexiBin {
0257
0258
0259
0260 FlexiBin(T & t, double cest = 0.0, double weight = 0.0)
0261 : _t(t), _cestLo(cest), _cestHi(cest), _weightsum(weight), _n(1), _m(0) {}
0262
0263
0264 FlexiBin(double cest)
0265 : _cestLo(cest), _cestHi(cest), _weightsum(0.0), _n(0), _m(0) {}
0266
0267
0268 void merge(const FlexiBin & fb) {
0269 _cestLo = min(_cestLo, fb._cestLo);
0270 _cestHi = max(_cestHi, fb._cestHi);
0271 _weightsum += fb._weightsum;
0272 CentralityBinTraits<T>::add(_t, fb._t);
0273 _n += fb._n;
0274 _m += fb._m + 1;
0275 }
0276
0277
0278 bool operator< (const FlexiBin & fb) const {
0279 return _cestLo < fb._cestLo;
0280 }
0281
0282
0283
0284 bool inRange(double cest) const {
0285 return cest == _cestLo || ( _cestLo < cest && cest < _cestHi );
0286 }
0287
0288
0289 T _t;
0290
0291
0292
0293 double _cestLo, _cestHi;
0294
0295
0296
0297 mutable double _weightsum;
0298
0299
0300 mutable int _n;
0301
0302
0303 mutable int _m;
0304
0305 };
0306
0307 struct Bin {
0308
0309
0310 Bin()
0311 : _centLo(-1.0), _centHi(-1.0), _cestLo(-1.0), _cestHi(-1.0),
0312 _weightsum(0.0), _underflow(0.0), _overflow(0.0),
0313 _ambiguous(0), _ambweight(0.0) {}
0314
0315
0316
0317
0318
0319 Bin(T t, double centLo, double centHi,
0320 double cestLo = -1.0, double cestHi = -1.0)
0321 : _t(t), _centLo(centLo), _centHi(centHi),
0322 _cestLo(cestLo), _cestHi(cestHi),
0323 _weightsum(0.0), _underflow(0.0), _overflow(0.0),
0324 _ambiguous(0.0), _ambweight(0.0) {}
0325
0326
0327
0328 bool inRange(double cest) const {
0329 return _cestLo >= 0 && _cestLo <= cest &&
0330 ( _cestHi < 0.0 || cest <= _cestHi );
0331 }
0332
0333
0334 void normalizePerEvent() {
0335 CentralityBinTraits<T>::normalize(_t, _weightsum);
0336 }
0337
0338
0339 T _t;
0340
0341
0342 double _centLo, _centHi;
0343
0344
0345 double _cestLo, _cestHi;
0346
0347
0348 double _weightsum;
0349
0350
0351
0352 double _underflow;
0353
0354
0355
0356 double _overflow;
0357
0358
0359 double _ambiguous;
0360
0361
0362 double _ambweight;
0363
0364 };
0365
0366 protected:
0367
0368
0369 typedef set<FlexiBin> FlexiBinSet;
0370
0371
0372
0373 typename FlexiBinSet::iterator _findBin(double cest) {
0374 if ( _flexiBins.empty() ) return _flexiBins.end();
0375 auto it = _flexiBins.lower_bound(FlexiBin(cest));
0376 if ( it->_cestLo == cest ) return it;
0377 if ( it != _flexiBins.begin() ) --it;
0378 if ( it->_cestLo < cest && cest < it->_cestHi ) return it;
0379 return _flexiBins.end();
0380 }
0381
0382
0383 string _estimator;
0384
0385
0386
0387 T _currenT;
0388
0389
0390 double _currentCEst;
0391
0392
0393
0394 int _maxBins;
0395
0396
0397
0398 double _warnlimit;
0399
0400
0401
0402 vector<Bin> _unfilled;
0403
0404
0405 FlexiBinSet _flexiBins;
0406
0407
0408 double _weightsum;
0409
0410
0411 set<double> _percentiles;
0412
0413
0414 map<T, Bin> _ready;
0415
0416
0417
0418 T _devnull;
0419
0420 public:
0421
0422
0423 void debug();
0424 void fulldebug();
0425
0426 };
0427
0428
0429
0430 template <>
0431 struct CentralityBinTraits<Profile1DPtr> {
0432
0433 typedef Profile1DPtr T;
0434
0435
0436 static T clone(const T & t) {
0437 return Profile1DPtr(t->newclone());
0438 }
0439
0440
0441 static void add(T & t, const T & o) {
0442 *t += *o;
0443 }
0444
0445
0446 static void scale(T & t, double f) {
0447 t->scaleW(f);
0448 }
0449
0450 static void normalize(T & t, double sumw) {}
0451
0452
0453 static string path(T t) {
0454 return t->path();
0455 }
0456
0457 };
0458
0459
0460
0461 template <>
0462 struct CentralityBinTraits<Profile2DPtr> {
0463
0464 typedef Profile2DPtr T;
0465
0466
0467 static T clone(const T & t) {
0468 return Profile2DPtr(t->newclone());
0469 }
0470
0471
0472 static void add(T & t, const T & o) {
0473 *t += *o;
0474 }
0475
0476
0477 static void scale(T & t, double f) {
0478 t->scaleW(f);
0479 }
0480
0481 static void normalize(T & t, double sumw) {}
0482
0483
0484 static string path(T t) {
0485 return t->path();
0486 }
0487
0488 };
0489
0490 template <typename T>
0491 struct CentralityBinTraits< vector<T> > {
0492
0493
0494 static vector<T> clone(const vector<T> & tv) {
0495 vector<T> rtv;
0496 for ( auto t : tv ) rtv.push_back(CentralityBinTraits<T>::clone(t));
0497 return rtv;
0498 }
0499
0500
0501 static void add(vector<T> & tv, const vector<T> & ov) {
0502 for ( int i = 0, N = tv.size(); i < N; ++i )
0503 CentralityBinTraits::add(tv[i], ov[i]);
0504 }
0505
0506
0507 static void scale(vector<T> & tv, double f) {
0508 for ( auto t : tv ) CentralityBinTraits<T>::scale(t, f);
0509 }
0510
0511 static void normalize(vector<T> & tv, double sumw) {
0512 for ( auto t : tv ) CentralityBinTraits<T>::normalize(t, sumw);
0513 }
0514
0515
0516 static string path(const vector<T> & tv) {
0517 string ret = "(vector:";
0518 for ( auto t : tv ) {
0519 ret += " ";
0520 ret += CentralityBinTraits<T>::path(t);
0521 }
0522 ret += ")";
0523 return ret;
0524 }
0525
0526 };
0527
0528 template <size_t I, typename... Types>
0529 struct TupleCentralityBinTraitsHelper {
0530
0531 typedef tuple<Types...> Tuple;
0532 typedef typename tuple_element<I-1,Tuple>::type T;
0533
0534 static void clone(Tuple & ret, const Tuple & tup) {
0535 get<I-1>(ret) = CentralityBinTraits<T>::clone(get<I-1>(tup));
0536 TupleCentralityBinTraitsHelper<I-1,Types...>::clone(ret, tup);
0537 }
0538
0539 static void add(Tuple & tup, const Tuple & otup) {
0540 CentralityBinTraits<T>::add(get<I-1>(tup),get<I-1>(otup));
0541 TupleCentralityBinTraitsHelper<I-1,Types...>::add(tup, otup);
0542 }
0543
0544 static void scale(Tuple & tup, double f) {
0545 CentralityBinTraits<T>::scale(get<I-1>(tup), f);
0546 TupleCentralityBinTraitsHelper<I-1,Types...>::scale(tup, f);
0547 }
0548
0549 static void normalize(Tuple & tup, double sumw) {
0550 CentralityBinTraits<T>::normalize(get<I-1>(tup), sumw);
0551 TupleCentralityBinTraitsHelper<I-1,Types...>::normalize(tup, sumw);
0552 }
0553
0554 static string path(const Tuple & tup) {
0555 return " " + CentralityBinTraits<T>::path(get<I-1>(tup))
0556 + TupleCentralityBinTraitsHelper<I-1,Types...>::path(tup);
0557 }
0558 };
0559
0560 template <typename... Types>
0561 struct TupleCentralityBinTraitsHelper<0,Types...> {
0562
0563 typedef tuple<Types...> Tuple;
0564
0565 static void clone(Tuple &, const Tuple &) {}
0566 static void add(Tuple & tup, const Tuple & otup) {}
0567 static void scale(Tuple & tup, double f) {}
0568 static void normalize(Tuple & tup, double sumw) {}
0569 static string path(const Tuple & tup) {return "";}
0570
0571 };
0572
0573 template <typename... Types>
0574 struct CentralityBinTraits< tuple<Types...> > {
0575
0576 typedef tuple<Types...> Tuple;
0577 static const size_t N = tuple_size<Tuple>::value;
0578
0579
0580 static Tuple clone(const Tuple & tup) {
0581 Tuple ret;
0582 TupleCentralityBinTraitsHelper<N,Types...>::clone(ret, tup);
0583 return ret;
0584 }
0585
0586
0587 static void add(Tuple & tup, const Tuple & otup) {
0588 TupleCentralityBinTraitsHelper<N,Types...>::add(tup, otup);
0589 }
0590
0591
0592 static void scale(Tuple & tup, double f) {
0593 TupleCentralityBinTraitsHelper<N,Types...>::scale(tup, f);
0594 }
0595
0596 static void normalize(Tuple & tup, double sumw) {
0597 TupleCentralityBinTraitsHelper<N,Types...>::normalize(tup, sumw);
0598 }
0599
0600
0601 static string path(const Tuple & tup) {
0602 string ret = "(tuple:";
0603 ret += TupleCentralityBinTraitsHelper<N,Types...>::path(tup);
0604 ret += ")";
0605 return ret;
0606 }
0607
0608 };
0609
0610 template <typename T, typename MDist>
0611 T CentralityBinner<T,MDist>::select(double cest, double weight) {
0612 _currenT = _devnull;
0613 _currentCEst = cest;
0614 _weightsum += weight;
0615
0616
0617 if ( _currentCEst < 0.0 ) return _currenT;
0618
0619
0620
0621
0622 if ( _unfilled.empty() ) {
0623 for ( auto & b : _ready ) if ( b.second.inRange(_currentCEst) ) {
0624 b.second._weightsum += weight;
0625 return b.second._t;
0626 }
0627 return _currenT;
0628 }
0629
0630 auto it = _findBin(cest);
0631 if ( it == _flexiBins.end() ) {
0632 _currenT = CentralityBinTraits<T>::clone(_unfilled.begin()->_t);
0633 it = _flexiBins.insert(FlexiBin(_currenT, _currentCEst, weight)).first;
0634 } else {
0635 it->_weightsum += weight;
0636 ++(it->_n);
0637 _currenT = it->_t;
0638 }
0639
0640 if ( (int)_flexiBins.size() <= _maxBins ) return _currenT;
0641
0642
0643 set<double>::iterator citn = _percentiles.begin();
0644 set<double>::iterator cit0 = citn++;
0645 auto selectit = _flexiBins.end();
0646 double mindist = -1.0;
0647 double acc = 0.0;
0648 auto next = _flexiBins.begin();
0649 auto prev = next++;
0650 for ( ; next != _flexiBins.end(); prev = next++ ) {
0651 acc += prev->_weightsum/_weightsum;
0652 if ( acc > *citn ) {
0653 cit0 = citn++;
0654 continue;
0655 }
0656 if ( acc + next->_weightsum/_weightsum > *citn ) continue;
0657 double dist = MDist::dist(prev->_cestLo, next->_cestHi,
0658 next->_weightsum + prev->_weightsum,
0659 *cit0, *citn, next->_n + prev->_n,
0660 next->_m + prev->_m);
0661 if ( mindist < 0.0 || dist < mindist ) {
0662 selectit = prev;
0663 mindist = dist;
0664 }
0665 }
0666
0667 if ( selectit == _flexiBins.end() ) return _currenT;
0668 auto mergeit = selectit++;
0669 FlexiBin merged = *mergeit;
0670 merged.merge(*selectit);
0671 if ( merged.inRange(cest) || selectit->inRange(cest) )
0672 _currenT = merged._t;
0673 _flexiBins.erase(mergeit);
0674 _flexiBins.erase(selectit);
0675 _flexiBins.insert(merged);
0676
0677 return _currenT;
0678
0679 }
0680
0681
0682 template <typename T, typename MDist>
0683 void CentralityBinner<T,MDist>::finalize() {
0684
0685
0686
0687
0688 double clo = 0.0;
0689 for ( const FlexiBin & fb : _flexiBins ) {
0690 double chi = min(clo + fb._weightsum/_weightsum, 1.0);
0691 for ( Bin & bin : _unfilled ) {
0692 double olo = bin._centLo;
0693 double ohi = bin._centHi;
0694 if ( clo > ohi || chi <= olo ) continue;
0695
0696 double lo = max(olo, clo);
0697 double hi = min(ohi, chi);
0698 T t = CentralityBinTraits<T>::clone(fb._t);
0699 double frac = (hi - lo)/(chi - clo);
0700 CentralityBinTraits<T>::scale(t, frac);
0701 CentralityBinTraits<T>::add(bin._t, t);
0702 bin._weightsum += fb._weightsum*frac;
0703 if ( clo <= olo ) bin._cestLo = fb._cestLo +
0704 (fb._cestHi - fb._cestLo)*(olo - clo)/(chi - clo);
0705 if ( clo < olo ) {
0706 bin._underflow = clo;
0707 bin._ambiguous += fb._n*frac;
0708 bin._ambweight += fb._weightsum*frac*(1.0 - frac);
0709 }
0710 if ( chi > ohi ) {
0711 bin._cestHi =
0712 fb._cestLo + (fb._cestHi - fb._cestLo)*(ohi - clo)/(chi - clo);
0713 bin._overflow = chi;
0714 bin._ambiguous += fb._n*frac;
0715 bin._ambweight += fb._weightsum*frac*(1.0 - frac);
0716 }
0717 }
0718 clo = chi;
0719 }
0720 _flexiBins.clear();
0721 for ( Bin & bin : _unfilled ) {
0722 if ( bin._overflow == 0.0 ) bin._overflow = 1.0;
0723 _ready[bin._t] = bin;
0724 if ( bin._ambweight/bin._weightsum >_warnlimit )
0725 MSG_WARNING("Analysis object \"" << CentralityBinTraits<T>::path(bin._t)
0726 << "\", contains events with centralities between "
0727 << bin._underflow*100.0
0728 << " and " << bin._overflow*100.0 << "% ("
0729 << int(bin._ambiguous + 0.5)
0730 << " ambiguous events with effectively "
0731 << 100.0*bin._ambweight/bin._weightsum
0732 << "% of the weights)."
0733 << "Consider increasing the number of bins.");
0734
0735 }
0736 _unfilled.clear();
0737
0738 }
0739
0740 template <typename T, typename MDist>
0741 void CentralityBinner<T,MDist>::fulldebug() {
0742 cerr << endl;
0743 double acc = 0.0;
0744 set<double>::iterator citn = _percentiles.begin();
0745 set<double>::iterator cit0 = citn++;
0746 int i = 0;
0747 for ( auto it = _flexiBins.begin(); it != _flexiBins.end(); ) {
0748 ++i;
0749 auto curr = it++;
0750 double w = curr->_weightsum/_weightsum;
0751 acc += w;
0752 if ( curr == _flexiBins.begin() || it == _flexiBins.end() || acc > *citn )
0753 cerr << "*";
0754 else
0755 cerr << " ";
0756 if ( acc > *citn ) cit0 = citn++;
0757 cerr << setw(6) << i
0758 << setw(12) << acc - w
0759 << setw(12) << acc
0760 << setw(8) << curr->_n
0761 << setw(8) << curr->_m
0762 << setw(12) << curr->_cestLo
0763 << setw(12) << curr->_cestHi << endl;
0764 }
0765 cerr << "Number of sampler bins: " << _flexiBins.size() << endl;
0766 }
0767
0768 template <typename T, typename MDist>
0769 void CentralityBinner<T,MDist>::debug() {
0770 cerr << endl;
0771 double acc = 0.0;
0772 int i = 0;
0773 set<double>::iterator citn = _percentiles.begin();
0774 set<double>::iterator cit0 = citn++;
0775 for ( auto it = _flexiBins.begin(); it != _flexiBins.end(); ) {
0776 auto curr = it++;
0777 ++i;
0778 double w = curr->_weightsum/_weightsum;
0779 acc += w;
0780 if ( curr == _flexiBins.begin() || it == _flexiBins.end() || acc > *citn ) {
0781 if ( acc > *citn ) cit0 = citn++;
0782 cerr << setw(6) << i
0783 << setw(12) << acc - w
0784 << setw(12) << acc
0785 << setw(8) << curr->_n
0786 << setw(8) << curr->_m
0787 << setw(12) << curr->_cestLo
0788 << setw(12) << curr->_cestHi << endl;
0789
0790 }
0791 }
0792 cerr << "Number of sampler bins: " << _flexiBins.size() << endl;
0793 }
0794
0795
0796
0797 class GeneratedCentrality: public CentralityEstimator {
0798
0799 public:
0800
0801
0802 GeneratedCentrality() {
0803 declare(HepMCHeavyIon(), "HI");
0804 }
0805
0806
0807 RIVET_DEFAULT_PROJ_CLONE(GeneratedCentrality);
0808
0809 protected:
0810
0811
0812 void project(const Event& e) {
0813 _estimate = apply<HepMCHeavyIon>(e, "HI").centrality();
0814 }
0815
0816
0817 CmpState compare(const Projection& p) const {
0818 return mkNamedPCmp(p, "GeneratedCentrality");
0819 }
0820
0821 };
0822
0823
0824
0825 }
0826
0827 #endif