Belle II Software  release-08-01-10
PXDGainCalibrationAlgorithm.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/PXDGainCalibrationAlgorithm.h>
10 #include <pxd/dbobjects/PXDGainMapPar.h>
11 #include <pxd/dbobjects/PXDClusterChargeMapPar.h>
12 
13 #include <string>
14 #include <algorithm>
15 #include <set>
16 
17 #include <sstream>
18 #include <iostream>
19 
20 #include <boost/format.hpp>
21 
22 //ROOT
23 #include <TRandom.h>
24 #include <TH1.h>
25 #include <TF1.h>
26 
27 using namespace std;
28 using boost::format;
29 using namespace Belle2;
30 
31 
32 // Anonymous namespace for data objects used by PXDGainCalibrationAlgorithm class
33 namespace {
34 
36  int m_signal;
38  int m_run;
40  int m_exp;
41 
43  void getNumberOfBins(const std::shared_ptr<TH1I>& histo_ptr, unsigned short& nBinsU, unsigned short& nBinsV)
44  {
45  set<unsigned short> uBinSet;
46  set<unsigned short> vBinSet;
47 
48  // Loop over all bins of input histo
49  for (auto histoBin = 1; histoBin <= histo_ptr->GetXaxis()->GetNbins(); histoBin++) {
50  // The bin label contains the vxdid, uBin and vBin
51  string label = histo_ptr->GetXaxis()->GetBinLabel(histoBin);
52 
53  // Parse label string format to read sensorID, uBin and vBin
54  istringstream stream(label);
55  string token;
56  getline(stream, token, '_');
57  getline(stream, token, '_');
58  unsigned short uBin = std::stoi(token);
59 
60  getline(stream, token, '_');
61  unsigned short vBin = std::stoi(token);
62 
63  uBinSet.insert(uBin);
64  vBinSet.insert(vBin);
65  }
66 
67  if (uBinSet.empty() || vBinSet.empty()) {
68  B2FATAL("Not able to determine the grid size. Something is wrong with collected data.");
69  } else {
70  nBinsU = *uBinSet.rbegin() + 1;
71  nBinsV = *vBinSet.rbegin() + 1;
72  }
73  }
74 
76  unsigned short getNumberOfSensors(const std::shared_ptr<TH1I>& histo_ptr)
77  {
78  set<unsigned short> sensorSet;
79 
80  // Loop over all bins of input histo
81  for (auto histoBin = 1; histoBin <= histo_ptr->GetXaxis()->GetNbins(); histoBin++) {
82  // The bin label contains the vxdid, uBin and vBin
83  string label = histo_ptr->GetXaxis()->GetBinLabel(histoBin);
84 
85  // Parse label string format to read sensorID, uBin and vBin
86  istringstream stream(label);
87  string token;
88  getline(stream, token, '_');
89  VxdID sensorID(token);
90  sensorSet.insert(sensorID.getID());
91  }
92  return sensorSet.size();
93  }
94 
95 }
96 
97 
98 PXDGainCalibrationAlgorithm::PXDGainCalibrationAlgorithm():
99  CalibrationAlgorithm("PXDClusterChargeCollector"),
100 
101  minClusters(1000), noiseSigma(0.6), safetyFactor(2.0), forceContinue(false), strategy(0)
102 {
104  " -------------------------- PXDGainCalibrationAlgorithm ---------------------------------\n"
105  " \n"
106  " Algorithm for estimating pxd gains (conversion factor from charge to ADU) \n"
107  " ----------------------------------------------------------------------------------------\n"
108  );
109 }
110 
111 
112 
113 
115 {
116 
117  // Get counter histograms
118  auto cluster_counter = getObjectPtr<TH1I>("PXDClusterCounter");
119 
120  // Extract number of sensors from counter histograms
121  auto nSensors = getNumberOfSensors(cluster_counter);
122 
123  // Extract the number of grid bins from counter histograms
124  unsigned short nBinsU = 0;
125  unsigned short nBinsV = 0;
126  getNumberOfBins(cluster_counter, nBinsU, nBinsV);
127 
128  // Check that we have collected enough Data
129  if (cluster_counter->GetEntries() < int(safetyFactor * minClusters * nSensors * nBinsU * nBinsV)) {
130  if (not forceContinue) {
131  B2WARNING("Not enough Data: Only " << cluster_counter->GetEntries() << " hits were collected but " << int(
133  nSensors * nBinsU * nBinsV) << " needed!");
134  return c_NotEnoughData;
135  } else {
136  B2WARNING("Continue despite low statistics: Only " << cluster_counter->GetEntries() << " hits were collected but" << int(
138  nSensors * nBinsU * nBinsV) << " would be desirable!");
139  }
140  }
141 
142  B2INFO("Start calibration using a " << nBinsU << "x" << nBinsV << " grid per sensor.");
143  B2INFO("Number of collected clusters is " << cluster_counter->GetEntries());
144 
145  // This is the PXD gain correction payload for conditions DB
146  PXDGainMapPar* gainMapPar = new PXDGainMapPar(nBinsU, nBinsV);
147  set<VxdID> pxdSensors;
148 
149  // Loop over all bins of input histo
150  for (auto histoBin = 1; histoBin <= cluster_counter->GetXaxis()->GetNbins(); histoBin++) {
151  // The bin label contains the vxdid, uBin and vBin
152  string label = cluster_counter->GetXaxis()->GetBinLabel(histoBin);
153 
154  // Parse label string format to read sensorID, uBin and vBin
155  istringstream stream(label);
156  string token;
157  getline(stream, token, '_');
158  VxdID sensorID(token);
159 
160  getline(stream, token, '_');
161  unsigned short uBin = std::stoi(token);
162 
163  getline(stream, token, '_');
164  unsigned short vBin = std::stoi(token);
165 
166  // Read back the counters for number of collected clusters
167  int numberOfHits = cluster_counter->GetBinContent(histoBin);
168 
169  // Only perform fitting, when enough data is available
170  if (numberOfHits >= minClusters) {
171 
172  // Estimate the gain on a certain part of PXD
173  auto gain = EstimateGain(sensorID, uBin, vBin);
174 
175  // Store the gain
176  gainMapPar->setContent(sensorID.getID(), uBin, vBin, gain);
177  } else {
178  B2WARNING(label << ": Number of mc hits too small for fitting (" << numberOfHits << " < " << minClusters <<
179  "). Use default gain.");
180  gainMapPar->setContent(sensorID.getID(), uBin, vBin, 1.0);
181  }
182  pxdSensors.insert(sensorID);
183  }
184 
185  // Post processing of gain map. It is possible that the gain
186  // computation failed on some parts. Here, we replace default
187  // values (1.0) by local averages of neighboring sensor parts.
188 
189  for (const auto& sensorID : pxdSensors) {
190  for (unsigned short vBin = 0; vBin < nBinsV; ++vBin) {
191  float meanGain = 0;
192  unsigned short nGood = 0;
193  unsigned short nBad = 0;
194  for (unsigned short uBin = 0; uBin < nBinsU; ++uBin) {
195  auto gain = gainMapPar->getContent(sensorID.getID(), uBin, vBin);
196  // Filter default gains
197  if (gain != 1.0) {
198  nGood += 1;
199  meanGain += gain;
200  } else {
201  nBad += 1;
202  }
203  }
204  B2RESULT("Gain calibration on sensor=" << sensorID << " and vBin=" << vBin << " was successful on " << nGood << "/" << nBinsU <<
205  " uBins.");
206 
207  // Check if we can repair bad calibrations with a local avarage
208  if (nGood > 0 && nBad > 0) {
209  meanGain /= nGood;
210  for (unsigned short uBin = 0; uBin < nBinsU; ++uBin) {
211  auto gain = gainMapPar->getContent(sensorID.getID(), uBin, vBin);
212  if (gain == 1.0) {
213  gainMapPar->setContent(sensorID.getID(), uBin, vBin, meanGain);
214  B2RESULT("Gain calibration on sensor=" << sensorID << ", vBin=" << vBin << " uBin " << uBin << ": Replace default gain wih average "
215  << meanGain);
216  }
217  }
218  }
219  }
220  }
221 
222 
223  // Save the gain map to database. Note that this will set the database object name to the same as the collector but you
224  // are free to change it.
225  saveCalibration(gainMapPar, "PXDGainMapPar");
226 
227  B2INFO("PXD Gain Calibration Successful");
228  return c_OK;
229 }
230 
231 
232 double PXDGainCalibrationAlgorithm::EstimateGain(VxdID sensorID, unsigned short uBin, unsigned short vBin)
233 {
234  // Construct a tree name for requested part of PXD
235  auto layerNumber = sensorID.getLayerNumber();
236  auto ladderNumber = sensorID.getLadderNumber();
237  auto sensorNumber = sensorID.getSensorNumber();
238  const string treename = str(format("tree_%1%_%2%_%3%_%4%_%5%") % layerNumber % ladderNumber % sensorNumber % uBin % vBin);
239  // Vector with cluster signals from collected mc
240  vector<double> mc_signals;
241 
242  auto tree_MC = getObjectPtr<TTree>(treename);
243  tree_MC->SetBranchAddress("signal", &m_signal);
244 
245  // Loop over tree_MC
246  const auto nEntries_MC = tree_MC->GetEntries();
247  for (int i = 0; i < nEntries_MC; ++i) {
248  tree_MC->GetEntry(i);
249 
250  double noise = gRandom->Gaus(0.0, noiseSigma);
251  mc_signals.push_back(m_signal + noise);
252  }
253 
254  double dataMedian = GetChargeMedianFromDB(sensorID, uBin, vBin);
255  double mcMedian = -1;
256  // check if dataMedian makes sense
257  if (dataMedian <= 0.0) {
258  B2WARNING("Retrieved negative charge median from DB for sensor=" << sensorID << " uBin=" << uBin << " vBin=" << vBin <<
259  ". Set gain to default value (=1.0) as well.");
260  return 1.0;
261  }
262  if (strategy == 0) mcMedian = CalculateMedian(mc_signals);
263  else if (strategy == 1) mcMedian = FitLandau(mc_signals);
264  else {
265  B2FATAL("strategy unavailable, use 0 for medians or 1 for landau fit!");
266  }
267 
268  // check if mcMedian makes sense
269  if (mcMedian <= 0.0) {
270  B2WARNING("Retrieved negative charge median from DB for sensor=" << sensorID << " uBin=" << uBin << " vBin=" << vBin <<
271  ". Set gain to default value (=1.0) as well.");
272  return 1.0;
273  }
274 
275 
276  double gain = dataMedian / mcMedian;
277  double gainFromDB = GetCurrentGainFromDB(sensorID, uBin, vBin);
278  B2DEBUG(10, "Gain from db used in PXDDigitizer is " << gainFromDB);
279  B2DEBUG(10, "New gain correction derived is " << gain);
280  B2DEBUG(10, "The total gain we should return is " << gain * gainFromDB);
281 
282  return gain * gainFromDB;
283 }
284 
285 double PXDGainCalibrationAlgorithm::GetChargeMedianFromDB(VxdID sensorID, unsigned short uBin, unsigned short vBin)
286 {
287  // Read back db payloads
288  PXDClusterChargeMapPar* chargeMapPtr = 0;
289  PXDGainMapPar* gainMapPtr = 0;
290 
291  auto dbtree = getObjectPtr<TTree>("dbtree");
292  dbtree->SetBranchAddress("run", &m_run);
293  dbtree->SetBranchAddress("exp", &m_exp);
294  dbtree->SetBranchAddress("chargeMap", &chargeMapPtr);
295  dbtree->SetBranchAddress("gainMap", &gainMapPtr);
296 
297  // Compute running average of charge medians from db
298  double sum = 0;
299  int counter = 0;
300 
301  // Loop over dbtree
302  const auto nEntries = dbtree->GetEntries();
303  for (int i = 0; i < nEntries; ++i) {
304  dbtree->GetEntry(i);
305  sum += chargeMapPtr->getContent(sensorID.getID(), uBin, vBin);
306  counter += 1;
307  }
308  delete chargeMapPtr;
309  chargeMapPtr = 0;
310  delete gainMapPtr;
311  gainMapPtr = 0;
312 
313  return sum / counter;
314 }
315 
316 double PXDGainCalibrationAlgorithm::GetCurrentGainFromDB(VxdID sensorID, unsigned short uBin, unsigned short vBin)
317 {
318  // Read back db payloads
319  PXDClusterChargeMapPar* chargeMapPtr = 0;
320  PXDGainMapPar* gainMapPtr = 0;
321 
322  auto dbtree = getObjectPtr<TTree>("dbtree");
323  dbtree->SetBranchAddress("run", &m_run);
324  dbtree->SetBranchAddress("exp", &m_exp);
325  dbtree->SetBranchAddress("chargeMap", &chargeMapPtr);
326  dbtree->SetBranchAddress("gainMap", &gainMapPtr);
327 
328  // Compute running average of gains from db
329  double sum = 0;
330  int counter = 0;
331 
332  // Loop over dbtree
333  const auto nEntries = dbtree->GetEntries();
334  for (int i = 0; i < nEntries; ++i) {
335  dbtree->GetEntry(i);
336  sum += gainMapPtr->getContent(sensorID.getID(), uBin, vBin);
337  counter += 1;
338  }
339  delete chargeMapPtr;
340  chargeMapPtr = 0;
341  delete gainMapPtr;
342  gainMapPtr = 0;
343 
344  return sum / counter;
345 }
346 
347 
348 double PXDGainCalibrationAlgorithm::CalculateMedian(vector<double>& signals)
349 {
350  auto size = signals.size();
351 
352  if (size == 0) {
353  return 0.0; // Undefined, really.
354  } else {
355  sort(signals.begin(), signals.end());
356  if (size % 2 == 0) {
357  return (signals[size / 2 - 1] + signals[size / 2]) / 2;
358  } else {
359  return signals[size / 2];
360  }
361  }
362 }
363 
364 double PXDGainCalibrationAlgorithm::FitLandau(vector<double>& signals)
365 {
366  auto size = signals.size();
367  if (size == 0) return 0.0; // Undefined, really.
368 
369  // get max and min values of signal vector
370  int max = *max_element(signals.begin(), signals.end());
371  int min = *min_element(signals.begin(), signals.end());
372 
373  // create histogram to hold signals and fill it
374  TH1D* hist_signals = new TH1D("", "", max - min, min, max);
375  for (auto it = signals.begin(); it != signals.end(); ++it) {
376  hist_signals->Fill(*it);
377  }
378 
379  // create fit function
380  TF1* landau = new TF1("landau", "TMath::Landau(x,[0],[1])*[2]", min, max);
381  landau->SetParNames("MPV", "sigma", "scale");
382  landau->SetParameters(100, 1, 1000);
383 
384  // do fit and get results
385  Int_t status = hist_signals->Fit("landau", "Lq", "", 0, 350);
386  double MPV = landau->GetParameter("MPV");
387 
388  // clean up
389  delete hist_signals;
390  delete landau;
391 
392  // check fit status
393  if (status == 0) return MPV;
394  else {
395  B2WARNING("Fit failed!. using default value.");
396  return 0.0;
397  }
398 }
Base class for calibration algorithms.
void saveCalibration(TClonesArray *data, const std::string &name)
Store DBArray payload with given name with default IOV.
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.
The payload class for PXD cluster charge calibrations.
float getContent(unsigned short sensorID, unsigned short globalID) const
Get content.
double EstimateGain(VxdID sensorID, unsigned short uBin, unsigned short vBin)
Estimate gain as ratio of medians from MC and data for a part of PXD.
double FitLandau(std::vector< double > &signals)
Calculate MPV from signal vector using a landau fit.
double GetCurrentGainFromDB(VxdID sensorID, unsigned short uBin, unsigned short vBin)
Retrive current gain value from pulled in data base payload.
int minClusters
Minimum number of collected clusters for estimating gains.
float noiseSigma
Artificial noise sigma for smearing cluster charge.
virtual EResult calibrate() override
Run algo on data.
int strategy
strategy to used for gain calibration, 0 for medians, 1 for landau fit
double GetChargeMedianFromDB(VxdID sensorID, unsigned short uBin, unsigned short vBin)
Retrive charge median value from pulled in data base payload.
float safetyFactor
Safety factor for determining whether the collected number of clusters is enough.
double CalculateMedian(std::vector< double > &signals)
Calculate a median from unsorted signal vector. The input vector gets sorted.
bool forceContinue
Force continue in low statistics runs instead of returning c_NotEnoughData.
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
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.
Abstract base class for different kinds of events.