Rivet  3.1.0
EventMixingFinalState.hh
1 // -*- C++ -*-
2 #ifndef RIVET_EventMixingFinalState_HH
3 #define RIVET_EventMixingFinalState_HH
4 
5 #include "Rivet/Projection.hh"
6 #include "Rivet/Projections/ParticleFinder.hh"
7 #include "Rivet/Tools/Random.hh"
8 #include <deque>
9 #include <algorithm>
10 
11 namespace Rivet {
12 
13 
14  // @brief Projects out an event mixed of several events, given
15  // a mixing observable (eg. number of final state particles),
16  // defining what should qualify as a mixable event.
17  // Binning in the mixing observable is defined in the constructor,
18  // as is the number of events one wants to mix with.
19  // The method calculateMixingObs() must can be overloaded
20  // in derived classes, to provide the definition of the mixing observable,
21  // on the provided projection, eg. centrality or something more elaborate.
22  //
23  // The implementation can correcly handle mixing of weighted events, but
24  // not multi-weighted events. Nor does the implementation attemt to handle
25  // calculation of one (or more) event weights for the mixed events. For most
26  // common use cases, like calculating a background sample, this is sufficient.
27  // If a more elaborate use case ever turns up, this must be reevaluated.
28  //
29  //
30  // @author Christian Bierlich <christian.bierlich@thep.lu.se>
31 
32  // Weighted random shuffle, similar to std::random_shuffle, which
33  // allows the passing of a weight for each element to be shuffled.
34  template <class RandomAccessIterator,
35  class WeightIterator, class RandomNumberGenerator>
36  void weighted_shuffle(RandomAccessIterator first, RandomAccessIterator last,
37  WeightIterator fw, WeightIterator lw, RandomNumberGenerator& g) {
38  while(first != last && fw != lw) {
39  std::discrete_distribution<int> weightDist(fw, lw);
40  int i = weightDist(g);
41  if(i){
42  std::iter_swap(first, next(first, i));
43  std::iter_swap(fw, next(fw, i));
44  }
45  ++first;
46  ++fw;
47  }
48  }
49  // A MixEvent is a vector of particles with and associated weight.
50  typedef pair<Particles, double> MixEvent;
51  typedef map<double, std::deque<MixEvent> > MixMap;
52 
66  class EventMixingBase : public Projection {
67  protected:
68  // Constructor
69  EventMixingBase(const Projection & mixObsProj, const ParticleFinder& mix,
70  size_t nMixIn, double oMin, double oMax, double deltao) : nMix(nMixIn),
71  unitWeights(true) {
72  // The base class contructor should be called explicitly in derived classes
73  // to add projections below.
74  setName("EventMixingBase");
75  declare(mixObsProj,"OBS");
76  declare(mix,"MIX");
77  MSG_WARNING("EventMixing is not fully validated. Use with caution.");
78 
79  // Set up the map for mixing events.
80  for(double o = oMin; o < oMax; o+=deltao )
81  mixEvents[o] = std::deque<MixEvent>();
82  }
83 
84  public:
85 
86  // Test if we have enough mixing events available for projected,
87  // current mixing observable.
88  bool hasMixingEvents() const {
89  MixMap::const_iterator mixItr = mixEvents.lower_bound(mObs);
90  if(mixItr == mixEvents.end() || mixItr->second.size() < nMix + 1)
91  return false;
92  return true;
93  }
94 
95  // Return a vector of mixing events.
96  vector<MixEvent> getMixingEvents() const {
97  if (!hasMixingEvents())
98  return vector<MixEvent>();
99  MixMap::const_iterator mixItr = mixEvents.lower_bound(mObs);
100  return vector<MixEvent>(mixItr->second.begin(), mixItr->second.end() - 1);
101  }
102 
103  // Return a vector of particles from the mixing events. Can
104  // be overloaded in derived classes, though normally not neccesary.
105  virtual const Particles particles() const {
106  // Test if we have enough mixing events.
107  if (!hasMixingEvents())
108  return Particles();
109  // Get mixing events for the current, projected mixing observable.
110  MixMap::const_iterator mixItr = mixEvents.lower_bound(mObs);
111  vector<MixEvent> mixEvents(mixItr->second.begin(), mixItr->second.end() - 1);
112  // Make the vector of mixed particles.
113  Particles mixParticles;
114  vector<double> weights;
115  size_t pSize = 0;
116  for (size_t i = 0; i < mixEvents.size(); ++i)
117  pSize+=mixEvents[i].first.size();
118  mixParticles.reserve(pSize);
119  weights.reserve(pSize);
120  // Put the particles in the vector.
121  for (size_t i = 0; i < mixEvents.size(); ++i) {
122  mixParticles.insert(mixParticles.end(), mixEvents[i].first.begin(), mixEvents[i].first.end());
123  vector<double> tmp(mixEvents[i].first.size(), mixEvents[i].second);
124  weights.insert(weights.end(), tmp.begin(), tmp.end());
125  }
126 
127  // Shuffle the particles.
128  if (unitWeights) {
129  // Use the thread safe random number generator.
130  //auto rnd = [&] (int i) {return rng()()%i;};
131  std::shuffle(mixParticles.begin(), mixParticles.end(), rng());
132  return mixParticles;
133  } else {
134  weighted_shuffle(mixParticles.begin(), mixParticles.end(), weights.begin(), weights.end(), rng());
135  Particles tmp = vector<Particle>(mixParticles.begin(), mixParticles.begin() + size_t(ceil(mixParticles.size() / 2)));
136  return tmp;
137  }
138  }
139 
140 
141  protected:
142 
143  // Calulate mixing observable.
144  // Must be overloaded in derived classes.
145  virtual void calculateMixingObs(const Projection* mProj) = 0;
146 
147 
149  void project(const Event& e) {
150  const Projection* mixObsProjPtr = &applyProjection<Projection>(e, "OBS");
151  calculateMixingObs(mixObsProjPtr);
152  MixMap::iterator mixItr = mixEvents.lower_bound(mObs);
153  if (mixItr == mixEvents.end()){
154  // We are out of bounds.
155  MSG_DEBUG("Mixing observable out of bounds.");
156  return;
157  }
158  const Particles mix = applyProjection<ParticleFinder>(e, "MIX").particles();
159  mixItr->second.push_back(make_pair(mix,e.weights()[0]));
160  // Assume unit weights until we see otherwise.
161  if (unitWeights && e.weights()[0] != 1.0 ) {
162  unitWeights = false;
163  nMix *= 2;
164  }
165  if (mixItr->second.size() > nMix + 1)
166  mixItr->second.pop_front();
167  }
168 
169 
171  CmpState compare(const Projection& p) const {
172  return mkNamedPCmp(p,"OBS");
173  }
174 
175 
177  double mObs;
178 
179 
180  private:
181 
183  size_t nMix;
184 
186  MixMap mixEvents;
187 
189  bool unitWeights;
190 
191  };
192 
193 
194  // EventMixingFinalState has multiplicity as the mixing observable
196  public:
197  EventMixingFinalState(const ParticleFinder & mixObsProj,
198  const ParticleFinder& mix, size_t nMixIn, double oMin, double oMax,
199  double deltao) :
200  EventMixingBase(mixObsProj, mix, nMixIn, oMin, oMax, deltao) {
201  setName("EventMixingFinalState");
202  }
203 
204  DEFAULT_RIVET_PROJ_CLONE(EventMixingFinalState);
205 
206  protected:
207 
208  // Calculate mixing observable
209  virtual void calculateMixingObs(const Projection* mProj) {
210  mObs = ((ParticleFinder*) mProj)->particles().size();
211  }
212 
213  };
214 
215 
216  // EventMixingCentrality has centrality as the mixing observable
218  public:
219 
221  const ParticleFinder& mix, size_t nMixIn, double oMin, double oMax,
222  double deltao) :
223  EventMixingBase(mixObsProj, mix, nMixIn, oMin, oMax, deltao) {
224  setName("EventMixingCentrality");
225  }
226 
227  DEFAULT_RIVET_PROJ_CLONE(EventMixingCentrality);
228 
229  protected:
230 
231  virtual void calculateMixingObs(const Projection* mProj) {
232  mObs = ((CentralityProjection*) mProj)->operator()();
233  }
234 
235  };
236 
237 
238 }
239 
240 #endif
void setName(const std::string &name)
Used by derived classes to set their name.
Definition: Projection.hh:142
Definition: MC_Cent_pPb.hh:10
Used together with the percentile-based analysis objects Percentile and PercentileXaxis.
Definition: CentralityProjection.hh:26
void project(const Event &e)
Perform the projection on the Event.
Definition: EventMixingFinalState.hh:149
Definition: EventMixingFinalState.hh:66
CmpState compare(const Projection &p) const
Compare with other projections.
Definition: EventMixingFinalState.hh:171
Base class for projections which return subsets of an event&#39;s particles.
Definition: ParticleFinder.hh:11
Representation of a HepMC event, and enabler of Projection caching.
Definition: Event.hh:22
double mObs
The mixing observable of the current event.
Definition: EventMixingFinalState.hh:177
const PROJ & declare(const PROJ &proj, const std::string &name)
Register a contained projection (user-facing version)
Definition: ProjectionApplier.hh:160
Cmp< Projection > mkNamedPCmp(const Projection &otherparent, const std::string &pname) const
Definition: EventMixingFinalState.hh:195
Definition: EventMixingFinalState.hh:217
Base class for all Rivet projections.
Definition: Projection.hh:29
std::mt19937 & rng()
Return a thread-safe random number generator (mainly for internal use)