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 average
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 <<
215 ": Replace default gain with average "
216 << meanGain);
217 }
218 }
219 }
220 }
221 }
222
223
224 // Save the gain map to database. Note that this will set the database object name to the same as the collector but you
225 // are free to change it.
226 saveCalibration(gainMapPar, "PXDGainMapPar");
227
228 B2INFO("PXD Gain Calibration Successful");
229 return c_OK;
230}
231
232
233double PXDGainCalibrationAlgorithm::EstimateGain(VxdID sensorID, unsigned short uBin, unsigned short vBin)
234{
235 // Construct a tree name for requested part of PXD
236 auto layerNumber = sensorID.getLayerNumber();
237 auto ladderNumber = sensorID.getLadderNumber();
238 auto sensorNumber = sensorID.getSensorNumber();
239 const string treename = str(format("tree_%1%_%2%_%3%_%4%_%5%") % layerNumber % ladderNumber % sensorNumber % uBin % vBin);
240 // Vector with cluster signals from collected mc
241 vector<double> mc_signals;
242
243 auto tree_MC = getObjectPtr<TTree>(treename);
244 tree_MC->SetBranchAddress("signal", &m_signal);
245
246 // Loop over tree_MC
247 const auto nEntries_MC = tree_MC->GetEntries();
248 for (int i = 0; i < nEntries_MC; ++i) {
249 tree_MC->GetEntry(i);
250
251 double noise = gRandom->Gaus(0.0, noiseSigma);
252 mc_signals.push_back(m_signal + noise);
253 }
254
255 double dataMedian = GetChargeMedianFromDB(sensorID, uBin, vBin);
256 double mcMedian = -1;
257 // check if dataMedian makes sense
258 if (dataMedian <= 0.0) {
259 B2WARNING("Retrieved negative charge median from DB for sensor=" << sensorID << " uBin=" << uBin << " vBin=" << vBin <<
260 ". Set gain to default value (=1.0) as well.");
261 return 1.0;
262 }
263 if (strategy == 0) mcMedian = CalculateMedian(mc_signals);
264 else if (strategy == 1) mcMedian = FitLandau(mc_signals);
265 else {
266 B2FATAL("strategy unavailable, use 0 for medians or 1 for landau fit!");
267 }
268
269 // check if mcMedian makes sense
270 if (mcMedian <= 0.0) {
271 B2WARNING("Retrieved negative charge median from DB for sensor=" << sensorID << " uBin=" << uBin << " vBin=" << vBin <<
272 ". Set gain to default value (=1.0) as well.");
273 return 1.0;
274 }
275
276
277 double gain = dataMedian / mcMedian;
278 double gainFromDB = GetCurrentGainFromDB(sensorID, uBin, vBin);
279 B2DEBUG(10, "Gain from db used in PXDDigitizer is " << gainFromDB);
280 B2DEBUG(10, "New gain correction derived is " << gain);
281 B2DEBUG(10, "The total gain we should return is " << gain * gainFromDB);
282
283 return gain * gainFromDB;
284}
285
286double PXDGainCalibrationAlgorithm::GetChargeMedianFromDB(VxdID sensorID, unsigned short uBin, unsigned short vBin)
287{
288 // Read back db payloads
289 PXDClusterChargeMapPar* chargeMapPtr = 0;
290 PXDGainMapPar* gainMapPtr = 0;
291
292 auto dbtree = getObjectPtr<TTree>("dbtree");
293 dbtree->SetBranchAddress("run", &m_run);
294 dbtree->SetBranchAddress("exp", &m_exp);
295 dbtree->SetBranchAddress("chargeMap", &chargeMapPtr);
296 dbtree->SetBranchAddress("gainMap", &gainMapPtr);
297
298 // Compute running average of charge medians from db
299 double sum = 0;
300 int counter = 0;
301
302 // Loop over dbtree
303 const auto nEntries = dbtree->GetEntries();
304 for (int i = 0; i < nEntries; ++i) {
305 dbtree->GetEntry(i);
306 sum += chargeMapPtr->getContent(sensorID.getID(), uBin, vBin);
307 counter += 1;
308 }
309 delete chargeMapPtr;
310 chargeMapPtr = 0;
311 delete gainMapPtr;
312 gainMapPtr = 0;
313
314 return sum / counter;
315}
316
317double PXDGainCalibrationAlgorithm::GetCurrentGainFromDB(VxdID sensorID, unsigned short uBin, unsigned short vBin)
318{
319 // Read back db payloads
320 PXDClusterChargeMapPar* chargeMapPtr = 0;
321 PXDGainMapPar* gainMapPtr = 0;
322
323 auto dbtree = getObjectPtr<TTree>("dbtree");
324 dbtree->SetBranchAddress("run", &m_run);
325 dbtree->SetBranchAddress("exp", &m_exp);
326 dbtree->SetBranchAddress("chargeMap", &chargeMapPtr);
327 dbtree->SetBranchAddress("gainMap", &gainMapPtr);
328
329 // Compute running average of gains from db
330 double sum = 0;
331 int counter = 0;
332
333 // Loop over dbtree
334 const auto nEntries = dbtree->GetEntries();
335 for (int i = 0; i < nEntries; ++i) {
336 dbtree->GetEntry(i);
337 sum += gainMapPtr->getContent(sensorID.getID(), uBin, vBin);
338 counter += 1;
339 }
340 delete chargeMapPtr;
341 chargeMapPtr = 0;
342 delete gainMapPtr;
343 gainMapPtr = 0;
344
345 return sum / counter;
346}
347
348
349double PXDGainCalibrationAlgorithm::CalculateMedian(vector<double>& signals)
350{
351 auto size = signals.size();
352
353 if (size == 0) {
354 return 0.0; // Undefined, really.
355 } else {
356 sort(signals.begin(), signals.end());
357 if (size % 2 == 0) {
358 return (signals[size / 2 - 1] + signals[size / 2]) / 2;
359 } else {
360 return signals[size / 2];
361 }
362 }
363}
364
365double PXDGainCalibrationAlgorithm::FitLandau(vector<double>& signals)
366{
367 auto size = signals.size();
368 if (size == 0) return 0.0; // Undefined, really.
369
370 // get max and min values of signal vector
371 int max = *max_element(signals.begin(), signals.end());
372 int min = *min_element(signals.begin(), signals.end());
373
374 // create histogram to hold signals and fill it
375 TH1D* hist_signals = new TH1D("", "", max - min, min, max);
376 for (auto it = signals.begin(); it != signals.end(); ++it) {
377 hist_signals->Fill(*it);
378 }
379
380 // create fit function
381 TF1* landau = new TF1("landau", "TMath::Landau(x,[0],[1])*[2]", min, max);
382 landau->SetParNames("MPV", "sigma", "scale");
383 landau->SetParameters(100, 1, 1000);
384
385 // do fit and get results
386 Int_t status = hist_signals->Fit("landau", "Lq", "", 0, 350);
387 double MPV = landau->GetParameter("MPV");
388
389 // clean up
390 delete hist_signals;
391 delete landau;
392
393 // check fit status
394 if (status == 0) return MPV;
395 else {
396 B2WARNING("Fit failed!. using default value.");
397 return 0.0;
398 }
399}
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)
Retrieve 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)
Retrieve 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.