ATLAS Offline Software
Loading...
Searching...
No Matches
JetSmearingCorrection.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
3*/
4
5#include <utility>
6
10
26
27JetSmearingCorrection::JetSmearingCorrection(const std::string& name, TEnv* config, TString jetAlgo, TString calibAreaTag, bool dev)
28 : JetCalibrationStep(name.c_str())
30 , m_jetAlgo(std::move(jetAlgo))
31 , m_calibAreaTag(std::move(calibAreaTag))
32 , m_dev(dev)
33 , m_jetOutScale("")
41{ }
42
44= default;
45
47{
48 ATH_MSG_INFO("Initializing the jet smearing correction tool");
49
50 if (!m_config)
51 {
52 ATH_MSG_FATAL("Config file not specified. Aborting.");
53 return StatusCode::FAILURE;
54 }
55 if (m_jetAlgo == "")
56 {
57 ATH_MSG_FATAL("No jet algorithm specified. Aborting.");
58 return StatusCode::FAILURE;
59 }
60
61 // Get the starting and ending jet scales
62 m_jetStartScale = m_config->GetValue("JSCStartingScale","JetGSCScaleMomentum");
63 m_jetOutScale = m_config->GetValue("JSCOutScale","JetSmearedMomentum");
64 ATH_MSG_INFO("Reading from " << m_jetStartScale.c_str() << " and writing to " << m_jetOutScale.Data());
65
66 // Get information about how to smear
67 TString smearType = m_config->GetValue("SmearType","");
68 if (smearType == "")
69 {
70 ATH_MSG_FATAL("No jet smearing type was specified. Aborting.");
71 return StatusCode::FAILURE;
72 }
73 else if (!smearType.CompareTo("pt",TString::kIgnoreCase))
75 else if (!smearType.CompareTo("mass",TString::kIgnoreCase))
77 else if (!smearType.CompareTo("FourVec",TString::kIgnoreCase))
79 else
80 {
81 ATH_MSG_FATAL("Unrecognized jet smearing type: " << smearType.Data());
82 return StatusCode::FAILURE;
83 }
84
85 // Determine the histogram parametrization
86 TString histType = m_config->GetValue("SmearingCorrectionHistType","");
87 if (histType == "")
88 {
89 ATH_MSG_FATAL("No jet smearing histogram parametrization was specified. Aborting.");
90 return StatusCode::FAILURE;
91 }
92 else if (!histType.CompareTo("pt",TString::kIgnoreCase))
94 else if (!histType.CompareTo("PtEta",TString::kIgnoreCase))
96 else if (!histType.CompareTo("PtAbsEta",TString::kIgnoreCase))
98 else
99 {
100 ATH_MSG_FATAL("Unrecognized jet smearing histogram parametrization: " << histType.Data());
101 return StatusCode::FAILURE;
102 }
103
104 // Determine the histogram interpolation strategy
105 TString interpType = m_config->GetValue("SmearingCorrectionInterpType","");
106 if (interpType == "")
107 {
108 ATH_MSG_FATAL("No jet smearing histogram interpolation type was specified. Aborting.");
109 return StatusCode::FAILURE;
110 }
111 else if (!interpType.CompareTo("full",TString::kIgnoreCase))
113 else if (!interpType.CompareTo("none",TString::kIgnoreCase))
115 else if (!interpType.CompareTo("onlyx",TString::kIgnoreCase))
117 else if (!interpType.CompareTo("onlyy",TString::kIgnoreCase))
119 else
120 {
121 ATH_MSG_FATAL("Unrecognized jet smearing interpolation type: " << interpType.Data());
122 return StatusCode::FAILURE;
123 }
124
125 // Find the ROOT file containing the smearing histogram, path comes from the config file
126 TString smearingFile = m_config->GetValue("SmearingCorrectionFile","");
127 if (smearingFile == "")
128 {
129 ATH_MSG_FATAL("No jet smearing correction file specified. Aborting.");
130 return StatusCode::FAILURE;
131 }
132
133 // Find the name of the MC nominal resolution histogram, from the config file
134 TString smearingHistNameMC = m_config->GetValue("SmearingHistNameResolutionMC","");
135 if (smearingHistNameMC == "")
136 {
137 ATH_MSG_FATAL("No MC jet smearing histogram name specified. Aborting.");
138 return StatusCode::FAILURE;
139 }
140 TString smearingHistNameData = m_config->GetValue("SmearingHistNameResolutionData","");
141 if (smearingHistNameData == "")
142 {
143 ATH_MSG_FATAL("No data jet smearing histogram name specified. Aborting.");
144 return StatusCode::FAILURE;
145 }
146
147 // Open the histogram file
148 if (m_dev)
149 {
150 smearingFile.Remove(0,33);
151 smearingFile.Insert(0,"JetCalibTools/");
152 }
153 else
154 smearingFile.Insert(14,m_calibAreaTag);
155
156 TString fileName = PathResolverFindCalibFile(smearingFile.Data());
157 std::unique_ptr<TFile> inputFile(TFile::Open(fileName));
158 if (!inputFile || inputFile->IsZombie())
159 {
160 ATH_MSG_FATAL("Cannot open jet smearing correction file: " << fileName);
161 return StatusCode::FAILURE;
162 }
163
164 // Retrieve the histogram froms the file
165 m_smearResolutionMC = std::unique_ptr<TH1>(dynamic_cast<TH1*>(inputFile->Get(smearingHistNameMC)));
167 {
168 ATH_MSG_FATAL("Failed to get specified histogram from the file: " << smearingHistNameMC.Data());
169 return StatusCode::FAILURE;
170 }
171 m_smearResolutionMC->SetDirectory(nullptr);
172
173 m_smearResolutionData = std::unique_ptr<TH1>(dynamic_cast<TH1*>(inputFile->Get(smearingHistNameData)));
175 {
176 ATH_MSG_FATAL("Failed to get specified histogram from the file: " << smearingHistNameData.Data());
177 return StatusCode::FAILURE;
178 }
179 m_smearResolutionData->SetDirectory(nullptr);
180
181 // Done with the input file, close it
182 inputFile->Close();
183
184 // Ensure that the histogram we retrieved has the right number of dimensions
185 // It must match the dimensionality of the parametrization
186 switch (m_histType)
187 {
188 case HistType::Pt:
189 if (m_smearResolutionMC->GetDimension() != 1)
190 {
191 ATH_MSG_FATAL("Specified MC histogram has " << m_smearResolutionMC->GetDimension() << " dimensions, but parametrization expects 1 dimension");
192 return StatusCode::FAILURE;
193 }
194 if (m_smearResolutionData->GetDimension() != 1)
195 {
196 ATH_MSG_FATAL("Specified data histogram has " << m_smearResolutionData->GetDimension() << " dimensions, but parametrization expects 1 dimension");
197 return StatusCode::FAILURE;
198 }
199 break;
200
201 case HistType::PtEta:
203 if (m_smearResolutionMC->GetDimension() != 2)
204 {
205 ATH_MSG_FATAL("Specified MC histogram has " << m_smearResolutionMC->GetDimension() << " dimensions, but parametrization expects 2 dimensions");
206 return StatusCode::FAILURE;
207 }
208 if (m_smearResolutionData->GetDimension() != 2)
209 {
210 ATH_MSG_FATAL("Specified data histogram has " << m_smearResolutionData->GetDimension() << " dimensions, but parametrization expects 2 dimensions");
211 return StatusCode::FAILURE;
212 }
213 break;
214
215 default:
216 ATH_MSG_FATAL("Read the histogram, but the parametrization is UNKNOWN");
217 return StatusCode::FAILURE;
218 }
219
220 // Pre-cache the histogram file in 1D projections if relevant (depends on InterpType)
222 {
223 if (cacheProjections(m_smearResolutionMC.get(), m_cachedProjResMC,"mc").isFailure())
224 return StatusCode::FAILURE;
225 if (cacheProjections(m_smearResolutionData.get(),m_cachedProjResData,"data").isFailure())
226 return StatusCode::FAILURE;
227 }
228
229 return StatusCode::SUCCESS;
230}
231
232StatusCode JetSmearingCorrection::readHisto(double& returnValue, const TH1* histo, double x) const
233{
234 // Ensure that the histogram exists
235 if (!histo)
236 {
237 ATH_MSG_ERROR("Unable to read histogram - address is NULL");
238 return StatusCode::FAILURE;
239 }
240
241 // Check dimensionality just to be safe
242 if (histo->GetDimension() != 1)
243 {
244 ATH_MSG_ERROR("Blocking reading of a " << histo->GetDimension() << "D histogram as a 1D histogram");
245 return StatusCode::FAILURE;
246 }
247
248 // Ensure we are within boundaries
249 const double minX = histo->GetXaxis()->GetBinLowEdge(1);
250 const double maxX = histo->GetXaxis()->GetBinLowEdge(histo->GetNbinsX()+1);
251 if ( x >= maxX )
252 x = maxX - 1.e-6;
253 else if ( x <= minX )
254 x = minX + 1.e-6;
255
256 // Get the result, interpolating as appropriate
257 switch (m_interpType)
258 {
259 case InterpType::Full:
261 returnValue = histo->Interpolate(x);
262 break;
263
264 case InterpType::None:
265 returnValue = histo->GetBinContent(histo->GetXaxis()->FindBin(x));
266 break;
267
268 default:
269 ATH_MSG_ERROR("Unsupported interpolation type for a 1D histogram");
270 return StatusCode::FAILURE;
271 }
272
273 return StatusCode::SUCCESS;
274}
275
276StatusCode JetSmearingCorrection::readHisto(double& returnValue, const TH1* histo, const std::vector< std::unique_ptr<TH1> >& projections, double x, double y) const
277{
278 // Ensure that the histogram exists
279 if (!histo)
280 {
281 ATH_MSG_ERROR("Unable to read histogram - address is NULL");
282 return StatusCode::FAILURE;
283 }
284
285 // Check dimensionality just to be safe
286 if (histo->GetDimension() != 2)
287 {
288 ATH_MSG_ERROR("Blocking reading of a " << histo->GetDimension() << "D histogram as a 2D histogram");
289 return StatusCode::FAILURE;
290 }
291
292 // Ensure we are within boundaries
293 const double minX = histo->GetXaxis()->GetBinLowEdge(1);
294 const double maxX = histo->GetXaxis()->GetBinLowEdge(histo->GetNbinsX()+1);
295 if ( x >= maxX )
296 x = maxX - 1.e-6;
297 else if ( x <= minX )
298 x = minX + 1.e-6;
299 const double minY = histo->GetYaxis()->GetBinLowEdge(1);
300 const double maxY = histo->GetYaxis()->GetBinLowEdge(histo->GetNbinsY()+1);
301 if ( y >= maxY )
302 y = maxY - 1.e-6;
303 else if ( y <= minY )
304 y = minY + 1.e-6;
305
306 // Get the result, interpolating as appropriate
307 switch (m_interpType)
308 {
309 case InterpType::Full:
310 returnValue = histo->Interpolate(x,y);
311 break;
312
313 case InterpType::None:
314 returnValue = histo->GetBinContent(histo->GetXaxis()->FindBin(x),histo->GetYaxis()->FindBin(y));
315 break;
316
318 // Determine the y-bin and use the cached projection to interpolate x
319 returnValue = projections.at(histo->GetYaxis()->FindBin(y))->Interpolate(x);
320 break;
321
323 // Determine the x-bin and use the cached projection to interpolate y
324 returnValue = projections.at(histo->GetXaxis()->FindBin(x))->Interpolate(y);
325 break;
326
327 default:
328 ATH_MSG_ERROR("Unsupported interpolation type for a 2D histogram");
329 return StatusCode::FAILURE;
330 }
331
332 return StatusCode::SUCCESS;
333}
334
335
336StatusCode JetSmearingCorrection::getSigmaSmear(xAOD::Jet& jet, double& sigmaSmear) const
337{
338 /*
339 Nominal jet smearing
340 If sigma_data > sigma_MC, then we want to smear MC to match data
341 If sigma_data < sigma_MC, then we do not want to smear data to match MC
342 The second case is instead the source of an uncertainty (see JetUncertainties)
343
344 To make MC agree with data:
345 if (sigma_data > sigma_MC) then sigma_smear^2 = sigma_data^2 - sigma_MC^2
346 if (sigma_data < sigma_MC) then do nothing
347 Smearing using a Gaussian centered at 1 and with a width of sigma_smear
348
349 Note that data is never smeared, as blocked in JetCalibrationTool.cxx
350 */
351
352 double resolutionMC = 0;
353 if (getNominalResolutionMC(jet,resolutionMC).isFailure())
354 return StatusCode::FAILURE;
355
356 double resolutionData = 0;
357 if (getNominalResolutionData(jet,resolutionData).isFailure())
358 return StatusCode::FAILURE;
359
360 // Nominal smearing only if data resolution is larger than MC resolution
361 // This is because we want to smear the MC to match the data
362 // if MC is larger than data, don't make the nominal data worse, so smear is 0
363 if (resolutionMC < resolutionData)
364 sigmaSmear = sqrt(resolutionData*resolutionData - resolutionMC*resolutionMC);
365 else
366 sigmaSmear = 0;
367
368 return StatusCode::SUCCESS;
369}
370
371StatusCode JetSmearingCorrection::getNominalResolution(const xAOD::Jet& jet, const TH1* histo, const std::vector< std::unique_ptr<TH1> >& projections, double& resolution) const
372{
373 double localRes = 0;
374 switch (m_histType)
375 {
376 case HistType::Pt:
377 if (readHisto(localRes,histo,jet.pt()/m_GeV).isFailure())
378 return StatusCode::FAILURE;
379 break;
380
381 case HistType::PtEta:
382 if (readHisto(localRes,histo,projections,jet.pt()/m_GeV,jet.eta()).isFailure())
383 return StatusCode::FAILURE;
384 break;
385
387 if (readHisto(localRes,histo,projections,jet.pt()/m_GeV,fabs(jet.eta())).isFailure())
388 return StatusCode::FAILURE;
389 break;
390
391 default:
392 // We should never reach this, it was checked during initialization
393 ATH_MSG_ERROR("Cannot get the nominal resolution, the smearing histogram type was not set");
394 return StatusCode::FAILURE;
395 }
396
397 // If we got here, everything went well
398 // Set the resolution and return success
399 resolution = localRes;
400 return StatusCode::SUCCESS;
401}
402
403StatusCode JetSmearingCorrection::getNominalResolutionData(const xAOD::Jet& jet, double& resolution) const
404{
406}
407
408StatusCode JetSmearingCorrection::getNominalResolutionMC(const xAOD::Jet& jet, double& resolution) const
409{
411}
412
413TRandom3* JetSmearingCorrection::getTLSRandomGen(unsigned long seed) const
414{
415 TRandom3* random = m_rand_tls.get();
416 if (!random) {
417 random = new TRandom3();
418 m_rand_tls.reset(random);
419 }
420 random->SetSeed(seed);
421 return random;
422}
423
425{
426 // Apply the jet smearing correction
427
428 // Calculate the smearing width to use
429 double sigmaSmear = 0;
430 if (getSigmaSmear(jet,sigmaSmear).isFailure())
431 return StatusCode::FAILURE;
432
433 // Set the random seed deterministically using jet phi
434 unsigned long seed = static_cast<unsigned long>(1.e5*fabs(jet.phi()));
435 // SetSeed(0) uses the clock, so avoid this
436 if(seed == 0) seed = 45583453; // arbitrary number which the seed couldn't otherwise be
437 TRandom3* rng = getTLSRandomGen(seed);
438
439 // Get the Gaussian-distributed random number
440 // Force this to be a positive value
441 // Negative values should be extraordinarily rare, but they do exist
442 double smearingFactor = -1;
443 while (smearingFactor < 0)
444 smearingFactor = rng->Gaus(1.,sigmaSmear);
445
446 // Apply the smearing factor to the jet as appropriate
447 xAOD::JetFourMom_t calibP4 = jet.jetP4();
448 switch (m_smearType)
449 {
450 case SmearType::Pt:
451 calibP4 = xAOD::JetFourMom_t(jet.pt()*smearingFactor,jet.eta(),jet.phi(),jet.m());
452 break;
453
454 case SmearType::Mass:
455 calibP4 = xAOD::JetFourMom_t(jet.pt(),jet.eta(),jet.phi(),smearingFactor*jet.m());
456 break;
457
459 calibP4 = xAOD::JetFourMom_t(jet.pt()*smearingFactor,jet.eta(),jet.phi(),jet.m()*smearingFactor);
460 break;
461
462 default:
463 // We should never reach this, it was checked during initialization
464 ATH_MSG_ERROR("Cannot smear the jet, the smearing type was not set");
465 return StatusCode::FAILURE;
466 }
467
468 // Set the output scale
469 jet.setAttribute<xAOD::JetFourMom_t>(m_jetOutScale.Data(),calibP4);
470 jet.setJetP4(calibP4);
471
472
473 return StatusCode::SUCCESS;
474}
475
476StatusCode JetSmearingCorrection::cacheProjections(TH1* fullHistogram, std::vector< std::unique_ptr<TH1> >& cacheLocation, const std::string& type)
477{
478 // Ensure the histogram exists
479 if (!fullHistogram)
480 {
481 ATH_MSG_FATAL("Cannot cache histogram as it doesn't exist: " << type);
482 return StatusCode::FAILURE;
483 }
484
485 // Ensure the number of dimensions is sane
486 if (fullHistogram->GetDimension() < 1 || fullHistogram->GetDimension() > 2)
487 {
488 ATH_MSG_FATAL("Unsupported histogram dimensionality for projection caching: " << fullHistogram->GetDimension());
489 return StatusCode::FAILURE;
490 }
491
492 // Protect vs InterpType
493 switch (m_interpType)
494 {
496 // Simple case of 1D
497 if (fullHistogram->GetDimension() == 1)
498 return StatusCode::SUCCESS;
499 break;
500
502 // Failure case of 1D
503 if (fullHistogram->GetDimension() == 1)
504 {
505 ATH_MSG_FATAL("Cannot project in Y for a 1D histogram: " << type);
506 return StatusCode::FAILURE;
507 }
508 break;
509
510 default:
511 ATH_MSG_FATAL("The interpolation type is not supported for caching: " << type);
512 return StatusCode::FAILURE;
513 }
514
515 // If we got here, then the request makes sense
516 // Start the projections
517 // Intentionally include underflow and overflow bins
518 // This keeps the same indexing scheme as root
519 // Avoids confusion and problems later at cost of a small amount of RAM
520 if (fullHistogram->GetDimension() == 2)
521 {
522 TH2* localHist = dynamic_cast<TH2*>(fullHistogram);
523 if (!localHist)
524 {
525 ATH_MSG_FATAL("Failed to convert histogram to a TH2, please check inputs: " << type);
526 return StatusCode::FAILURE;
527 }
529 {
530 for (Long64_t binY = 0; binY < localHist->GetNbinsY()+1; ++binY)
531 {
532 // Single bin of Y, interpolate across X
533 cacheLocation.emplace_back(localHist->ProjectionX(Form("projx_%s_%lld",type.c_str(),binY),binY,binY));
534 }
535 }
537 {
538 for (Long64_t binX = 0; binX < localHist->GetNbinsX()+1; ++binX)
539 {
540 // Single bin of X, interpolate across Y
541 cacheLocation.emplace_back(localHist->ProjectionY(Form("projy_%s_%lld",type.c_str(),binX),binX,binX));
542 }
543 }
544 else
545 {
546 // We shouldn't make it here due to earlier checks
547 ATH_MSG_FATAL("Unexpected interpolation type, somehow escaped earlier checks: " << type);
548 return StatusCode::FAILURE;
549 }
550 }
551 else
552 {
553 // We shouldn't make it here due to earlier checks
554 ATH_MSG_FATAL("Unexpected dimensionality: " << fullHistogram->GetDimension());
555 return StatusCode::FAILURE;
556 }
557
558 // Ensure that ROOT doesn't try to take posession
559 for (auto& hist : cacheLocation)
560 {
561 hist->SetDirectory(nullptr);
562 }
563
564 // All done
565 return StatusCode::SUCCESS;
566}
567
568
#define ATH_MSG_ERROR(x)
#define ATH_MSG_FATAL(x)
#define ATH_MSG_INFO(x)
std::string PathResolverFindCalibFile(const std::string &logical_file_name)
#define y
#define x
JetCalibrationStep(const char *name="JetCalibrationStep")
StatusCode getSigmaSmear(xAOD::Jet &jet, double &sigmaSmear) const
StatusCode readHisto(double &returnValue, const TH1 *histo, double x) const
boost::thread_specific_ptr< TRandom3 > m_rand_tls
StatusCode cacheProjections(TH1 *fullHistogram, std::vector< std::unique_ptr< TH1 > > &cacheLocation, const std::string &type)
std::vector< std::unique_ptr< TH1 > > m_cachedProjResData
virtual StatusCode initialize() override
StatusCode getNominalResolution(const xAOD::Jet &jet, const TH1 *histo, const std::vector< std::unique_ptr< TH1 > > &projections, double &resolution) const
std::unique_ptr< TH1 > m_smearResolutionMC
TRandom3 * getTLSRandomGen(unsigned long seed) const
virtual StatusCode getNominalResolutionMC(const xAOD::Jet &jet, double &resolution) const override
std::vector< std::unique_ptr< TH1 > > m_cachedProjResMC
virtual ~JetSmearingCorrection()
virtual StatusCode calibrate(xAOD::Jet &jet, JetEventInfo &) const override
virtual StatusCode getNominalResolutionData(const xAOD::Jet &jet, double &resolution) const override
std::unique_ptr< TH1 > m_smearResolutionData
STL namespace.
Jet_v1 Jet
Definition of the current "jet version".
ROOT::Math::LorentzVector< ROOT::Math::PtEtaPhiM4D< double > > JetFourMom_t
Base 4 Momentum type for Jet.
Definition JetTypes.h:17