Rivet 3.1.9
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
86 using Projection::operator =;
87
88
89 public:
90
91 // Test if we have enough mixing events available for projected,
92 // current mixing observable.
93 bool hasMixingEvents() const {
94 MixMap::const_iterator mixItr = mixEvents.lower_bound(mObs);
95 if(mixItr == mixEvents.end() || mixItr->second.size() < nMix + 1)
96 return false;
97 return true;
98 }
99
100 // Return a vector of mixing events.
101 vector<MixEvent> getMixingEvents() const {
102 if (!hasMixingEvents())
103 return vector<MixEvent>();
104 MixMap::const_iterator mixItr = mixEvents.lower_bound(mObs);
105 return vector<MixEvent>(mixItr->second.begin(), mixItr->second.end() - 1);
106 }
107
108 // Return a vector of particles from the mixing events. Can
109 // be overloaded in derived classes, though normally not neccesary.
110 virtual const Particles particles() const {
111 // Test if we have enough mixing events.
112 if (!hasMixingEvents())
113 return Particles();
114 // Get mixing events for the current, projected mixing observable.
115 MixMap::const_iterator mixItr = mixEvents.lower_bound(mObs);
116 vector<MixEvent> mixEvents(mixItr->second.begin(), mixItr->second.end() - 1);
117 // Make the vector of mixed particles.
118 Particles mixParticles;
119 vector<double> weights;
120 size_t pSize = 0;
121 for (size_t i = 0; i < mixEvents.size(); ++i)
122 pSize+=mixEvents[i].first.size();
123 mixParticles.reserve(pSize);
124 weights.reserve(pSize);
125 // Put the particles in the vector.
126 for (size_t i = 0; i < mixEvents.size(); ++i) {
127 mixParticles.insert(mixParticles.end(), mixEvents[i].first.begin(), mixEvents[i].first.end());
128 vector<double> tmp(mixEvents[i].first.size(), mixEvents[i].second);
129 weights.insert(weights.end(), tmp.begin(), tmp.end());
130 }
131
132 // Shuffle the particles.
133 if (unitWeights) {
134 // Use the thread safe random number generator.
135 //auto rnd = [&] (int i) {return rng()()%i;};
136 std::shuffle(mixParticles.begin(), mixParticles.end(), rng());
137 return mixParticles;
138 } else {
139 weighted_shuffle(mixParticles.begin(), mixParticles.end(), weights.begin(), weights.end(), rng());
140 Particles tmp = vector<Particle>(mixParticles.begin(), mixParticles.begin() + size_t(ceil(mixParticles.size() / 2)));
141 return tmp;
142 }
143 }
144
145
146 protected:
147
148 // Calulate mixing observable.
149 // Must be overloaded in derived classes.
150 virtual void calculateMixingObs(const Projection* mProj) = 0;
151
152
154 void project(const Event& e) {
155 const Projection* mixObsProjPtr = &applyProjection<Projection>(e, "OBS");
156 calculateMixingObs(mixObsProjPtr);
157 MixMap::iterator mixItr = mixEvents.lower_bound(mObs);
158 if (mixItr == mixEvents.end()){
159 // We are out of bounds.
160 MSG_DEBUG("Mixing observable out of bounds.");
161 return;
162 }
163 const Particles mix = applyProjection<ParticleFinder>(e, "MIX").particles();
164 mixItr->second.push_back(make_pair(mix,e.weights()[_defaultWeightIdx]));
165 // Assume unit weights until we see otherwise.
166 if (unitWeights && e.weights()[_defaultWeightIdx] != 1.0 ) {
167 unitWeights = false;
168 nMix *= 2;
169 }
170 if (mixItr->second.size() > nMix + 1)
171 mixItr->second.pop_front();
172 }
173
174
176 CmpState compare(const Projection& p) const {
177 return mkNamedPCmp(p,"OBS");
178 }
179
180
182 double mObs;
183
184
185 protected:
186
188 size_t nMix;
189
191 MixMap mixEvents;
192
195
196 size_t _defaultWeightIdx;
197
198 };
199
200
201 // EventMixingFinalState has multiplicity as the mixing observable
203 public:
204 EventMixingFinalState(const ParticleFinder & mixObsProj,
205 const ParticleFinder& mix, size_t nMixIn, double oMin, double oMax,
206 double deltao, const size_t defaultIdx) :
207 EventMixingBase(mixObsProj, mix, nMixIn, oMin, oMax, deltao, defaultIdx) {
208 setName("EventMixingFinalState");
209 }
210
211 DEFAULT_RIVET_PROJ_CLONE(EventMixingFinalState);
212
214 using Projection::operator =;
215
216
217 protected:
218
219 // Calculate mixing observable
220 virtual void calculateMixingObs(const Projection* mProj) {
221 mObs = ((ParticleFinder*) mProj)->particles().size();
222 }
223
224 };
225
226
227 // EventMixingCentrality has centrality as the mixing observable
229 public:
230
232 const ParticleFinder& mix, size_t nMixIn, double oMin, double oMax,
233 double deltao, const size_t defaultIdx) :
234 EventMixingBase(mixObsProj, mix, nMixIn, oMin, oMax, deltao, defaultIdx) {
235 setName("EventMixingCentrality");
236 }
237
238 DEFAULT_RIVET_PROJ_CLONE(EventMixingCentrality);
239
241 using Projection::operator =;
242
243
244 protected:
245
246 virtual void calculateMixingObs(const Projection* mProj) {
247 mObs = ((CentralityProjection*) mProj)->operator()();
248 }
249
250 };
251
252
253}
254
255#endif
Used together with the percentile-based analysis objects Percentile and PercentileXaxis.
Definition CentralityProjection.hh:27
Definition EventMixingFinalState.hh:66
size_t nMix
The number of event to mix with.
Definition EventMixingFinalState.hh:188
bool unitWeights
Using unit weights or not.
Definition EventMixingFinalState.hh:194
MixMap mixEvents
The event map.
Definition EventMixingFinalState.hh:191
void project(const Event &e)
Perform the projection on the Event.
Definition EventMixingFinalState.hh:154
double mObs
The mixing observable of the current event.
Definition EventMixingFinalState.hh:182
CmpState compare(const Projection &p) const
Compare with other projections.
Definition EventMixingFinalState.hh:176
Definition EventMixingFinalState.hh:228
Definition EventMixingFinalState.hh:202
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:52
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:684
Definition MC_Cent_pPb.hh:10
std::mt19937 & rng()
Return a thread-safe random number generator (mainly for internal use)