Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-05-12 09:05:02

0001 // -*- C++ -*-
0002 #ifndef RIVET_EventMixingFinalState_HH
0003 #define RIVET_EventMixingFinalState_HH
0004 
0005 #include "Rivet/Projection.hh"
0006 #include "Rivet/Projections/ParticleFinder.hh"
0007 #include "Rivet/Tools/Random.hh"
0008 #include <deque>
0009 #include <algorithm>
0010 
0011 namespace Rivet {
0012 
0013 
0014   /// @brief Make an event mixed together from several events
0015   ///
0016   /// Project out an event mixed of several events, given a mixing
0017   /// observable (e.g. number of final state particles), defining what
0018   /// should qualify as a mixable event.  Binning in the mixing
0019   /// observable is defined in the constructor, as is the number of
0020   /// events one wants to mix with.  The method calculateMixingObs()
0021   /// must can be overloaded in derived classes, to provide the
0022   /// definition of the mixing observable, on the provided projection,
0023   /// eg. centrality or something more elaborate.
0024   ///
0025   /// The implementation can correcly handle mixing of weighted
0026   /// events, but not multi-weighted events. Nor does the
0027   /// implementation attemt to handle calculation of one (or more)
0028   /// event weights for the mixed events. For most common use cases,
0029   /// like calculating a background sample, this is sufficient.  If a
0030   /// more elaborate use case ever turns up, this must be reevaluated.
0031   ///
0032   /// @author Christian Bierlich <christian.bierlich@thep.lu.se>
0033 
0034   /// Weighted random shuffle, similar to std::random_shuffle, which
0035   /// allows the passing of a weight for each element to be shuffled.
0036   ///
0037   /// @todo Move to utils?
0038   template <class RandomAccessIterator,
0039             class WeightIterator, class RandomNumberGenerator>
0040   void weighted_shuffle(RandomAccessIterator first, RandomAccessIterator last,
0041                         WeightIterator fw, WeightIterator lw, RandomNumberGenerator& g) {
0042     while(first != last && fw != lw) {
0043       std::discrete_distribution<int> weightDist(fw, lw);
0044       int i = weightDist(g);
0045       if(i){
0046         std::iter_swap(first, next(first, i));
0047         std::iter_swap(fw, next(fw, i));
0048       }
0049       ++first;
0050       ++fw;
0051     }
0052   }
0053 
0054   /// A MixEvent is a vector of particles with and associated weight.
0055   typedef pair<Particles, double> MixEvent;
0056   typedef map<double, std::deque<MixEvent> > MixMap;
0057 
0058   /// EventMixingBase is the base class for event mixing projections.
0059   ///
0060   /// Most methods are defined in this base class as they should.
0061   /// In order to use it, a derived class should be implemented where:
0062   /// - The constructor is reimplmented, giving the derived projection type
0063   /// from which the mixing observable is calculated. The constructor must also
0064   /// be declared public in the derived class.
0065   /// - The calculateMixingObs is implemented.
0066   /// To examples of such derived classes are given below,
0067   /// 1) EventMixingFinalState, where the mixing observable are calculated
0068   /// on a multiplicity of a charged final state, and:
0069   /// 2) EventMixingCentrality, where the mixing observable is centrality.
0070   ///
0071   class EventMixingBase : public Projection {
0072   protected:
0073     /// Constructor
0074     EventMixingBase(const Projection & mixObsProj, const ParticleFinder& mix,
0075                     size_t nMixIn, double oMin, double oMax, double deltao,
0076                     const size_t defaultIdx) : nMix(nMixIn), unitWeights(true) {
0077       // The base class contructor should be called explicitly in derived classes
0078       // to add projections below.
0079       setName("EventMixingBase");
0080       declare(mixObsProj,"OBS");
0081       declare(mix,"MIX");
0082       MSG_WARNING("EventMixing is not fully validated. Use with caution.");
0083 
0084       _defaultWeightIdx = defaultIdx;
0085       // Set up the map for mixing events.
0086       for(double o = oMin; o < oMax; o+=deltao )
0087         mixEvents[o] = std::deque<MixEvent>();
0088     }
0089 
0090     /// Import to avoid warnings about overload-hiding
0091     using Projection::operator =;
0092 
0093 
0094   public:
0095 
0096     /// Test if we have enough mixing events available for projected,
0097     /// current mixing observable.
0098     bool hasMixingEvents() const {
0099       MixMap::const_iterator mixItr = mixEvents.lower_bound(mObs);
0100       if(mixItr == mixEvents.end() || mixItr->second.size() < nMix + 1)
0101         return false;
0102       return true;
0103     }
0104 
0105     /// Return a vector of mixing events.
0106     vector<MixEvent> getMixingEvents() const {
0107       if (!hasMixingEvents())
0108         return vector<MixEvent>();
0109       MixMap::const_iterator mixItr = mixEvents.lower_bound(mObs);
0110       return vector<MixEvent>(mixItr->second.begin(), mixItr->second.end() - 1);
0111     }
0112 
0113     /// @brief Return a vector of particles from the mixing events.
0114     ///
0115     /// Can be overloaded in derived classes, though normally not
0116     /// necessary.
0117     virtual const Particles particles() const {
0118       // Test if we have enough mixing events.
0119       if (!hasMixingEvents())
0120         return Particles();
0121       // Get mixing events for the current, projected mixing observable.
0122       MixMap::const_iterator mixItr = mixEvents.lower_bound(mObs);
0123       vector<MixEvent> mixEvents(mixItr->second.begin(), mixItr->second.end() - 1);
0124       // Make the vector of mixed particles.
0125       Particles mixParticles;
0126       vector<double> weights;
0127       size_t pSize = 0;
0128       for (size_t i = 0; i < mixEvents.size(); ++i)
0129         pSize+=mixEvents[i].first.size();
0130       mixParticles.reserve(pSize);
0131       weights.reserve(pSize);
0132       // Put the particles in the vector.
0133       for (size_t i = 0; i < mixEvents.size(); ++i) {
0134         mixParticles.insert(mixParticles.end(), mixEvents[i].first.begin(), mixEvents[i].first.end());
0135         vector<double> tmp(mixEvents[i].first.size(), mixEvents[i].second);
0136         weights.insert(weights.end(), tmp.begin(), tmp.end());
0137       }
0138 
0139       // Shuffle the particles.
0140       if (unitWeights) {
0141         // Use the thread safe random number generator.
0142         //auto rnd = [&] (int i) {return rng()()%i;};
0143         std::shuffle(mixParticles.begin(), mixParticles.end(), rng());
0144         return mixParticles;
0145       } else {
0146         weighted_shuffle(mixParticles.begin(), mixParticles.end(), weights.begin(), weights.end(), rng());
0147         Particles tmp = vector<Particle>(mixParticles.begin(), mixParticles.begin() + size_t(ceil(mixParticles.size() / 2)));
0148         return tmp;
0149       }
0150     }
0151 
0152 
0153   protected:
0154 
0155     /// Calculate the mixing observable.
0156     ///
0157     /// Must be overloaded in derived classes.
0158     virtual void calculateMixingObs(const Projection* mProj) = 0;
0159 
0160 
0161     /// Perform the projection on the Event.
0162     void project(const Event& e) {
0163       const Projection* mixObsProjPtr = &apply<Projection>(e, "OBS");
0164       calculateMixingObs(mixObsProjPtr);
0165       MixMap::iterator mixItr = mixEvents.lower_bound(mObs);
0166       if (mixItr == mixEvents.end()){
0167         // We are out of bounds.
0168         MSG_DEBUG("Mixing observable out of bounds.");
0169         return;
0170       }
0171       const Particles mix = apply<ParticleFinder>(e, "MIX").particles();
0172       mixItr->second.push_back(make_pair(mix,e.weights()[_defaultWeightIdx]));
0173       // Assume unit weights until we see otherwise.
0174       if (unitWeights && e.weights()[_defaultWeightIdx] != 1.0 ) {
0175         unitWeights = false;
0176         nMix *= 2;
0177       }
0178       if (mixItr->second.size() > nMix + 1)
0179         mixItr->second.pop_front();
0180     }
0181 
0182 
0183     /// Compare with other projections
0184     CmpState compare(const Projection& p) const {
0185       return mkNamedPCmp(p,"OBS");
0186     }
0187 
0188 
0189     /// The mixing observable of the current event.
0190     double mObs;
0191 
0192 
0193   protected:
0194 
0195     /// The number of event to mix with.
0196     size_t nMix;
0197 
0198     /// The event map.
0199     MixMap mixEvents;
0200 
0201     /// Using unit weights or not.
0202     bool unitWeights;
0203 
0204     size_t _defaultWeightIdx;
0205 
0206   };
0207 
0208 
0209   /// EventMixingFinalState has multiplicity as the mixing observable
0210   class EventMixingFinalState : public EventMixingBase {
0211   public:
0212     EventMixingFinalState(const ParticleFinder & mixObsProj,
0213                           const ParticleFinder& mix, size_t nMixIn, double oMin, double oMax,
0214                           double deltao, const size_t defaultIdx) :
0215       EventMixingBase(mixObsProj, mix, nMixIn, oMin, oMax, deltao, defaultIdx) {
0216       setName("EventMixingFinalState");
0217     }
0218 
0219     RIVET_DEFAULT_PROJ_CLONE(EventMixingFinalState);
0220 
0221     /// Import to avoid warnings about overload-hiding
0222     using Projection::operator =;
0223 
0224 
0225   protected:
0226 
0227     /// Calculate the mixing observable
0228     virtual void calculateMixingObs(const Projection* mProj) {
0229       mObs = ((ParticleFinder*) mProj)->particles().size();
0230     }
0231 
0232   };
0233 
0234 
0235   /// EventMixingCentrality has centrality as the mixing observable
0236   class EventMixingCentrality : public EventMixingBase {
0237   public:
0238 
0239     EventMixingCentrality(const CentralityProjection& mixObsProj,
0240                           const ParticleFinder& mix, size_t nMixIn, double oMin, double oMax,
0241                           double deltao, const size_t defaultIdx) :
0242       EventMixingBase(mixObsProj, mix, nMixIn, oMin, oMax, deltao, defaultIdx) {
0243       setName("EventMixingCentrality");
0244     }
0245 
0246     RIVET_DEFAULT_PROJ_CLONE(EventMixingCentrality);
0247 
0248     /// Import to avoid warnings about overload-hiding
0249     using Projection::operator =;
0250 
0251   protected:
0252 
0253     /// Calculate the mixing observable
0254     virtual void calculateMixingObs(const Projection* mProj) {
0255       mObs = ((CentralityProjection*) mProj)->operator()();
0256     }
0257 
0258   };
0259 
0260 
0261 }
0262 
0263 #endif