ATLAS Offline Software
Loading...
Searching...
No Matches
JetCleaningTool.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
3*/
4
5/******************************************************************************
6Name: JetCleaningTool
7
8Author: Zach Marshall
9Created: Feb 2014
10
11Description: Class for selecting jets that pass some cleaning cuts
12******************************************************************************/
13
16
17// This class header and package headers
20
21// xAOD/ASG includes
23
24// STL includes
25#include <iostream>
26#include <cmath>
27#include <cfloat>
28#include <stdexcept>
29#include <utility>
30// ROOT includes
31#include "TEnv.h"
32
33//=============================================================================
34// Constructors
35//=============================================================================
36namespace JCT
37{
39{
40 public:
41 HotCell(const int layer, const float etaMin, const float etaMax, const float phiMin, const float phiMax);
42 virtual ~HotCell() {}
43 bool jetAffectedByHotCell(const xAOD::Jet& jet) const;
44 private:
45 const int m_layer;
46 const float m_etaMin;
47 const float m_etaMax;
48 const float m_phiMin;
49 const float m_phiMax;
51};
52
53HotCell::HotCell(const int layer, const float etaMin, const float etaMax, const float phiMin, const float phiMax)
54 : asg::AsgMessaging("HotCell")
55 , m_layer(layer)
56 , m_etaMin(etaMin)
57 , m_etaMax(etaMax)
58 , m_phiMin(phiMin)
59 , m_phiMax(phiMax) { }
60
62{
63 // First check if the jet points to the cell
64 const float eta = jet.eta();
65 const float phi = jet.phi();
66 if ( (m_etaMin < eta && eta < m_etaMax) && (m_phiMin < phi && phi < m_phiMax) )
67 {
68 // It points to the cell, now check if the maximum layer is the hot cell or if at least 40% of the energy is the hot cell
69 const int fmaxIndex = jet.getAttribute<int>(xAOD::JetAttribute::FracSamplingMaxIndex);
70 float ePerSamp = 0;
71 if( m_ePerSamp.isAvailable(jet) )
72 ePerSamp = m_ePerSamp(jet).at(m_layer)/jet.e();
73 else
74 ATH_MSG_WARNING("Could not retrieve EnergyPerSampling from jet, cleaning performance may be reduced");
75
76 if (fmaxIndex == m_layer || ePerSamp > 0.4)
77 return true;
78 }
79 return false;
80}
81
82} // end JCT namespace
83
84
85
86//=============================================================================
87// Constructors
88//=============================================================================
89JetCleaningTool::JetCleaningTool(const std::string& name)
90 : asg::AsgTool(name) {}
91
92JetCleaningTool::JetCleaningTool(const CleaningLevel alevel, const bool doUgly)
93 : JetCleaningTool( "JetCleaningTool_"+getCutName(alevel) )
94{
95 m_cutLevel=alevel;
96 m_doUgly = doUgly;
97}
98
100JetCleaningTool::JetCleaningTool(const std::string& name , const CleaningLevel alevel, const bool doUgly)
101 : JetCleaningTool(name)
102{
103 m_cutLevel=alevel;
104 m_doUgly = doUgly;
105}
106
107
108//=============================================================================
109// Destructor
110//=============================================================================
112
113//=============================================================================
114// Initialize
115//=============================================================================
117{
119 ATH_MSG_ERROR( "Tool initialized with unknown cleaning level." );
120 return StatusCode::FAILURE;
121 }
122
124 ATH_MSG_INFO( "Configured with cut level " << getCutName( m_cutLevel ) );
125 std::string jetCleanDFName = "DFCommonJets_jetClean_"+getCutName(m_cutLevel);
126 // if UseLooseDecorForTightCut=true, retrieve loose cleaning decoration to compute tight cleaning
127 if (m_useLooseDecorForTightCut) jetCleanDFName = "DFCommonJets_jetClean_"+getCutName(LooseBad);
128 m_acc_jetClean = jetCleanDFName;
130
131 ATH_MSG_DEBUG( "Initialized decorator name: " << jetCleanDFName );
132
133 m_accept.addCut( "Cleaning", "Cleaning of the jet" );
134
135 // Read in the map of runNumbers to bad cells
137
138 ATH_CHECK(m_jetCleanKey.initialize(!m_jetContainerName.empty()));
139
140 return StatusCode::SUCCESS;
141}
142//===============================================================
143// Calculate the accept from the DFCommonJets_jetClean decorator
144//===============================================================
145asg::AcceptData JetCleaningTool::accept( const int isJetClean, const int fmaxIndex ) const
146{
147 asg::AcceptData acceptData (&m_accept);
148 acceptData.setCutResult( "Cleaning", false );
149
150 //=============================================================
151 //Run-II ugly cuts
152 //=============================================================
153 if(m_doUgly && fmaxIndex==17) return acceptData;
154
155 //=============================================================
156 //Loose/tight cleaning taken from decoration
157 //=============================================================
158 if(isJetClean==0) return acceptData;
159 else{
160 acceptData.setCutResult( "Cleaning", true );
161 return acceptData;
162 }
163
164 // We should never arrive here!
165 ATH_MSG_ERROR( "Unknown cut name: " << getCutName( m_cutLevel ) << " in JetCleaningTool" );
166 return acceptData;
167
168}
169//===============================================================
170// Calculate tight cleaning from loose decoration + variables
171//===============================================================
173 const double sumpttrk, //in MeV, same as sumpttrk
174 const double fmax,
175 const double eta,
176 const double pt,
177 const int fmaxIndex
178 ) const
179{
180 asg::AcceptData acceptData (&m_accept);
181 acceptData.setCutResult( "Cleaning", false );
182 const double chf=sumpttrk/pt;
183
184 //=============================================================
185 //Run-II ugly cuts
186 //=============================================================
187 if(m_doUgly && fmaxIndex==17) return acceptData;
188
189 //=============================================================
190 //Tight cleaning taken from decoration
191 //=============================================================
192 if(isJetClean==0) return acceptData; //fails Loose cleaning
193 else if (fmax<DBL_MIN) return acceptData;
194 else if(std::fabs(eta)<2.4 && chf/fmax<0.1) return acceptData;
195 else{
196 acceptData.setCutResult( "Cleaning", true );
197 return acceptData;
198 }
199
200 // We should never arrive here!
201 ATH_MSG_ERROR( "Unknown cut name: " << getCutName( m_cutLevel ) << " in JetCleaningTool" );
202 return acceptData;
203
204}
205
206//=============================================================================
207// Calculate the actual accept of each cut individually.
208//=============================================================================
210 const double hecf,
211 const double larq,
212 const double hecq,
213 //const double time, //in ns
214 const double sumpttrk, //in MeV, same as sumpttrk
215 const double eta, //emscale Eta
216 const double pt, //in MeV, same as sumpttrk
217 const double fmax,
218 const double negE , //in MeV
219 const double AverageLArQF,
220 const int fmaxIndex
221 ) const
222{
223 asg::AcceptData acceptData (&m_accept);
224 acceptData.setCutResult( "Cleaning", false );
225
226 // -----------------------------------------------------------
227 // Do the actual selection
228 if (pt<DBL_MIN) return acceptData;
229 const double chf=sumpttrk/pt;
230
231 //=============================================================
232 //Run-II ugly cuts
233 //=============================================================
234 if(m_doUgly && fmaxIndex==17) return acceptData;
235
236 //=============================================================
237 //Run-II very loose LLP cuts
238 // From https://indico.cern.ch/event/642438/contributions/2704590/attachments/1514445/2362870/082117a_HCW_NCB_LLP.pdf
239 //=============================================================
241 if (fmax>0.80) return acceptData;
242 if (emf>0.96) return acceptData;
243 acceptData.setCutResult( "Cleaning", true );
244 return acceptData;
245 }
246
247 //=============================================================
248 //Run-II loose cuts
249 //=============================================================
250 //Non-collision background & cosmics
251 const bool useLLP = (LooseBadLLP == m_cutLevel); // LLP cleaning cannot use emf...
252 const bool isTrigger = (LooseBadTrigger == m_cutLevel); // trigger cannot use chf
253 const bool useSuperLLP = (SuperLooseBadLLP == m_cutLevel); // other LLP cleaning...
254 if (!useLLP && !useSuperLLP) {
255 if(!isTrigger && emf<0.05 && chf<0.05 && std::fabs(eta)<2) return acceptData;
256 if(emf<0.05 && std::fabs(eta)>=2) return acceptData;
257 }
258 if(fmax>0.99 && std::fabs(eta)<2) return acceptData;
259 //HEC spike-- gone as of 2017!
260 if(hecf>0.5 && std::fabs(hecq)>0.5 && AverageLArQF/65535>0.8) return acceptData;
261 //EM calo noise
262 if(emf>0.95 && std::fabs(larq)>0.8 && std::fabs(eta)<2.8 && AverageLArQF/65535>0.8) return acceptData;
263 // LLP cleaning uses negative energy cut
264 // (https://indico.cern.ch/event/472320/contribution/8/attachments/1220731/1784456/JetTriggerMeeting_20160102.pdf)
265 if (useLLP && std::fabs(negE*0.001)>4 && fmax >0.85) return acceptData;
266 // another LLP cleaning cutting softer on negative energy
267 if (useSuperLLP && std::fabs(negE*0.001)>60) return acceptData;
268
270 acceptData.setCutResult( "Cleaning", true );
271 return acceptData;
272 }
273
274 //=============================================================
275 //Run-II tight cuts
276 //=============================================================
277 // NCB monojet-style cut in central, EMF cut in forward
278 if (fmax<DBL_MIN) return acceptData;
279 if(std::fabs(eta)<2.4 && chf/fmax<0.1) return acceptData;
280 //if(std::fabs(eta)>=2.4 && emf<0.1) return acceptData;
281 if(TightBad==m_cutLevel){
282 acceptData.setCutResult( "Cleaning", true );
283 return acceptData;
284 }
285
286 // We should never arrive here!
287 ATH_MSG_ERROR( "Unknown cut name: " << getCutName( m_cutLevel ) << " in JetCleaningTool" );
288 return acceptData;
289}
290
291
292void JetCleaningTool::missingVariable(const std::string& varName) const
293{
294 ATH_MSG_FATAL(Form("JetCleaningTool failed to retrieve a required variable - please confirm that the xAOD::Jet being passed contains the variable named %s",varName.c_str()));
295 throw std::runtime_error(Form("JetCleaningTool failed to retrieve a required variable - please confirm that the xAOD::Jet being passed contains the variable named %s",varName.c_str()));
296}
297
299{
300 std::vector<float> sumPtTrkvec;
301 jet.getAttribute( xAOD::JetAttribute::SumPtTrkPt500, sumPtTrkvec );
302 double sumpttrk = 0;
303 if( ! sumPtTrkvec.empty() ) sumpttrk = sumPtTrkvec[0];
304 // fmax index is not necessarily required
305 // This is only used if doUgly is set
306 // Handle it gracefully if the variable is not present but doUgly is false
307 int FracSamplingMaxIndex = -1;
308 if (!jet.getAttribute(xAOD::JetAttribute::FracSamplingMaxIndex,FracSamplingMaxIndex) && m_doUgly)
309 missingVariable("FracSamplingMaxIndex");
310 // get tight cleaning variables
311 float FracSamplingMax = 0;
312 if (!jet.getAttribute(xAOD::JetAttribute::FracSamplingMax,FracSamplingMax))
313 missingVariable("FracSamplingMax");
314
315 //start jet cleaning
316 int isJetClean = 0;
317 if( m_useDecorations && m_acc_jetClean.isAvailable(jet) ) { //decoration is already available for all jets
318 isJetClean = m_acc_jetClean(jet);
319
320 // compute tight cleaning on top of the loose cleaning decoration
322 return accept (isJetClean, sumpttrk, FracSamplingMax, jet.eta(), jet.pt(), FracSamplingMaxIndex);
323 }
324 else {
325 return accept (isJetClean, FracSamplingMaxIndex);
326 }
327 }
328 else{ //running over AOD, need to use all variables
329 ATH_MSG_DEBUG("DFCommon jet cleaning variable not available ... Using jet cleaning tool");
330 // Get all of the required variables
331 // Do it this way so we can gracefully handle missing variables (rather than segfaults)
332
333 float EMFrac = 0;
334 if (!jet.getAttribute(xAOD::JetAttribute::EMFrac,EMFrac))
335 missingVariable("EMFrac");
336
337 float HECFrac = 0;
338 if (!jet.getAttribute(xAOD::JetAttribute::HECFrac,HECFrac))
339 missingVariable("HECFrac");
340
341 float LArQuality = 0;
342 if (!jet.getAttribute(xAOD::JetAttribute::LArQuality,LArQuality))
343 missingVariable("LArQuality");
344
345
346 float HECQuality = 0;
347 if (!jet.getAttribute(xAOD::JetAttribute::HECQuality,HECQuality))
348 missingVariable("HECQuality");
349
350
351 float NegativeE = 0;
352 if (!jet.getAttribute(xAOD::JetAttribute::NegativeE,NegativeE))
353 missingVariable("NegativeE");
354
355
356
357 float AverageLArQF = 0;
358 if (!jet.getAttribute(xAOD::JetAttribute::AverageLArQF,AverageLArQF))
359 missingVariable("AverageLArQF");
360
361 return accept (EMFrac,
362 HECFrac,
363 LArQuality,
364 HECQuality,
365 sumpttrk,
366 jet.eta(),
367 jet.pt(),
368 FracSamplingMax,
369 NegativeE,
370 AverageLArQF,
371 FracSamplingMaxIndex);}
372}
373
375{
376 ATH_MSG_DEBUG(" Decorating jets with jet cleaning decoration : " << m_jetCleanKey.key());
377
379
380 for (const xAOD::Jet *jet : jets) {
381 cleanHandle(*jet) = accept(*jet).getCutResult("Cleaning");
382 }
383
384 return StatusCode::SUCCESS;
385
386}
387
389bool JetCleaningTool::containsHotCells( const xAOD::Jet& jet, const unsigned int runNumber) const
390{
391 // Check if the runNumber contains bad cells
392 std::unordered_map<unsigned int, std::vector<std::unique_ptr<JCT::HotCell>>>::const_iterator hotCells = m_hotCellsMap.find(runNumber);
393 if (hotCells != m_hotCellsMap.end())
394 {
395 // The run contains hot cells
396 // Check if the jet is affected by one of the hot cells
397 for (const std::unique_ptr<JCT::HotCell>& cell : hotCells->second)
398 if (cell->jetAffectedByHotCell(jet))
399 return true;
400 }
401 return false;
402}
403
406{
407 if (s=="SuperLooseBadLLP") return SuperLooseBadLLP;
408 if (s=="VeryLooseBadLLP") return VeryLooseBadLLP;
409 if (s=="LooseBad") return LooseBad;
410 if (s=="LooseBadLLP") return LooseBadLLP;
411 if (s=="LooseBadTrigger") return LooseBadTrigger;
412 if (s=="TightBad") return TightBad;
413 ATH_MSG_ERROR( "Unknown cut level requested: " << s );
414 return UnknownCut;
415}
416
418{
419 if (c==SuperLooseBadLLP) return "SuperLooseBadLLP";
420 if (c==VeryLooseBadLLP) return "VeryLooseBadLLP";
421 if (c==LooseBad) return "LooseBad";
422 if (c==LooseBadLLP) return "LooseBadLLP";
423 if (c==LooseBadTrigger) return "LooseBadTrigger";
424 if (c==TightBad) return "TightBad";
425 return "UnknownCut";
426}
427
428
431{
432 if (m_hotCellsFile.empty()) return StatusCode::SUCCESS;
433
434 // Ensure that the file exists
436 {
437 ATH_MSG_ERROR("Failed to find hot cells file: " << m_hotCellsFile);
438 return StatusCode::FAILURE;
439 }
440
441 // Now parse the file
442 TEnv readCells;
443 if (readCells.ReadFile(m_hotCellsFile.value().c_str(),kEnvGlobal))
444 {
445 ATH_MSG_ERROR("Cannot read hot cells file: " << m_hotCellsFile);
446 return StatusCode::FAILURE;
447 }
448
449 // Get the list of run numbers
450 const TString runNumbersString = readCells.GetValue("RunNumbers","");
451 if (runNumbersString=="")
452 {
453 ATH_MSG_ERROR("No RunNumbers field was specified in the hot cells file: " << m_hotCellsFile);
454 return StatusCode::FAILURE;
455 }
456
457 // Convert into a vector
458 std::vector<unsigned int> runNumbers = JCT::utils::vectorize<unsigned int>(runNumbersString,", ");
459 if (runNumbers.empty())
460 {
461 ATH_MSG_ERROR("RunNumbers field specified, but value is empty or not unsigned ints for hot cells file: " << m_hotCellsFile);
462 return StatusCode::FAILURE;
463 }
464
465 // Loop over the run numbers
466 for (unsigned int run : runNumbers){
467 std::vector<std::unique_ptr<JCT::HotCell>>& cellVec = m_hotCellsMap[run];
468
469 // The number of hot cells should be below 100 for a given run...
470 for (size_t iCell = 0; iCell < 100; ++iCell)
471 {
472 const TString baseName = Form("Run%u.Cell%zu.",run,iCell);
473
474 // Read the cell info
475 const int layer = readCells.GetValue(baseName+"Layer", -1 );
476 const float minEta = readCells.GetValue(baseName+"EtaMin",-10.);
477 const float maxEta = readCells.GetValue(baseName+"EtaMax", 10.);
478 const float minPhi = readCells.GetValue(baseName+"PhiMin",-10.);
479 const float maxPhi = readCells.GetValue(baseName+"PhiMax", 10.);
480
481 // Check that the input makes sense
482 if (layer < 0 && minEta < -5 && maxEta > 5 && minPhi < -5 && maxPhi > 5)
483 continue;
484 if (layer < 0 || minEta < -5 || maxEta > 5 || minPhi < -5 || maxPhi > 5)
485 {
486 ATH_MSG_ERROR("Partially specified cell - please check the file: " << m_hotCellsFile);
487 ATH_MSG_ERROR(Form("Got Layer=%d, EtaMin=%f, EtaMax=%f, PhiMin=%f, PhiMax=%f",layer,minEta,maxEta,minPhi,maxPhi));
488 return StatusCode::FAILURE;
489 }
490 cellVec.emplace_back(std::make_unique<JCT::HotCell>(layer,minEta,maxEta,minPhi,maxPhi));
491 }
492
493 // Ensure we found the expected run
494 if (cellVec.empty())
495 {
496 ATH_MSG_ERROR("Specified that Run# " << run << " contains hot cells, but did not find any corresponding cells in the file: " << m_hotCellsFile);
497 return StatusCode::FAILURE;
498 }
499 }
500
501 // Done
502 return StatusCode::SUCCESS;
503}
Scalar eta() const
pseudorapidity method
Scalar phi() const
phi method
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#define ATH_MSG_FATAL(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
Handle class for reading a decoration on an object.
Handle class for adding a decoration to an object.
const float m_etaMin
SG::ConstAccessor< std::vector< float > > m_ePerSamp
const float m_etaMax
const float m_phiMax
const float m_phiMin
bool jetAffectedByHotCell(const xAOD::Jet &jet) const
HotCell(const int layer, const float etaMin, const float etaMax, const float phiMin, const float phiMax)
asg::AcceptInfo m_accept
asg::AcceptData accept(const int isJetClean, const int fmaxIndex) const
The DFCommonJets decoration accept method.
JetCleaningTool(const std::string &name="JetCleaningTool")
Standard constructor.
Gaudi::Property< std::string > m_jetContainerName
SG::ConstAccessor< char > m_acc_jetClean
void missingVariable(const std::string &varName) const
CleaningLevel getCutLevel(const std::string &) const
Helpers for cut names.
Gaudi::Property< std::string > m_cutName
Name of the cut.
std::string getCutName(const CleaningLevel) const
Gaudi::Property< bool > m_useDecorations
std::unordered_map< unsigned int, std::vector< std::unique_ptr< JCT::HotCell > > > m_hotCellsMap
bool containsHotCells(const xAOD::Jet &jet, const unsigned int runNumber) const
Hot cell checks.
virtual StatusCode decorate(const xAOD::JetContainer &jets) const override
Decorate a jet collection without otherwise modifying it.
Gaudi::Property< std::string > m_hotCellsFile
Hot cells caching.
CleaningLevel m_cutLevel
CleaningLevel
Levels of cut.
Gaudi::Property< bool > m_doUgly
SG::WriteDecorHandleKey< xAOD::JetContainer > m_jetCleanKey
Gaudi::Property< bool > m_useLooseDecorForTightCut
virtual ~JetCleaningTool()
Standard destructor.
StatusCode readHotCells()
Hot cells reading helper.
virtual StatusCode initialize() override
Initialize method.
Helper class to provide constant type-safe access to aux data.
Handle class for adding a decoration to an object.
void setCutResult(const std::string &cutName, bool cutResult)
Set the result of a cut, based on the cut name (safer)
Definition AcceptData.h:134
bool getCutResult(const std::string &cutName) const
Get the result of a cut, based on the cut name (safer)
Definition AcceptData.h:98
Class mimicking the AthMessaging class from the offline software.
AsgMessaging(const std::string &name)
Constructor with a name.
AsgTool(const std::string &name)
Constructor specifying the tool instance's name.
Definition AsgTool.cxx:58
bool vectorize(const TString &str, const TString &sep, std::vector< T > &result)
Definition run.py:1
Jet_v1 Jet
Definition of the current "jet version".
JetContainer_v1 JetContainer
Definition of the current "jet container version".