ATLAS Offline Software
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 
19 namespace jet
20 {
21 
23 // //
24 // Constructor/destructor/initialization //
25 // //
27 
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 
44 { }
45 
48 { }
49 
51  : asg::AsgMessaging(Form("%s",toCopy.m_name.Data()))
52  , m_isInit(toCopy.m_isInit)
53  , m_name(toCopy.m_name)
54  , m_interpolate(toCopy.m_interpolate)
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 
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 
141 double 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 
151 double 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 
161 double 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 
178 double 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  {
201  case Interpolate::Full:
202  case Interpolate::OnlyX:
203  return JetHelpers::Interpolate(m_histo,valX);
204 
205  case Interpolate::None:
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  {
221  case Interpolate::Full:
222  return JetHelpers::Interpolate(m_histo,valX,valY);
223 
224  case Interpolate::OnlyX:
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 
228  case Interpolate::OnlyY:
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 
232  case Interpolate::None:
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  {
247  case Interpolate::Full:
248  return JetHelpers::Interpolate(m_histo,valX,valY,valZ);
249 
250  case Interpolate::OnlyX:
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 
254  case Interpolate::OnlyY:
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 
258  case Interpolate::None:
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 
270 double 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 
293 double 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  {
348  case Interpolate::OnlyX:
349  // Simple case of 1D
350  if (m_histo->GetDimension() == 1)
351  return StatusCode::SUCCESS;
352  break;
353 
354  case Interpolate::OnlyY:
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  }
393  else if (m_interpolate == Interpolate::OnlyY)
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  }
431  else if (m_interpolate == Interpolate::OnlyY)
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 
jet::UncertaintyHistogram::getName
const TString & getName() const
Definition: UncertaintyHistogram.h:36
jet::UncertaintyHistogram::readHisto
double readHisto(const double var1, const double var2=0, const double var3=0) const
Definition: UncertaintyHistogram.cxx:178
ATH_MSG_FATAL
#define ATH_MSG_FATAL(x)
Definition: AthMsgStreamMacros.h:34
UncertaintyHistogram.h
jet::Interpolate::TypeEnum
TypeEnum
Definition: UncertaintyEnum.h:241
AddEmptyComponent.histName
string histName
Definition: AddEmptyComponent.py:64
jet::UncertaintyHistogram::checkBoundaries
double checkBoundaries(const TAxis *axis, const int numBins, const double valInput) const
Definition: UncertaintyHistogram.cxx:293
Trk::binZ
@ binZ
Definition: BinningType.h:49
index
Definition: index.py:1
Data
@ Data
Definition: BaseObject.h:11
plotmaker.hist
hist
Definition: plotmaker.py:148
yodamerge_tmp.axis
list axis
Definition: yodamerge_tmp.py:241
asg
Definition: DataHandleTestTool.h:28
jet::UncertaintyHistogram::m_interpolate
const Interpolate::TypeEnum m_interpolate
Definition: UncertaintyHistogram.h:50
jet::UncertaintyHistogram::getValue
double getValue(const double var1) const
Definition: UncertaintyHistogram.cxx:141
Helpers.h
JetHelpers::Interpolate
double Interpolate(const TH1 *histo, const double x)
Definition: JetHelpers.cxx:16
jet::UncertaintyHistogram::initialize
virtual StatusCode initialize(TFile *histFile)
Definition: UncertaintyHistogram.cxx:85
JESUNC_ERROR_CODE
#define JESUNC_ERROR_CODE
Definition: Reconstruction/Jet/JetUncertainties/JetUncertainties/Helpers.h:23
Trk::binY
@ binY
Definition: BinningType.h:48
jet
Definition: JetCalibTools_PlotJESFactors.cxx:23
JESUNC_SAFE_DELETE
#define JESUNC_SAFE_DELETE(T)
Definition: Reconstruction/Jet/JetUncertainties/JetUncertainties/Helpers.h:25
ATH_MSG_ERROR
#define ATH_MSG_ERROR(x)
Definition: AthMsgStreamMacros.h:33
jet::UncertaintyHistogram::UncertaintyHistogram
UncertaintyHistogram(const std::string &histName, const Interpolate::TypeEnum interpolate)
Definition: UncertaintyHistogram.cxx:28
EL::StatusCode
::StatusCode StatusCode
StatusCode definition for legacy code.
Definition: PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/StatusCode.h:22
ATH_MSG_DEBUG
#define ATH_MSG_DEBUG(x)
Definition: AthMsgStreamMacros.h:29
TH3
Definition: rootspy.cxx:440
jet::UncertaintyHistogram::m_name
const TString m_name
Definition: UncertaintyHistogram.h:49
IDPVM::binIndex
unsigned int binIndex(const T &val, const std::vector< T > &partitions)
general utility function to return bin index given a value and the upper endpoints of each bin
Definition: InDetPhysValMonitoringUtilities.h:43
jet::UncertaintyHistogram::m_isInit
bool m_isInit
Definition: UncertaintyHistogram.h:48
jet::UncertaintyHistogram::m_nDim
int m_nDim
Definition: UncertaintyHistogram.h:52
Trk::binX
@ binX
Definition: BinningType.h:47
TH2
Definition: rootspy.cxx:373
jet::Interpolate::None
@ None
Definition: UncertaintyEnum.h:243
jet::Interpolate::enumToString
TString enumToString(const TypeEnum type)
Definition: UncertaintyEnum.cxx:645
TH1::GetBinContent
double GetBinContent(int) const
Definition: rootspy.cxx:298
jet::UncertaintyHistogram
Definition: UncertaintyHistogram.h:25
JESUNC_NO_DEFAULT_CONSTRUCTOR
#define JESUNC_NO_DEFAULT_CONSTRUCTOR
Definition: Reconstruction/Jet/JetUncertainties/JetUncertainties/Helpers.h:24
jet::Interpolate::OnlyY
@ OnlyY
Definition: UncertaintyEnum.h:246
DeMoScan.index
string index
Definition: DeMoScan.py:362
DiTauMassTools::MaxHistStrategyV2::e
e
Definition: PhysicsAnalysis/TauID/DiTauMassTools/DiTauMassTools/HelperFunctions.h:26
jet::UncertaintyHistogram::m_cachedProj
std::vector< std::vector< std::unique_ptr< TH1 > > > m_cachedProj
Definition: UncertaintyHistogram.h:56
jet::UncertaintyHistogram::cacheProjections
StatusCode cacheProjections()
Definition: UncertaintyHistogram.cxx:322
ATH_MSG_WARNING
#define ATH_MSG_WARNING(x)
Definition: AthMsgStreamMacros.h:32
jet::Interpolate::OnlyX
@ OnlyX
Definition: UncertaintyEnum.h:245
Pythia8_RapidityOrderMPI.val
val
Definition: Pythia8_RapidityOrderMPI.py:14
TH1
Definition: rootspy.cxx:268
CaloClusterCorr::interpolate
float interpolate(const CaloRec::Array< 2 > &a, float x, unsigned int degree, unsigned int ycol=1, const CaloRec::Array< 1 > &regions=CaloRec::Array< 1 >(), int n_points=-1, bool fixZero=false)
Polynomial interpolation in a table.
Definition: interpolate.cxx:75
jet::Interpolate::Full
@ Full
Definition: UncertaintyEnum.h:244
jet::UncertaintyHistogram::checkBoundariesByBin
double checkBoundariesByBin(const TAxis *axis, const int numBins, const double valInput) const
Definition: UncertaintyHistogram.cxx:270
plotBeamSpotCompare.histo
histo
Definition: plotBeamSpotCompare.py:415
jet::UncertaintyHistogram::~UncertaintyHistogram
virtual ~UncertaintyHistogram()
Definition: UncertaintyHistogram.cxx:79
jet::UncertaintyHistogram::m_histo
TH1 * m_histo
Definition: UncertaintyHistogram.h:51