Belle II Software development
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
27using namespace std;
28using boost::format;
29using namespace Belle2;
30
31
32// Anonymous namespace for data objects used by PXDGainCalibrationAlgorithm class
33namespace {
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
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
232double 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
285double 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
316double 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
348double 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
364double 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 successfully =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.
PXDGainCalibrationAlgorithm()
Constructor set the prefix to PXDGainCalibrationAlgorithm.
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.
STL namespace.