rivet is hosted by Hepforge, IPPP Durham
Rivet 3.1.6
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
11namespace 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
Used together with the percentile-based analysis objects Percentile and PercentileXaxis.
Definition: CentralityProjection.hh:26
Definition: EventMixingFinalState.hh:66
void project(const Event &e)
Perform the projection on the Event.
Definition: EventMixingFinalState.hh:150
double mObs
The mixing observable of the current event.
Definition: EventMixingFinalState.hh:178
CmpState compare(const Projection &p) const
Compare with other projections.
Definition: EventMixingFinalState.hh:172
Definition: EventMixingFinalState.hh:220
Definition: EventMixingFinalState.hh:198
Representation of a HepMC event, and enabler of Projection caching.
Definition: Event.hh:22
std::valarray< double > weights() const
The generation weights associated with the event.
Base class for projections which return subsets of an event's particles.
Definition: ParticleFinder.hh:11
virtual const Particles & particles() const
Get the particles in no particular order, with no cuts.
Definition: ParticleFinder.hh:49
Specialised vector of Particle objects.
Definition: Particle.hh:25
const PROJ & declare(const PROJ &proj, const std::string &name)
Register a contained projection (user-facing version)
Definition: ProjectionApplier.hh:170
Base class for all Rivet projections.
Definition: Projection.hh:29
void setName(const std::string &name)
Used by derived classes to set their name.
Definition: Projection.hh:142
Cmp< Projection > mkNamedPCmp(const Projection &otherparent, const std::string &pname) const
#define MSG_DEBUG(x)
Debug messaging, not enabled by default, using MSG_LVL.
Definition: Logging.hh:195
#define MSG_WARNING(x)
Warning messages for non-fatal bad things, using MSG_LVL.
Definition: Logging.hh:200
double p(const ParticleBase &p)
Unbound function access to p.
Definition: ParticleBaseUtils.hh:653
Definition: MC_Cent_pPb.hh:10
std::mt19937 & rng()
Return a thread-safe random number generator (mainly for internal use)