Rivet  3.1.4
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,
71  const size_t defaultIdx) : nMix(nMixIn), 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  _defaultWeightIdx = defaultIdx;
80  // Set up the map for mixing events.
81  for(double o = oMin; o < oMax; o+=deltao )
82  mixEvents[o] = std::deque<MixEvent>();
83  }
84 
85  public:
86 
87  // Test if we have enough mixing events available for projected,
88  // current mixing observable.
89  bool hasMixingEvents() const {
90  MixMap::const_iterator mixItr = mixEvents.lower_bound(mObs);
91  if(mixItr == mixEvents.end() || mixItr->second.size() < nMix + 1)
92  return false;
93  return true;
94  }
95 
96  // Return a vector of mixing events.
97  vector<MixEvent> getMixingEvents() const {
98  if (!hasMixingEvents())
99  return vector<MixEvent>();
100  MixMap::const_iterator mixItr = mixEvents.lower_bound(mObs);
101  return vector<MixEvent>(mixItr->second.begin(), mixItr->second.end() - 1);
102  }
103 
104  // Return a vector of particles from the mixing events. Can
105  // be overloaded in derived classes, though normally not neccesary.
106  virtual const Particles particles() const {
107  // Test if we have enough mixing events.
108  if (!hasMixingEvents())
109  return Particles();
110  // Get mixing events for the current, projected mixing observable.
111  MixMap::const_iterator mixItr = mixEvents.lower_bound(mObs);
112  vector<MixEvent> mixEvents(mixItr->second.begin(), mixItr->second.end() - 1);
113  // Make the vector of mixed particles.
114  Particles mixParticles;
115  vector<double> weights;
116  size_t pSize = 0;
117  for (size_t i = 0; i < mixEvents.size(); ++i)
118  pSize+=mixEvents[i].first.size();
119  mixParticles.reserve(pSize);
120  weights.reserve(pSize);
121  // Put the particles in the vector.
122  for (size_t i = 0; i < mixEvents.size(); ++i) {
123  mixParticles.insert(mixParticles.end(), mixEvents[i].first.begin(), mixEvents[i].first.end());
124  vector<double> tmp(mixEvents[i].first.size(), mixEvents[i].second);
125  weights.insert(weights.end(), tmp.begin(), tmp.end());
126  }
127 
128  // Shuffle the particles.
129  if (unitWeights) {
130  // Use the thread safe random number generator.
131  //auto rnd = [&] (int i) {return rng()()%i;};
132  std::shuffle(mixParticles.begin(), mixParticles.end(), rng());
133  return mixParticles;
134  } else {
135  weighted_shuffle(mixParticles.begin(), mixParticles.end(), weights.begin(), weights.end(), rng());
136  Particles tmp = vector<Particle>(mixParticles.begin(), mixParticles.begin() + size_t(ceil(mixParticles.size() / 2)));
137  return tmp;
138  }
139  }
140 
141 
142  protected:
143 
144  // Calulate mixing observable.
145  // Must be overloaded in derived classes.
146  virtual void calculateMixingObs(const Projection* mProj) = 0;
147 
148 
150  void project(const Event& e) {
151  const Projection* mixObsProjPtr = &applyProjection<Projection>(e, "OBS");
152  calculateMixingObs(mixObsProjPtr);
153  MixMap::iterator mixItr = mixEvents.lower_bound(mObs);
154  if (mixItr == mixEvents.end()){
155  // We are out of bounds.
156  MSG_DEBUG("Mixing observable out of bounds.");
157  return;
158  }
159  const Particles mix = applyProjection<ParticleFinder>(e, "MIX").particles();
160  mixItr->second.push_back(make_pair(mix,e.weights()[_defaultWeightIdx]));
161  // Assume unit weights until we see otherwise.
162  if (unitWeights && e.weights()[_defaultWeightIdx] != 1.0 ) {
163  unitWeights = false;
164  nMix *= 2;
165  }
166  if (mixItr->second.size() > nMix + 1)
167  mixItr->second.pop_front();
168  }
169 
170 
172  CmpState compare(const Projection& p) const {
173  return mkNamedPCmp(p,"OBS");
174  }
175 
176 
178  double mObs;
179 
180 
181  private:
182 
184  size_t nMix;
185 
187  MixMap mixEvents;
188 
190  bool unitWeights;
191 
192  size_t _defaultWeightIdx;
193 
194  };
195 
196 
197  // EventMixingFinalState has multiplicity as the mixing observable
199  public:
200  EventMixingFinalState(const ParticleFinder & mixObsProj,
201  const ParticleFinder& mix, size_t nMixIn, double oMin, double oMax,
202  double deltao, const size_t defaultIdx) :
203  EventMixingBase(mixObsProj, mix, nMixIn, oMin, oMax, deltao, defaultIdx) {
204  setName("EventMixingFinalState");
205  }
206 
207  DEFAULT_RIVET_PROJ_CLONE(EventMixingFinalState);
208 
209  protected:
210 
211  // Calculate mixing observable
212  virtual void calculateMixingObs(const Projection* mProj) {
213  mObs = ((ParticleFinder*) mProj)->particles().size();
214  }
215 
216  };
217 
218 
219  // EventMixingCentrality has centrality as the mixing observable
221  public:
222 
224  const ParticleFinder& mix, size_t nMixIn, double oMin, double oMax,
225  double deltao, const size_t defaultIdx) :
226  EventMixingBase(mixObsProj, mix, nMixIn, oMin, oMax, deltao, defaultIdx) {
227  setName("EventMixingCentrality");
228  }
229 
230  DEFAULT_RIVET_PROJ_CLONE(EventMixingCentrality);
231 
232  protected:
233 
234  virtual void calculateMixingObs(const Projection* mProj) {
235  mObs = ((CentralityProjection*) mProj)->operator()();
236  }
237 
238  };
239 
240 
241 }
242 
243 #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:150
Definition: EventMixingFinalState.hh:66
#define MSG_DEBUG(x)
Debug messaging, not enabled by default, using MSG_LVL.
Definition: Logging.hh:195
CmpState compare(const Projection &p) const
Compare with other projections.
Definition: EventMixingFinalState.hh:172
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:178
const PROJ & declare(const PROJ &proj, const std::string &name)
Register a contained projection (user-facing version)
Definition: ProjectionApplier.hh:170
Cmp< Projection > mkNamedPCmp(const Projection &otherparent, const std::string &pname) const
Definition: EventMixingFinalState.hh:198
Definition: EventMixingFinalState.hh:220
Base class for all Rivet projections.
Definition: Projection.hh:29
#define MSG_WARNING(x)
Warning messages for non-fatal bad things, using MSG_LVL.
Definition: Logging.hh:200
std::mt19937 & rng()
Return a thread-safe random number generator (mainly for internal use)