ATLAS Offline Software
Loading...
Searching...
No Matches
UncertaintyHistogram.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
3*/
4
7
8#include "TObject.h"
9#include "TMath.h"
10#include "TFile.h"
11#include "TAxis.h"
12#include "TH2.h"
13#include "TH3.h"
14#include "TH3F.h"
15#include "TH3D.h"
16
17#include <stdexcept>
18
19namespace jet
20{
21
23// //
24// Constructor/destructor/initialization //
25// //
27
28UncertaintyHistogram::UncertaintyHistogram(const std::string& histName, const Interpolate::TypeEnum interpolate)
29 : asg::AsgMessaging(histName)
30 , m_isInit(false)
31 , m_name(histName.c_str())
32 , m_interpolate(interpolate)
33 , m_histo(nullptr)
34 , m_nDim(0)
35 , m_cachedProj()
36{
37 if (histName.empty())
39 ATH_MSG_DEBUG(Form("Creating UncertaintyHistogram named %s",getName().Data()));
40}
41
42UncertaintyHistogram::UncertaintyHistogram(const TString& histName, const Interpolate::TypeEnum interpolate)
43 : UncertaintyHistogram(std::string(histName.Data()),interpolate)
44{ }
45
47 : UncertaintyHistogram(std::string(histName),interpolate)
48{ }
49
51 : asg::AsgMessaging(Form("%s",toCopy.m_name.Data()))
52 , m_isInit(toCopy.m_isInit)
53 , m_name(toCopy.m_name)
55 , m_histo(nullptr)
56 , m_nDim(toCopy.m_nDim)
57 , m_cachedProj()
58{
59 ATH_MSG_DEBUG("Creating copy of UncertaintyHistogram named " << getName().Data());
60
61 if (toCopy.m_histo)
62 {
63 m_histo = dynamic_cast<TH1*>(toCopy.m_histo->Clone());
64 if (!m_histo)
65 {
66 ATH_MSG_FATAL("Failed to copy uncertainty histogram for " << getName().Data());
67 throw std::runtime_error("Failed to copy histogram in UncertaintyHistogram copy constructor");
68 }
69 }
70
71 if (!toCopy.m_cachedProj.empty())
72 if (cacheProjections().isFailure())
73 {
74 ATH_MSG_FATAL("Failed to build the required cache for " << getName().Data());
75 throw std::runtime_error("Failed to build projection cache in UncertaintyHistogram copy constructor");
76 }
77}
78
80{
81 ATH_MSG_DEBUG(Form("Deleting UncertaintyHistogram named %s",getName().Data()));
83}
84
85StatusCode UncertaintyHistogram::initialize(TFile* histFile)
86{
87 // Ensure it wasn't already initialized
88 if (m_isInit)
89 {
90 ATH_MSG_ERROR(Form("UncertaintyHistogram was already initialized: %s",getName().Data()));
91 return StatusCode::FAILURE;
92 }
93
94 // Ensure we can read from the file
95 if (!histFile->IsOpen())
96 {
97 ATH_MSG_ERROR(Form("Histogram file is not open for reading (%s)",getName().Data()));
98 return StatusCode::FAILURE;
99 }
100
101 // Find the histogram by name
102 TObject* histo = histFile->Get(m_name);
103 if (!histo)
104 {
105 ATH_MSG_ERROR(Form("Histogram file does not contain a histogram named %s",getName().Data()));
106 return StatusCode::FAILURE;
107 }
108
109 // Ensure the object is a histogram
110 m_histo = dynamic_cast<TH1*>(histo);
111 if (!m_histo)
112 {
113 ATH_MSG_ERROR(Form("Histogram file contains the expected key, but it's not a TH1* (%s)",getName().Data()));
114 return StatusCode::FAILURE;
115 }
116 m_histo->SetDirectory(nullptr);
117
118 // Cache dimensionality
119 m_nDim = m_histo->GetDimension();
120
121 // Cache the projections if relevant
123 if (cacheProjections().isFailure())
124 return StatusCode::FAILURE;
125
126 // Print a summary
127 ATH_MSG_DEBUG(Form("%s: Found histogram",getName().Data()));
128
129 m_isInit = true;
130 return StatusCode::SUCCESS;
131}
132
133
134
136// //
137// Histogram information access //
138// //
140
141double UncertaintyHistogram::getValue(const double var1) const
142{
143 if (m_nDim != 1)
144 {
145 ATH_MSG_ERROR(Form("Wrong dimensionality - asked for 1D for a %dD histo (%s)",m_nDim,getName().Data()));
146 return false;
147 }
148 return readHisto(var1);
149}
150
151double UncertaintyHistogram::getValue(const double var1, const double var2) const
152{
153 if (m_nDim != 2)
154 {
155 ATH_MSG_ERROR(Form("Wrong dimensionality - asked for 2D for a %dD histo (%s)",m_nDim,getName().Data()));
156 return false;
157 }
158 return readHisto(var1,var2);
159}
160
161double UncertaintyHistogram::getValue(const double var1, const double var2, const double var3) const
162{
163 if (m_nDim != 3)
164 {
165 ATH_MSG_ERROR(Form("Wrong dimensionality - asked for 3D for a %dD histo (%s)",m_nDim,getName().Data()));
166 return false;
167 }
168 return readHisto(var1,var2,var3);
169}
170
171
173// //
174// Histogram reading helpers //
175// //
177
178double UncertaintyHistogram::readHisto(const double var1, const double var2, const double var3) const
179{
180 // Ensure the component was initialized
181 if (!m_isInit)
182 {
183 ATH_MSG_ERROR(Form("Component was not initialized (%s)",getName().Data()));
184 return JESUNC_ERROR_CODE;
185 }
186
187
188 // Ensure that the histogram exists
189 if (!m_histo)
190 {
191 ATH_MSG_ERROR(Form("Histogram to read is NULL (%s)",getName().Data()));
192 return JESUNC_ERROR_CODE;
193 }
194
195 // Check first dimension boundaries, always applicable
196 const double valX = checkBoundariesByBin(std::as_const(*m_histo).GetXaxis(),m_histo->GetNbinsX(),var1);
197 if (m_nDim == 1)
198 {
199 switch (m_interpolate)
200 {
203 return JetHelpers::Interpolate(m_histo,valX);
204
206 return m_histo->GetBinContent(std::as_const(*m_histo).GetXaxis()->FindFixBin(valX));
207
208 default:
209 ATH_MSG_ERROR("Unsupported histogram interpolation type of \"" << Interpolate::enumToString(m_interpolate).Data() << " for 1D histogram named " << m_name.Data());
210 return JESUNC_ERROR_CODE;
211 }
212 }
213
214 // Check second dimension boundaries, if applicable
215 const double valY = checkBoundariesByBin(std::as_const(*m_histo).GetYaxis(),m_histo->GetNbinsY(),var2);
216 if (m_nDim == 2)
217 {
218 // We need a 2D histogram for the projection calls
219 switch (m_interpolate)
220 {
222 return JetHelpers::Interpolate(m_histo,valX,valY);
223
225 // Interpolate on the x projection for a given Y bin
226 return JetHelpers::Interpolate(m_cachedProj.at(0).at(std::as_const(*m_histo).GetYaxis()->FindFixBin(valY)).get(),valX);
227
229 // Interpolate on the y projection for a given X bin
230 return JetHelpers::Interpolate(m_cachedProj.at(0).at(std::as_const(*m_histo).GetXaxis()->FindFixBin(valX)).get(),valY);
231
233 return m_histo->GetBinContent(std::as_const(*m_histo).GetXaxis()->FindFixBin(valX),
234 std::as_const(*m_histo).GetYaxis()->FindFixBin(valY));
235
236 default:
237 ATH_MSG_ERROR("Unsupported histogram interpolation type of \"" << Interpolate::enumToString(m_interpolate).Data() << " for 1D histogram named " << m_name.Data());
238 return JESUNC_ERROR_CODE;
239 }
240 }
241
242 // Check third dimension boundaries, if applicable
243 const double valZ = checkBoundariesByBin(std::as_const(*m_histo).GetZaxis(),m_histo->GetNbinsZ(),var3);
244
245 switch (m_interpolate)
246 {
248 return JetHelpers::Interpolate(m_histo,valX,valY,valZ);
249
251 // Interpolate on the x projection for a given y,z bin
252 return JetHelpers::Interpolate(m_cachedProj.at(std::as_const(*m_histo).GetYaxis()->FindFixBin(valY)).at(std::as_const(*m_histo).GetZaxis()->FindFixBin(valZ)).get(),valX);
253
255 // Interpolate on the y projection for a given x,z bin
256 return JetHelpers::Interpolate(m_cachedProj.at(std::as_const(*m_histo).GetXaxis()->FindFixBin(valX)).at(std::as_const(*m_histo).GetZaxis()->FindFixBin(valZ)).get(),valY);
257
259 return m_histo->GetBinContent(std::as_const(*m_histo).GetXaxis()->FindFixBin(valX),
260 std::as_const(*m_histo).GetYaxis()->FindFixBin(valY),
261 std::as_const(*m_histo).GetZaxis()->FindFixBin(valZ));
262
263 default:
264 ATH_MSG_ERROR("Unsupported histogram interpolation type of \"" << Interpolate::enumToString(m_interpolate).Data() << " for 1D histogram named " << m_name.Data());
265 return JESUNC_ERROR_CODE;
266 }
267}
268
269
270double UncertaintyHistogram::checkBoundariesByBin(const TAxis* axis, const int numBins, const double valInput) const
271{
272 double val = valInput;
273
274 // Root histogram binning:
275 // bin 0 = underflow bin
276 // bin 1 = first actual bin
277 // bin N = last actual bin
278 // bin N+1 = overflow bin
279 const int binIndex = axis->FindBin(valInput);
280 if (binIndex < 1)
281 val = axis->GetBinLowEdge(1);
282 else if (binIndex > numBins)
283 {
284 // Don't use the upper edge as floating point can make it roll into the next bin (which is overflow)
285 // Instead, use the last bin width to go slightly within the boundary
286 // An offset of 1.e-3*binWidth is negligible for physics as the values don't change fast, but above floating point precision
287 val = (1.-1.e-3)*axis->GetBinWidth(numBins)+axis->GetBinLowEdge(numBins);
288 }
289
290 return val;
291}
292
293double UncertaintyHistogram::checkBoundaries(const TAxis* axis, const int numBins, const double valInput) const
294{
295 const static int maxNumWarn = 0; //100
296 static std::atomic<int> numWarn = 0;
297
298 // Bins are structured for [lowEdge,highEdge)
299 // As such, do not need to worry about equality sign for low edge
300 // However, do need to check equality sign for high edge
301 // High edge is expected and supported (no warning needed)
302 float val = valInput;
303 const double lowVal = axis->GetBinLowEdge(1);
304 const double highVal = axis->GetBinLowEdge(numBins+1);
305 if (val < lowVal || val >= highVal)
306 {
307 if (val != highVal && ++numWarn < maxNumWarn)
308 ATH_MSG_WARNING(Form("Variable value is %f, outside of the axis range of (%f,%f) for %s. "
309 "Using closest valid value.",val,lowVal,highVal,getName().Data())
310 << " (Only first " << maxNumWarn << " instances printed, this is " << numWarn << ")");
311
312 // Watch for the boundary sign (controls the scale factor)
313 if (val < lowVal)
314 val = lowVal>0 ? (1.0+1.e-4)*lowVal : (1.0-1.e-4)*lowVal;
315 else
316 val = highVal>0 ? (1.0-1.e-4)*highVal : (1.0+1.e-4)*highVal;
317 }
318
319 return val;
320}
321
323{
324 // Ensure the histogram exists
325 if (!m_histo)
326 {
327 ATH_MSG_FATAL("Cannot cache histogram as it doesn't exist: " << m_name.Data());
328 return StatusCode::FAILURE;
329 }
330
331 // Ensure the number of dimensions is sane
332 if (m_histo->GetDimension() < 1 || m_histo->GetDimension() > 3)
333 {
334 ATH_MSG_FATAL("Unsupported histogram dimensionality for projection caching: " << m_histo->GetDimension());
335 return StatusCode::FAILURE;
336 }
337
338 // Ensure the cache doesn't already exist
339 if (!m_cachedProj.empty())
340 {
341 ATH_MSG_FATAL("Cannot cache histogram as the cache is non-empty: " << m_name.Data());
342 return StatusCode::FAILURE;
343 }
344
345 // Protect vs Interpolate
346 switch (m_interpolate)
347 {
349 // Simple case of 1D
350 if (m_histo->GetDimension() == 1)
351 return StatusCode::SUCCESS;
352 break;
353
355 // Failure case of 1D
356 if (m_histo->GetDimension() == 1)
357 {
358 ATH_MSG_FATAL("Cannot project in Y for a 1D histogram: " << m_name.Data());
359 return StatusCode::FAILURE;
360 }
361 break;
362
363 default:
364 ATH_MSG_FATAL("The interpolation type is not supported for caching: " << m_name.Data());
365 return StatusCode::FAILURE;
366 }
367
368 // If we got here, then the request makes sense
369 // Start the projections
370 // Intentionally include underflow and overflow bins
371 // This keeps the same indexing scheme as root
372 // Avoids confusion and problems later at cost of a small amount of RAM
373 if (m_histo->GetDimension() == 2)
374 {
375 // Prepare to cache
376 TH2* localHist = dynamic_cast<TH2*>(m_histo);
377 m_cachedProj.resize(1); // 2D is a single slice of 3D
378 if (!localHist)
379 {
380 ATH_MSG_FATAL("Failed to convert histogram to a TH2, please check inputs: " << m_name.Data());
381 return StatusCode::FAILURE;
382 }
383
384 // Create the projections
386 {
387 for (Long64_t binY = 0; binY < localHist->GetNbinsY()+1; ++binY)
388 {
389 // Single bin of Y, interpolate across X
390 m_cachedProj.at(0).emplace_back(localHist->ProjectionX(Form("projx_%lld",binY),binY,binY));
391 }
392 }
394 {
395 for (Long64_t binX = 0; binX < localHist->GetNbinsX()+1; ++binX)
396 {
397 // Single bin of X, interpolate across Y
398 m_cachedProj.at(0).emplace_back(localHist->ProjectionY(Form("projy_%lld",binX),binX,binX));
399 }
400 }
401 else
402 {
403 // We shouldn't make it here due to earlier checks
404 ATH_MSG_FATAL("Unexpected interpolation type, somehow escaped earlier checks: " << m_name.Data());
405 return StatusCode::FAILURE;
406 }
407 }
408 else if (m_histo->GetDimension() == 3)
409 {
410 // Prepare to cache
411 TH3* localHist = dynamic_cast<TH3*>(m_histo);
412 if (!localHist)
413 {
414 ATH_MSG_FATAL("Failed to convert histogram to a TH3, please check inputs: " << m_name.Data());
415 return StatusCode::FAILURE;
416 }
417
418 // Create the projections
420 {
421 m_cachedProj.resize(localHist->GetNbinsY()+1); // 3D is a full double-index scan
422 for (Long64_t binY = 0; binY < localHist->GetNbinsY()+1; ++binY)
423 {
424 for (Long64_t binZ = 0; binZ < localHist->GetNbinsZ()+1; ++binZ)
425 {
426 // Single bin of Y-Z, interpolate across X
427 m_cachedProj.at(binY).emplace_back(localHist->ProjectionX(Form("projx_%lld_%lld",binY,binZ),binY,binY,binZ,binZ));
428 }
429 }
430 }
432 {
433 m_cachedProj.resize(localHist->GetNbinsX()+1); // 3D is a full double-index scan
434 for (Long64_t binX = 0; binX < localHist->GetNbinsX()+1; ++binX)
435 {
436 for (Long64_t binZ = 0; binZ < localHist->GetNbinsZ()+1; ++binZ)
437 {
438 // Single bin of X-Z, interpolate across Y
439 m_cachedProj.at(binX).emplace_back(localHist->ProjectionY(Form("projy_%lld_%lld",binX,binZ),binX,binX,binZ,binZ));
440 }
441 }
442 }
443 else
444 {
445 // We shouldn't make it here due to earlier checks
446 ATH_MSG_FATAL("Unexpected interpolation type, somehow escaped earlier checks: " << m_name.Data());
447 return StatusCode::FAILURE;
448 }
449 }
450 else
451 {
452 // We shouldn't make it here due to earlier checks
453 ATH_MSG_FATAL("Unexpected dimensionality: " << m_histo->GetDimension());
454 return StatusCode::FAILURE;
455 }
456
457 // Ensure that ROOT doesn't try to take posession of any of the projections
458 for (size_t index = 0; index < m_cachedProj.size(); ++index)
459 {
460 for (auto& hist : m_cachedProj.at(index))
461 {
462 hist->SetDirectory(nullptr);
463 }
464 }
465
466 // All done
467 return StatusCode::SUCCESS;
468}
469
470} // end jet namespace
471
#define ATH_MSG_ERROR(x)
#define ATH_MSG_FATAL(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
@ Data
Definition BaseObject.h:11
AsgMessaging(const std::string &name)
Constructor with a name.
virtual StatusCode initialize(TFile *histFile)
double checkBoundaries(const TAxis *axis, const int numBins, const double valInput) const
double getValue(const double var1) const
double readHisto(const double var1, const double var2=0, const double var3=0) const
const TString & getName() const
std::vector< std::vector< std::unique_ptr< TH1 > > > m_cachedProj
UncertaintyHistogram(const std::string &histName, const Interpolate::TypeEnum interpolate)
double checkBoundariesByBin(const TAxis *axis, const int numBins, const double valInput) const
const Interpolate::TypeEnum m_interpolate
STL class.
double Interpolate(const TH1 *histo, const double x)
Definition index.py:1
TString enumToString(const TypeEnum type)
STL namespace.