ATLAS Offline Software
Loading...
Searching...
No Matches
VoronoiWeightTool.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
3*/
4
6#include "fastjet/ClusterSequenceArea.hh"
7#include "fastjet/Selector.hh"
8#include "fastjet/JetDefinition.hh"
9#include "fastjet/tools/JetMedianBackgroundEstimator.hh"
10#include <fastjet/tools/Subtractor.hh>
11
12#include "xAODPFlow/PFO.h"
15
16namespace SortHelper {
17 //
18 // For Sorting
19 //
20 struct pt_sort
21 {
22
23 inline bool operator() (const TLorentzVector& lhs, const TLorentzVector& rhs)
24 {
25 return (lhs.Pt() > rhs.Pt());
26 }
27
28 inline bool operator() (const TLorentzVector* lhs, const TLorentzVector* rhs)
29 {
30 return (lhs->Pt() > rhs->Pt());
31 }
32
33 inline bool operator() (const xAOD::IParticle& lhs, const xAOD::IParticle& rhs)
34 {
35 return (lhs.pt() > rhs.pt());
36 }
37
38 inline bool operator() (const xAOD::IParticle* lhs, const xAOD::IParticle* rhs)
39 {
40 return (lhs->pt() > rhs->pt());
41 }
42 };
43
44
45 template<typename T>
46 T sort_container_pt(T* inCont) {
47 T sortedCont(SG::VIEW_ELEMENTS);
48 for(auto el : *inCont) sortedCont.push_back( el );
49 std::sort(sortedCont.begin(), sortedCont.end(), pt_sort());
50 return sortedCont;
51 }
52
53 template<typename T>
54 const T sort_container_pt(const T* inCont) {
56
57 for(auto el : *inCont) sortedCont.push_back( el );
58 std::sort(sortedCont.begin(), sortedCont.end(), pt_sort());
59 return *sortedCont.asDataVector();
60 }
61
62 struct PJcomp {
63 bool operator() (const std::pair<fastjet::PseudoJet, std::vector<float> >& lhsp, const std::pair<fastjet::PseudoJet, std::vector<float> >& rhsp)
64 {
65 fastjet::PseudoJet lhs = lhsp.first;
66 fastjet::PseudoJet rhs = rhsp.first;
67 return lhs.pt()>rhs.pt();
68 //The comparator must be a strict weak ordering.
69 }
70 };
71
72
73} // end SortHelper
74
75
76VoronoiWeightTool :: VoronoiWeightTool(const std::string& name) :
78{
79
80 declareProperty("doSpread", m_doSpread);
81 declareProperty("nSigma", m_nSigma);
82 declareProperty("maxArea", m_maxArea);
83
84 declareProperty("IgnoreChargedPFO", m_ignoreChargedPFOs);
85}
86
88
90
93 ATH_MSG_ERROR("Incompatible configuration: setting both IgnoreChargedPFO and ApplyToChargedPFO to true"
94 << "will set all cPFOs to zero");
95 return StatusCode::FAILURE;
96 }
98 ATH_MSG_ERROR("Incompatible configuration: ApplyToNeutralPFO=False -- what kind of pileup do you wish to suppress?");
99 return StatusCode::FAILURE;
100 }
101 }
102 return StatusCode::SUCCESS;
103}
104
105//Have to define custom comparator for PseudoJets in order to have a map from PJs to anything
106//Comparison is fuzzy to account for rounding errors
107
109{
110 std::vector<fastjet::PseudoJet> particles; particles.reserve(particlesin->size());
111
112 for(xAOD::IParticle* part: *particlesin){
113 // Only use positive E
114 bool accept = part->e() > -1*FLT_MIN;
115 // For PFlow we would only want to apply the correction to neutral PFOs,
116 // because charged hadron subtraction handles the charged PFOs.
117 // However, we might still want to use the cPFOs for the min pt calculation
119 // The auto loop returns an ElementProxy, so we need to dereference/reference
120 const xAOD::PFO* pfo = static_cast<const xAOD::PFO*>(part);
121 accept &= fabs(pfo->charge())<FLT_MIN;
122 }
124 xAOD::TrackCaloCluster* tcc = static_cast<xAOD::TrackCaloCluster*>(part);
125 accept &= (tcc->taste()!= 0);
126 }
128 xAOD::FlowElement* fe = static_cast<xAOD::FlowElement*>(part);
130 if(m_ignoreChargedPFOs) accept &= !fe->isCharged();
131 }
132 else
133 accept &= !fe->isCharged();
134 }
135 if(accept) {
136 particles.emplace_back(part->p4() );
137 // ATH_MSG_VERBOSE( "Accepted particle with pt " << part->pt() );
138 }
139 }
140
141 std::vector< std::pair< fastjet::PseudoJet, std::vector<float> > > ptvec; //vector of pairs of PJs and their corrected pTs
142 if(makeVoronoiParticles(particles, ptvec) != StatusCode::SUCCESS) ATH_MSG_ERROR("Error in makeVoronoiParticles");
143 std::sort(ptvec.begin(), ptvec.end(), SortHelper::PJcomp());
144
145 if(m_doSpread && m_nSigma > 0) ATH_MSG_ERROR("Can't combine spreading with nSigma yet");
146 int alg;
147 if(m_doSpread && m_nSigma == 0) alg = 3;
148 if(!m_doSpread && m_nSigma == 0) alg = 1;
149 if(!m_doSpread && m_nSigma > 0) alg = 2;
150
151 // This tracks the index of the object in the fastjet container
152 size_t i=0;
153 const static SG::AuxElement::Accessor<float> weightAcc("VoronoiWeight"); // Handle for PU weighting here
154 for(xAOD::IParticle* part : SortHelper::sort_container_pt(particlesin)){
155 // Skip the check on charged PFOs if needed
156 // A subtle change in this check because fastjet will not return anything
157 // with <= 0 pt
158 bool accept = part->e() > FLT_MIN;
160 // The auto loop returns an ElementProxy, so we need to dereference/reference
161 const xAOD::PFO* pfo = static_cast<const xAOD::PFO*>(part);
162 accept &= fabs(pfo->charge())<FLT_MIN;
163 }
165 xAOD::TrackCaloCluster* tcc = static_cast<xAOD::TrackCaloCluster*>(part);
166 accept &= (tcc->taste()!= 0);
167 }
169 xAOD::FlowElement* fe = static_cast<xAOD::FlowElement*>(part);
171 if(m_ignoreChargedPFOs) accept &= !fe->isCharged();
172 }
173 else
174 accept &= !fe->isCharged();
175 }
176
177 float newPt(0.);
178 if(accept) {
179 //There should be the same number of positive E particles in the container as particles in the ptvec
180 bool endContainer = part->e()<FLT_MIN;
181 bool endVec = i>=ptvec.size();
182 if(endVec && endContainer){
183 newPt = 0; //remove negative energy particles
184 }
185 else if(endContainer || endVec){
186 ATH_MSG_ERROR("Filtered particle list doesn't have same number of elements as the list returned by FastJet.");
187 return StatusCode::FAILURE;
188 }
189 else{
190 //And the particles should match
191 float Containerpt = part->pt();
192 float PJpt = ptvec[i].first.pt();
193 ATH_MSG_VERBOSE( "Container pt: " << Containerpt << ", fastjet initial pt: " << PJpt
194 << ", subtracted pt: " << ptvec[i].second[alg]);
195 if (fabs(Containerpt-PJpt) > 0.1){
196 ATH_MSG_VERBOSE( fabs(Containerpt-PJpt) );
197 ATH_MSG_ERROR("Particle pt's don't match.");
198 return StatusCode::FAILURE;
199 }
200 newPt = ptvec[i].second[alg];
201 }
202 ++i; // Only increment if the particle was used.
203 }
204 // Call set on every object
205 // Leave it to the base class to determine if the correction
206 // should not be applied
207 float w = part->pt()>FLT_MIN ? newPt/part->pt() : 0.;
208 ATH_CHECK(setEnergyPt(part,part->e()*w,newPt,&weightAcc));
209 }
210 return StatusCode::SUCCESS;
211 }
212
213StatusCode VoronoiWeightTool::makeVoronoiParticles(std::vector<fastjet::PseudoJet>& particles,std::vector< std::pair< fastjet::PseudoJet,std::vector<float> > >& correctedptvec) const{
214 std::vector<fastjet::PseudoJet> & inputConst = particles;
215 fastjet::Selector jselector = fastjet::SelectorAbsRapRange(0.0,2.1);
216 fastjet::JetAlgorithm algo = fastjet::kt_algorithm;
217 float jetR = 0.4;
218 fastjet::JetDefinition jetDef(algo, jetR,fastjet::E_scheme, fastjet::Best);
219 fastjet::AreaDefinition area_def(fastjet::voronoi_area, fastjet::VoronoiAreaSpec(0.9));
220
221 fastjet::ClusterSequenceArea clustSeq(inputConst, jetDef, area_def);
222 fastjet::JetMedianBackgroundEstimator bge(jselector,jetDef,area_def);
223
224 bge.set_particles(inputConst);
225 std::vector<fastjet::PseudoJet> inclusiveJets = sorted_by_pt(clustSeq.inclusive_jets(0));
226
227 int nsigma = m_nSigma;
228 float rho = bge.rho();
229 float sigma = bge.sigma();
230 for(unsigned int iJet = 0 ; iJet < inclusiveJets.size() ; iJet++){
231 fastjet::PseudoJet jet = inclusiveJets[iJet];
232 std::vector<fastjet::PseudoJet> constituents = jet.constituents();
233 for(const auto& cons : constituents){
234 float pt = cons.pt();
235 // Allow a limit on the area to prevent over-subtraction in sparse regions
236 float area = m_maxArea<cons.area() ? m_maxArea : cons.area();
237 float subPt = pt-rho*area;
238 float voro0pt = subPt * (subPt > 0);
239 float voro1pt = subPt * (subPt > sqrt(area)*sigma*(float)nsigma);
240 std::vector<float> algopts;
241 algopts.push_back(subPt);
242 algopts.push_back(voro0pt);
243 algopts.push_back(voro1pt);
244 algopts.push_back(0);
245 std::pair <fastjet::PseudoJet,std::vector<float> > pjcptpair (cons,algopts);
246 correctedptvec.push_back(pjcptpair);
247 } // end loop over cons
248 } // end loop over jets
249
250 if(m_doSpread) spreadPt(correctedptvec);
251
252 return StatusCode::SUCCESS;
253}
254
255void VoronoiWeightTool::spreadPt(std::vector< std::pair< fastjet::PseudoJet,std::vector<float> > >& correctedptvec, float spreadr, float alpha) const{
256 //default alpha = 2
257 //Set up neighbors within spreadr:
258 size_t Nparticles = correctedptvec.size();
259 std::vector<float> spreadPT(Nparticles);
260 std::vector<bool> isPositive(Nparticles);
261 for(size_t iPart = 0; iPart < Nparticles; iPart++){
262 spreadPT[iPart] = correctedptvec[iPart].second[0];
263 isPositive[iPart] = spreadPT[iPart]>0;
264 }
265
266 std::vector<std::vector<std::pair<size_t,float> > > particle_drs; //for each particle, list of nearby positive pT particles and their distances
267 for(size_t iPart = 0; iPart < Nparticles; iPart++){
268 fastjet::PseudoJet iparticle = correctedptvec[iPart].first;
269 std::vector<std::pair<size_t,float> > this_particle_drs;
270 for(size_t jPart = 0; jPart < Nparticles; jPart++){
271 if(iPart == jPart) continue;
272 if(!isPositive[jPart]) continue;
273 fastjet::PseudoJet jparticle = correctedptvec[jPart].first;
274 float dphi = iparticle.delta_phi_to(jparticle);
275 float deta = iparticle.eta() - jparticle.eta(); //fastjet::pseudojet::delta_R(const PseudoJet& other) gives rap-phi distance
276 float dr2 = pow(dphi,2) + pow(deta,2);
277 if(dr2 > pow(spreadr,2)) continue;
278 std::pair<size_t,float> jdrpair (jPart,dr2);
279 this_particle_drs.push_back(jdrpair);
280 }
281 particle_drs.push_back(this_particle_drs);
282 }
283
284 for(size_t i = 0; i < Nparticles; i++){
285 if(!(spreadPT[i]<0)) continue; //only spread from negative pT particles
286 //find closest positive PT particle:
287 float sumdR2 = 0;
288 //iterate over nearby positive pT particles
289 for(size_t j=0; j<particle_drs[i].size(); j++){
290 float dr = particle_drs[i][j].second;
291 if(dr>FLT_MIN) sumdR2 += 1./(pow(dr,alpha/2));
292 }
293 //if at least one neighbor
294 if(sumdR2 > FLT_MIN){
295 float spreadPT_orig = spreadPT[i];
296 for(size_t j=0; j<particle_drs[i].size(); j++){
297 float dr = particle_drs[i][j].second;
298 float realid = particle_drs[i][j].first;
299 if(dr>FLT_MIN){
300 float weight = (1./pow(dr,alpha/2))/sumdR2;
301 if(fabs(weight*spreadPT_orig)>spreadPT[realid]){
302 spreadPT[i]+=spreadPT[realid];
303 spreadPT[realid]=0;
304 }
305 else{
306 spreadPT[realid]+=weight*spreadPT_orig;
307 spreadPT[i]-=weight*spreadPT_orig;
308 }
309 }
310 }
311 }
312 }
313
314 for(size_t iPart = 0; iPart < Nparticles; iPart++){
315 correctedptvec[iPart].second[3] = spreadPT[iPart] * (spreadPT[iPart] > 0);
316 }
317}
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#define ATH_MSG_VERBOSE(x)
double area(double R)
constexpr int pow(int base, int exp) noexcept
Gaudi::Details::PropertyBase & declareProperty(Gaudi::Property< T, V, H > &t)
DataVector adapter that acts like it holds const pointers.
value_type push_back(value_type pElem)
Add an element to the end of the collection.
iterator begin() noexcept
Return an iterator pointing at the beginning of the collection.
iterator end() noexcept
Return an iterator pointing past the end of the collection.
const DV * asDataVector() const
Return a pointer to this object, as a const DataVector.
size_type size() const noexcept
Returns the number of elements in the collection.
JetConstituentModifierBase(const std::string &name)
StatusCode setEnergyPt(xAOD::IParticle *obj, float e, float pt, const SG::AuxElement::Accessor< float > *weightAcc=nullptr) const
SG::Accessor< T, ALLOC > Accessor
Definition AuxElement.h:572
StatusCode initialize()
Dummy implementation of the initialisation function.
StatusCode makeVoronoiParticles(std::vector< fastjet::PseudoJet > &particles, std::vector< std::pair< fastjet::PseudoJet, std::vector< float > > > &) const
void spreadPt(std::vector< std::pair< fastjet::PseudoJet, std::vector< float > > > &correctedptvec, float spreadr=0.4, float alpha=2) const
StatusCode process_impl(xAOD::IParticleContainer *cont) const
signal_t signalType() const
Class providing the definition of the 4-vector interface.
virtual double pt() const =0
The transverse momentum ( ) of the particle.
float charge() const
get charge of PFO
virtual int taste() const
The taste of the particle.
@ VIEW_ELEMENTS
this data object is a view, it does not own its elmts
T sort_container_pt(T *inCont)
void sort(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end)
Specialization of sort for DataVector/List.
@ ParticleFlow
The object is a particle-flow object.
Definition ObjectType.h:41
@ FlowElement
The object is a track-calo-cluster.
Definition ObjectType.h:52
@ TrackCaloCluster
The object is a track-calo-cluster.
Definition ObjectType.h:51
PFO_v1 PFO
Definition of the current "pfo version".
Definition PFO.h:17
FlowElement_v1 FlowElement
Definition of the current "pfo version".
Definition FlowElement.h:16
TrackCaloCluster_v1 TrackCaloCluster
Reference the current persistent version:
DataVector< IParticle > IParticleContainer
Simple convenience declaration of IParticleContainer.
bool operator()(const std::pair< fastjet::PseudoJet, std::vector< float > > &lhsp, const std::pair< fastjet::PseudoJet, std::vector< float > > &rhsp)
bool operator()(const TLorentzVector &lhs, const TLorentzVector &rhs)