Belle II Software development
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
18using namespace std;
19using boost::format;
20using namespace Belle2;
21using namespace Belle2::PXD;
22
23
24// Anonymous namespace for data objects used by PXDAnalyticGainCalibrationAlgorithm class
25namespace {
26
28 int m_signal;
30 float m_estimated;
32 int m_run;
34 int m_exp;
35}
36
37
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
218double 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
276double 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
303bool 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.
PXDAnalyticGainCalibrationAlgorithm()
Constructor set the prefix to PXDAnalyticGainCalibrationAlgorithm.
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.
STL namespace.