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, const 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) * static_cast<double>(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 shapeClassifier,
214 const PXDClusterShapeIndexPar* shapeIndexer, const PXDClusterShapeIndexPar* globalShapeIndexer)
215{
216 // Create a shape classifier using global shape indices
217 auto globalShapeClassifier = PXDClusterShapeClassifierPar();
218
219 // Re-index the the shape likelyhood map
220 auto shapeLikelyhoodMap = shapeClassifier->getShapeLikelyhoodMap();
221 for (auto indexAndValue : shapeLikelyhoodMap) {
222 // Compute the global shape index
223 auto shapeIndex = indexAndValue.first;
224 auto shapeName = shapeIndexer->getShapeName(shapeIndex);
225 auto globalIndex = globalShapeIndexer->getShapeIndex(shapeName);
226 // Store the result
227 globalShapeClassifier.addShapeLikelyhood(globalIndex, indexAndValue.second);
228 }
229
230 // Re-index the offset related maps
231 auto offsetMap = shapeClassifier->getOffsetMap();
232 auto percentileMap = shapeClassifier->getPercentilesMap();
233 auto likelyhoodMap = shapeClassifier->getLikelyhoodMap();
234 for (auto indexAndValue : offsetMap) {
235 // Compute the global shape index
236 auto shapeIndex = indexAndValue.first;
237 auto shapeName = shapeIndexer->getShapeName(shapeIndex);
238 auto globalIndex = globalShapeIndexer->getShapeIndex(shapeName);
239
240 globalShapeClassifier.addShape(globalIndex);
241
242 int etaBin = 0;
243 for (auto offset : indexAndValue.second) {
244 // Copy over percentile
245 auto percentile = percentileMap[shapeIndex][etaBin];
246 globalShapeClassifier.addEtaPercentile(globalIndex, percentile);
247 // Copy over likelyhood
248 auto likelyhood = likelyhoodMap[shapeIndex][etaBin];
249 globalShapeClassifier.addEtaLikelyhood(globalIndex, likelyhood);
250 // Copy over offset
251 globalShapeClassifier.addEtaOffset(globalIndex, offset);
252 etaBin++;
253 }
254 }
255 return globalShapeClassifier;
256}
257
259 PXDClusterShapeClassifierPar* shapeClassifier, PXDClusterShapeIndexPar* shapeIndexer)
260{
261
262 auto tree = getObjectPtr<TTree>(treename);
263
264 const auto nEntries = tree->GetEntries();
265 B2INFO("Number of clusters is " << nEntries);
266
267 string* shapeNamePtr = &m_shapeName;
268 string* mirroredShapeNamePtr = &m_mirroredShapeName;
269
270 tree->SetBranchAddress("ShapeName", &shapeNamePtr);
271 tree->SetBranchAddress("MirroredShapeName", &mirroredShapeNamePtr);
272 tree->SetBranchAddress("ClusterEta", &m_clusterEta);
273 tree->SetBranchAddress("OffsetU", &m_positionOffsetU);
274 tree->SetBranchAddress("OffsetV", &m_positionOffsetV);
275 tree->SetBranchAddress("SizeV", &m_sizeV);
276
277 // Vector to enumerate all shapes by unique name and count their
278 // occurrence in training data.
279 vector< pair<string, float> > shapeList;
280
281 for (int i = 0; i < nEntries; ++i) {
282 tree->GetEntry(i);
283
284 string shapeName = m_shapeName;
285
286 auto it = std::find_if(shapeList.begin(), shapeList.end(),
287 [&](const pair<string, float>& element) { return element.first == shapeName;});
288
289 //Shape name exists in vector
290 if (it != shapeList.end()) {
291 //increment key in map
292 it->second++;
293 }
294 //Shape name does not exist
295 else {
296 //Not found, insert in vector
297 shapeList.push_back(pair<string, int>(shapeName, 1));
298 // Remember the relation between name of a shape and name of mirrored shape
299 m_mirrorMap[shapeName] = m_mirroredShapeName;
300 // Remember the relation between name of a shape and its size
301 m_sizeMap[shapeName] = m_sizeV;
302 }
303 }
304
305 // Loop over shapeList to select shapes for
306 // next calibration step
307
308 // Vector with eta histograms for selected shapes
309 vector< pair<string, TH1D> > etaHistos;
310
311 // Index for enumerating selected shapes
312 int tmpIndex = 0;
313
314 // Coverage of position offsets on training data
315 double coverage = 0.0;
316
317 for (auto iter : shapeList) {
318 auto name = iter.first;
319 auto counter = iter.second;
320
321 double likelyhood = counter / nEntries;
322
323 if (counter >= minClusterForShapeLikelyhood) {
324 //B2INFO("Adding shape " << name << " with index " << tmpIndex << " and shape likelyhood " << 100*likelyhood << "% and count " << counter);
325 shapeIndexer->addShape(name, tmpIndex);
326 shapeClassifier->addShapeLikelyhood(tmpIndex, likelyhood);
327
328 // Add name of shape to global (all clusterkinds + all angle bins) shape set
329 m_shapeSet.insert(name);
330 // Add name of mirrored shape as well
331 m_shapeSet.insert(m_mirrorMap[name]);
332 // Increment the index
333 tmpIndex++;
334 }
335
336 if (counter >= minClusterForPositionOffset) {
337 coverage += likelyhood;
338 string etaname = str(format("eta_%1%") % name);
339
340 // Single pixel case: Eta value is cluster charge
341 if (name == "SD0.0") {
342 TH1D etaHisto(etaname.c_str(), etaname.c_str(), 255, 0, 255);
343 etaHisto.SetDirectory(0);
344 etaHistos.push_back(pair<string, TH1D>(name, etaHisto));
345 }
346 // Multipixel case: Eta value is ratio head/(tail+head) of charges (to be less gain sensitive)
347 else {
348 TH1D etaHisto(etaname.c_str(), etaname.c_str(), 301, 0, 1);
349 etaHisto.SetDirectory(0);
350 etaHistos.push_back(pair<string, TH1D>(name, etaHisto));
351 }
352 }
353 }
354
355 B2INFO("Offset coverage is " << 100 * coverage << "%");
356
357 // Loop over the tree is to fill the eta histograms for
358 // selected shapes.
359
360 for (int i = 0; i < nEntries; ++i) {
361 tree->GetEntry(i);
362
363 string shapeName = m_shapeName;
364 auto it = std::find_if(etaHistos.begin(), etaHistos.end(),
365 [&](const pair<string, TH1D>& element) { return element.first == shapeName;});
366 //Item exists in map
367 if (it != etaHistos.end()) {
368 // increment key in map
369 it->second.Fill(m_clusterEta);
370 }
371 }
372
373 // Vector for offset histograms stored by offset shape name and eta bin
374 vector< pair< string, vector<TH2D> > > offsetHistosVec;
375
376 for (auto iter : etaHistos) {
377 auto name = iter.first;
378 auto& histo = iter.second;
379 int nClusters = histo.GetEntries();
380
381 // Add shape for offset correction
382 int shapeIndex = shapeIndexer->getShapeIndex(name);
383 shapeClassifier->addShape(shapeIndex);
384
385 // Try to split clusters into n bins with minClusterForPositionOffset clusters
386 int nEtaBins = std::max(int(nClusters / minClusterForPositionOffset), 1);
387 nEtaBins = std::min(nEtaBins, maxEtaBins);
388
389 //B2INFO("SHAPE NAME:" << name << " WITH BINS " << nEtaBins);
390
391 vector< TH2D > offsetHistos;
392
393 for (int i = 0; i < nEtaBins; i++) {
394 // Position where to compute the quantiles in [0,1]
395 double xq = double(i) / nEtaBins;
396 // Double to contain the quantile
397 double yq = 0;
398 histo.GetQuantiles(1, &yq, &xq);
399 //B2INFO(" Quantile at xq =" << xq << " is yq=" << yq);
400 shapeClassifier->addEtaPercentile(shapeIndex, yq);
401
402 string offsetname = str(format("offset_%1%_%2%") % name % i);
403 TH2D offsetHisto(offsetname.c_str(), offsetname.c_str(), 1, 0, 1, 1, 0, 1);
404 offsetHisto.SetDirectory(0);
405 offsetHisto.StatOverflows();
406 offsetHistos.push_back(offsetHisto);
407
408 }
409 offsetHistosVec.push_back(pair< string, vector<TH2D> >(name, offsetHistos));
410 }
411
412 // Loop over the tree is to fill offset histograms
413
414 for (int i = 0; i < nEntries; ++i) {
415 tree->GetEntry(i);
416
417 string shapeName = m_shapeName;
418 auto it = std::find_if(offsetHistosVec.begin(), offsetHistosVec.end(),
419 [&](const pair<string, vector<TH2D>>& element) { return element.first == shapeName;});
420 //Item exists in map
421 if (it != offsetHistosVec.end()) {
422 int shapeIndex = shapeIndexer->getShapeIndex(shapeName);
423 int etaBin = shapeClassifier->getEtaIndex(shapeIndex, m_clusterEta);
424 it->second.at(etaBin).Fill(m_positionOffsetU, m_positionOffsetV);
425 }
426 }
427
428 // Compute the moments of the offset histograms and finalize the shape classifier object
429
430 // Loop over shape names
431 for (auto iter : offsetHistosVec) {
432 auto name = iter.first;
433 auto& histovec = iter.second;
434
435 int shapeIndex = shapeIndexer->getShapeIndex(name);
436
437 // Loop over eta bins
438 for (auto& histo : histovec) {
439 // Compute offset moments
440 double etaLikelyhood = double(histo.GetEntries()) / nEntries;
441 double offsetU = histo.GetMean(1);
442 double offsetV = histo.GetMean(2);
443 double covUV = histo.GetCovariance();
444 double covU = square(histo.GetRMS(1));
445 double covV = square(histo.GetRMS(2));
446
447 B2INFO("Name " << name << ", posU=" << offsetU << ", posV=" << offsetV << ", covU=" << covU << ", covV=" << covV << ", covUV=" <<
448 covUV);
449
450 TMatrixDSym HitCov(2);
451 HitCov(0, 0) = covU;
452 HitCov(1, 0) = covUV;
453 HitCov(0, 1) = covUV;
454 HitCov(1, 1) = covV;
455
456 TMatrixDSymEigen HitCovE(HitCov);
457 TVectorD eigenval = HitCovE.GetEigenValues();
458 if (eigenval(0) <= 0 || eigenval(1) <= 0) {
459 B2ERROR("Estimated covariance matrix not positive definite.");
460 }
461
462 auto offset = PXDClusterOffsetPar(offsetU, offsetV, covU, covV, covUV);
463 shapeClassifier->addEtaLikelyhood(shapeIndex, etaLikelyhood);
464 shapeClassifier->addEtaOffset(shapeIndex, offset);
465 }
466 }
467
468 B2INFO("Added shape classifier with coverage " << 100 * coverage << "% on training data sample.");
469
470 return;
471}
472
473
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(const PXDClusterShapeClassifierPar *shapeClassifier, const PXDClusterShapeIndexPar *shapeIndexer, int clusterKind)
Returns a mirrored version of shape classifier.
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.
static PXDClusterShapeClassifierPar localToGlobal(const PXDClusterShapeClassifierPar *localShapeClassifier, const PXDClusterShapeIndexPar *localShapeIndexer, const PXDClusterShapeIndexPar *globalShapeIndexer)
Returns a shape classifier using global shape indices instead of local ones.
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.