Back to home page

EIC code displayed by LXR

 
 

    


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

0001 // -*- C++ -*-
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   /// @brief Base class for projections profile observable value vs the collision centrality
0013   ///
0014   /// @author Leif Lönnblad
0015   ///
0016   /// The centrality of a collision is not really an observable, but the
0017   /// concept is anyway often used in the heavy ion community as if it
0018   /// were just that.
0019   ///
0020   /// This base class can be used to provide a an estimator for the
0021   /// centrality by projecting down to a single number which then can be
0022   /// used by a CentralityBinner object to select a histogram to be
0023   /// filled with another observable depending on centrality percentile.
0024   ///
0025   /// The estimate() should be a non-negative number with large values
0026   /// indicating a higher overlap than small ones. A negative value
0027   /// indicates that the centrality estimate could not be calculated.
0028   ///
0029   /// In the best of all worlds the centrality estimator should be a
0030   /// proper hadron-level observable corrected for detector effects,
0031   /// however, this base class only returns the inverse of the
0032   /// impact_parameter member of the @c GenHeavyIon object in an GenEvent
0033   /// if present and zero otherwise.
0034   class CentralityEstimator : public Projection {
0035   public:
0036 
0037     /// Constructor.
0038     CentralityEstimator(): _estimate(-1.0) {
0039       setName("CentralityEstimator");
0040       declare(HepMCHeavyIon(), "HepMC");
0041     }
0042 
0043     /// Clone on the heap.
0044     RIVET_DEFAULT_PROJ_CLONE(CentralityEstimator);
0045 
0046   protected:
0047 
0048     /// Perform the projection on the Event
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     /// Compare projections
0057     CmpState compare(const Projection& p) const {
0058       return mkNamedPCmp(p, "HepMC");
0059     }
0060 
0061 
0062   public:
0063 
0064     /// The value of the centrality estimate.
0065     double estimate() const { return _estimate; }
0066 
0067 
0068   protected:
0069 
0070     /// The value of the centrality estimate.
0071     double _estimate;
0072 
0073   };
0074 
0075 
0076   /// This is a traits class describing how to handle object handles by
0077   /// CentralityBinner. The default implementation basically describes
0078   /// what to do with Histo1DPtr.
0079   template <typename T>
0080   struct CentralityBinTraits {
0081 
0082     /// Make a clone of the given object.
0083     static T clone(const T & t) {
0084       return T(t->newclone());
0085     }
0086 
0087     /// Add the contents of @a o to @a t.
0088     static void add(T & t, const T & o) {
0089       *t += *o;
0090     }
0091 
0092     /// Scale the contents of a given object.
0093     static void scale(T & t, double f) {
0094       t->scaleW(f);
0095     }
0096 
0097     /// Normalize the AnalysisObject to the sum of weights in a
0098     /// centrality bin.
0099     static void normalize(T & t, double sumw) {
0100       if ( t->sumW() > 0.0 ) t->normalize(t->sumW()/sumw);
0101     }
0102 
0103     /// Return the path of an AnalysisObject.
0104     static string path(T t) {
0105       return t->path();
0106     }
0107 
0108   };
0109 
0110   /// The sole purpose of the MergeDistance class is to provide a
0111   /// "distance" for a potential merging of two neighboring bins in a
0112   /// CentralityBinner.
0113   struct MergeDistance {
0114 
0115     /// This function should return a generalized distance between two
0116     /// adjecent centrality bins to be merged. CentralityBinner will
0117     /// always try to merge bins with the smallest distance. @a cestLo
0118     /// and @a cestHi are the lower and upper edges of resulting bin. @a
0119     /// weight is the resulting sum of event weights in the bin. @a
0120     /// centLo and @a centHi are the lower and upper prcentile limits
0121     /// where the two bins currently resides. The two last arguments are
0122     /// the total number of events in the two bins and the total number
0123     /// of previous mergers repectively.
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    * CentralityBinner contains a series of AnalysisObject of the same
0133    * quantity each in a different percentiles of another quantity.  For
0134    * example, a CentralityBinner may e.g. contain histograms of the
0135    * cross section differential in \f$ p_T \f$ in different centrality
0136    * regions for heavy ion collisions based on forward energy flow.
0137    **/
0138   template <typename T = Histo1DPtr, typename MDist = MergeDistance>
0139   class CentralityBinner: public ProjectionApplier {
0140   public:
0141 
0142     /// Create a new empty CentralityBinner. @a maxbins is the maximum
0143     /// number of bins used by the binner. Default is 1000, which is
0144     /// typically enough. @a wlim is the mximum allowed error allowed
0145     /// for the centrality limits before a warning is emitted.
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     /// Set the centrality projection to be used. Note that this
0153     /// projection must have already been declared to Rivet.
0154     void setProjection(const CentralityEstimator & p, string pname) {
0155       declare(p, pname);
0156       _estimator = pname;
0157     }
0158 
0159     /// Return the class name.
0160     virtual std::string name() const {
0161       return "Rivet::CentralityBinner";
0162     }
0163 
0164     /// Add an AnalysisObject in the region between @a cmin and @a cmax to
0165     /// this set of CentralityBinners. The range represent
0166     /// percentiles and must be between 0 and 100. No overlaping bins
0167     /// are allowed.
0168 
0169     /// Note that (cmin=0, cmax=5), means the five percent MOST central
0170     /// events although the internal notation is reversed for
0171     /// convenience.
0172 
0173     /// Optionally supply corresponding limits @a cestmin and @a cestmax
0174     /// of the centrality extimator.
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     /// Return one of the AnalysisObjects in the CentralityBinner for
0189     /// the given @a event. This version requires that a
0190     /// CentralityEstimator object has been assigned that can compute
0191     /// the value of the centrality estimator from the @a
0192     /// event. Optionally the @a weight of the event is given. This
0193     /// should be the weight that will be used to fill the
0194     /// AnalysisObject. If the centrality estimate is less than zero,
0195     /// the _devnull object will be returned.
0196     T select(const Event & event, double weight = 1.0) {
0197       return select(applyProjection<CentralityEstimator>
0198                     (event, _estimator).estimate(), weight);
0199     }
0200 
0201     /// Return one of the AnalysisObjecsts in the Setup the
0202     /// CentralityBinner depending on the value of the centrality
0203     /// estimator, @a cest. Optionally the @a weight of the event is
0204     /// given. This should be the weight that will be used to fill the
0205     /// AnalysisObject. If the centrality estimate is less than zero,
0206     /// the _devnull object will be returned.
0207     T select(double cest, double weight = 1.0);
0208 
0209     /// At the end of the run, calculate the percentiles and fill the
0210     /// AnalysisObjectss provided with the add() function. This is
0211     /// typically called from the finalize method in an Analysis, but
0212     /// can also be called earlier in which case the the select
0213     /// functions can be continued to run as before with the edges
0214     /// between the centrality regions now fixed.
0215     void finalize();
0216 
0217     /// Normalize each AnalysisObjects to the sum of event weights in the
0218     /// corresponding centrality bin.
0219     void normalizePerEvent() {
0220       for ( auto & b : _ready ) b.second.normalizePerEvent();
0221     }
0222 
0223     /// Return a map bin edges of the centrality extimator indexed by
0224     /// the corresponing percentile.
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     /// Return the current AnalysisObject from the latest call to select().
0235     const T & current() const {
0236       return _currenT;
0237     }
0238 
0239     /// Return the value of the centrality estimator set in the latest
0240     /// call to select().
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     /// A flexible bin struct to be used to store temporary AnalysisObjects.
0256     struct FlexiBin {
0257 
0258       /// Construct with an initial centrality estimate and an event
0259       /// weight.
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       /// Construct a temporary FlexiBin for finding a bin in a set.
0264       FlexiBin(double cest)
0265         : _cestLo(cest), _cestHi(cest), _weightsum(0.0), _n(0), _m(0) {}
0266 
0267       /// Merge in the contents of another FlexiBin into this.
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       /// Comparisons for containers.
0278       bool operator< (const FlexiBin & fb) const {
0279         return _cestLo < fb._cestLo;
0280       }
0281 
0282       /// Return true if the given centrality estimate is in the range
0283       /// of this bin.
0284       bool inRange(double cest) const {
0285         return cest == _cestLo || ( _cestLo < cest && cest < _cestHi );
0286       }
0287 
0288       /// The associated AnalysisObject.
0289       T _t;
0290 
0291       /// Current lower and upper edge of the centrality estimator for
0292       /// the fills in the associated AnalysiObject.
0293       double _cestLo, _cestHi;
0294 
0295       /// The sum of weights for all events entering the associated
0296       /// AnalysisObject.
0297       mutable double _weightsum;
0298 
0299       /// The number of times this bin has been selected.
0300       mutable int _n;
0301 
0302       /// The number of times this bin has been merged.
0303       mutable int _m;
0304 
0305     };
0306 
0307     struct Bin {
0308 
0309       /// Construct a completely empty bin.
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       /// Constructor taking an AnalysisObject and centrality interval
0316       /// as argument. Optionally the interval in the estimator can be
0317       /// given, in which case this bin is considered to be
0318       /// "final".
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       /// Return true if the given centrality estimate is in the range
0327       /// of this AnalysisObject.
0328       bool inRange(double cest) const {
0329         return _cestLo >= 0 && _cestLo <= cest &&
0330           ( _cestHi < 0.0 || cest <= _cestHi );
0331       }
0332 
0333       /// Normalise the AnalysisObject to the tital cross section.
0334       void normalizePerEvent() {
0335         CentralityBinTraits<T>::normalize(_t, _weightsum);
0336       }
0337 
0338       /// The AnalysisObject.
0339       T _t;
0340 
0341       /// The range in centrality.
0342       double _centLo, _centHi;
0343 
0344       /// The corresponding range in the centrality estimator.
0345       double _cestLo, _cestHi;
0346 
0347       /// The sum of event weights for this bin;
0348       double _weightsum;
0349 
0350       /// The weight in a final AnalysisObject that contains events
0351       /// below the centrality limit.
0352       double _underflow;
0353 
0354       /// The weight in a final AnalysisObject that contain events above
0355       /// the centrality limit.
0356       double _overflow;
0357 
0358       /// Number of ambiguous events in this bin.
0359       double _ambiguous;
0360 
0361       /// Sum of abmiguous weights.
0362       double _ambweight;
0363 
0364     };
0365 
0366   protected:
0367 
0368     /// Convenient typedefs.
0369     typedef set<FlexiBin> FlexiBinSet;
0370 
0371     /// Find a bin corresponding to a given value of the centrality
0372     /// estimator.
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     /// The name of the CentralityEstimator projection to be used.
0383     string _estimator;
0384 
0385     /// The current temporary AnalysisObject selected for the centrality
0386     /// estimator calculated from the event presented in setup().
0387     T _currenT;
0388 
0389     /// The current value of the centrality estimator.
0390     double _currentCEst;
0391 
0392     /// The oversampling of centrality bins. For each requested
0393     /// centrality bin this number of dynamic bins will be used.
0394     int _maxBins;
0395 
0396     /// If the fraction of events in a bin that comes from adjecent
0397     /// centrality bins exceeds this, emit a warning.
0398     double _warnlimit;
0399 
0400     /// The unfilled AnalysisObjectss where the esimator edges has not yet
0401     /// been determined.
0402     vector<Bin> _unfilled;
0403 
0404     /// The dynamic bins for ranges of centrality estimators.
0405     FlexiBinSet _flexiBins;
0406 
0407     /// The sum of all event weights so far.
0408     double _weightsum;
0409 
0410     /// The requested percentile limits.
0411     set<double> _percentiles;
0412 
0413     /// The filled AnalysisObjects where the estimator edges has been determined.
0414     map<T, Bin> _ready;
0415 
0416     /// A special AnalysisObject which will be filled if the centrality
0417     /// estimate is out of range (negative).
0418     T _devnull;
0419 
0420   public:
0421 
0422     /// Print out the _flexiBins to cerr.
0423     void debug();
0424     void fulldebug();
0425 
0426   };
0427 
0428 
0429   /// Traits specialization for Profile histograms.
0430   template <>
0431   struct CentralityBinTraits<Profile1DPtr> {
0432 
0433     typedef Profile1DPtr T;
0434 
0435     /// Make a clone of the given object.
0436     static T clone(const T & t) {
0437       return Profile1DPtr(t->newclone());
0438     }
0439 
0440     /// Add the contents of @a o to @a t.
0441     static void add(T & t, const T & o) {
0442       *t += *o;
0443     }
0444 
0445     /// Scale the contents of a given object.
0446     static void scale(T & t, double f) {
0447       t->scaleW(f);
0448     }
0449 
0450     static void normalize(T & t, double sumw) {}
0451 
0452     /// Return the path of an AnalysisObject.
0453     static string path(T t) {
0454       return t->path();
0455     }
0456 
0457   };
0458 
0459 
0460   /// Traits specialization for Profile histograms.
0461   template <>
0462   struct CentralityBinTraits<Profile2DPtr> {
0463 
0464     typedef Profile2DPtr T;
0465 
0466     /// Make a clone of the given object.
0467     static T clone(const T & t) {
0468       return Profile2DPtr(t->newclone());
0469     }
0470 
0471     /// Add the contents of @a o to @a t.
0472     static void add(T & t, const T & o) {
0473       *t += *o;
0474     }
0475 
0476     /// Scale the contents of a given object.
0477     static void scale(T & t, double f) {
0478       t->scaleW(f);
0479     }
0480 
0481     static void normalize(T & t, double sumw) {}
0482 
0483     /// Return the name of an AnalysisObject.
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     /// Make a clone of the given object.
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     /// Add the contents of @a o to @a t.
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     /// Scale the contents of a given object.
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     /// Return the path of an AnalysisObject.
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     /// Make a clone of the given object.
0580     static Tuple clone(const Tuple & tup) {
0581       Tuple ret;
0582       TupleCentralityBinTraitsHelper<N,Types...>::clone(ret, tup);
0583       return ret;
0584     }
0585 
0586     /// Add the contents of @a o to @a t.
0587     static void add(Tuple & tup, const Tuple & otup) {
0588       TupleCentralityBinTraitsHelper<N,Types...>::add(tup, otup);
0589     }
0590 
0591     /// Scale the contents of a given object.
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     /// Return the path of an AnalysisObject.
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     // If estimator is negative, something has gone wrong.
0617     if ( _currentCEst < 0.0 ) return _currenT;
0618 
0619     // If we already have finalized the limits on the centrality
0620     // estimator, we just add the weights to their bins and return the
0621     // corresponding AnalysisObject.
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     // Take the contents of the dynamical binning and fill the original
0686     // AnalysisObjects.
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         // If we only have partial overlap we need to scale
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   /// Example of CentralityEstimator projection that the generated
0796   /// centrality as given in the GenHeavyIon object in HepMC3.
0797   class GeneratedCentrality: public CentralityEstimator {
0798 
0799   public:
0800 
0801     /// Constructor.
0802     GeneratedCentrality() {
0803       declare(HepMCHeavyIon(), "HI");
0804     }
0805 
0806     /// Clone on the heap.
0807     RIVET_DEFAULT_PROJ_CLONE(GeneratedCentrality);
0808 
0809   protected:
0810 
0811     /// Perform the projection on the Event
0812     void project(const Event& e) {
0813       _estimate = apply<HepMCHeavyIon>(e, "HI").centrality();
0814     }
0815 
0816     /// Compare projections
0817     CmpState compare(const Projection& p) const {
0818       return mkNamedPCmp(p, "GeneratedCentrality");
0819     }
0820 
0821   };
0822 
0823 
0824 
0825 }
0826 
0827 #endif