Belle II Software development
PXDClusterPositionCalibrationAlgorithm.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/PXDClusterPositionCalibrationAlgorithm.h>
10#include <pxd/dbobjects/PXDClusterShapeIndexPar.h>
11#include <pxd/dbobjects/PXDClusterPositionEstimatorPar.h>
12#include <framework/utilities/MathHelpers.h>
13
14#include <string>
15#include <tuple>
16#include <TH2F.h>
17#include <TMatrixDSym.h>
18#include <TVectorD.h>
19#include <TMatrixDSymEigen.h>
20
21#include <boost/format.hpp>
22#include <cmath>
23
24using namespace std;
25using boost::format;
26using namespace Belle2;
27
29 CalibrationAlgorithm("PXDClusterPositionCollector"),
31 // Branches from TTree
34{
36 " -------------------------- PXDClusterPositionCalibrationAlgorithm ----------------------\n"
37 " \n"
38 " Algorithm for estimating cluster position offsets and shape likelyhoods. \n"
39 " ----------------------------------------------------------------------------------------\n"
40 );
41}
42
44{
45
46 // Read back the V pitch of all cluster kinds in source data
47 // This avoids relying on VXD::GeoCache.
48 auto pitchtree = getObjectPtr<TTree>("pitchtree");
49 pitchtree->SetBranchAddress("PitchV", &m_pitchV);
50 pitchtree->SetBranchAddress("ClusterKind", &m_clusterKind);
51
52 for (int i = 0; i < pitchtree->GetEntries(); ++i) {
53 pitchtree->GetEntry(i);
55 }
56
57 // Buffer temporary payloads for shape calibration for all
58 // clusterkinds and angle bins
59 typedef tuple<int, int, int> bufferkey_t;
60 typedef pair<PXDClusterShapeIndexPar, PXDClusterShapeClassifierPar> buffervalue_t;
61 map<bufferkey_t, buffervalue_t> localCalibrationMap;
62
63 for (auto clusterKind : clusterKinds) {
64
65 B2INFO("Start calibration of clusterkind=" << clusterKind << " ...");
66
67 string gridname = str(format("GridKind_%1%") % clusterKind);
68 auto grid = getObjectPtr<TH2F>(gridname);
69
70 for (auto uBin = 1; uBin <= grid->GetXaxis()->GetNbins(); uBin++) {
71 for (auto vBin = 1; vBin <= grid->GetYaxis()->GetNbins(); vBin++) {
72
73 // Bin is centered around angles
74 auto thetaU = grid->GetXaxis()->GetBinCenter(uBin);
75 auto thetaV = grid->GetYaxis()->GetBinCenter(vBin);
76
77 if (thetaV < 0) {
78 B2INFO("Skip training estimator on thetaU=" << thetaU << ", thetaV=" << thetaV);
79 continue;
80 } else {
81 B2INFO("Start training estimator on thetaU=" << thetaU << ", thetaV=" << thetaV);
82 }
83
84 string treename = str(format("tree_%1%_%2%_%3%") % clusterKind % uBin % vBin);
85
86 auto localShapeIndexer = PXDClusterShapeIndexPar();
87 auto localShapeClassifier = PXDClusterShapeClassifierPar();
88 createShapeClassifier(treename, &localShapeClassifier, &localShapeIndexer);
89
90 bufferkey_t key = std::make_tuple(clusterKind, uBin, vBin);
91 localCalibrationMap[key] = buffervalue_t(localShapeIndexer, localShapeClassifier);
92 }
93 }
94 }
95
96 // Create a ShapeIndexer payload
98
99 int globalShapeIndex = 0;
100 for (auto it = m_shapeSet.begin(); it != m_shapeSet.end(); ++it) {
101 shapeIndexer->addShape(*it, globalShapeIndex);
102 globalShapeIndex++;
103 }
104
105 B2INFO("Number of cluster shapes is " << globalShapeIndex);
106
107 // Save the cluster shape index table to database.
108 saveCalibration(shapeIndexer, "PXDClusterShapeIndexPar");
109
110 // Create position estimator
112
113 for (auto clusterKind : clusterKinds) {
114
115 string gridname = str(format("GridKind_%1%") % clusterKind);
116 auto grid = getObjectPtr<TH2F>(gridname);
117
118 positionEstimator->addGrid(clusterKind, *grid);
119
120 for (auto uBin = 1; uBin <= grid->GetXaxis()->GetNbins(); uBin++) {
121 for (auto vBin = 1; vBin <= grid->GetYaxis()->GetNbins(); vBin++) {
122
123 // Bin is centered around angles
124 auto thetaU = grid->GetXaxis()->GetBinCenter(uBin);
125 auto thetaV = grid->GetYaxis()->GetBinCenter(vBin);
126
127 if (thetaV < 0) {
128 // We skipped this part in training before
129 continue;
130 }
131
132 // Find the local calibration results
133 auto iter = localCalibrationMap.find(std::make_tuple(clusterKind, uBin, vBin));
134 auto localShapeIndexer = iter->second.first;
135 auto localShapeClassifer = iter->second.second;
136
137 // Require that all shape classifiers use a common shape indexer
138 auto shapeClassifier = localToGlobal(&localShapeClassifer, &localShapeIndexer, shapeIndexer);
139
140 // Mirror the shape classifier along v
141 auto mirror_vBin = grid->GetYaxis()->FindBin(-thetaV);
142 auto mirroredClassifier = mirrorShapeClassifier(&shapeClassifier, shapeIndexer, clusterKind);
143
144 // and fill into position estimator payload
145 B2INFO("Add shape classifier for angles thetaU=" << thetaU << ", thetaV=" << thetaV << ", clusterkind=" << clusterKind);
146 positionEstimator->setShapeClassifier(shapeClassifier, uBin, vBin, clusterKind);
147 B2INFO("Add mirrored shape classifier for angles thetaU=" << thetaU << ", thetaV=" << -thetaV << ", clusterkind=" << clusterKind);
148 positionEstimator->setShapeClassifier(mirroredClassifier, uBin, mirror_vBin, clusterKind);
149 }
150 }
151 }
152
153 // Save the cluster positions to database.
154 saveCalibration(positionEstimator, "PXDClusterPositionEstimatorPar");
155
156 B2INFO("PXDClusterPosition Calibration Successful");
157 return c_OK;
158}
159
160
162 shapeClassifier, PXDClusterShapeIndexPar* shapeIndexer, int clusterKind)
163{
164 // Create a mirrored shape classifier
165 auto mirroredShapeClassifier = PXDClusterShapeClassifierPar();
166
167 // Mirror the shape likelyhood map
168 auto shapeLikelyhoodMap = shapeClassifier->getShapeLikelyhoodMap();
169 for (auto indexAndValue : shapeLikelyhoodMap) {
170 // Compute the mirrored shape index
171 auto shapeIndex = indexAndValue.first;
172 auto shapeName = shapeIndexer->getShapeName(shapeIndex);
173 auto mirroredName = m_mirrorMap[shapeName];
174 auto mirroredIndex = shapeIndexer->getShapeIndex(mirroredName);
175 // Store the result
176 mirroredShapeClassifier.addShapeLikelyhood(mirroredIndex, indexAndValue.second);
177 }
178
179 // Mirror the offset related maps
180 auto offsetMap = shapeClassifier->getOffsetMap();
181 auto percentileMap = shapeClassifier->getPercentilesMap();
182 auto likelyhoodMap = shapeClassifier->getLikelyhoodMap();
183 for (auto indexAndValue : offsetMap) {
184 // Compute the mirrored shape index
185 auto shapeIndex = indexAndValue.first;
186 auto shapeName = shapeIndexer->getShapeName(shapeIndex);
187 auto mirroredName = m_mirrorMap[shapeName];
188 auto mirroredIndex = shapeIndexer->getShapeIndex(mirroredName);
189
190 mirroredShapeClassifier.addShape(mirroredIndex);
191
192 int etaBin = 0;
193 for (auto offset : indexAndValue.second) {
194 // Copy over percentile
195 auto percentile = percentileMap[shapeIndex][etaBin];
196 mirroredShapeClassifier.addEtaPercentile(mirroredIndex, percentile);
197 // Copy over likelyhood
198 auto likelyhood = likelyhoodMap[shapeIndex][etaBin];
199 mirroredShapeClassifier.addEtaLikelyhood(mirroredIndex, likelyhood);
200 // Mirror the offset: v offset shifts and covariance swaps sign
201 double shift = (m_sizeMap[shapeName] - 1) * m_pitchMap[clusterKind];
202 auto mirroredOffset = PXDClusterOffsetPar(offset.getU(), shift - offset.getV(), offset.getUSigma2(), offset.getVSigma2(),
203 -offset.getUVCovariance());
204 mirroredShapeClassifier.addEtaOffset(mirroredIndex, mirroredOffset);
205 etaBin++;
206 }
207 }
208
209 return mirroredShapeClassifier;
210}
211
213 PXDClusterShapeIndexPar* shapeIndexer, PXDClusterShapeIndexPar* globalShapeIndexer)
214{
215 // Create a shape classifier using global shape indices
216 auto globalShapeClassifier = PXDClusterShapeClassifierPar();
217
218 // Re-index the the shape likelyhood map
219 auto shapeLikelyhoodMap = shapeClassifier->getShapeLikelyhoodMap();
220 for (auto indexAndValue : shapeLikelyhoodMap) {
221 // Compute the global shape index
222 auto shapeIndex = indexAndValue.first;
223 auto shapeName = shapeIndexer->getShapeName(shapeIndex);
224 auto globalIndex = globalShapeIndexer->getShapeIndex(shapeName);
225 // Store the result
226 globalShapeClassifier.addShapeLikelyhood(globalIndex, indexAndValue.second);
227 }
228
229 // Re-index the offset related maps
230 auto offsetMap = shapeClassifier->getOffsetMap();
231 auto percentileMap = shapeClassifier->getPercentilesMap();
232 auto likelyhoodMap = shapeClassifier->getLikelyhoodMap();
233 for (auto indexAndValue : offsetMap) {
234 // Compute the global shape index
235 auto shapeIndex = indexAndValue.first;
236 auto shapeName = shapeIndexer->getShapeName(shapeIndex);
237 auto globalIndex = globalShapeIndexer->getShapeIndex(shapeName);
238
239 globalShapeClassifier.addShape(globalIndex);
240
241 int etaBin = 0;
242 for (auto offset : indexAndValue.second) {
243 // Copy over percentile
244 auto percentile = percentileMap[shapeIndex][etaBin];
245 globalShapeClassifier.addEtaPercentile(globalIndex, percentile);
246 // Copy over likelyhood
247 auto likelyhood = likelyhoodMap[shapeIndex][etaBin];
248 globalShapeClassifier.addEtaLikelyhood(globalIndex, likelyhood);
249 // Copy over offset
250 globalShapeClassifier.addEtaOffset(globalIndex, offset);
251 etaBin++;
252 }
253 }
254 return globalShapeClassifier;
255}
256
258 PXDClusterShapeClassifierPar* shapeClassifier, PXDClusterShapeIndexPar* shapeIndexer)
259{
260
261 auto tree = getObjectPtr<TTree>(treename);
262
263 const auto nEntries = tree->GetEntries();
264 B2INFO("Number of clusters is " << nEntries);
265
266 string* shapeNamePtr = &m_shapeName;
267 string* mirroredShapeNamePtr = &m_mirroredShapeName;
268
269 tree->SetBranchAddress("ShapeName", &shapeNamePtr);
270 tree->SetBranchAddress("MirroredShapeName", &mirroredShapeNamePtr);
271 tree->SetBranchAddress("ClusterEta", &m_clusterEta);
272 tree->SetBranchAddress("OffsetU", &m_positionOffsetU);
273 tree->SetBranchAddress("OffsetV", &m_positionOffsetV);
274 tree->SetBranchAddress("SizeV", &m_sizeV);
275
276 // Vector to enumerate all shapes by unique name and count their
277 // occurrence in training data.
278 vector< pair<string, float> > shapeList;
279
280 for (int i = 0; i < nEntries; ++i) {
281 tree->GetEntry(i);
282
283 string shapeName = m_shapeName;
284
285 auto it = std::find_if(shapeList.begin(), shapeList.end(),
286 [&](const pair<string, float>& element) { return element.first == shapeName;});
287
288 //Shape name exists in vector
289 if (it != shapeList.end()) {
290 //increment key in map
291 it->second++;
292 }
293 //Shape name does not exist
294 else {
295 //Not found, insert in vector
296 shapeList.push_back(pair<string, int>(shapeName, 1));
297 // Remember the relation between name of a shape and name of mirrored shape
298 m_mirrorMap[shapeName] = m_mirroredShapeName;
299 // Remember the relation between name of a shape and its size
300 m_sizeMap[shapeName] = m_sizeV;
301 }
302 }
303
304 // Loop over shapeList to select shapes for
305 // next calibration step
306
307 // Vector with eta histograms for selected shapes
308 vector< pair<string, TH1D> > etaHistos;
309
310 // Index for enumerating selected shapes
311 int tmpIndex = 0;
312
313 // Coverage of position offsets on training data
314 double coverage = 0.0;
315
316 for (auto iter : shapeList) {
317 auto name = iter.first;
318 auto counter = iter.second;
319
320 double likelyhood = counter / nEntries;
321
322 if (counter >= minClusterForShapeLikelyhood) {
323 //B2INFO("Adding shape " << name << " with index " << tmpIndex << " and shape likelyhood " << 100*likelyhood << "% and count " << counter);
324 shapeIndexer->addShape(name, tmpIndex);
325 shapeClassifier->addShapeLikelyhood(tmpIndex, likelyhood);
326
327 // Add name of shape to global (all clusterkinds + all angle bins) shape set
328 m_shapeSet.insert(name);
329 // Add name of mirrored shape as well
330 m_shapeSet.insert(m_mirrorMap[name]);
331 // Increment the index
332 tmpIndex++;
333 }
334
335 if (counter >= minClusterForPositionOffset) {
336 coverage += likelyhood;
337 string etaname = str(format("eta_%1%") % name);
338
339 // Single pixel case: Eta value is cluster charge
340 if (name == "SD0.0") {
341 TH1D etaHisto(etaname.c_str(), etaname.c_str(), 255, 0, 255);
342 etaHisto.SetDirectory(0);
343 etaHistos.push_back(pair<string, TH1D>(name, etaHisto));
344 }
345 // Multipixel case: Eta value is ratio head/(tail+head) of charges (to be less gain sensitive)
346 else {
347 TH1D etaHisto(etaname.c_str(), etaname.c_str(), 301, 0, 1);
348 etaHisto.SetDirectory(0);
349 etaHistos.push_back(pair<string, TH1D>(name, etaHisto));
350 }
351 }
352 }
353
354 B2INFO("Offset coverage is " << 100 * coverage << "%");
355
356 // Loop over the tree is to fill the eta histograms for
357 // selected shapes.
358
359 for (int i = 0; i < nEntries; ++i) {
360 tree->GetEntry(i);
361
362 string shapeName = m_shapeName;
363 auto it = std::find_if(etaHistos.begin(), etaHistos.end(),
364 [&](const pair<string, TH1D>& element) { return element.first == shapeName;});
365 //Item exists in map
366 if (it != etaHistos.end()) {
367 // increment key in map
368 it->second.Fill(m_clusterEta);
369 }
370 }
371
372 // Vector for offset histograms stored by offset shape name and eta bin
373 vector< pair< string, vector<TH2D> > > offsetHistosVec;
374
375 for (auto iter : etaHistos) {
376 auto name = iter.first;
377 auto& histo = iter.second;
378 int nClusters = histo.GetEntries();
379
380 // Add shape for offset correction
381 int shapeIndex = shapeIndexer->getShapeIndex(name);
382 shapeClassifier->addShape(shapeIndex);
383
384 // Try to split clusters into n bins with minClusterForPositionOffset clusters
385 int nEtaBins = std::max(int(nClusters / minClusterForPositionOffset), 1);
386 nEtaBins = std::min(nEtaBins, maxEtaBins);
387
388 //B2INFO("SHAPE NAME:" << name << " WITH BINS " << nEtaBins);
389
390 vector< TH2D > offsetHistos;
391
392 for (int i = 0; i < nEtaBins; i++) {
393 // Position where to compute the quantiles in [0,1]
394 double xq = double(i) / nEtaBins;
395 // Double to contain the quantile
396 double yq = 0;
397 histo.GetQuantiles(1, &yq, &xq);
398 //B2INFO(" Quantile at xq =" << xq << " is yq=" << yq);
399 shapeClassifier->addEtaPercentile(shapeIndex, yq);
400
401 string offsetname = str(format("offset_%1%_%2%") % name % i);
402 TH2D offsetHisto(offsetname.c_str(), offsetname.c_str(), 1, 0, 1, 1, 0, 1);
403 offsetHisto.SetDirectory(0);
404 offsetHisto.StatOverflows();
405 offsetHistos.push_back(offsetHisto);
406
407 }
408 offsetHistosVec.push_back(pair< string, vector<TH2D> >(name, offsetHistos));
409 }
410
411 // Loop over the tree is to fill offset histograms
412
413 for (int i = 0; i < nEntries; ++i) {
414 tree->GetEntry(i);
415
416 string shapeName = m_shapeName;
417 auto it = std::find_if(offsetHistosVec.begin(), offsetHistosVec.end(),
418 [&](const pair<string, vector<TH2D>>& element) { return element.first == shapeName;});
419 //Item exists in map
420 if (it != offsetHistosVec.end()) {
421 int shapeIndex = shapeIndexer->getShapeIndex(shapeName);
422 int etaBin = shapeClassifier->getEtaIndex(shapeIndex, m_clusterEta);
423 it->second.at(etaBin).Fill(m_positionOffsetU, m_positionOffsetV);
424 }
425 }
426
427 // Compute the moments of the offset histograms and finalize the shape classifier object
428
429 // Loop over shape names
430 for (auto iter : offsetHistosVec) {
431 auto name = iter.first;
432 auto& histovec = iter.second;
433
434 int shapeIndex = shapeIndexer->getShapeIndex(name);
435
436 // Loop over eta bins
437 for (auto& histo : histovec) {
438 // Compute offset moments
439 double etaLikelyhood = double(histo.GetEntries()) / nEntries;
440 double offsetU = histo.GetMean(1);
441 double offsetV = histo.GetMean(2);
442 double covUV = histo.GetCovariance();
443 double covU = square(histo.GetRMS(1));
444 double covV = square(histo.GetRMS(2));
445
446 B2INFO("Name " << name << ", posU=" << offsetU << ", posV=" << offsetV << ", covU=" << covU << ", covV=" << covV << ", covUV=" <<
447 covUV);
448
449 TMatrixDSym HitCov(2);
450 HitCov(0, 0) = covU;
451 HitCov(1, 0) = covUV;
452 HitCov(0, 1) = covUV;
453 HitCov(1, 1) = covV;
454
455 TMatrixDSymEigen HitCovE(HitCov);
456 TVectorD eigenval = HitCovE.GetEigenValues();
457 if (eigenval(0) <= 0 || eigenval(1) <= 0) {
458 B2ERROR("Estimated covariance matrix not positive definite.");
459 }
460
461 auto offset = PXDClusterOffsetPar(offsetU, offsetV, covU, covV, covUV);
462 shapeClassifier->addEtaLikelyhood(shapeIndex, etaLikelyhood);
463 shapeClassifier->addEtaOffset(shapeIndex, offset);
464 }
465 }
466
467 B2INFO("Added shape classifier with coverage " << 100 * coverage << "% on training data sample.");
468
469 return;
470}
471
472
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.
CalibrationAlgorithm(const std::string &collectorModuleName)
Constructor - sets the prefix for collected objects (won't be accesses until execute(....
The class for PXD cluster position offset payload.
void createShapeClassifier(std::string treename, PXDClusterShapeClassifierPar *shapeClassifier, PXDClusterShapeIndexPar *shapeIndexer)
Returns a new classifier and index trained on cluster tree.
std::vector< int > clusterKinds
Vector of clusterkinds to calibrate.
PXDClusterShapeClassifierPar mirrorShapeClassifier(PXDClusterShapeClassifierPar *shapeClassifier, PXDClusterShapeIndexPar *shapeIndexer, int clusterKind)
Returns a mirrored version of shape classifier.
PXDClusterShapeClassifierPar localToGlobal(PXDClusterShapeClassifierPar *localShapeClassifier, PXDClusterShapeIndexPar *localShapeIndexer, PXDClusterShapeIndexPar *globalShapeIndexer)
Returns a shape classifier using global shape indices instead of local ones.
std::map< std::string, int > m_sizeMap
Helper needed to map the name of a shape to the V size of the cluster.
int maxEtaBins
Maximum number of eta bins for estimating cluster position offsets.
int minClusterForPositionOffset
Minimum number of collected clusters for estimating cluster position offsets.
int minClusterForShapeLikelyhood
Minimum number of collected clusters for estimating shape likelyhood.
PXDClusterPositionCalibrationAlgorithm()
Constructor set the prefix to PXDClusterPositionCalibrationAlgorithm.
std::map< int, float > m_pitchMap
Helper needed to map the clusterkind to the V pitch of the sensor.
std::set< std::string > m_shapeSet
Set of unique shape names.
std::map< std::string, std::string > m_mirrorMap
Helper needed to map the name of a shape to the name of the mirrored shape.
The class for PXD cluster position lookup table payload.
void setShapeClassifier(const PXDClusterShapeClassifierPar &classifier, int uBin, int vBin, int clusterkind)
Set shape classifier.
void addGrid(int clusterkind, const TH2F &grid)
Add grid for clusterkind.
The class for PXD cluster shape classifier payload.
void addShapeLikelyhood(int shape_index, float likelyhood)
Add shape likelihood.
const std::map< int, std::vector< PXDClusterOffsetPar > > & getOffsetMap() const
Return offset map for position correction.
void addEtaLikelyhood(int shape_index, float likelyhood)
Add eta likelyhood to shape for position correction.
const std::map< int, std::vector< float > > & getPercentilesMap() const
Return percentiles map for position correction.
void addEtaPercentile(int shape_index, float percentile)
Add eta percentile to shape for position correction.
const std::map< int, std::vector< float > > & getLikelyhoodMap() const
Return likelyhood map for position correction.
unsigned int getEtaIndex(int shape_index, float eta) const
Get eta index for position correction.
void addShape(int shape_index)
Add shape for position correction.
const std::map< int, float > & getShapeLikelyhoodMap() const
Return shape likelyhood map.
void addEtaOffset(int shape_index, PXDClusterOffsetPar &offset)
Add offset to shape for position correction.
The class for PXD cluster shape index payload.
int getShapeIndex(const std::string &name) const
Returns shape index from name.
const std::string & getShapeName(int index) const
Returns shape name from index.
void addShape(const std::string &name, int index)
Add shape with name and index.
std::shared_ptr< T > getObjectPtr(const std::string &name, const std::vector< Calibration::ExpRun > &requestedRuns)
Get calibration data object by name and list of runs, the Merge function will be called to generate t...
constexpr T square(const T &x)
Calculate the square of the input.
Definition MathHelpers.h:21
Abstract base class for different kinds of events.
STL namespace.