Belle II Software  release-08-01-10
PXDAnalyticGainCalibrationAlgorithm.cc
1 /**************************************************************************
2  * basf2 (Belle II Analysis Software Framework) *
3  * Author: The Belle II Collaboration *
4  * *
5  * See git log for contributors and copyright holders. *
6  * This file is licensed under LGPL-3.0, see LICENSE.md. *
7  **************************************************************************/
8 
9 #include <pxd/calibration/PXDAnalyticGainCalibrationAlgorithm.h>
10 #include <pxd/calibration/PXDCalibrationUtilities.h>
11 #include <pxd/dbobjects/PXDGainMapPar.h>
12 
13 #include <boost/format.hpp>
14 
15 //ROOT
16 #include <TH2F.h>
17 
18 using namespace std;
19 using boost::format;
20 using namespace Belle2;
21 using namespace Belle2::PXD;
22 
23 
24 // Anonymous namespace for data objects used by PXDAnalyticGainCalibrationAlgorithm class
25 namespace {
26 
28  int m_signal;
30  float m_estimated;
32  int m_run;
34  int m_exp;
35 }
36 
37 
38 PXDAnalyticGainCalibrationAlgorithm::PXDAnalyticGainCalibrationAlgorithm():
39  CalibrationAlgorithm("PXDPerformanceCollector"),
40  minClusters(1000), safetyFactor(2.0), forceContinue(false), strategy(0), useChargeRatioHistogram(true), correctForward(false)
41 {
43  " -------------------------- PXDAnalyticGainCalibrationAlgorithm ---------------------------------\n"
44  " \n"
45  " Algorithm for estimating pxd gains (conversion factor from charge to ADU) \n"
46  " ----------------------------------------------------------------------------------------\n"
47  );
48 }
49 
50 
52 {
53 
54  // Get counter histograms
55  auto cluster_counter = getObjectPtr<TH1I>("PXDClusterCounter");
56 
57  // Check if there is any PXD cluster
58  if (cluster_counter == nullptr) {
59  B2WARNING("No PXD cluster reconstructed!");
60  if (not forceContinue)
61  return c_NotEnoughData;
62  else {
63  B2WARNING("Skip processing.");
64  return c_OK;
65  }
66  }
67 
68  // Extract number of sensors from counter histograms
69  auto nSensors = getNumberOfSensors(cluster_counter);
70 
71  // Extract the number of grid bins from counter histograms
72  unsigned short nBinsU = 0;
73  unsigned short nBinsV = 0;
74  getNumberOfBins(cluster_counter, nBinsU, nBinsV);
75 
76  // Check that we have collected enough Data
77  if (cluster_counter->GetEntries() < int(safetyFactor * minClusters * nSensors * nBinsU * nBinsV)) {
78  if (not forceContinue) {
79  B2WARNING("Not enough Data: Only " << cluster_counter->GetEntries() << " hits were collected but " << int(
81  nSensors * nBinsU * nBinsV) << " needed!");
82  return c_NotEnoughData;
83  } else {
84  B2WARNING("Continue despite low statistics: Only " << cluster_counter->GetEntries() << " hits were collected but" << int(
86  nSensors * nBinsU * nBinsV) << " would be desirable!");
87  }
88  }
89 
90  B2INFO("Start calibration using a " << nBinsU << "x" << nBinsV << " grid per sensor.");
91  B2INFO("Number of collected clusters is " << cluster_counter->GetEntries());
92 
93  // This is the PXD gain correction payload for conditions DB
94  PXDGainMapPar* gainMapPar = new PXDGainMapPar(nBinsU, nBinsV);
95  set<VxdID> pxdSensors;
96 
97  // Loop over all bins of input histo
98  for (auto histoBin = 1; histoBin <= cluster_counter->GetXaxis()->GetNbins(); histoBin++) {
99  // The bin label contains the vxdid, uBin and vBin
100  string label = cluster_counter->GetXaxis()->GetBinLabel(histoBin);
101 
102  // Parse label string format to read sensorID, uBin and vBin
103  istringstream stream(label);
104  string token;
105  getline(stream, token, '_');
106  VxdID sensorID(token);
107 
108  getline(stream, token, '_');
109  unsigned short uBin = std::stoi(token);
110 
111  getline(stream, token, '_');
112  unsigned short vBin = std::stoi(token);
113 
114  // Read back the counters for number of collected clusters
115  int numberOfHits = cluster_counter->GetBinContent(histoBin);
116 
117  // Only perform estimation, when enough data is available
118  if (numberOfHits >= minClusters) {
119 
120  double gain = 1.0;
121  // Estimate the gain on a certain part of PXD
123  auto hClusterChargeRatio = getObjectPtr<TH2F>("PXDClusterChargeRatio");
124  TH1D* hRatios = hClusterChargeRatio->ProjectionY("proj", histoBin, histoBin);
125  gain = EstimateGain(sensorID, uBin, vBin, hRatios);
126  } else {
127  gain = EstimateGain(sensorID, uBin, vBin);
128  }
129  // Store the gain
130  gainMapPar->setContent(sensorID.getID(), uBin, vBin, gain);
131  } else {
132  B2WARNING(label << ": Number of hits is too small (" << numberOfHits << " < " << minClusters <<
133  "). Use default gain.");
134  gainMapPar->setContent(sensorID.getID(), uBin, vBin, 1.0);
135  }
136  pxdSensors.insert(sensorID);
137  }
138 
139  // Post processing of gain map. It is possible that the gain
140  // computation failed on some parts. Here, we replace default
141  // values (1.0) by local averages of neighboring sensor parts.
142 
143  for (const auto& sensorID : pxdSensors) {
144 
145  // Special treatement for the last 2 vBin as Bhabha 2-track events
146  // have no enough statistics there if nBinsV = 6
147  if (correctForward && sensorID.getSensorNumber() == 1)
148  for (unsigned short uBin = 0; uBin < nBinsU; ++uBin) {
149  // Search for a vaid gain along v
150  double gainForwardRegion = 1.0;
151  unsigned short vBinToCheck = nBinsV - 1;
152  for (unsigned short vBinGood = nBinsV - 2; vBinGood >= 1; --vBinGood) {
153  auto temp = gainMapPar->getContent(sensorID.getID(), uBin, vBinGood);
154  if (temp != 1.0) {
155  gainForwardRegion = temp;
156  vBinToCheck = vBinGood + 1;
157  break;
158  }
159  }
160  // loop part of the forward regions and check values
161  if (gainForwardRegion != 1.0)
162  for (unsigned short vBin = nBinsV - 1; vBin >= vBinToCheck; --vBin) {
163  auto gain = gainMapPar->getContent(sensorID.getID(), uBin, vBin);
164  if (gain == 1.0) {
165  gainMapPar->setContent(sensorID.getID(), uBin, vBin, gainForwardRegion);
166  B2RESULT("Gain calibration on sensor=" << sensorID << ", vBin=" << vBin << " uBin " << uBin <<
167  ": Replace default gain with that from the closest vBin with non default value "
168  << gainForwardRegion);
169  }
170  }
171  }
172 
173  // general value check
174  for (unsigned short vBin = 0; vBin < nBinsV; ++vBin) {
175 
176  float meanGain = 0;
177  unsigned short nGood = 0;
178  unsigned short nBad = 0;
179  for (unsigned short uBin = 0; uBin < nBinsU; ++uBin) {
180  auto gain = gainMapPar->getContent(sensorID.getID(), uBin, vBin);
181  // Filter default gains
182  if (gain != 1.0) {
183  nGood += 1;
184  meanGain += gain;
185  } else {
186  nBad += 1;
187  }
188  }
189  B2RESULT("Gain calibration on sensor=" << sensorID << " and vBin=" << vBin << " was successful on " << nGood << "/" << nBinsU <<
190  " uBins.");
191 
192  // Check if we can repair bad calibrations with a local avarage
193  if (nGood > 0 && nBad > 0) {
194  meanGain /= nGood;
195  for (unsigned short uBin = 0; uBin < nBinsU; ++uBin) {
196  auto gain = gainMapPar->getContent(sensorID.getID(), uBin, vBin);
197  if (gain == 1.0) {
198  gainMapPar->setContent(sensorID.getID(), uBin, vBin, meanGain);
199  B2RESULT("Gain calibration on sensor=" << sensorID << ", vBin=" << vBin << " uBin " << uBin <<
200  ": Replace default gain with average "
201  << meanGain << " of uBins with non-default gains.");
202  }
203  }
204  }
205  }
206  }
207 
208 
209  // Save the gain map to database. Note that this will set the database object name to the same as the collector but you
210  // are free to change it.
211  saveCalibration(gainMapPar, "PXDGainMapPar");
212 
213  B2INFO("PXD Gain Calibration Successful");
214  return c_OK;
215 }
216 
217 
218 double PXDAnalyticGainCalibrationAlgorithm::EstimateGain(VxdID sensorID, unsigned short uBin, unsigned short vBin, TH1* hist)
219 {
220  double gain = 1.0; // default gain
221 
222  // function pointers for different strategy
223  double (*estimateGainFromHist)(TH1*) = &CalculateMedian;
224  double (*estimateGainFromVec)(std::vector<double>&) = &CalculateMedian;
225  if (strategy == 0) {
226  //estimateGainFromVec = &CalculateMedian;
227  //estimateGainFromHist = &CalculateMedian;
228  } else if (strategy == 1) {
229  estimateGainFromVec = &FitLandau;
230  estimateGainFromHist = &FitLandau;
231  } else {
232  B2FATAL("strategy unavailable, use 0 for medians or 1 for landau fit!");
233  }
234 
235  // Do estimation
236  if (hist) { // estimate gain from existing histogram
237  gain = estimateGainFromHist(hist);
238  } else { // estimate from TTree
239  // Construct a tree name for requested part of PXD
240  auto layerNumber = sensorID.getLayerNumber();
241  auto ladderNumber = sensorID.getLadderNumber();
242  auto sensorNumber = sensorID.getSensorNumber();
243  const string treename = str(format("tree_%1%_%2%_%3%_%4%_%5%") % layerNumber % ladderNumber % sensorNumber % uBin % vBin);
244  // Vector with ratios (cluster charge to its estimation)
245  vector<double> ratios;
246 
247  auto tree = getObjectPtr<TTree>(treename);
248  tree->SetBranchAddress("signal", &m_signal);
249  tree->SetBranchAddress("estimated", &m_estimated);
250 
251  // Loop over tree
252  const auto nEntries_MC = tree->GetEntries();
253  for (int i = 0; i < nEntries_MC; ++i) {
254  tree->GetEntry(i);
255  ratios.push_back(double(m_signal) / m_estimated);
256  }
257  gain = estimateGainFromVec(ratios);
258  }
259 
260  // check if gain makes sense
261  if (gain <= 0.0) {
262  B2WARNING("Retrieved negative median/MPV for sensor=" << sensorID << " uBin=" << uBin << " vBin=" << vBin <<
263  ". Set gain to default value (=1.0) as well.");
264  return 1.0;
265  }
266 
267  // calculate and return the absolute gain
268  double gainFromDB = GetCurrentGainFromDB(sensorID, uBin, vBin);
269  B2DEBUG(10, "Gain from db used in PXDDigitizer is " << gainFromDB);
270  B2DEBUG(10, "New gain correction derived is " << gain);
271  B2DEBUG(10, "The total gain we should return is " << gain * gainFromDB);
272 
273  return gain * gainFromDB;
274 }
275 
276 double PXDAnalyticGainCalibrationAlgorithm::GetCurrentGainFromDB(VxdID sensorID, unsigned short uBin, unsigned short vBin)
277 {
278  // Read back db payloads
279  PXDGainMapPar* gainMapPtr = nullptr;
280 
281  auto dbtree = getObjectPtr<TTree>("dbtree");
282  dbtree->SetBranchAddress("run", &m_run);
283  dbtree->SetBranchAddress("exp", &m_exp);
284  dbtree->SetBranchAddress("gainMap", &gainMapPtr);
285 
286  // Compute running average of gains from db
287  double sum = 0;
288  int counter = 0;
289 
290  // Loop over dbtree
291  const auto nEntries = dbtree->GetEntries();
292  for (int i = 0; i < nEntries; ++i) {
293  dbtree->GetEntry(i);
294  sum += gainMapPtr->getContent(sensorID.getID(), uBin, vBin);
295  counter += 1;
296  }
297  delete gainMapPtr;
298  gainMapPtr = nullptr;
299 
300  return sum / counter;
301 }
302 
303 bool PXDAnalyticGainCalibrationAlgorithm::isBoundaryRequired(const Calibration::ExpRun& /*currentRun*/)
304 {
305  // First run in data as we iterate, but our boundaries weren't set manually already?
306  // Just set the first run to be a boundary and we are done.
307  if (m_boundaries.empty()) {
308  B2INFO("This is the first run encountered, let's say it is a boundary.");
309  return true;
310  } else {
311  return false;
312  }
313 }
Base class for calibration algorithms.
void saveCalibration(TClonesArray *data, const std::string &name)
Store DBArray payload with given name with default IOV.
std::vector< Calibration::ExpRun > m_boundaries
When using the boundaries functionality from isBoundaryRequired, this is used to store the boundaries...
void setDescription(const std::string &description)
Set algorithm description (in constructor)
EResult
The result of calibration.
@ c_OK
Finished successfuly =0 in Python.
@ c_NotEnoughData
Needs more data =2 in Python.
double GetCurrentGainFromDB(VxdID sensorID, unsigned short uBin, unsigned short vBin)
Retrive current gain value from pulled in data base payload.
bool correctForward
Flag to update default gains in forward region due to low statistics.
int minClusters
Minimum number of collected clusters for estimating gains.
double EstimateGain(VxdID sensorID, unsigned short uBin, unsigned short vBin, TH1 *hist=nullptr)
Estimate gain as ratio of medians from MC and data for a part of PXD.
virtual EResult calibrate() override
Run algo on data.
int strategy
strategy to used for gain calibration, 0 for medians, 1 for landau fit
float safetyFactor
Safety factor for determining whether the collected number of clusters is enough.
bool useChargeRatioHistogram
Flag to use histogram of charge ratio (relative to expected MPV)
bool forceContinue
Force continue in low statistics runs instead of returning c_NotEnoughData.
virtual bool isBoundaryRequired(const Calibration::ExpRun &) override
Decide if a run should be a payload boundary. Only used in certain Python Algorithm Starategies.
The payload class for PXD gain corrections.
Definition: PXDGainMapPar.h:43
float getContent(unsigned short sensorID, unsigned short globalID) const
Get content.
void setContent(unsigned short sensorID, unsigned short globalID, float value)
Set map content.
Definition: PXDGainMapPar.h:68
Class to uniquely identify a any structure of the PXD and SVD.
Definition: VxdID.h:33
baseType getID() const
Get the unique id.
Definition: VxdID.h:94
baseType getSensorNumber() const
Get the sensor id.
Definition: VxdID.h:100
baseType getLadderNumber() const
Get the ladder id.
Definition: VxdID.h:98
baseType getLayerNumber() const
Get the layer id.
Definition: VxdID.h:96
Namespace to encapsulate code needed for simulation and reconstrucion of the PXD.
unsigned short getNumberOfSensors(const std::shared_ptr< TH1I > &histo_ptr)
Helper function to extract number of sensors from counter histogram labels.
void getNumberOfBins(const std::shared_ptr< TH1I > &histo_ptr, unsigned short &nBinsU, unsigned short &nBinsV)
Helper function to extract number of bins along u side and v side from counter histogram labels.
double CalculateMedian(std::vector< double > &signals)
Helper function to calculate a median from unsorted signal vector.
double FitLandau(TH1 *hist)
Helper function to estimate MPV from 1D histogram.
Abstract base class for different kinds of events.