Project
Loading...
Searching...
No Matches
TrackResiduals.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
18
20#include "TPCBase/Mapper.h"
23#include "MathUtils/fit.h"
24
25#include "TMatrixDSym.h"
26#include "TDecompChol.h"
27#include "TVectorD.h"
28
29#include <cmath>
30#include <cstring>
31#include <algorithm>
32
33// for debugging
34#include "TStopwatch.h"
35#include "TSystem.h"
36#include <iostream>
37#include <fstream>
38#include <limits>
39#include <iomanip>
40
41#include <fairlogger/Logger.h>
42
43using namespace o2::tpc;
44
50
51//______________________________________________________________________________
52void TrackResiduals::init(bool doBinning)
53{
54 mSmoothPol2[VoxX] = true;
55 mSmoothPol2[VoxF] = true;
58 mMaxZ2X = mParams->maxZ2X;
59 mIsInitialized = true;
60
61 if (doBinning) {
62 // initialize binning
64 }
65
66 LOG(info) << "Initialization complete";
67}
68
69//______________________________________________________________________________
70void TrackResiduals::setY2XBinning(const std::vector<float>& binning)
71{
72 if (mIsInitialized) {
73 LOG(error) << "Binning already initialized, not changing y/x binning";
74 return;
75 }
76
77 if (binning.size() == 0) {
78 LOGP(info, "Empty binning provided, will use default uniform y/x binning with {} bins", mNY2XBins);
79 return;
80 } else if (binning.size() == 1) {
81 const int bins = static_cast<int>(binning.at(0));
83 LOGP(info, "Setting uniform binning for y/x with {} bins", bins - 1);
84 return;
85 }
86
87 const int nBins = binning.size() - 1;
88 if (std::abs(binning[0] + 1.f) > param::sEps || std::abs(binning[nBins] - 1.f) > param::sEps) {
89 LOG(error) << "Provided binning for y/x not in range -1 to 1: " << binning[0] << " - " << binning[nBins] << ". Not changing y/x binning";
90 return;
91 }
92
93 LOGP(info, "Setting custom binning for y/x with {} bins", nBins);
94 setNY2XBins(nBins);
95 mUniformBins[VoxF] = false;
96 mY2XBinsDH.clear();
97 mY2XBinsDI.clear();
98 mY2XBinsCenter.clear();
99 for (int iBin = 0; iBin < nBins; ++iBin) {
100 mY2XBinsDH.push_back(.5f * (binning[iBin + 1] - binning[iBin]));
101 mY2XBinsDI.push_back(.5f / mY2XBinsDH[iBin]);
102 mY2XBinsCenter.push_back(binning[iBin] + mY2XBinsDH[iBin]);
103 LOGF(info, "Bin %i: center (%.3f), half bin width (%.3f)", iBin, mY2XBinsCenter.back(), mY2XBinsDH.back());
104 }
105}
106
107//______________________________________________________________________________
108void TrackResiduals::setZ2XBinning(const std::vector<float>& binning)
109{
110 if (mIsInitialized) {
111 LOG(error) << "Binning already initialized, not changing z/x binning";
112 return;
113 }
114
115 if (binning.size() == 0) {
116 LOGP(info, "Empty binning provided, will use default uniform z/x binning with {} bins", mNZ2XBins);
117 return;
118 } else if (binning.size() == 1) {
119 const int bins = static_cast<int>(binning.at(0));
121 LOGP(info, "Setting uniform binning for z/x with {} bins", bins);
122 return;
123 }
124
125 int nBins = binning.size() - 1;
126 if (std::abs(binning[0]) > param::sEps || std::abs(binning[nBins] - 1.f) > param::sEps) {
127 LOG(error) << "Provided binning for z/x not in range 0 to 1: " << binning[0] << " - " << binning[nBins] << ". Not changing z/x binning";
128 return;
129 }
130
131 LOGP(info, "Setting custom binning for z/x with {} bins", nBins);
132 setNZ2XBins(nBins);
133 mUniformBins[VoxZ] = false;
134 mZ2XBinsDH.clear();
135 mZ2XBinsDI.clear();
136 mZ2XBinsCenter.clear();
137 const float maxZ2X = SpacePointsCalibConfParam::Instance().maxZ2X;
138 LOGP(info, "Using maxZ2X {} for setZ2XBinning", maxZ2X);
139 for (int iBin = 0; iBin < nBins; ++iBin) {
140 mZ2XBinsDH.push_back(.5f * (binning[iBin + 1] - binning[iBin]) * maxZ2X);
141 mZ2XBinsDI.push_back(.5f / mZ2XBinsDH[iBin]);
142 mZ2XBinsCenter.push_back(binning[iBin] * maxZ2X + mZ2XBinsDH[iBin]);
143 LOGF(info, "Bin %i: center (%.3f), half bin width (%.3f)", iBin, mZ2XBinsCenter.back(), mZ2XBinsDH.back());
144 }
145}
146
147//______________________________________________________________________________
149{
150 // initialize binning structures
151 //
152 // X binning
153 if (mNXBins > 0 && mNXBins < param::NPadRows) {
154 // uniform binning in X
155 LOGF(info, "X-binning is uniform with %i bins from %.2f to %.2f", mNXBins, param::MinX, param::MaxX);
156 mDXI = mNXBins / (param::MaxX - param::MinX);
157 mDX = 1.0f / mDXI;
158 mUniformBins[VoxX] = true;
159 } else {
160 // binning per pad row
161 LOGF(info, "X-binning is per pad-row");
162 mNXBins = param::NPadRows;
163 mUniformBins[VoxX] = false;
164 mDX = param::RowDX[0];
165 mDXI = 1.f / mDX; // should not be used
166 }
167 //
168 // Y/X binning: y/x = +-mMaxY2X is the pad plane's edge in the pad row of the x bin (the outer edge of its outermost pads; with uniform
169 // X-binning the pad row closest to the bin centre), so the outermost y/x bins end with the row's last pads
170 mMaxY2X.resize(mNXBins);
171 mDY2XI.resize(mNXBins);
172 mDY2X.resize(mNXBins);
173 //
174 const auto& mapper = Mapper::instance();
175 for (int ix = 0; ix < mNXBins; ++ix) {
176 float x = getX(ix);
177 int row = ix;
178 if (mUniformBins[VoxX]) {
179 row = int(std::min_element(param::RowX, param::RowX + param::NPadRows, [x](float a, float b) { return std::abs(a - x) < std::abs(b - x); }) - param::RowX);
180 }
181 const float halfWidth = 0.5f * mapper.getNumberOfPadsInRowSector(row) * mapper.getPadRegionInfo(Mapper::REGION[row]).getPadWidth();
182 mMaxY2X[ix] = halfWidth / x;
183 mDY2XI[ix] = mNY2XBins / (2.f * mMaxY2X[ix]);
184 mDY2X[ix] = 1.f / mDY2XI[ix];
185 }
186 if (mUniformBins[VoxF]) {
187 LOGF(info, "Y/X-binning is uniform with %i bins from -MaxY2X to +MaxY2X (values depend on X-bin)", mNY2XBins);
188 for (int ip = 0; ip < mNY2XBins; ++ip) {
189 mY2XBinsDH.push_back(1.f / mNY2XBins);
190 mY2XBinsDI.push_back(.5f / mY2XBinsDH[ip]);
191 mY2XBinsCenter.push_back(-1.f + (ip + 0.5f) * 2.f * mY2XBinsDH[ip]);
192 LOGF(info, "Bin %i: center (%.3f), half bin width (%.3f)", ip, mY2XBinsCenter.back(), mY2XBinsDH.back());
193 }
194 }
195 //
196 // Z/X binning
197 mDZ2XI = mNZ2XBins / mMaxZ2X;
198 mDZ2X = 1.0f / mDZ2XI; // for uniform case only
199 if (mUniformBins[VoxZ]) {
200 LOGF(info, "Z/X-binning is uniform with %i bins from 0 to %f", mNZ2XBins, mMaxZ2X);
201 for (int iz = 0; iz < mNZ2XBins; ++iz) {
202 mZ2XBinsDH.push_back(.5f * mDZ2X);
203 mZ2XBinsDI.push_back(mDZ2XI);
204 mZ2XBinsCenter.push_back((iz + 0.5f) * mDZ2X);
205 LOGF(info, "Bin %i: center (%.3f), half bin width (%.3f)", iz, mZ2XBinsCenter.back(), mZ2XBinsDH.back());
206 }
207 }
208 //
209 mNVoxPerSector = mNY2XBins * mNZ2XBins * mNXBins;
210 LOGF(info, "Each TPC sector is divided into %i voxels", mNVoxPerSector);
211}
212
213//______________________________________________________________________________
215{
216 if (mInitResultsContainer.test(iSec)) {
217 return;
218 }
219 mInitResultsContainer.set(iSec);
220 mVoxelResults[iSec].resize(mNVoxPerSector);
221 for (int ix = 0; ix < mNXBins; ++ix) {
222 for (int ip = 0; ip < mNY2XBins; ++ip) {
223 for (int iz = 0; iz < mNZ2XBins; ++iz) {
224 const size_t binGlb = getGlbVoxBin(ix, ip, iz);
225 VoxRes& resVox = mVoxelResults[iSec][binGlb];
226 resVox.bvox[VoxX] = ix;
227 resVox.bvox[VoxF] = ip;
228 resVox.bvox[VoxZ] = iz;
229 resVox.bsec = iSec;
230 }
231 }
232 }
233 LOG(debug) << "initialized the container for the main results";
234}
235
236//______________________________________________________________________________
238{
239 for (int iSec = 0; iSec < SECTORSPERSIDE * SIDES; ++iSec) {
240 mXBinsIgnore[iSec].reset();
241 std::fill(mVoxelResults[iSec].begin(), mVoxelResults[iSec].end(), VoxRes());
242 std::fill(mValidFracXBins[iSec].begin(), mValidFracXBins[iSec].end(), 0);
243 }
244}
245
246//______________________________________________________________________________
248{
249 int ix;
250 if (x < param::RowX[param::NRowsAccumulated[0] - 1] + param::RowDX[0]) {
251 // we are in the IROC
252 ix = (x - (param::RowX[0] - .5f * param::RowDX[0])) / param::RowDX[0];
253 if (ix < 0) {
254 // x is smaller than the inner radius of the first pad row
255 ix = -1;
256 }
257 } else if (x >= param::RowX[param::NRowsAccumulated[param::NROCTypes - 2]] - .5f * param::RowDX[param::NROCTypes - 1]) {
258 // we are in the OROC3
259 ix = (x - (param::RowX[param::NRowsAccumulated[param::NROCTypes - 2]] - .5f * param::RowDX[param::NROCTypes - 1])) / param::RowDX[param::NROCTypes - 1] + param::NRowsAccumulated[param::NROCTypes - 2];
260 if (ix >= param::NPadRows) {
261 // x is larger than the outer radius of the last OROC pad row
262 ix = -1;
263 }
264 } else if (x > param::RowX[param::NRowsAccumulated[0]] - .5f * param::RowDX[1] && x < param::RowX[param::NRowsAccumulated[1] - 1] + .5f * param::RowDX[1]) {
265 // we are in the OROC1
266 ix = (x - (param::RowX[param::NRowsAccumulated[0]] - .5f * param::RowDX[1])) / param::RowDX[1] + param::NRowsAccumulated[0];
267 } else if (x > param::RowX[param::NRowsAccumulated[1]] - .5f * param::RowDX[2] && x < param::RowX[param::NRowsAccumulated[2] - 1] + .5f * param::RowDX[2]) {
268 // we are in the OROC2
269 ix = (x - (param::RowX[param::NRowsAccumulated[1]] - .5f * param::RowDX[2])) / param::RowDX[2] + param::NRowsAccumulated[1];
270 } else {
271 // x is in one of the gaps between the ROCs
272 ix = -1;
273 }
274 return ix;
275}
276
277bool TrackResiduals::findVoxelBin(int secID, float x, float y, float z, std::array<unsigned char, VoxDim>& bvox) const
278{
279 // Z/X bin
280 if (std::abs(z / x) > mMaxZ2X) {
281 return false;
282 }
283 int bz = getZ2XBinExact(secID < SECTORSPERSIDE ? z / x : -z / x);
284 if (bz < 0) {
285 return false;
286 }
287 // X bin
288 int bx = getXBinExact(x);
289 if (bx < 0 || bx >= mNXBins) {
290 return false;
291 }
292 // Y/X bin
293 int bp = getY2XBinExact(y / x, bx);
294 if (bp < 0 || bp >= mNY2XBins) {
295 return false;
296 }
297
298 bvox[VoxZ] = bz;
299 bvox[VoxX] = bx;
300 bvox[VoxF] = bp;
301 return true;
302}
303
304void TrackResiduals::setKernelType(KernelType kernel, float bwX, float bwP, float bwZ, float scX, float scP, float scZ)
305{
306 // set kernel type and widths in terms of binning in x, y/x, z/x and define aux variables
307 mKernelType = kernel;
308
309 mKernelScaleEdge[VoxX] = scX;
310 mKernelScaleEdge[VoxF] = scP;
311 mKernelScaleEdge[VoxZ] = scZ;
312
313 mKernelWInv[VoxX] = (bwX > 0) ? 1. / bwX : 1.;
314 mKernelWInv[VoxF] = (bwP > 0) ? 1. / bwP : 1.;
315 mKernelWInv[VoxZ] = (bwZ > 0) ? 1. / bwZ : 1.;
316
317 if (mKernelType == KernelType::Epanechnikov) {
318 // bandwidth 1
319 mStepKern[VoxX] = static_cast<int>(nearbyint(bwX + 0.5));
320 mStepKern[VoxF] = static_cast<int>(nearbyint(bwP + 0.5));
321 mStepKern[VoxZ] = static_cast<int>(nearbyint(bwZ + 0.5));
322 } else if (mKernelType == KernelType::Gaussian) {
323 // look in ~5 sigma
324 mStepKern[VoxX] = static_cast<int>(nearbyint(bwX * 5. + 0.5));
325 mStepKern[VoxF] = static_cast<int>(nearbyint(bwP * 5. + 0.5));
326 mStepKern[VoxZ] = static_cast<int>(nearbyint(bwZ * 5. + 0.5));
327 } else {
328 LOG(error) << "given kernel type is not defined";
329 }
330 for (int i = VoxDim; i--;) {
331 if (mStepKern[i] < 1) {
332 mStepKern[i] = 1;
333 }
334 }
335}
336
342
343void TrackResiduals::setStats(const std::vector<TrackResiduals::VoxStats>& statsIn, int iSec)
344{
346 std::vector<VoxRes>& secDataTmp = mVoxelResults[iSec];
347 for (int iVox = 0; iVox < mNVoxPerSector; ++iVox) {
348 secDataTmp[iVox].stat[VoxX] = statsIn[iVox].meanPos[VoxX];
349 secDataTmp[iVox].stat[VoxF] = statsIn[iVox].meanPos[VoxF];
350 secDataTmp[iVox].stat[VoxZ] = statsIn[iVox].meanPos[VoxZ];
351 secDataTmp[iVox].stat[VoxDim] = statsIn[iVox].nEntries;
352 }
353}
354
356{
358 std::vector<VoxRes>& secDataTmp = mVoxelResults[iSec];
359 for (int iVox = 0; iVox < mNVoxPerSector; ++iVox) {
360 const auto& voxStat = mVoxStatsIn[iVox];
361 VoxRes& resVox = secDataTmp[iVox];
362 for (int iDim = VoxDim; iDim--;) {
363 const auto sumStat = (resVox.stat[VoxDim] + voxStat.nEntries);
364 if (sumStat == 0) {
365 continue;
366 }
367 double norm = 1. / sumStat;
368 resVox.stat[iDim] = (resVox.stat[iDim] * resVox.stat[VoxDim] + voxStat.meanPos[iDim] * voxStat.nEntries) * norm;
369 }
370 resVox.stat[VoxDim] += voxStat.nEntries;
371 }
372}
373
374//______________________________________________________________________________
376{
377 LOGP(info, "Processing {} voxel residuals for sector {}", mLocalResidualsIn.size(), iSec);
379 // effective t0 correction changes sign between A-/C-side
380 float effT0corr = (iSec < SECTORSPERSIDE) ? mEffT0Corr : -1. * mEffT0Corr;
381 std::vector<size_t> binData;
382 for (const auto& res : mLocalResidualsIn) {
383 binData.push_back(getGlbVoxBin(res.bvox));
384 }
385 // sort in voxel increasing order
386 std::vector<size_t> binIndices(binData.size());
387 o2::math_utils::SortData(binData, binIndices);
388 // fill the voxel statistics into the results container
389 std::vector<VoxRes>& secData = mVoxelResults[iSec];
390
391 // vectors holding the data for one voxel at a time
392 std::vector<float> dyVec;
393 std::vector<float> dzVec;
394 std::vector<float> tgVec;
395 // assuming we will always have around 1000 entries per voxel
396 dyVec.reserve(1e3);
397 dzVec.reserve(1e3);
398 tgVec.reserve(1e3);
399 size_t currVoxBin = -1;
400 unsigned int nPointsInVox = 0;
401 unsigned int nProcessed = 0;
402 while (nProcessed < binData.size()) {
403 // read all points, voxel by voxel
404 int idx = binIndices[nProcessed];
405 if (currVoxBin != binData[idx]) {
406 if (nPointsInVox) {
407 VoxRes& resVox = secData[currVoxBin];
408 processVoxelResiduals(dyVec, dzVec, tgVec, resVox);
409 }
410 currVoxBin = binData[idx];
411 nPointsInVox = 0;
412 dyVec.clear();
413 dzVec.clear();
414 tgVec.clear();
415 }
416 dyVec.push_back(mLocalResidualsIn[idx].dy * param::MaxResid / 0x7fff);
417 dzVec.push_back(mLocalResidualsIn[idx].dz * param::MaxResid / 0x7fff -
418 mEffVdriftCorr * secData[currVoxBin].stat[VoxZ] * secData[currVoxBin].stat[VoxX] -
419 effT0corr);
420 tgVec.push_back(mLocalResidualsIn[idx].tgSlp * param::MaxTgSlp / 0x7fff);
421
422 ++nPointsInVox;
423 ++nProcessed;
424 }
425 if (nPointsInVox) {
426 // process last voxel
427 VoxRes& resVox = secData[currVoxBin];
428 processVoxelResiduals(dyVec, dzVec, tgVec, resVox);
429 }
430 LOG(info) << "extracted residuals for sector " << iSec;
431
432 int nRowsOK = validateVoxels(iSec);
433 LOG(info) << "number of validated X rows: " << nRowsOK;
434 if (!nRowsOK) {
435 LOG(warning) << "sector " << iSec << ": all X-bins disabled, abandon smoothing";
436 return;
437 } else {
438 smooth(iSec);
439 }
440
441 // process dispersions
442 dyVec.clear();
443 tgVec.clear();
444 currVoxBin = -1;
445 nProcessed = 0;
446 nPointsInVox = 0;
447 while (nProcessed < binData.size()) {
448 int idx = binIndices[nProcessed];
449 if (currVoxBin != binData[idx]) {
450 if (nPointsInVox) {
451 VoxRes& resVox = secData[currVoxBin];
452 if (!getXBinIgnored(iSec, resVox.bvox[VoxX])) {
453 processVoxelDispersions(tgVec, dyVec, resVox);
454 }
455 }
456 currVoxBin = binData[idx];
457 nPointsInVox = 0;
458 dyVec.clear();
459 tgVec.clear();
460 }
461 dyVec.push_back(mLocalResidualsIn[idx].dy * param::MaxResid / 0x7fff);
462 tgVec.push_back(mLocalResidualsIn[idx].tgSlp * param::MaxTgSlp / 0x7fff);
463 ++nPointsInVox;
464 ++nProcessed;
465 }
466 if (nPointsInVox) {
467 // process last voxel
468 VoxRes& resVox = secData[currVoxBin];
469 if (!getXBinIgnored(iSec, resVox.bvox[VoxX])) {
470 processVoxelDispersions(tgVec, dyVec, resVox);
471 }
472 }
473 // smooth dispersions
474 for (int ix = 0; ix < mNXBins; ++ix) {
475 if (getXBinIgnored(iSec, ix)) {
476 continue;
477 }
478 for (int iz = 0; iz < mNZ2XBins; ++iz) {
479 for (int ip = 0; ip < mNY2XBins; ++ip) {
480 int voxBin = getGlbVoxBin(ix, ip, iz);
481 VoxRes& resVox = secData[voxBin];
482 getSmoothEstimate(iSec, resVox.stat[VoxX], resVox.stat[VoxF], resVox.stat[VoxZ], resVox.DS, 0x1 << VoxV);
483 }
484 }
485 }
486 LOG(info) << "Done processing residuals for sector " << iSec;
487 dumpResults(iSec);
488}
489
490//______________________________________________________________________________
491void TrackResiduals::processVoxelResiduals(std::vector<float>& dy, std::vector<float>& dz, std::vector<float>& tg, VoxRes& resVox)
492{
493 int nPoints = dy.size();
494 if (nPoints < mParams->minEntriesPerVoxel) {
495 LOG(debug) << "voxel " << getGlbVoxBin(resVox.bvox) << " is skipped due to too few entries (" << nPoints << " < " << mParams->minEntriesPerVoxel << ")";
496 return;
497 } else {
498 LOGF(debug, "Processing voxel %i with %i entries", getGlbVoxBin(resVox.bvox), nPoints);
499 }
500 std::array<float, 7> zResults;
501 resVox.flags = 0;
502 std::vector<size_t> indices(dz.size());
503 if (!o2::math_utils::LTMUnbinned(dz, indices, zResults, mParams->LTMCut)) {
504 LOG(debug) << "failed trimming input array for voxel " << getGlbVoxBin(resVox.bvox);
505 return;
506 }
507 if (!mParams->isBfieldZero) {
508 std::array<float, 2> res{0.f};
509 std::array<float, 3> err{0.f};
510 float sigMAD = fitPoly1Robust(tg, dy, res, err, mParams->LTMCut);
511 if (sigMAD < 0) {
512 LOG(debug) << "failed robust linear fit, sigMAD = " << sigMAD;
513 return;
514 }
515 float corrErr = err[0] * err[2];
516 corrErr = corrErr > 0 ? err[1] / std::sqrt(corrErr) : -999;
517 //
518 resVox.D[ResX] = -res[1];
519 resVox.D[ResY] = res[0];
520 resVox.D[ResZ] = zResults[1];
521 resVox.E[ResX] = std::sqrt(err[2]);
522 resVox.E[ResY] = std::sqrt(err[0]);
523 resVox.E[ResZ] = zResults[4];
524 resVox.EXYCorr = corrErr;
525 resVox.D[ResD] = resVox.dYSigMAD = sigMAD; // later will be overwritten by real dispersion
526 resVox.dZSigLTM = zResults[2];
527 } else {
528 // for B=0 we cannot disentangle radial distortions from distortions in y,
529 // so simply use average for dy as well and set distortion in X to zero
530 std::array<float, 7> yResults;
531 std::vector<size_t> indicesY(dy.size());
532 if (!o2::math_utils::LTMUnbinned(dy, indicesY, yResults, mParams->LTMCut)) {
533 LOG(debug) << "failed trimming input array for voxel " << getGlbVoxBin(resVox.bvox);
534 return;
535 }
536 resVox.D[ResX] = 0; // force to zero
537 resVox.D[ResY] = yResults[1];
538 resVox.D[ResZ] = zResults[1];
539 resVox.E[ResX] = 0;
540 resVox.E[ResY] = yResults[4];
541 resVox.E[ResZ] = zResults[4];
542 resVox.EXYCorr = 0;
543 resVox.D[ResD] = resVox.dYSigMAD = yResults[2];
544 resVox.dZSigLTM = zResults[2];
545 }
546
547 LOGF(debug, "D[0]=%.2f, D[1]=%.2f, D[2]=%.2f, E[0]=%.2f, E[1]=%.2f, E[2]=%.2f, EXYCorr=%.4f, dYSigMAD=%.3f, dZSigLTM=%.3f",
548 resVox.D[0], resVox.D[1], resVox.D[2], resVox.E[0], resVox.E[1], resVox.E[2], resVox.EXYCorr, resVox.dYSigMAD, resVox.dZSigLTM);
549
550 resVox.flags |= DistDone;
551
552 return;
553}
554
555void TrackResiduals::processVoxelDispersions(std::vector<float>& tg, std::vector<float>& dy, VoxRes& resVox)
556{
557 size_t nPoints = tg.size();
558 LOG(debug) << "processing voxel dispersions for vox " << getGlbVoxBin(resVox.bvox) << " with " << nPoints << " points";
559 if (nPoints < 2) {
560 return;
561 }
562 for (size_t i = nPoints; i--;) {
563 dy[i] -= resVox.DS[ResY] - resVox.DS[ResX] * tg[i];
564 }
565 resVox.D[ResD] = getMAD2Sigma(dy);
566 resVox.E[ResD] = resVox.D[ResD] / sqrt(2.f * nPoints); // a la gaussioan RMS error (very crude)
567 resVox.flags |= DispDone;
568}
569
570//______________________________________________________________________________
572{
573 // apply voxel validation cuts
574 // return number of good voxels for given sector
575 int cntMasked = 0; // number of voxels masked for any reason (either low statistics or bad fit)
576 int cntLowStat = 0; // number of voxels which were not processed due to too low statistics
577 mXBinsIgnore[iSec].reset();
578 std::vector<VoxRes>& secData = mVoxelResults[iSec];
579
580 int cntMaskedFit = 0;
581 int cntMaskedSigma = 0;
582
583 // find bad voxels in sector
584 for (int ix = 0; ix < mNXBins; ++ix) {
585 int cntValid = 0;
586 for (int ip = 0; ip < mNY2XBins; ++ip) {
587 for (int iz = 0; iz < mNZ2XBins; ++iz) {
588 int binGlb = getGlbVoxBin(ix, ip, iz);
589 VoxRes& resVox = secData[binGlb];
590 if ((resVox.flags & DistDone) == 0) {
591 ++cntLowStat;
592 }
593 bool voxelOK = (resVox.flags & DistDone) && !(resVox.flags & Masked);
594 if (voxelOK) {
595 // check fit errors
596 if (resVox.E[ResY] * resVox.E[ResY] > mParams->maxFitErrY2 ||
597 resVox.E[ResX] * resVox.E[ResX] > mParams->maxFitErrX2 ||
598 std::abs(resVox.EXYCorr) > mParams->maxFitCorrXY) {
599 voxelOK = false;
600 ++cntMaskedFit;
601 }
602 // check raw distribution sigmas
603 if (resVox.dYSigMAD > mParams->maxSigY ||
604 resVox.dZSigLTM > mParams->maxSigZ) {
605 voxelOK = false;
606 ++cntMaskedSigma;
607 }
608 }
609 if (voxelOK) {
610 ++cntValid;
611 } else {
612 ++cntMasked;
613 resVox.flags |= Masked;
614 }
615 } // loop over Z
616 } // loop over Y/X
617 mValidFracXBins[iSec][ix] = static_cast<float>(cntValid) / (mNY2XBins * mNZ2XBins);
618 LOGP(debug, "Sector {}: xBin {} has {} % of voxels valid. Total masked due to fit: {} ,and sigma: {}",
619 iSec, ix, mValidFracXBins[iSec][ix] * 100., cntMaskedFit, cntMaskedSigma);
620 } // loop over X
621
622 // mask X-bins which cannot be smoothed
623
624 short nBadReg = 0; // count bad regions (one or more consecutive bad X-bins)
625 std::array<short, param::NPadRows> badStart; // to store indices to the beginnings of the bad regions
626 std::array<short, param::NPadRows> badEnd; // to store indices to the end of the bad regions
627 bool prevBad = false;
628 float fracBadRows = 0.f;
629 for (int ix = 0; ix < mNXBins; ++ix) {
630 if (mValidFracXBins[iSec][ix] < mParams->minValidVoxFracDrift) {
631 LOG(debug) << "row " << ix << " is bad";
632 ++fracBadRows;
633 if (prevBad) {
634 badEnd[nBadReg] = ix;
635 } else {
636 badStart[nBadReg] = ix;
637 badEnd[nBadReg] = ix;
638 prevBad = true;
639 }
640 } else {
641 if (prevBad) {
642 ++nBadReg;
643 prevBad = false;
644 }
645 }
646 }
647 if (prevBad) {
648 ++nBadReg;
649 }
650 fracBadRows /= mNXBins;
651 if (fracBadRows > mParams->maxFracBadRowsPerSector) {
652 LOG(warning) << "sector " << iSec << ": Fraction of bad X-bins: " << fracBadRows << " -> masking whole sector";
653 mXBinsIgnore[iSec].set();
654 } else {
655 for (int iBad = 0; iBad < nBadReg; ++iBad) {
656 LOG(debug) << "masking bad region " << iBad;
657 short badInReg = badEnd[iBad] - badStart[iBad] + 1;
658 short badInNextReg = iBad < (nBadReg - 1) ? badEnd[iBad] - badStart[iBad] + 1 : 0;
659 if (badInReg > mParams->maxBadXBinsToCover) {
660 // disable too large bad patches
661 for (int i = 0; i < badInReg; ++i) {
662 LOG(debug) << "disabling too large patch in bad region " << iBad << ", badStart(" << badStart[iBad] << "), i(" << i << ")";
663 mXBinsIgnore[iSec].set(badStart[iBad] + i);
664 }
665 }
666 if (badInNextReg > mParams->maxBadXBinsToCover && (badStart[iBad + 1] - badEnd[iBad] - 1) < mParams->minGoodXBinsToCover) {
667 // disable too small isolated good patches`
668 for (int i = badEnd[iBad] + 1; i < badStart[iBad + 1]; ++i) {
669 LOG(debug) << "disabling too small good patch before bad region " << iBad + 1 << ", badStart(" << badEnd[iBad] << "), badEnd(" << badStart[iBad + 1] << ")";
670 mXBinsIgnore[iSec].set(i);
671 }
672 }
673 }
674 if (nBadReg) {
675 if (mXBinsIgnore[iSec].test(badStart[0]) && badStart[0] < mParams->minGoodXBinsToCover) {
676 // 1st good patch is too small
677 for (int i = 0; i < badStart[0]; ++i) {
678 LOG(debug) << "disabling too small first good patch badStart(0), badEnd(" << badStart[0] << ")";
679 mXBinsIgnore[iSec].set(i);
680 }
681 }
682 if (mXBinsIgnore[iSec].test(badStart[nBadReg - 1]) && (mNXBins - badEnd[nBadReg - 1] - 1) < mParams->minGoodXBinsToCover) {
683 // last good patch is too small
684 for (int i = badEnd[nBadReg - 1] + 1; i < mNXBins; ++i) {
685 LOG(debug) << "disabling too small last good patch badStart(" << badEnd[nBadReg - 1] << "), badEnd(" << mNXBins << ")";
686 mXBinsIgnore[iSec].set(i);
687 }
688 }
689 }
690 }
691 //
692 int nMaskedRows = mXBinsIgnore[iSec].count();
693 LOGP(info, "Sector {}: out of {} voxels {} are masked. {} (low stat), {} (invalid fit) and {} (raw distrib sigma)",
694 iSec, mNVoxPerSector, cntMasked, cntLowStat, cntMaskedFit, cntMaskedSigma);
695 //
696 return mNXBins - nMaskedRows;
697}
698
700{
701 std::vector<VoxRes>& secData = mVoxelResults[iSec];
702 for (int ix = 0; ix < mNXBins; ++ix) {
703 if (getXBinIgnored(iSec, ix)) {
704 continue;
705 }
706 for (int ip = 0; ip < mNY2XBins; ++ip) {
707 for (int iz = 0; iz < mNZ2XBins; ++iz) {
708 int voxBin = getGlbVoxBin(ix, ip, iz);
709 VoxRes& resVox = secData[voxBin];
710 resVox.flags &= ~SmoothDone;
711 bool res = getSmoothEstimate(resVox.bsec, resVox.stat[VoxX], resVox.stat[VoxF], resVox.stat[VoxZ], resVox.DS, (0x1 << VoxX | 0x1 << VoxF | 0x1 << VoxZ));
712 if (!res) {
713 mNSmoothingFailedBins[iSec]++;
714 } else {
715 resVox.flags |= SmoothDone;
716 }
717 }
718 }
719 }
720 // substract dX contribution to dZ
721 for (int ix = 0; ix < mNXBins; ++ix) {
722 if (getXBinIgnored(iSec, ix)) {
723 continue;
724 }
725 for (int ip = 0; ip < mNY2XBins; ++ip) {
726 for (int iz = 0; iz < mNZ2XBins; ++iz) {
727 int voxBin = getGlbVoxBin(ix, ip, iz);
728 VoxRes& resVox = secData[voxBin];
729 if (!(resVox.flags & SmoothDone)) {
730 continue;
731 }
732 // TODO: Usage of Z/X is bug???
733 float z2x = resVox.stat[VoxZ];
734 if (mDoAdhocCorrectionZ2X) {
735 //
736 const float z = z2x * resVox.stat[VoxX] - resVox.DS[ResZ];
737 const float x = resVox.stat[VoxX] - resVox.DS[ResX]; // is subration of DS[ResX] correct?
738 z2x = z / x;
739 }
740 resVox.DS[ResZ] += z2x * resVox.DS[ResX]; // remove slope*dX contribution from dZ
741 resVox.D[ResZ] += z2x * resVox.DS[ResX]; // remove slope*dX contribution from dZ
742 //
743 if (mAdhocScalingX[iSec >= 18] != 0) {
744 const float aDX = resVox.DS[ResX] * mAdhocScalingX[iSec >= 18];
745 resVox.D[ResX] += aDX;
746 resVox.DS[ResX] += aDX;
747 resVox.D[ResY] += aDX * resVox.stat[VoxF];
748 resVox.DS[ResY] += aDX * resVox.stat[VoxF];
749 resVox.D[ResZ] += aDX * z2x;
750 resVox.DS[ResZ] += aDX * z2x;
751 }
752 }
753 }
754 }
755}
756
757bool TrackResiduals::getSmoothEstimate(int iSec, float x, float p, float z, std::array<float, ResDim>& res, int whichDim)
758{
759 // get smooth estimate for distortions for point in sector coordinates
761
762 std::array<int, VoxDim> minPointsDir{0}; // min number of points per direction
763 const float kTrialStep = 0.5;
764 std::array<bool, ResDim> doDim{false};
765 for (int i = 0; i < ResDim; ++i) {
766 doDim[i] = (whichDim & (0x1 << i)) > 0;
767 if (doDim[i]) {
768 res[i] = 0.f;
769 }
770 }
771
772 int matSize = sSmtLinDim;
773 for (int i = 0; i < VoxDim; ++i) {
774 minPointsDir[i] = 3; // for pol1 smoothing require at least 3 points
775 if (mSmoothPol2[i]) {
776 ++minPointsDir[i];
777 ++matSize;
778 }
779 }
780
781 int ix0, ip0, iz0;
782 findVoxel(x, p, iSec < SECTORSPERSIDE ? z : -z, ix0, ip0, iz0); // find nearest voxel
783 std::vector<VoxRes>& secData = mVoxelResults[iSec];
784 int binCenter = getGlbVoxBin(ix0, ip0, iz0); // global bin of nearest voxel
785 VoxRes& voxCenter = secData[binCenter]; // nearest voxel
786 LOG(debug) << "getting smooth estimate around voxel " << binCenter;
787
788 // cache
789 // \todo maybe a 1-D cache would be more efficient?
790 std::array<std::array<double, sMaxSmtDim*(sMaxSmtDim + 1) / 2>, ResDim> cmat;
791 int maxNeighb = 10 * 10 * 10;
792 std::vector<VoxRes*> currVox;
793 currVox.reserve(maxNeighb);
794 std::vector<float> currCache;
795 currCache.reserve(maxNeighb * VoxHDim);
796
797 std::array<int, VoxDim> maxTrials;
798 maxTrials[VoxZ] = mNZ2XBins / 2;
799 maxTrials[VoxF] = mNY2XBins / 2;
800 maxTrials[VoxX] = mParams->maxBadXBinsToCover * 2;
801
802 std::array<int, VoxDim> trial{0};
803
804 while (true) {
805 std::fill(mLastSmoothingRes.begin(), mLastSmoothingRes.end(), 0);
806 memset(&cmat[0][0], 0, sizeof(cmat));
807
808 int nbOK = 0; // accounted neighbours
809
810 float stepX = mStepKern[VoxX] * (1. + kTrialStep * trial[VoxX]);
811 float stepF = mStepKern[VoxF] * (1. + kTrialStep * trial[VoxF]);
812 float stepZ = mStepKern[VoxZ] * (1. + kTrialStep * trial[VoxZ]);
813
814 if (!(voxCenter.flags & DistDone) || (voxCenter.flags & Masked) || getXBinIgnored(iSec, ix0)) {
815 // closest voxel has no data -> increase smoothing step
816 stepX += kTrialStep * mStepKern[VoxX];
817 stepF += kTrialStep * mStepKern[VoxF];
818 stepZ += kTrialStep * mStepKern[VoxZ];
819 }
820
821 // effective kernel widths accounting for the increased bandwidth at the edges and missing data
822 float kWXI = getDXI(ix0) * mKernelWInv[VoxX] * mStepKern[VoxX] / stepX;
823 float kWFI = getDY2XI(ix0, ip0) * mKernelWInv[VoxF] * mStepKern[VoxF] / stepF;
824 float kWZI = getDZ2XI(iz0) * mKernelWInv[VoxZ] * mStepKern[VoxZ] / stepZ;
825 int iStepX = static_cast<int>(nearbyint(stepX + 0.5));
826 int iStepF = static_cast<int>(nearbyint(stepF + 0.5));
827 int iStepZ = static_cast<int>(nearbyint(stepZ + 0.5));
828 int ixMin = ix0 - iStepX;
829 int ixMax = ix0 + iStepX;
830 if (ixMin < 0) {
831 ixMin = 0;
832 ixMax = std::min(static_cast<int>(nearbyint(ix0 + stepX * mKernelScaleEdge[VoxX])), mNXBins - 1);
833 kWXI /= mKernelScaleEdge[VoxX];
834 }
835 if (ixMax >= mNXBins) {
836 ixMax = mNXBins - 1;
837 ixMin = std::max(static_cast<int>(nearbyint(ix0 - stepX * mKernelScaleEdge[VoxX])), 0);
838 kWXI /= mKernelScaleEdge[VoxX];
839 }
840
841 int ipMin = ip0 - iStepF;
842 int ipMax = ip0 + iStepF;
843 if (ipMin < 0) {
844 ipMin = 0;
845 ipMax = std::min(static_cast<int>(nearbyint(ip0 + stepF * mKernelScaleEdge[VoxF])), mNY2XBins - 1);
846 kWFI /= mKernelScaleEdge[VoxF];
847 }
848 if (ipMax >= mNY2XBins) {
849 ipMax = mNY2XBins - 1;
850 ipMin = std::max(static_cast<int>(nearbyint(ip0 - stepF * mKernelScaleEdge[VoxF])), 0);
851 kWFI /= mKernelScaleEdge[VoxF];
852 }
853
854 int izMin = iz0 - iStepZ;
855 int izMax = iz0 + iStepZ;
856 if (izMin < 0) {
857 izMin = 0;
858 izMax = std::min(static_cast<int>(nearbyint(iz0 + stepZ * mKernelScaleEdge[VoxZ])), mNZ2XBins - 1);
859 kWZI /= mKernelScaleEdge[VoxZ];
860 }
861 if (izMax >= mNZ2XBins) {
862 izMax = mNZ2XBins - 1;
863 izMin = std::max(static_cast<int>(nearbyint(iz0 - stepZ * mKernelScaleEdge[VoxZ])), 0);
864 kWZI /= mKernelScaleEdge[VoxZ];
865 }
866
867 std::vector<unsigned short> nOccX(ixMax - ixMin + 1, 0);
868 std::vector<unsigned short> nOccF(ipMax - ipMin + 1, 0);
869 std::vector<unsigned short> nOccZ(izMax - izMin + 1, 0);
870
871 int nbCheck = (ixMax - ixMin + 1) * (ipMax - ipMin + 1) * (izMax - izMin + 1);
872 if (nbCheck >= maxNeighb) {
873 maxNeighb = nbCheck + 100;
874 currCache.reserve(maxNeighb * VoxHDim);
875 currVox.reserve(maxNeighb);
876 }
877 std::array<double, 3> u2Vec;
878
879 // first loop, check presence of enough points
880 for (int ix = ixMin; ix <= ixMax; ++ix) {
881 for (int ip = ipMin; ip <= ipMax; ++ip) {
882 for (int iz = izMin; iz <= izMax; ++iz) {
883 int binNb = getGlbVoxBin(ix, ip, iz);
884 VoxRes& voxNb = secData[binNb];
885 if (!(voxNb.flags & DistDone) ||
886 (voxNb.flags & Masked) ||
887 getXBinIgnored(iSec, ix)) {
888 // skip voxels w/o data
889 continue;
890 }
891 // estimate weighted distance
892 float dx = voxNb.stat[VoxX] - x;
893 float df = voxNb.stat[VoxF] - p;
894 float dz = voxNb.stat[VoxZ] - z;
895 float dxw = dx * kWXI;
896 float dfw = df * kWFI;
897 float dzw = dz * kWZI;
898 u2Vec[0] = dxw * dxw;
899 u2Vec[1] = dfw * dfw;
900 u2Vec[2] = dzw * dzw;
901 double kernelWeight = getKernelWeight(u2Vec);
902 if (kernelWeight < 1e-6) {
903 continue;
904 }
905 // new point is validated
906 ++nOccX[ix - ixMin];
907 ++nOccF[ip - ipMin];
908 ++nOccZ[iz - izMin];
909 currVox[nbOK] = &voxNb;
910 currCache[nbOK * VoxHDim + VoxX] = dx;
911 currCache[nbOK * VoxHDim + VoxF] = df;
912 currCache[nbOK * VoxHDim + VoxZ] = dz;
913 currCache[nbOK * VoxHDim + VoxV] = kernelWeight;
914 ++nbOK;
915 }
916 }
917 }
918
919 // check if we have enough points in every dimension
920 std::array<int, VoxDim> nPoints{0};
921 for (int i = ixMax - ixMin + 1; i--;) {
922 if (nOccX[i]) {
923 ++nPoints[VoxX];
924 }
925 }
926 for (int i = ipMax - ipMin + 1; i--;) {
927 if (nOccF[i]) {
928 ++nPoints[VoxF];
929 }
930 }
931 for (int i = izMax - izMin + 1; i--;) {
932 if (nOccZ[i]) {
933 ++nPoints[VoxZ];
934 }
935 }
936 bool enoughPoints = true;
937 std::array<bool, VoxDim> incrDone{false};
938 for (int i = 0; i < VoxDim; ++i) {
939 if (nPoints[i] < minPointsDir[i]) {
940 // need to extend smoothing neighbourhood
941 enoughPoints = false;
942 if (trial[i] < maxTrials[i] && !incrDone[i]) {
943 // try to increment only missing direction
944 ++trial[i];
945 incrDone[i] = true;
946 } else if (trial[i] == maxTrials[i]) {
947 // cannot increment missing direction, try others
948 for (int j = VoxDim; j--;) {
949 if (i != j && trial[j] < maxTrials[j] && !incrDone[j]) {
950 ++trial[j];
951 incrDone[j] = true;
952 }
953 }
954 }
955 }
956 }
957
958 if (!enoughPoints) {
959 if (!(incrDone[VoxX] || incrDone[VoxF] || incrDone[VoxZ])) {
960 LOG(error) << fmt::format("trial limit reached, skipping this voxel: incrDone[VoxX] {}, incrDone[VoxF] {}, incrDone[VoxZ] {}", incrDone[VoxX], incrDone[VoxF], incrDone[VoxZ]);
961 return false;
962 }
963 LOG(debug) << "sector " << iSec << ": increasing filter bandwidth around voxel " << binCenter;
964 // printf("Sector:%2d x=%.2f y/x=%.2f z/x=%.2f (iX: %d iY2X:%d iZ2X:%d)\n", iSec, x, p, z, ix0, ip0, iz0);
965 // printf("not enough neighbours (need min %d) %d %d %d (tot: %d) | Steps: %.1f %.1f %.1f\n", 2, nPoints[VoxX], nPoints[VoxF], nPoints[VoxZ], nbOK, stepX, stepF, stepZ);
966 // printf("trying to increase filter bandwidth (trialXFZ: %d %d %d)\n", trial[VoxX], trial[VoxF], trial[VoxZ]);
967 continue;
968 }
969
970 // now fill matrices and solve
971 for (int iNb = 0; iNb < nbOK; ++iNb) {
972 double wiCache = currCache[iNb * VoxHDim + VoxV];
973 double dxi = currCache[iNb * VoxHDim + VoxX];
974 double dfi = currCache[iNb * VoxHDim + VoxF];
975 double dzi = currCache[iNb * VoxHDim + VoxZ];
976 double dxi2 = dxi * dxi;
977 double dfi2 = dfi * dfi;
978 double dzi2 = dzi * dzi;
979 const VoxRes* voxNb = currVox[iNb];
980 for (int iDim = 0; iDim < ResDim; ++iDim) {
981 if (!doDim[iDim]) {
982 continue;
983 }
984 double vi = voxNb->D[iDim];
985 double wi = wiCache;
986 if (mUseErrInSmoothing && std::abs(voxNb->E[iDim]) > 1e-6) {
987 // account for point error apart from kernel value
988 wi /= (voxNb->E[iDim] * voxNb->E[iDim]);
989 }
990 std::array<double, sMaxSmtDim*(sMaxSmtDim + 1) / 2>& cmatD = cmat[iDim];
991 double* rhsD = &mLastSmoothingRes[iDim * sMaxSmtDim];
992 unsigned short iMat = 0;
993 unsigned short iRhs = 0;
994 // linear part
995 cmatD[iMat++] += wi;
996 rhsD[iRhs++] += wi * vi;
997 //
998 cmatD[iMat++] += wi * dxi;
999 cmatD[iMat++] += wi * dxi2;
1000 rhsD[iRhs++] += wi * dxi * vi;
1001 //
1002 cmatD[iMat++] += wi * dfi;
1003 cmatD[iMat++] += wi * dxi * dfi;
1004 cmatD[iMat++] += wi * dfi2;
1005 rhsD[iRhs++] += wi * dfi * vi;
1006 //
1007 cmatD[iMat++] += wi * dzi;
1008 cmatD[iMat++] += wi * dxi * dzi;
1009 cmatD[iMat++] += wi * dfi * dzi;
1010 cmatD[iMat++] += wi * dzi2;
1011 rhsD[iRhs++] += wi * dzi * vi;
1012 //
1013 // check if quadratic part is needed
1014 if (mSmoothPol2[VoxX]) {
1015 cmatD[iMat++] += wi * dxi2;
1016 cmatD[iMat++] += wi * dxi * dxi2;
1017 cmatD[iMat++] += wi * dfi * dxi2;
1018 cmatD[iMat++] += wi * dzi * dxi2;
1019 cmatD[iMat++] += wi * dxi2 * dxi2;
1020 rhsD[iRhs++] += wi * dxi2 * vi;
1021 }
1022 if (mSmoothPol2[VoxF]) {
1023 cmatD[iMat++] += wi * dfi2;
1024 cmatD[iMat++] += wi * dxi * dfi2;
1025 cmatD[iMat++] += wi * dfi * dfi2;
1026 cmatD[iMat++] += wi * dzi * dfi2;
1027 cmatD[iMat++] += wi * dxi2 * dfi2;
1028 cmatD[iMat++] += wi * dfi2 * dfi2;
1029 rhsD[iRhs++] += wi * dfi2 * vi;
1030 }
1031 if (mSmoothPol2[VoxZ]) {
1032 cmatD[iMat++] += wi * dzi2;
1033 cmatD[iMat++] += wi * dxi * dzi2;
1034 cmatD[iMat++] += wi * dfi * dzi2;
1035 cmatD[iMat++] += wi * dzi * dzi2;
1036 cmatD[iMat++] += wi * dxi2 * dzi2;
1037 cmatD[iMat++] += wi * dfi2 * dzi2;
1038 cmatD[iMat++] += wi * dzi2 * dzi2;
1039 rhsD[iRhs++] += wi * dzi2 * vi;
1040 }
1041 }
1042 }
1043
1044 bool fitRes = true;
1045
1046 // solve system of linear equations
1047
1048 TMatrixDSym matrix(matSize);
1049 TDecompChol chol(matSize);
1050 TVectorD rhsVec(matSize);
1051 for (int iDim = 0; iDim < ResDim; ++iDim) {
1052 if (!doDim[iDim]) {
1053 continue;
1054 }
1055 matrix.Zero(); // reset matrix
1056 std::array<double, sMaxSmtDim*(sMaxSmtDim + 1) / 2>& cmatD = cmat[iDim];
1057 double* rhsD = &mLastSmoothingRes[iDim * sMaxSmtDim];
1058 short iMat = -1;
1059 short row = -1;
1060
1061 // with the studid implementation of TMatrixDSym we need to set all elements of the matrix explicitly (or maybe only upper triangle?)
1062 matrix(++row, 0) = cmatD[++iMat];
1063 matrix(++row, 0) = cmatD[++iMat];
1064 matrix(row, 1) = cmatD[++iMat];
1065 matrix(0, row) = matrix(row, 0);
1066 matrix(++row, 0) = cmatD[++iMat];
1067 matrix(row, 1) = cmatD[++iMat];
1068 matrix(row, 2) = cmatD[++iMat];
1069 matrix(0, row) = matrix(row, 0);
1070 matrix(1, row) = matrix(row, 1);
1071 matrix(++row, 0) = cmatD[++iMat];
1072 matrix(row, 1) = cmatD[++iMat];
1073 matrix(row, 2) = cmatD[++iMat];
1074 matrix(row, 3) = cmatD[++iMat];
1075 matrix(0, row) = matrix(row, 0);
1076 matrix(1, row) = matrix(row, 1);
1077 matrix(2, row) = matrix(row, 2);
1078 // add pol2 elements if needed
1079 if (mSmoothPol2[VoxX]) {
1080 const int colLim = (++row) + 1;
1081 for (int iCol = 0; iCol < colLim; ++iCol) {
1082 matrix(row, iCol) = cmatD[++iMat];
1083 matrix(iCol, row) = matrix(row, iCol);
1084 }
1085 }
1086 if (mSmoothPol2[VoxF]) {
1087 const int colLim = (++row) + 1;
1088 for (int iCol = 0; iCol < colLim; ++iCol) {
1089 matrix(row, iCol) = cmatD[++iMat];
1090 matrix(iCol, row) = matrix(row, iCol);
1091 }
1092 }
1093 if (mSmoothPol2[VoxZ]) {
1094 const int colLim = (++row) + 1;
1095 for (int iCol = 0; iCol < colLim; ++iCol) {
1096 matrix(row, iCol) = cmatD[++iMat];
1097 matrix(iCol, row) = matrix(row, iCol);
1098 }
1099 }
1100 rhsVec.SetElements(rhsD);
1101 chol.SetMatrix(matrix);
1102 chol.Decompose();
1103 fitRes = chol.Solve(rhsVec);
1104 if (!fitRes) {
1105 for (int i = VoxDim; i--;) {
1106 trial[i]++;
1107 }
1108 LOG(error) << "solution for smoothing failed, trying to increase filter bandwidth";
1109 continue;
1110 }
1111 res[iDim] = rhsVec[0];
1112 }
1113
1114 break;
1115 }
1116
1117 return true;
1118}
1119
1120double TrackResiduals::getKernelWeight(std::array<double, 3> u2vec) const
1121{
1122 double w = 1.;
1123 if (mKernelType == KernelType::Epanechnikov) {
1124 for (size_t i = u2vec.size(); i--;) {
1125 if (u2vec[i] > 1) {
1126 return 0.;
1127 }
1128 w *= 3. / 4. * (1. - u2vec[i]);
1129 }
1130 } else if (mKernelType == KernelType::Gaussian) {
1131 double u2 = 0.;
1132 for (size_t i = u2vec.size(); i--;) {
1133 u2 += u2vec[i];
1134 }
1135 w = u2 < mParams->maxGaussStdDev * mParams->maxGaussStdDev * u2vec.size() ? std::exp(-u2) / std::sqrt(2. * M_PI) : 0;
1136 }
1137 return w;
1138}
1139
1145
1146float TrackResiduals::fitPoly1Robust(std::vector<float>& x, std::vector<float>& y, std::array<float, 2>& res, std::array<float, 3>& err, float cutLTM) const
1147{
1148 // robust pol1 fit, modifies input arrays order
1149 if (x.size() != y.size()) {
1150 LOG(error) << "x and y must not have different sizes for fitPoly1Robust (" << x.size() << " != " << y.size() << ")";
1151 }
1152 size_t nPoints = x.size();
1153 res[0] = res[1] = 0.f;
1154 if (nPoints < 2) {
1155 return -1;
1156 }
1157 std::array<float, 7> yResults;
1158 std::vector<size_t> indY(nPoints);
1159 if (!o2::math_utils::LTMUnbinned(y, indY, yResults, cutLTM)) {
1160 return -1;
1161 }
1162 // rearrange used events in increasing order
1165 //
1166 // 1st fit to get crude slope
1167 int nPointsUsed = std::lrint(yResults[0]);
1168 int vecOffset = std::lrint(yResults[5]);
1169 // use only entries selected by LTM for the fit
1170 float a, b;
1171 medFit(nPointsUsed, vecOffset, x, y, a, b, err);
1172 //
1173 std::vector<float> ycm(nPoints);
1174 for (size_t i = nPoints; i-- > 0;) {
1175 ycm[i] = y[i] - (a + b * x[i]);
1176 }
1177 std::vector<size_t> indices(nPoints);
1182 //
1183 // robust estimate of sigma after crude slope correction
1184 float sigMAD = getMAD2Sigma({ycm.begin() + vecOffset, ycm.begin() + vecOffset + nPointsUsed});
1185 // find LTM estimate matching to sigMAD, keaping at least given fraction
1186 if (!o2::math_utils::LTMUnbinnedSig(ycm, indY, yResults, mParams->minFracLTM, sigMAD, true)) {
1187 return -1;
1188 }
1189 // final fit
1190 nPointsUsed = std::lrint(yResults[0]);
1191 vecOffset = std::lrint(yResults[5]);
1192 medFit(nPointsUsed, vecOffset, x, y, a, b, err);
1193 res[0] = a;
1194 res[1] = b;
1195 return sigMAD;
1196}
1197
1198//___________________________________________________________________
1199void TrackResiduals::medFit(int nPoints, int offset, const std::vector<float>& x, const std::vector<float>& y, float& a, float& b, std::array<float, 3>& err) const
1200{
1201 // fitting a straight line y(x|a, b) = a + b * x
1202 // to given x and y data minimizing the absolute deviation
1203 float aa, bb, chi2 = 0.f;
1204 if (nPoints < 2) {
1205 a = b = 0.f;
1206 err[0] = err[1] = err[2] = 999.f;
1207 return;
1208 }
1209 // do least squares minimization as first guess
1210 float sx = 0.f, sxx = 0.f, sy = 0.f, sxy = 0.f;
1211 for (int j = nPoints + offset; j-- > offset;) { // same order as in AliRoot version such that resulting sums are identical
1212 sx += x[j];
1213 sxx += x[j] * x[j];
1214 sy += y[j];
1215 sxy += x[j] * y[j];
1216 }
1217 float del = nPoints * sxx - sx * sx;
1218 float delI = 1. / del;
1219 aa = (sxx * sy - sx * sxy) * delI;
1220 bb = (nPoints * sxy - sx * sy) * delI;
1221 err[0] = sxx * delI;
1222 err[1] = sx * delI;
1223 err[2] = nPoints * delI;
1224
1225 for (int j = nPoints + offset; j-- > offset;) {
1226 float tmp = y[j] - (aa + bb * x[j]);
1227 chi2 += tmp * tmp;
1228 }
1229 float sigb = std::sqrt(chi2 * delI); // expected sigma for b
1230 float b1 = bb;
1231 float f1 = roFunc(nPoints, offset, x, y, b1, aa);
1232 if (sigb > 0) {
1233 float b2 = bb + std::copysign(3.f * sigb, f1);
1234 float f2 = roFunc(nPoints, offset, x, y, b2, aa);
1235 if (std::abs(f1 - f2) < sFloatEps) {
1236 a = aa;
1237 b = bb;
1238 return;
1239 }
1240 while (f1 * f2 > 0.f) {
1241 bb = b2 + 1.6f * (b2 - b1);
1242 b1 = b2;
1243 f1 = f2;
1244 b2 = bb;
1245 f2 = roFunc(nPoints, offset, x, y, b2, aa);
1246 }
1247 sigb = .01f * sigb;
1248 while (std::abs(b2 - b1) > sigb) {
1249 bb = b1 + .5f * (b2 - b1);
1250 if (bb == b1 || bb == b2) {
1251 break;
1252 }
1253 float f = roFunc(nPoints, offset, x, y, bb, aa);
1254 if (f * f1 >= .0f) {
1255 f1 = f;
1256 b1 = bb;
1257 } else {
1258 f2 = f;
1259 b2 = bb;
1260 }
1261 }
1262 }
1263 a = aa;
1264 b = bb;
1265}
1266
1267float TrackResiduals::roFunc(int nPoints, int offset, const std::vector<float>& x, const std::vector<float>& y, float b, float& aa) const
1268{
1269 // calculate sum(x_i * sgn(y_i - a - b * x_i)) for given b
1270 // see numberical recipies paragraph 15.7.3
1271 std::vector<float> vecTmp(nPoints);
1272 float sum = 0.f;
1273 for (int j = nPoints; j-- > 0;) {
1274 vecTmp[j] = y[j + offset] - b * x[j + offset];
1275 }
1276 int nPointsHalf = nPoints / 2;
1277 if (nPoints < 20) { // it is faster to do insertion sort
1278 for (int i = 1; i < nPoints; i++) {
1279 float v = vecTmp[i];
1280 int j;
1281 for (j = i; j--;) {
1282 if (vecTmp[j] > v) {
1283 vecTmp[j + 1] = vecTmp[j];
1284 } else {
1285 break;
1286 }
1287 }
1288 vecTmp[j + 1] = v;
1289 }
1290 aa = (nPoints & 0x1) ? vecTmp[nPointsHalf] : .5f * (vecTmp[nPointsHalf - 1] + vecTmp[nPointsHalf]);
1291 } else {
1292 std::vector<float>::iterator nth = vecTmp.begin() + vecTmp.size() / 2;
1293 if (nPoints & 0x1) {
1294 std::nth_element(vecTmp.begin(), nth, vecTmp.end());
1295 aa = *nth;
1296 } else {
1297 std::nth_element(vecTmp.begin(), nth - 1, vecTmp.end());
1298 std::nth_element(nth, nth, vecTmp.end());
1299 aa = 0.5 * (*(nth - 1) + *(nth));
1300 }
1301 // aa = (nPoints & 0x1) ? selectKthMin(nPointsHalf, vecTmp) : .5f * (selectKthMin(nPointsHalf - 1, vecTmp) + selectKthMin(nPointsHalf, vecTmp));
1302 }
1303 for (int j = nPoints; j-- > 0;) {
1304 float d = y[j + offset] - (b * x[j + offset] + aa);
1305 if (y[j + offset] != 0.f) {
1306 d /= std::abs(y[j + offset]);
1307 }
1308 if (std::abs(d) > sFloatEps) {
1309 sum += (d >= 0.f ? x[j + offset] : -x[j + offset]);
1310 }
1311 }
1312 return sum;
1313}
1314
1315//______________________________________________________________________________
1316float TrackResiduals::selectKthMin(const int k, std::vector<float>& data) const
1317{
1318 // Returns the k th smallest value in the vector. The input vector will be rearranged
1319 // to have this value in location data[k] , with all smaller elements moved before it
1320 // (in arbitrary order) and all larger elements after (also in arbitrary order).
1321 // From Numerical Recipes in C++ (paragraph 8.5)
1322 // Probably it is not needed anymore, since std::nth_element() can also be used
1323
1324 int i, ir, j, l, mid, n = data.size();
1325 float a; // partitioning element
1326 l = 0; // left hand side of active partition
1327 ir = n - 1; // right hand side of active partition
1328
1329 while (true) {
1330 if (ir <= l + 1) { // active partition with 1 or 2 elements
1331 if (ir == l + 1 && data[ir] < data[l]) { // case of 2 elements
1332 std::swap(data[l], data[ir]);
1333 }
1334 return data[k];
1335 } else {
1336 mid = (l + ir) >> 1;
1337 std::swap(data[mid], data[l + 1]);
1338 if (data[l] > data[ir]) {
1339 std::swap(data[l], data[ir]);
1340 }
1341 if (data[l + 1] > data[ir]) {
1342 std::swap(data[l + 1], data[ir]);
1343 }
1344 if (data[l] > data[l + 1]) {
1345 std::swap(data[l], data[l + 1]);
1346 }
1347 i = l + 1; // initialize pointers for partitioning
1348 j = ir;
1349 a = data[l + 1];
1350 while (true) {
1351 // innermost loop used for partitioning
1352 do {
1353 i++;
1354 } while (data[i] < a);
1355 do {
1356 j--;
1357 } while (data[j] > a);
1358 if (j < i) {
1359 // pointers crossed -> partitioning complete
1360 break;
1361 }
1362 std::swap(data[i], data[j]);
1363 }
1364 data[l + 1] = data[j];
1365 data[j] = a;
1366 // keep the partition which contains kth element active
1367 if (j >= k) {
1368 ir = j - 1;
1369 }
1370 if (j <= k) {
1371 l = i;
1372 }
1373 }
1374 }
1375}
1376
1377//___________________________________________________________________
1378float TrackResiduals::getMAD2Sigma(std::vector<float> data) const
1379{
1380 // Sigma calculated from median absolute deviations
1381 // see: https://en.wikipedia.org/wiki/Median_absolute_deviation
1382 // the data is passed by value (copied!), such that the original vector
1383 // is not rearranged
1384
1385 int nPoints = data.size();
1386 if (nPoints < 2) {
1387 return 0;
1388 }
1389
1390 // calculate median of the input data
1391 float medianOfData;
1392 std::vector<float>::iterator nth = data.begin() + data.size() / 2;
1393 if (nPoints & 0x1) {
1394 std::nth_element(data.begin(), nth, data.end());
1395 medianOfData = *nth;
1396 } else {
1397 std::nth_element(data.begin(), nth - 1, data.end());
1398 std::nth_element(nth, nth, data.end());
1399 medianOfData = .5f * (*(nth - 1) + (*nth));
1400 }
1401
1402 // fill vector with absolute deviations to median
1403 for (auto& entry : data) {
1404 entry = std::abs(entry - medianOfData);
1405 }
1406
1407 // calculate median of abs deviations
1408 float medianOfAbsDeviations;
1409 if (nPoints & 0x1) {
1410 std::nth_element(data.begin(), nth, data.end());
1411 medianOfAbsDeviations = *nth;
1412 } else {
1413 std::nth_element(data.begin(), nth - 1, data.end());
1414 std::nth_element(nth, nth, data.end());
1415 medianOfAbsDeviations = .5f * (*(nth - 1) + (*nth));
1416 }
1417
1418 float k = 1.4826f; // scale factor for normally distributed data
1419 return k * medianOfAbsDeviations;
1420}
1421
1423{
1424 // this fast algebraic circle fit is described here:
1425 // https://dtcenter.org/met/users/docs/write_ups/circle_fit.pdf
1426 double xMean = 0., yMean = 0.;
1427 int ncl = params.points.size();
1428 for (const auto& pnt : params.points) {
1429 xMean += pnt.xLab;
1430 yMean += pnt.yLab;
1431 }
1432 xMean /= ncl;
1433 yMean /= ncl;
1434 // define sums needed for circular fit
1435 double su2 = 0., sv2 = 0., suv = 0., su3 = 0., sv3 = 0., su2v = 0., suv2 = 0.;
1436 for (const auto& pnt : params.points) {
1437 double ui = pnt.xLab - xMean;
1438 double vi = pnt.yLab - yMean;
1439 double ui2 = ui * ui;
1440 double vi2 = vi * vi;
1441 suv += ui * vi;
1442 su2 += ui2;
1443 sv2 += vi2;
1444 su3 += ui2 * ui;
1445 sv3 += vi2 * vi;
1446 su2v += ui2 * vi;
1447 suv2 += ui * vi2;
1448 }
1449 double rhsU = .5f * (su3 + suv2);
1450 double rhsV = .5f * (sv3 + su2v);
1451 double det = su2 * sv2 - suv * suv;
1452 double uc = (rhsU * sv2 - rhsV * suv) / det;
1453 double vc = (su2 * rhsV - suv * rhsU) / det;
1454 double r2 = uc * uc + vc * vc + (su2 + sv2) / ncl;
1455 params.xcLab = uc + xMean;
1456 params.ycLab = vc + yMean;
1457 params.r = sqrt(r2);
1458 // write residuals to residHelixY
1459 for (auto& pnt : params.points) {
1460 double dx = pnt.xLab - params.xcLab;
1461 double dxr = r2 - dx * dx;
1462 double ys = dxr > 0 ? sqrt(dxr) : 0.f; // distance of point in y from the circle center (using fit results for r and xc)
1463 double dy = pnt.yLab - params.ycLab; // distance of point in y from the circle center (using fit result for yc)
1464 double dysp = dy - ys;
1465 double dysm = dy + ys;
1466 pnt.residHelixY = std::abs(dysp) < std::abs(dysm) ? dysp : dysm;
1467 }
1468}
1469
1470void fitCircle(int nCl, std::array<float, param::NPadRows>& x, std::array<float, param::NPadRows>& y, float& xc, float& yc, float& r, std::array<float, param::NPadRows>& residHelixY)
1471{
1472 // this fast algebraic circle fit is described here:
1473 // https://dtcenter.org/met/users/docs/write_ups/circle_fit.pdf
1474 float xMean = 0.f;
1475 float yMean = 0.f;
1476
1477 for (int i = nCl; i--;) {
1478 xMean += x[i];
1479 yMean += y[i];
1480 }
1481 xMean /= nCl;
1482 yMean /= nCl;
1483 // define sums needed for circular fit
1484 float su2 = 0.f, sv2 = 0.f, suv = 0.f, su3 = 0.f, sv3 = 0.f, su2v = 0.f, suv2 = 0.f;
1485 for (int i = nCl; i--;) {
1486 float ui = x[i] - xMean;
1487 float vi = y[i] - yMean;
1488 float ui2 = ui * ui;
1489 float vi2 = vi * vi;
1490 suv += ui * vi;
1491 su2 += ui2;
1492 sv2 += vi2;
1493 su3 += ui2 * ui;
1494 sv3 += vi2 * vi;
1495 su2v += ui2 * vi;
1496 suv2 += ui * vi2;
1497 }
1498 float rhsU = .5f * (su3 + suv2);
1499 float rhsV = .5f * (sv3 + su2v);
1500 float det = su2 * sv2 - suv * suv;
1501 float uc = (rhsU * sv2 - rhsV * suv) / det;
1502 float vc = (su2 * rhsV - suv * rhsU) / det;
1503 float r2 = uc * uc + vc * vc + (su2 + sv2) / nCl;
1504 xc = uc + xMean;
1505 yc = vc + yMean;
1506 r = sqrt(r2);
1507 // write residuals to residHelixY
1508 for (int i = nCl; i--;) {
1509 float dx = x[i] - xc;
1510 float dxr = r2 - dx * dx;
1511 float ys = dxr > 0 ? sqrt(dxr) : 0.f; // distance of point in y from the circle center (using fit results for r and xc)
1512 float dy = y[i] - yc; // distance of point in y from the circle center (using fit result for yc)
1513 float dysp = dy - ys;
1514 float dysm = dy + ys;
1515 residHelixY[i] = std::abs(dysp) < std::abs(dysm) ? dysp : dysm;
1516 }
1517 // printf("r = %.4f m, xc = %.4f, yc = %.4f\n", r/100.f, xc, yc);
1518}
1519
1521{
1522 // fit a straight line y = ax + b to a given set of points (x,y)
1523 // no measurement errors assumed, no fit errors calculated
1524 // res[0] = a (slope)
1525 // res[1] = b (offset)
1526 int ncl = params.points.size();
1527 if (ncl < 2) {
1528 // not enough points
1529 return false;
1530 }
1531 double sumX = 0., sumY = 0., sumXY = 0., sumX2 = 0., nInv = 1. / ncl;
1532 for (const auto& pnt : params.points) {
1533 sumX += pnt.sPath;
1534 sumY += pnt.zTrk;
1535 sumXY += pnt.sPath * pnt.zTrk;
1536 sumX2 += pnt.sPath * pnt.sPath;
1537 }
1538 auto det = sumX2 - nInv * sumX * sumX;
1539 if (std::abs(det) < 1e-12) {
1540 return false;
1541 }
1542 params.tgl = (sumXY - nInv * sumX * sumY) / det;
1543 params.zOffs = nInv * sumY - nInv * params.tgl * sumX;
1544 return true;
1545}
1546
1547bool TrackResiduals::fitPoly1(int nCl, std::array<float, param::NPadRows>& x, std::array<float, param::NPadRows>& y, std::array<float, 2>& res)
1548{
1549 // fit a straight line y = ax + b to a given set of points (x,y)
1550 // no measurement errors assumed, no fit errors calculated
1551 // res[0] = a (slope)
1552 // res[1] = b (offset)
1553 if (nCl < 2) {
1554 // not enough points
1555 return false;
1556 }
1557 float sumX = 0.f, sumY = 0.f, sumXY = 0.f, sumX2 = 0.f, nInv = 1.f / nCl;
1558 for (int i = nCl; i--;) {
1559 sumX += x[i];
1560 sumY += y[i];
1561 sumXY += x[i] * y[i];
1562 sumX2 += x[i] * x[i];
1563 }
1564 float det = sumX2 - nInv * sumX * sumX;
1565 if (std::abs(det) < 1e-12f) {
1566 return false;
1567 }
1568 res[0] = (sumXY - nInv * sumX * sumY) / det;
1569 res[1] = nInv * sumY - nInv * res[0] * sumX;
1570 return true;
1571}
1572
1578
1580{
1581 if (getNVoxelsPerSector() == 0) {
1582 LOG(warn) << "For the tree aliases to work you must initialize the binning before calling createOutputFile()";
1583 }
1584 mFileOut = std::make_unique<TFile>(filename, "recreate");
1585 mTreeOut = std::make_unique<TTree>("voxResTree", "Voxel results and statistics");
1586 mTreeOut->SetAlias("z2xBin", "bvox[0]");
1587 mTreeOut->SetAlias("y2xBin", "bvox[1]");
1588 mTreeOut->SetAlias("xBin", "bvox[2]");
1589 mTreeOut->SetAlias("z2xAV", "stat[0]");
1590 mTreeOut->SetAlias("y2xAV", "stat[1]");
1591 mTreeOut->SetAlias("xAV", "stat[2]");
1592 mTreeOut->SetAlias("fsector", "bsec+0.5+9.*(y2xAV)/pi");
1593 mTreeOut->SetAlias("phi", "(bsec%18+0.5+9.*(stat[1])/pi)/9*pi");
1594 mTreeOut->SetAlias("r", "stat[2]");
1595 mTreeOut->SetAlias("z", "z2xAV*xAV");
1596 mTreeOut->SetAlias("dX", "D[0]");
1597 mTreeOut->SetAlias("dY", "D[1]");
1598 mTreeOut->SetAlias("dZ", "D[2]");
1599 mTreeOut->SetAlias("dXS", "DS[0]");
1600 mTreeOut->SetAlias("dYS", "DS[1]");
1601 mTreeOut->SetAlias("dZS", "DS[2]");
1602 mTreeOut->SetAlias("dXE", "E[0]");
1603 mTreeOut->SetAlias("dYE", "E[1]");
1604 mTreeOut->SetAlias("dZE", "E[2]");
1605 mTreeOut->SetAlias("voxelIndex", Form("xBin + %i * (y2xBin + %i * z2xBin) + %i * bsec", getNXBins(), getNY2XBins(), getNVoxelsPerSector()));
1606 mTreeOut->SetAlias("entries", "stat[3]");
1607 mTreeOut->SetAlias("fitOK", Form("(flags & %u) == %u", DistDone, DistDone));
1608 mTreeOut->SetAlias("dispOK", Form("(flags & %u) == %u", DispDone, DispDone));
1609 mTreeOut->SetAlias("smtOK", Form("(flags & %u) == %u", SmoothDone, SmoothDone));
1610 mTreeOut->SetAlias("masked", Form("(flags & %u) == %u", Masked, Masked));
1611 mTreeOut->Branch("voxRes", &mVoxelResultsOutPtr);
1612}
1613
1615{
1616 mFileOut->cd();
1617 mTreeOut->Write();
1618 mTreeOut.reset();
1619 mFileOut->Close();
1620 mFileOut.reset();
1621}
1622
1624{
1625 if (mTreeOut) {
1626 printf("Dumping results for sector %i. Don't forget the call to closeOutputFile() in the end...\n", iSec);
1627 for (int i = 0; i < mNVoxPerSector; ++i) {
1628 mVoxelResultsOut = mVoxelResults[iSec][i];
1629 mTreeOut->Fill();
1630 }
1631 }
1632}
1633
1635{
1636 static float mres = 0, mvir = 0, mres0 = 0, mvir0 = 0;
1637 static ProcInfo_t procInfo;
1638 static TStopwatch sw;
1639 const Long_t kMB = 1024;
1640 gSystem->GetProcInfo(&procInfo);
1641 mres = float(procInfo.fMemResident) / kMB;
1642 mvir = float(procInfo.fMemVirtual) / kMB;
1643 sw.Stop();
1644 printf("RSS: %.3f(%.3f) VMEM: %.3f(%.3f) MB | CpuTime:%.3f RealTime:%.3f s\n",
1645 mres, mres - mres0, mvir, mvir - mvir0, sw.CpuTime(), sw.RealTime());
1646 mres0 = mres;
1647 mvir0 = mvir;
1648 sw.Start();
1649}
1650
1652{
1653 getLocalResVec().clear();
1654 mInitResultsContainer.reset();
1655}
Base track model for the Barrel, params only, w/o covariance.
std::ostringstream debug
std::vector< double > sum
int32_t i
Timeframe settings Average distance in cm of events merged into Randomize distance around average distance of merged events Simulate correct bunch interactions instead of placing only the average number of events A value[n] sets TFlen for[n] collisions in Number of bunches per trainsort Number of bunch trains Instantaneous interaction rate Do not simulate border Total number of collisions to be placed in the interior of all time Do not select random but walk over array of events in stride steps h Event generator settings h configStandalone Use CPU for GPU Force usage of the specified GPU device no CPU fallback Continue processing after an error First event to Number of events to Number of iterations to Number of iterations to Number of initial iterations excluded from const char e
Timeframe settings Average distance in cm of events merged into Randomize distance around average distance of merged events Simulate correct bunch interactions instead of placing only the average number of events A value[n] sets TFlen for[n] collisions in Number of bunches per trainsort Number of bunch trains Instantaneous interaction rate Do not simulate border Total number of collisions to be placed in the interior of all time Do not select random but walk over array of events in stride steps h Event generator settings h configStandalone Use CPU for GPU Force usage of the specified GPU device no CPU fallback Continue processing after an error First event to Number of events to Number of iterations to Number of iterations to Number of initial iterations excluded from const char Directory with events to Events directory is and not inside events d
configStandalone Reconstruction rec_tpc float
const int16_t bb
char error[CURL_ERROR_SIZE]
float chi2
bounded_vector< float > bins
useful math constants
uint32_t j
Definition RawData.h:0
uint32_t res
Definition RawData.h:0
void fitCircle(int nCl, std::array< float, param::NPadRows > &x, std::array< float, param::NPadRows > &y, float &xc, float &yc, float &r, std::array< float, param::NPadRows > &residHelixY)
Definition of the TrackResiduals class.
static Mapper & instance(const std::string mappingDir="")
Definition Mapper.h:44
static constexpr unsigned REGION[PADROWS]
region for global pad row
Definition Mapper.h:537
std::vector< LocalResid > & getLocalResVec()
float getDZ2XI(int iz=0) const
void setKernelType(KernelType kernel=KernelType::Epanechnikov, float bwX=2.1f, float bwP=2.1f, float bwZ=1.7f, float scX=1.f, float scP=1.f, float scZ=1.f)
bool getSmoothEstimate(int iSec, float x, float p, float z, std::array< float, ResDim > &res, int whichDim=0)
int getXBinExact(float x) const
void setY2XBinning(const std::vector< float > &binning)
void processSectorResiduals(Int_t iSec)
static bool fitPoly1(int nCl, std::array< float, param::NPadRows > &x, std::array< float, param::NPadRows > &y, std::array< float, 2 > &res)
size_t getGlbVoxBin(int ix, int ip, int iz) const
void setStats(const std::vector< TrackResiduals::VoxStats > &statsIn, int iSec)
Set the voxel statistics directly from outside.
void closeOutputFile()
Closes the file with the debug output.
void clear()
clear member to be able to process new sector or new input files
float getX(int ix) const
void fillStats(int iSec)
Fill statistics from TTree.
void printMem() const
Prints the current memory usage.
void setZ2XBinning(const std::vector< float > &binning)
void initResultsContainer(int iSec)
@ DispDone
voxel dispersions have been processed
@ DistDone
voxel residuals have been processed
@ SmoothDone
voxel has been smoothed
int getNVoxelsPerSector() const
Get the total number of voxels per TPC sector (mNXBins * mNY2XBins * mNZ2XBins)
int getRowID(float x) const
void createOutputFile(const char *filename="debugVoxRes.root")
Creates a file for the debug output.
float getDY2XI(int ix, int iy=0) const
void findVoxel(float x, float y2x, float z2x, int &ix, int &ip, int &iz) const
float roFunc(int nPoints, int offset, const std::vector< float > &x, const std::vector< float > &y, float b, float &aa) const
void medFit(int nPoints, int offset, const std::vector< float > &x, const std::vector< float > &y, float &a, float &b, std::array< float, 3 > &err) const
static void fitCircle(int nCl, std::array< float, param::NPadRows > &x, std::array< float, param::NPadRows > &y, float &xc, float &yc, float &r, std::array< float, param::NPadRows > &residHelixY)
float getDXI(int ix) const
float fitPoly1Robust(std::vector< float > &x, std::vector< float > &y, std::array< float, 2 > &res, std::array< float, 3 > &err, float cutLTM) const
int getY2XBinExact(float y2x, int ix) const
bool findVoxelBin(int secID, float x, float y, float z, std::array< unsigned char, VoxDim > &bvox) const
Calculates the bin indices for given x, y, z in sector coordinates.
bool getXBinIgnored(int iSec, int bin) const
@ VoxDim
dimensionality of the voxels
@ VoxV
voxel dispersions
void initBinning()
Initializes the binning in X, Y/X and Z.
float getMAD2Sigma(std::vector< float > data) const
void reset()
Resets all (also intermediate) results.
int getZ2XBinExact(float z2x) const
double getKernelWeight(std::array< double, 3 > u2vec) const
void processVoxelDispersions(std::vector< float > &tg, std::vector< float > &dy, VoxRes &resVox)
void init(bool doBinning=true)
float selectKthMin(const int k, std::vector< float > &data) const
@ ResD
index for dispersions
void setNY2XBins(int nBins)
void setNZ2XBins(int nBins)
void processVoxelResiduals(std::vector< float > &dy, std::vector< float > &dz, std::vector< float > &tg, VoxRes &resVox)
GLdouble n
Definition glcorearb.h:1982
GLint GLenum GLint x
Definition glcorearb.h:403
GLuint GLuint end
Definition glcorearb.h:469
const GLdouble * v
Definition glcorearb.h:832
GLdouble f
Definition glcorearb.h:310
GLboolean GLboolean GLboolean b
Definition glcorearb.h:1233
GLint y
Definition glcorearb.h:270
GLenum const GLfloat * params
Definition glcorearb.h:272
GLintptr offset
Definition glcorearb.h:660
GLsizei GLenum const void * indices
Definition glcorearb.h:400
GLboolean GLboolean GLboolean GLboolean a
Definition glcorearb.h:1233
GLubyte GLubyte GLubyte GLubyte w
Definition glcorearb.h:852
GLdouble GLdouble GLdouble z
Definition glcorearb.h:843
GPUdoubleBinary64 b
void Reorder(std::vector< T > &data, const std::vector< size_t > &index)
Definition fit.h:626
bool LTMUnbinnedSig(const std::vector< T > &data, std::vector< size_t > &index, std::array< float, 7 > &params, float fracKeepMin, float sigTgt, bool sorted=false)
Definition fit.h:649
void SortData(std::vector< T > const &values, std::vector< size_t > &index)
Definition fit.h:540
bool LTMUnbinned(const std::vector< T > &data, std::vector< size_t > &index, std::array< float, 7 > &params, float fracKeep)
Definition fit.h:570
Global TPC definitions and constants.
Enum< T >::Iterator begin(Enum< T >)
Definition Defs.h:158
FIXME: do not use data model tables.
std::string filename()
float minFracLTM
minimum fraction of points to keep when trimming data to fit expected sigma
float maxFitErrX2
maximum fit error for X2
float minValidVoxFracDrift
if more than this fraction of bins are bad for one pad row the whole pad row is declared bad
float maxGaussStdDev
maximum number of sigmas to be considered for gaussian kernel smoothing
int minEntriesPerVoxel
minimum number of points in voxel for processing
float LTMCut
fraction op points to keep when trimming input data
int maxBadXBinsToCover
a lower number of consecutive bad X bins will not be declared bad
float maxFitErrY2
maximum fit error for Y2
float maxSigY
maximum sigma for y of the voxel
float maxFitCorrXY
maximum fit correlation for x and y
float maxSigZ
maximum sigma for z of the voxel
int minGoodXBinsToCover
minimum number of consecutive good bins, otherwise bins are declared bad
float maxFracBadRowsPerSector
maximum fraction of bad rows before whole sector is masked
bool isBfieldZero
for B=0 we set the radial distortions to zero and don't fit dy vs tgSlp
Structure which gets filled with the results for each voxel.
std::array< unsigned char, VoxDim > bvox
voxel identifier: VoxZ, VoxF, VoxX
std::array< float, ResDim > E
their errors
float dYSigMAD
MAD estimator of dY sigma (dispersion after slope removal)
std::array< float, ResDim > D
values of extracted distortions
float dZSigLTM
Z sigma from unbinned LTM estimator.
unsigned char flags
status flag
std::array< float, ResDim > DS
smoothed residual
unsigned char bsec
sector ID (0-35)
float EXYCorr
correlation between extracted X and Y
std::array< float, VoxHDim > stat
statistics: averages of each voxel dimension + entries
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
o2::InteractionRecord ir(0, 0)
TStopwatch sw
std::vector< int > row