Belle II Software  release-08-01-10
PXDChargeCalibrationAlgorithm.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/PXDChargeCalibrationAlgorithm.h>
10 #include <pxd/dbobjects/PXDClusterChargeMapPar.h>
11 
12 #include <string>
13 #include <algorithm>
14 #include <set>
15 
16 #include <sstream>
17 #include <iostream>
18 
19 #include <boost/format.hpp>
20 
21 //ROOT
22 #include <TRandom.h>
23 #include <TH1I.h>
24 #include <TF1.h>
25 
26 
27 using namespace std;
28 using boost::format;
29 using namespace Belle2;
30 
31 
32 // Anonymous namespace for data objects used by PXDChargeCalibrationAlgorithm class
33 namespace {
34 
36  int m_signal;
37 
39  void getNumberOfBins(const std::shared_ptr<TH1I>& histo_ptr, unsigned short& nBinsU, unsigned short& nBinsV)
40  {
41  set<unsigned short> uBinSet;
42  set<unsigned short> vBinSet;
43 
44  // Loop over all bins of input histo
45  for (auto histoBin = 1; histoBin <= histo_ptr->GetXaxis()->GetNbins(); histoBin++) {
46  // The bin label contains the vxdid, uBin and vBin
47  string label = histo_ptr->GetXaxis()->GetBinLabel(histoBin);
48 
49  // Parse label string format to read sensorID, uBin and vBin
50  istringstream stream(label);
51  string token;
52  getline(stream, token, '_');
53  getline(stream, token, '_');
54  unsigned short uBin = std::stoi(token);
55 
56  getline(stream, token, '_');
57  unsigned short vBin = std::stoi(token);
58 
59  uBinSet.insert(uBin);
60  vBinSet.insert(vBin);
61  }
62 
63  if (uBinSet.empty() || vBinSet.empty()) {
64  B2FATAL("Not able to determine the grid size. Something is wrong with collected data.");
65  } else {
66  nBinsU = *uBinSet.rbegin() + 1;
67  nBinsV = *vBinSet.rbegin() + 1;
68  }
69  }
70 
72  unsigned short getNumberOfSensors(const std::shared_ptr<TH1I>& histo_ptr)
73  {
74  set<unsigned short> sensorSet;
75 
76  // Loop over all bins of input histo
77  for (auto histoBin = 1; histoBin <= histo_ptr->GetXaxis()->GetNbins(); histoBin++) {
78  // The bin label contains the vxdid, uBin and vBin
79  string label = histo_ptr->GetXaxis()->GetBinLabel(histoBin);
80 
81  // Parse label string format to read sensorID, uBin and vBin
82  istringstream stream(label);
83  string token;
84  getline(stream, token, '_');
85  VxdID sensorID(token);
86  sensorSet.insert(sensorID.getID());
87  }
88 
89  return sensorSet.size();
90  }
91 
92 }
93 
94 
95 PXDChargeCalibrationAlgorithm::PXDChargeCalibrationAlgorithm():
96  CalibrationAlgorithm("PXDClusterChargeCollector"),
97  minClusters(5000), noiseSigma(0.6), safetyFactor(2.0), forceContinue(false), strategy(0)
98 {
100  " ------------------------------ PXDChargeCalibrationAlgorithm -----------------------------------\n"
101  " \n"
102  " Algorithm for estimating pxd median/MPV cluster charges for different position on sensor in ADU \n"
103  " ------------------------------------------------------------------------------------------------\n"
104  );
105 }
106 
107 
108 
109 
111 {
112 
113  // Get counter histogram
114  auto cluster_counter = getObjectPtr<TH1I>("PXDClusterCounter");
115 
116  // Extract number of sensors from counter histograms
117  auto nSensors = getNumberOfSensors(cluster_counter);
118 
119  // Extract the number of grid bins from counter histograms
120  unsigned short nBinsU = 0;
121  unsigned short nBinsV = 0;
122  getNumberOfBins(cluster_counter, nBinsU, nBinsV);
123 
124  // Check that we have collected enough Data
125  if (cluster_counter->GetEntries() < int(safetyFactor * minClusters * nSensors * nBinsU * nBinsV)) {
126  if (not forceContinue) {
127  B2INFO("Not enough Data: Only " << cluster_counter->GetEntries() << " hits were collected but " << int(safetyFactor * minClusters*
128  nSensors * nBinsU * nBinsV) << " needed!");
129  return c_NotEnoughData;
130  } else {
131  B2INFO("Continue despite low statistics: Only " << cluster_counter->GetEntries() << " hits were collected but" << int(
133  nSensors * nBinsU * nBinsV) << " would be desirable!");
134  }
135  }
136 
137  B2INFO("Start calibration using a " << nBinsU << "x" << nBinsV << " grid per sensor.");
138 
139  // This is the PXD charge calibration payload for conditions DB
140  PXDClusterChargeMapPar* chargeMapPar = new PXDClusterChargeMapPar(nBinsU, nBinsV);
141 
142  // Loop over all bins of input histo
143  for (auto histoBin = 1; histoBin <= cluster_counter->GetXaxis()->GetNbins(); histoBin++) {
144  // The bin label contains the vxdid, uBin and vBin
145  string label = cluster_counter->GetXaxis()->GetBinLabel(histoBin);
146 
147  // Parse label string format to read sensorID, uBin and vBin
148  istringstream stream(label);
149  string token;
150  getline(stream, token, '_');
151  VxdID sensorID(token);
152 
153  getline(stream, token, '_');
154  unsigned short uBin = std::stoi(token);
155 
156  getline(stream, token, '_');
157  unsigned short vBin = std::stoi(token);
158 
159  // Read back the counters for number of collected clusters
160  int numberOfDataHits = cluster_counter->GetBinContent(histoBin);
161 
162  // Only perform fitting, when enough data is available
163  if (numberOfDataHits >= minClusters) {
164 
165  // Compute the median cluster charge for the part of PXD
166  auto Charge = EstimateCharge(sensorID, uBin, vBin);
167 
168  // Store the charge
169  chargeMapPar->setContent(sensorID.getID(), uBin, vBin, Charge);
170  } else {
171  B2WARNING(label << ": Number of data hits too small for fitting (" << numberOfDataHits << " < " << minClusters <<
172  "). Use default value of 0.");
173  chargeMapPar->setContent(sensorID.getID(), uBin, vBin, 0.0);
174  }
175  }
176 
177  // Save the charge map to database. Note that this will set the database object name to the same as the collector but you
178  // are free to change it.
179  saveCalibration(chargeMapPar, "PXDClusterChargeMapPar");
180 
181  B2INFO("PXD Cluster Charge Calibration Successful");
182  return c_OK;
183 }
184 
185 
186 double PXDChargeCalibrationAlgorithm::EstimateCharge(VxdID sensorID, unsigned short uBin, unsigned short vBin)
187 {
188 
189  // Construct a tree name for requested part of PXD
190  auto layerNumber = sensorID.getLayerNumber();
191  auto ladderNumber = sensorID.getLadderNumber();
192  auto sensorNumber = sensorID.getSensorNumber();
193  const string treename = str(format("tree_%1%_%2%_%3%_%4%_%5%") % layerNumber % ladderNumber % sensorNumber % uBin % vBin);
194  // Vector with cluster signals from collected data
195  vector<double> signals;
196 
197  // Fill data_signal vector from input data
198  auto tree = getObjectPtr<TTree>(treename);
199  tree->SetBranchAddress("signal", &m_signal);
200 
201  // Loop over tree
202  const auto nEntries = tree->GetEntries();
203  for (int i = 0; i < nEntries; ++i) {
204  tree->GetEntry(i);
205 
206  double noise = gRandom->Gaus(0.0, noiseSigma);
207  signals.push_back(m_signal + noise);
208  }
209  if (strategy == 0) return CalculateMedian(signals);
210  if (strategy == 1) return FitLandau(signals);
211  else {
212  B2FATAL("strategy unavailable, use 0 for medians or 1 for landau fit!");
213  }
214 }
215 
216 
217 double PXDChargeCalibrationAlgorithm::CalculateMedian(vector<double>& signals)
218 {
219  auto size = signals.size();
220 
221  if (size == 0) {
222  return 0.0; // Undefined, really.
223  } else {
224  sort(signals.begin(), signals.end());
225  if (size % 2 == 0) {
226  return (signals[size / 2 - 1] + signals[size / 2]) / 2;
227  } else {
228  return signals[size / 2];
229  }
230  }
231 }
232 
233 double PXDChargeCalibrationAlgorithm::FitLandau(vector<double>& signals)
234 {
235  auto size = signals.size();
236  if (size == 0) return 0.0; // Undefined, really.
237 
238  // get max and min values of vector
239  int max = *max_element(signals.begin(), signals.end());
240  int min = *min_element(signals.begin(), signals.end());
241 
242 
243  // create histogram to hold signals and fill it
244  TH1D* hist_signals = new TH1D("", "", max - min, min, max);
245  for (auto it = signals.begin(); it != signals.end(); ++it) {
246  hist_signals->Fill(*it);
247  }
248 
249  // create fit function
250  TF1* landau = new TF1("landau", "TMath::Landau(x,[0],[1])*[2]", min, max);
251  landau->SetParNames("MPV", "sigma", "scale");
252  landau->SetParameters(100, 1, 1000);
253 
254  //do fit and get results, fit range restricted to exclude low charge peak
255  Int_t status = hist_signals->Fit("landau", "Lq", "", 30, 350);
256  double MPV = landau->GetParameter("MPV");
257 
258  // clean up
259  delete hist_signals;
260  delete landau;
261 
262  // check fit status
263  if (status == 0) return MPV;
264  else {
265  B2WARNING("Fit failed! using default value 0.0!");
266  return 0.0;
267  }
268 }
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.
double EstimateCharge(VxdID sensorID, unsigned short uBin, unsigned short vBin)
Estimate median charge form collected clusters on part of PXD.
double FitLandau(std::vector< double > &signals)
calculate MPV of unsorted signal vector using a Landau fit
int minClusters
Minimum number of collected clusters for estimating median charge.
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
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 cluster charge calibrations.
void setContent(unsigned short sensorID, unsigned short globalID, float value)
Set map content.
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.