Project
Loading...
Searching...
No Matches
TPCFastSpaceChargeCorrectionHelper.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
14
16
17#include "TPCBase/Mapper.h"
22#include "TPCBase/Sector.h"
24#include "DataFormatsTPC/Defs.h"
25#include "TPCFastTransform.h"
26#include "ChebyshevFit1D.h"
27#include "Spline2DHelper.h"
28#include "Riostream.h"
29#include <fairlogger/Logger.h>
30#include <thread>
31#include "TStopwatch.h"
32#include "TTreeReader.h"
33#include "TTreeReaderValue.h"
34#include "ROOT/TTreeProcessorMT.hxx"
35#include <algorithm>
36#include <sstream>
37
38using namespace o2::gpu;
39
40namespace o2
41{
42namespace tpc
43{
44
45TPCFastSpaceChargeCorrectionHelper* TPCFastSpaceChargeCorrectionHelper::sInstance = nullptr;
46
48{
49 // returns TPCFastSpaceChargeCorrectionHelper instance (singleton)
50 if (!sInstance) {
51 sInstance = new TPCFastSpaceChargeCorrectionHelper();
52 sInstance->initGeometry();
53 }
54 return sInstance;
55}
56
57void TPCFastSpaceChargeCorrectionHelper::initGeometry()
58{
59 // initialize geometry
60
61 const Mapper& mapper = Mapper::instance();
62
63 const int nRows = mapper.getNumberOfRows();
64
65 mGeo.startConstruction(nRows);
66
67 auto& detParam = ParameterDetector::Instance();
68
69 mGeo.setTPCzLength(detParam.TPClength);
70
71 for (int iRow = 0; iRow < mGeo.getNumberOfRows(); iRow++) {
72 Sector sector = 0;
73 int regionNumber = 0;
74 while (iRow >= mapper.getGlobalRowOffsetRegion(regionNumber) + mapper.getNumberOfRowsRegion(regionNumber)) {
75 regionNumber++;
76 }
77
78 const PadRegionInfo& region = mapper.getPadRegionInfo(regionNumber);
79
80 int nPads = mapper.getNumberOfPadsInRowSector(iRow);
81 float padWidth = region.getPadWidth();
82
83 const GlobalPadNumber pad = mapper.globalPadNumber(PadPos(iRow, nPads / 2));
84 const PadCentre& padCentre = mapper.padCentre(pad);
85 float xRow = padCentre.X();
86
87 mGeo.setTPCrow(iRow, xRow, nPads, padWidth);
88 }
89
90 mGeo.finishConstruction();
91
92 // check if calculated pad geometry is consistent with the map
93 testGeometry(mGeo);
94
95 mIsInitialized = 1;
96}
97
99{
100 LOG(info) << "fast space charge correction helper: use " << n << ((n > 1) ? " cpu threads" : " cpu thread");
101 mNthreads = (n > 0) ? n : 1;
102}
103
105{
107
108 mNthreads = std::thread::hardware_concurrency();
109
110 LOG(info) << "fast space charge correction helper: use " << mNthreads << ((mNthreads > 1) ? " cpu threads" : " cpu thread");
111
112 if (mNthreads < 1) {
113 mNthreads = 1;
114 }
115}
116
117void TPCFastSpaceChargeCorrectionHelper::fillSpaceChargeCorrectionFromMap(TPCFastSpaceChargeCorrection& correction, bool processingInverseCorrection)
118{
119 // calculate correction map: dx,du,dv = ( origTransform() -> x,u,v) - fastTransformNominal:x,u,v
120 // for the future: switch TOF correction off for a while
121
122 TStopwatch watch;
123
124 if (!mIsInitialized) {
125 initGeometry();
126 }
127
128 if (!mCorrectionMap.isInitialized()) {
129 correction.setNoCorrection();
130 return;
131 }
132
133 LOG(info) << "fast space charge correction helper: init from data points";
134
135 for (int sector = 0; sector < correction.getGeometry().getNumberOfSectors(); sector++) {
136
137 auto myThread = [&](int iThread) {
138 for (int row = iThread; row < correction.getGeometry().getNumberOfRows(); row += mNthreads) {
139
140 TPCFastSpaceChargeCorrection::SplineType& spline = correction.getSplineForRow(row);
142 std::vector<float> splineParameters;
143 splineParameters.resize(spline.getNumberOfParameters());
144
145 const std::vector<o2::gpu::TPCFastSpaceChargeCorrectionMap::CorrectionPoint>& data = mCorrectionMap.getPoints(sector, row);
146 int nDataPoints = data.size();
147
148 if (nDataPoints >= 4) {
149 std::vector<double> pointGU(nDataPoints);
150 std::vector<double> pointGV(nDataPoints);
151 std::vector<double> pointWeight(nDataPoints);
152 std::vector<double> pointCorr(3 * nDataPoints); // 3 dimensions
153 for (int i = 0; i < nDataPoints; ++i) {
155 // not corrected grid coordinates
156 float gu, gv, scale;
157 correction.convLocalToGrid(sector, row, p.mY, p.mZ, gu, gv, scale);
158 if (scale - 1.f > 1.e-6) { // point is outside the grid
159 continue;
160 }
161 pointGU[i] = gu;
162 pointGV[i] = gv;
163 pointWeight[i] = p.mWeight;
164 pointCorr[3 * i + 0] = p.mDx;
165 pointCorr[3 * i + 1] = p.mDy;
166 pointCorr[3 * i + 2] = p.mDz;
167 }
168 helper.approximateDataPoints(spline, splineParameters.data(), 0., spline.getGridX1().getUmax(), 0., spline.getGridX2().getUmax(), pointGU.data(),
169 pointGV.data(), pointCorr.data(), pointWeight.data(), nDataPoints);
170 } else {
171 for (int i = 0; i < spline.getNumberOfParameters(); i++) {
172 splineParameters[i] = 0.f;
173 }
174 }
175
176 if (processingInverseCorrection) {
177 float* splineX = correction.getCorrectionDataInvX(sector, row);
178 float* splineYZ = correction.getCorrectionDataInvYZ(sector, row);
179 for (int i = 0; i < spline.getNumberOfParameters() / 3; i++) {
180 splineX[i] = splineParameters[3 * i + 0];
181 splineYZ[2 * i + 0] = splineParameters[3 * i + 1];
182 splineYZ[2 * i + 1] = splineParameters[3 * i + 2];
183 }
184 } else {
185 float* splineXYZ = correction.getCorrectionData(sector, row);
186 for (int i = 0; i < spline.getNumberOfParameters(); i++) {
187 splineXYZ[i] = splineParameters[i];
188 }
189 }
190 } // row
191 }; // thread
192
193 std::vector<std::thread> threads(mNthreads);
194
195 // run n threads
196 for (int i = 0; i < mNthreads; i++) {
197 threads[i] = std::thread(myThread, i);
198 }
199
200 // wait for the threads to finish
201 for (auto& th : threads) {
202 th.join();
203 }
204
205 } // sector
206
207 watch.Stop();
208
209 LOGP(info, "Space charge correction tooks: {}s", watch.RealTime());
210} // fillSpaceChargeCorrectionFromMap
211
212std::unique_ptr<TPCFastSpaceChargeCorrection> TPCFastSpaceChargeCorrectionHelper::createFromGlobalCorrection(
213 std::function<void(int sector, double gx, double gy, double gz,
214 double& dgx, double& dgy, double& dgz)>
215 correctionGlobal,
216 const int nKnotsY, const int nKnotsZ)
217{
219
220 auto correctionLocal = [&](int sector, int irow, double ly, double lz,
221 double& dlx, double& dly, double& dlz) {
222 double lx = mGeo.getRowInfo(irow).x;
223 float gx, gy, gz;
224 mGeo.convLocalToGlobal(sector, lx, ly, lz, gx, gy, gz);
225 double dgx, dgy, dgz;
226 correctionGlobal(sector, gx, gy, gz, dgx, dgy, dgz);
227 float lx1, ly1, lz1;
228 mGeo.convGlobalToLocal(sector, gx + dgx, gy + dgy, gz + dgz, lx1, ly1, lz1);
229 dlx = lx1 - lx;
230 dly = ly1 - ly;
231 dlz = lz1 - lz;
232 };
233 return std::move(createFromLocalCorrection(correctionLocal, nKnotsY, nKnotsZ));
234}
235
236std::unique_ptr<TPCFastSpaceChargeCorrection> TPCFastSpaceChargeCorrectionHelper::createFromLocalCorrection(
237 std::function<void(int sector, int irow, double y, double z, double& dx, double& dy, double& dz)> correctionLocal,
238 const int nKnotsY, const int nKnotsZ)
239{
241
242 LOG(info) << "fast space charge correction helper: create correction using " << mNthreads << " threads";
243
244 std::unique_ptr<TPCFastSpaceChargeCorrection> correctionPtr(new TPCFastSpaceChargeCorrection);
245 TPCFastSpaceChargeCorrection& correction = *correctionPtr;
246
247 { // create a correction map
248
249 if (!mIsInitialized) {
250 initGeometry();
251 }
252
253 const int nRows = mGeo.getNumberOfRows();
254 const int nCorrectionScenarios = nRows / 10 + 1;
255
256 correction.startConstruction(mGeo, nCorrectionScenarios);
257
258 // assign spline type for TPC rows
259 for (int row = 0; row < mGeo.getNumberOfRows(); row++) {
260 int scenario = row / 10;
261 if (scenario >= nCorrectionScenarios) {
262 scenario = nCorrectionScenarios - 1;
263 }
264 correction.setRowScenarioID(row, scenario);
265 }
266
267 for (int scenario = 0; scenario < nCorrectionScenarios; scenario++) {
268 int row = scenario * 10;
270 spline.recreate(nKnotsY, nKnotsZ);
271 correction.setSplineScenario(scenario, spline);
272 }
273 correction.finishConstruction();
274 }
275
276 LOG(info) << "fast space charge correction helper: fill data points from an input SP correction function";
277
278 {
281
282 int nSectors = mGeo.getNumberOfSectors();
283 int nRows = mGeo.getNumberOfRows();
284 mCorrectionMap.init(nSectors, nRows);
285
286 for (int iSector = 0; iSector < nSectors; iSector++) {
287
288 auto myThread = [&](int iThread) {
289 for (int iRow = iThread; iRow < nRows; iRow += mNthreads) {
290 const auto& info = mGeo.getRowInfo(iRow);
291 double dl = mGeo.getTPCzLength() / (6. * (nKnotsZ - 1));
292 double dpad = info.maxPad / (6. * (nKnotsY - 1));
293 for (double pad = 0; pad < info.maxPad + .5 * dpad; pad += dpad) {
294 for (double l = 0.; l < mGeo.getTPCzLength() + .5 * dl; l += dl) {
295 float y, z;
296 mGeo.convPadDriftLengthToLocal(iSector, iRow, pad, l, y, z);
297 double dx, dy, dz;
298 correctionLocal(iSector, iRow, y, z, dx, dy, dz);
299 mCorrectionMap.addCorrectionPoint(iSector, iRow,
300 y, z, dx, dy, dz, 1.);
301 }
302 }
303 } // row
304 }; // thread
305
306 std::vector<std::thread> threads(mNthreads);
307
308 // run n threads
309 for (int i = 0; i < mNthreads; i++) {
310 threads[i] = std::thread(myThread, i);
311 }
312
313 // wait for the threads to finish
314 for (auto& th : threads) {
315 th.join();
316 }
317
318 } // sector
319
320 fillSpaceChargeCorrectionFromMap(correction, false);
321 initInverse(correction, false);
322 }
323
324 return std::move(correctionPtr);
325} // createFromLocalCorrection
326
327std::unique_ptr<TPCFastSpaceChargeCorrection> TPCFastSpaceChargeCorrectionHelper::createFromLocalCorrection(
328 std::function<void(int sector, int irow, double y, double z, double& dx, double& dy, double& dz)> correctionLocal,
329 const std::vector<double>& knotsY, const std::vector<double>& knotsZ)
330{
333
334 LOG(info) << "fast space charge correction helper: create correction with irregular knots using " << mNthreads << " threads";
335
336 if (!mIsInitialized) {
337 initGeometry();
338 }
339
340 // spline knots must be positioned on the grid with an integer internal coordinate:
341 // the knot positioning accuracy is set to 0.1 * the smallest knot distance
342 std::vector<int> knotsInt[2];
343 for (int dim = 0; dim < 2; dim++) {
344 const std::vector<double>& knots = (dim == 0) ? knotsY : knotsZ;
345 if (knots.size() < 2) {
346 LOG(error) << "fast space charge correction helper: at least 2 knots needed in " << (dim == 0 ? "y" : "z");
347 return nullptr;
348 }
349 double minDistance = knots[1] - knots[0];
350 for (size_t i = 2; i < knots.size(); i++) {
351 minDistance = std::min(minDistance, knots[i] - knots[i - 1]);
352 }
353 if (!(minDistance > 0.)) {
354 LOG(error) << "fast space charge correction helper: the knots in " << (dim == 0 ? "y" : "z") << " must be strictly increasing";
355 return nullptr;
356 }
357 const double pitch = 0.1 * minDistance;
358 for (const double knot : knots) {
359 knotsInt[dim].push_back(int((knot - knots[0]) / pitch + 0.5));
360 }
361 }
362
363 std::unique_ptr<TPCFastSpaceChargeCorrection> correctionPtr(new TPCFastSpaceChargeCorrection);
364 TPCFastSpaceChargeCorrection& correction = *correctionPtr;
365
366 { // create the correction object: one spline scenario for all rows
367 correction.startConstruction(mGeo, 1);
368 for (int row = 0; row < mGeo.getNumberOfRows(); row++) {
369 correction.setRowScenarioID(row, 0);
370 }
372 spline.recreate(knotsInt[0].size(), knotsInt[0].data(), knotsInt[1].size(), knotsInt[1].data());
373 correction.setSplineScenario(0, spline);
374 correction.finishConstruction();
375 }
376
377 // sampling points, relative to the grid (0: the first knot, 1: the last one): 6 per knot interval
378 std::vector<double> samplingPoints[2];
379 for (int dim = 0; dim < 2; dim++) {
380 const std::vector<int>& knots = knotsInt[dim];
381 const double uMax = knots.back();
382 for (size_t i = 0; i + 1 < knots.size(); i++) {
383 for (int j = 0; j < 6; j++) {
384 samplingPoints[dim].push_back((knots[i] + (knots[i + 1] - knots[i]) * j / 6.) / uMax);
385 }
386 }
387 samplingPoints[dim].push_back(1.);
388 }
389
390 LOG(info) << "fast space charge correction helper: fill data points from an input SP correction function";
391
392 const int nSectors = mGeo.getNumberOfSectors();
393 const int nRows = mGeo.getNumberOfRows();
394 mCorrectionMap.init(nSectors, nRows);
395
396 for (int iSector = 0; iSector < nSectors; iSector++) {
397
398 auto myThread = [&](int iThread) {
399 for (int iRow = iThread; iRow < nRows; iRow += mNthreads) {
400 const auto& rowInfo = mGeo.getRowInfo(iRow);
401 for (const double pointY : samplingPoints[0]) {
402 const double y = rowInfo.getYmin() + pointY * rowInfo.getYwidth();
403 for (const double pointZ : samplingPoints[1]) {
404 // z of the grid: 0 at the central electrode, mirrored on the C side
405 const double z = (iSector < mGeo.getNumberOfSectorsA() ? 1. : -1.) * pointZ * mGeo.getTPCzLength();
406 double dx;
407 double dy;
408 double dz;
409 correctionLocal(iSector, iRow, y, z, dx, dy, dz);
410 mCorrectionMap.addCorrectionPoint(iSector, iRow, y, z, dx, dy, dz, 1.);
411 }
412 }
413 } // row
414 }; // thread
415
416 std::vector<std::thread> threads(mNthreads);
417
418 // run n threads
419 for (int i = 0; i < mNthreads; i++) {
420 threads[i] = std::thread(myThread, i);
421 }
422
423 // wait for the threads to finish
424 for (auto& th : threads) {
425 th.join();
426 }
427
428 } // sector
429
430 fillSpaceChargeCorrectionFromMap(correction, false);
431 initInverse(correction, false);
432
433 return correctionPtr;
434} // createFromLocalCorrection with irregular knots
435
437{
438 const Mapper& mapper = Mapper::instance();
439
441 LOG(fatal) << "Wrong number of sectors :" << geo.getNumberOfSectors() << " instead of " << Sector::MAXSECTOR << std::endl;
442 }
443
444 if (geo.getNumberOfRows() != mapper.getNumberOfRows()) {
445 LOG(fatal) << "Wrong number of rows :" << geo.getNumberOfRows() << " instead of " << mapper.getNumberOfRows() << std::endl;
446 }
447
448 double maxDx = 0, maxDy = 0;
449
450 for (int row = 0; row < geo.getNumberOfRows(); row++) {
451
452 const int nPads = geo.getRowInfo(row).maxPad + 1;
453
454 if (nPads != mapper.getNumberOfPadsInRowSector(row)) {
455 LOG(fatal) << "Wrong number of pads :" << nPads << " instead of " << mapper.getNumberOfPadsInRowSector(row) << std::endl;
456 }
457
458 const double x = geo.getRowInfo(row).x;
459
460 // check if calculated pad positions are equal to the real ones
461
462 for (int pad = 0; pad < nPads; pad++) {
463 const GlobalPadNumber p = mapper.globalPadNumber(PadPos(row, pad));
464 const PadCentre& c = mapper.padCentre(p);
465 float y, z;
466 geo.convPadDriftLengthToLocal(0, row, pad, 0., y, z);
467 const double dx = x - c.X();
468 const double dy = y - (-c.Y()); // diferent sign convention for Y coordinate in the map
469
470 if (fabs(dx) >= 1.e-6 || fabs(dy) >= 1.e-5) {
471 LOG(warning) << "wrong calculated pad position:"
472 << " row " << row << " pad " << pad << " x calc " << x << " x in map " << c.X() << " dx " << (x - c.X())
473 << " y calc " << y << " y in map " << -c.Y() << " dy " << dy << std::endl;
474 }
475 if (fabs(maxDx) < fabs(dx)) {
476 maxDx = dx;
477 }
478 if (fabs(maxDy) < fabs(dy)) {
479 maxDy = dy;
480 }
481 }
482 }
483
484 if (fabs(maxDx) >= 1.e-4 || fabs(maxDy) >= 1.e-4) {
485 LOG(fatal) << "wrong calculated pad position:"
486 << " max Dx " << maxDx << " max Dy " << maxDy << std::endl;
487 }
488}
489
490std::unique_ptr<o2::gpu::TPCFastSpaceChargeCorrection> TPCFastSpaceChargeCorrectionHelper::createFromTrackResiduals(
491 const o2::tpc::TrackResiduals& trackResiduals, TTree* voxResTree, TTree* voxResTreeInverse, bool useSmoothed, bool invertSigns,
492 TPCFastSpaceChargeCorrectionMap* fitPointsDirect,
493 TPCFastSpaceChargeCorrectionMap* fitPointsInverse)
494{
495 // create o2::gpu::TPCFastSpaceChargeCorrection from o2::tpc::TrackResiduals::VoxRes voxel tree
496
497 LOG(info) << "fast space charge correction helper: create correction from track residuals using " << mNthreads << " threads";
498
499 TStopwatch watch, watch1;
500
501 std::unique_ptr<o2::gpu::TPCFastSpaceChargeCorrection> correctionPtr(new o2::gpu::TPCFastSpaceChargeCorrection);
502
503 o2::gpu::TPCFastSpaceChargeCorrection& correction = *correctionPtr;
504
506 const o2::gpu::TPCFastTransformGeo& geo = helper->getGeometry();
507
508 int nY2Xbins = trackResiduals.getNY2XBins();
509 int nZ2Xbins = trackResiduals.getNZ2XBins();
510
511 std::vector<double> knotsDouble[2];
512
513 knotsDouble[0].reserve(nY2Xbins);
514 knotsDouble[1].reserve(nZ2Xbins);
515
516 // to get enouth measurements, make a spline knot at every second bin. Boundary bins are always included.
517
518 for (int i = 0, j = nY2Xbins - 1; i <= j; i += 2, j -= 2) {
519 knotsDouble[0].push_back(trackResiduals.getY2X(0, i));
520 if (j >= i + 1) {
521 knotsDouble[0].push_back(trackResiduals.getY2X(0, j));
522 }
523 }
524
525 for (int i = 0, j = nZ2Xbins - 1; i <= j; i += 2, j -= 2) {
526 knotsDouble[1].push_back(trackResiduals.getZ2X(i));
527 if (j >= i + 1) {
528 knotsDouble[1].push_back(trackResiduals.getZ2X(j));
529 }
530 }
531
532 std::vector<int> knotsInt[2];
533
534 for (int dim = 0; dim < 2; dim++) {
535 auto& knotsD = knotsDouble[dim];
536 std::sort(knotsD.begin(), knotsD.end());
537
538 double pitch = knotsD[1] - knotsD[0]; // min distance between the knots
539 for (int i = 2; i < knotsD.size(); i++) {
540 double d = knotsD[i] - knotsD[i - 1];
541 if (d < pitch) {
542 pitch = d;
543 }
544 }
545 // spline knots must be positioned on the grid with an integer internal coordinate
546 // we set the knot positioning accuracy to 0.1*pitch
547 pitch = 0.1 * pitch;
548 auto& knotsI = knotsInt[dim];
549 knotsI.reserve(knotsD.size());
550 double u0 = knotsD[0];
551 double u1 = knotsD[knotsD.size() - 1];
552 for (auto& u : knotsD) {
553 u -= u0;
554 int iu = int(u / pitch + 0.5);
555 knotsI.push_back(iu);
556 // debug printout: corrected vs original knot positions, scaled to [-1,1] interval
557 double uorig = u / (u1 - u0) * 2 - 1.;
558 u = (iu * pitch) / (u1 - u0) * 2 - 1.;
559 LOG(info) << "TPC SC splines: convert " << (dim == 0 ? "y" : (dim == 1 ? "z" : "-z")) << " bin to the knot: " << uorig << " -> " << u << " -> " << iu;
560 }
561
562 if (knotsI.size() < 2) { // minimum 2 knots
563 knotsI.clear();
564 knotsI.push_back(0);
565 knotsI.push_back(1);
566 }
567 }
568
569 auto& yKnotsInt = knotsInt[0];
570 auto& zKnotsInt = knotsInt[1];
571
572 int nKnotsY = yKnotsInt.size();
573 int nKnotsZ = zKnotsInt.size();
574
575 // std::cout << "n knots Y: " << nKnotsY << std::endl;
576 // std::cout << "n knots Z: " << nKnotsZA << ", " << nKnotsZC << std::endl;
577
578 const int nRows = geo.getNumberOfRows();
579 const int nSectors = geo.getNumberOfSectors();
580
581 { // create the correction object
582
583 const int nCorrectionScenarios = 1;
584
585 correction.startConstruction(geo, nCorrectionScenarios);
586
587 // init rows
588 for (int row = 0; row < geo.getNumberOfRows(); row++) {
589 correction.setRowScenarioID(row, 0);
590 }
591
592 { // init spline scenario
594 spline.recreate(nKnotsY, &yKnotsInt[0], nKnotsZ, &zKnotsInt[0]);
595 correction.setSplineScenario(0, spline);
596 }
597 correction.finishConstruction();
598 } // .. create the correction object
599
600 // set the grid borders
601 for (int iRow = 0; iRow < geo.getNumberOfRows(); iRow++) {
602 auto& info = correction.getRowInfo(iRow);
603 const auto& spline = correction.getSplineForRow(iRow);
604 double rowX = geo.getRowInfo(iRow).x;
605 double yMin = rowX * trackResiduals.getY2X(iRow, 0);
606 double yMax = rowX * trackResiduals.getY2X(iRow, trackResiduals.getNY2XBins() - 1);
607 double zMin = rowX * trackResiduals.getZ2X(0);
608 double zMax = rowX * trackResiduals.getZ2X(trackResiduals.getNZ2XBins() - 1);
609 double zOut = std::min(zMax, (double)geo.getTPCzLength()); // clamp to physical TPC extent to avoid negative spline scale
610 info.gridMeasured.set(yMin, spline.getGridX1().getUmax() / (yMax - yMin), // y
611 zMin, spline.getGridX2().getUmax() / (zMax - zMin), // z
612 zOut, geo.getTPCzLength()); // correction scaling region
613
614 info.gridReal = info.gridMeasured;
615
616 // std::cout << " iSector " << iSector << " iRow " << iRow << " uMin: " << uMin << " uMax: " << uMax << " vMin: " << vMin << " vMax: " << vMax
617 //<< " grid scale u "<< info.scaleUtoGrid << " grid scale v "<< info.scaleVtoGrid<< std::endl;
618 }
619
620 LOG(info) << "fast space charge correction helper: preparation took " << watch1.RealTime() << "s";
621
622 for (int processingInverseCorrection = 0; processingInverseCorrection <= 1; processingInverseCorrection++) {
623
624 TTree* currentTree = (processingInverseCorrection) ? voxResTreeInverse : voxResTree;
625
626 if (!currentTree) {
627 continue;
628 }
629 const char* directionName = (processingInverseCorrection) ? "inverse" : "direct";
630 LOG(info) << "\n fast space charge correction helper: Process " << directionName
631 << " correction: fill data points from track residuals.. ";
632
633 TStopwatch watch3;
634 o2::gpu::TPCFastSpaceChargeCorrectionMap& map = helper->getCorrectionMap();
635 map.init(geo.getNumberOfSectors(), geo.getNumberOfRows());
636
637 // read the data Sector by Sector
638
639 // data in the tree is not sorted by row
640 // first find which data belong to which row
641
642 struct VoxelData {
643 int mNentries{0}; // number of entries
644 float mX, mY, mZ; // mean position in the local coordinates
645 float mCx, mCy, mCz; // corrections to the local coordinates
646 };
647
648 std::vector<VoxelData> vSectorData[nRows * nSectors];
649 for (int ir = 0; ir < nRows * nSectors; ir++) {
650 vSectorData[ir].resize(nY2Xbins * nZ2Xbins);
651 }
652
653 { // read data from the tree to vSectorData
654
655 ROOT::TTreeProcessorMT processor(*currentTree, mNthreads);
656 std::string errMsg = std::string("Error reading ") + directionName + " track residuals: ";
657 auto myThread = [&](TTreeReader& readerSubRange) {
658 TTreeReaderValue<o2::tpc::TrackResiduals::VoxRes> v(readerSubRange, "voxRes");
659 while (readerSubRange.Next()) {
660 int iSector = (int)v->bsec;
661 if (iSector < 0 || iSector >= nSectors) {
662 LOG(fatal) << errMsg << "Sector number " << iSector << " is out of range";
663 continue;
664 }
665 int iRow = (int)v->bvox[o2::tpc::TrackResiduals::VoxX]; // bin number in x (= pad row)
666 if (iRow < 0 || iRow >= nRows) {
667 LOG(fatal) << errMsg << "Row number " << iRow << " is out of range";
668 }
669 double rowX = trackResiduals.getX(iRow); // X of the pad row
670 int iy = v->bvox[o2::tpc::TrackResiduals::VoxF]; // bin number in y/x 0..14
671 int iz = v->bvox[o2::tpc::TrackResiduals::VoxZ]; // bin number in z/x 0..4
672 auto& data = vSectorData[iSector * nRows + iRow][iy * nZ2Xbins + iz];
673 data.mNentries = (int)v->stat[o2::tpc::TrackResiduals::VoxV];
675 data.mY = v->stat[o2::tpc::TrackResiduals::VoxF] * rowX;
676 data.mZ = v->stat[o2::tpc::TrackResiduals::VoxZ] * rowX;
680 if (invertSigns) {
681 data.mCx *= -1.;
682 data.mCy *= -1.;
683 data.mCz *= -1.;
684 }
685 }
686 };
687 processor.Process(myThread);
688 }
689
690 // debug: mirror the data for TPC C side
691
692 if (mDebugMirrorAdata2C) {
693 for (int iSector = 0; iSector < geo.getNumberOfSectorsA(); iSector++) {
694 for (int iRow = 0; iRow < nRows; iRow++) {
695 for (int iy = 0; iy < nY2Xbins; iy++) {
696 for (int iz = 0; iz < nZ2Xbins; iz++) {
697 auto& dataA = vSectorData[iSector * nRows + iRow][iy * nZ2Xbins + iz];
698 auto& dataC = vSectorData[(iSector + geo.getNumberOfSectorsA()) * nRows + iRow][iy * nZ2Xbins + iz];
699 dataC = dataA; // copy the data
700 dataC.mZ = -dataC.mZ; // mirror the Z coordinate
701 dataC.mCz = -dataC.mCz; // mirror the Z correction
702 }
703 }
704 }
705 }
706 }
707
708 double maxError[3] = {0., 0., 0.};
709 int nErrors = 0;
710
711 for (int iSector = 0; iSector < nSectors; iSector++) {
712
713 // now process the data row-by-row
714
715 auto myThread = [&](int iThread, int nTreads) {
716 struct Voxel {
717 float mY, mZ; // non-distorted local coordinates
718 float mDy, mDz; // voxel size
719 int mSmoothingStep{100}; // is the voxel data original or smoothed at this step
720 };
721
722 std::vector<Voxel> vRowVoxels(nY2Xbins * nZ2Xbins);
723
724 for (int iRow = iThread; iRow < nRows; iRow += nTreads) {
725 // LOG(info) << "Processing Sector " << iSector << " row " << iRow;
726
727 // complete the voxel data
728 {
729 int xBin = iRow;
730 double x = trackResiduals.getX(xBin); // radius of the pad row
731 double dx = 1. / trackResiduals.getDXI(xBin);
732 bool isDataFound = false;
733 for (int iy = 0; iy < nY2Xbins; iy++) {
734 for (int iz = 0; iz < nZ2Xbins; iz++) {
735 auto& data = vSectorData[iSector * nRows + iRow][iy * nZ2Xbins + iz];
736 auto& vox = vRowVoxels[iy * nZ2Xbins + iz];
737 // y/x coordinate of the bin ~-0.15 ... 0.15
738 double y2x = trackResiduals.getY2X(xBin, iy);
739 // z/x coordinate of the bin 0.1 .. 0.9
740 double z2x = trackResiduals.getZ2X(iz);
741 vox.mY = x * y2x;
742 vox.mZ = x * z2x;
743 vox.mDy = x / trackResiduals.getDY2XI(xBin, iy);
744 vox.mDz = x * trackResiduals.getDZ2X(iz);
745 if (iSector >= geo.getNumberOfSectorsA()) {
746 vox.mZ = -vox.mZ;
747 }
748 if (data.mNentries > 0) { // voxel contains data
749 vox.mSmoothingStep = 0; // take original data
750 isDataFound = true;
751
752 // correct the mean position if it is outside the voxel
753 std::stringstream msg;
754 if (fabs(x - data.mX) > mVoxelMeanValidityRange * dx / 2.) {
755 msg << "\n x: center " << x << " dx " << data.mX - x << " half bin size " << dx / 2;
756 }
757
758 if (fabs(vox.mY - data.mY) > mVoxelMeanValidityRange * vox.mDy / 2.) {
759 msg << "\n y: center " << vox.mY << " dy " << data.mY - vox.mY << " half bin size " << vox.mDy / 2;
760 data.mY = vox.mY;
761 }
762
763 if (fabs(vox.mZ - data.mZ) > mVoxelMeanValidityRange * vox.mDz / 2.) {
764 msg << "\n z: center " << vox.mZ << " dz " << data.mZ - vox.mZ << " half bin size " << vox.mDz / 2;
765 data.mZ = vox.mZ;
766 }
767
768 if (!msg.str().empty()) {
769 bool isMaxErrorExceeded = (fabs(data.mX - x) / dx > maxError[0]) ||
770 (fabs(data.mY - vox.mY) / vox.mDy > maxError[1]) ||
771 (fabs(data.mZ - vox.mZ) / vox.mDz > maxError[2]);
772 static std::mutex mutex;
773 mutex.lock();
774 nErrors++;
775 if (nErrors < 20 || isMaxErrorExceeded) {
776 LOG(warning) << directionName << " correction: error N " << nErrors << "fitted voxel position is outside the voxel: "
777 << " sector " << iSector << " row " << iRow << " bin y " << iy << " bin z " << iz
778 << msg.str();
779 maxError[0] = GPUCommonMath::Max<double>(maxError[0], fabs(data.mX - x) / dx);
780 maxError[1] = GPUCommonMath::Max<double>(maxError[1], fabs(data.mY - vox.mY) / vox.mDy);
781 maxError[2] = GPUCommonMath::Max<double>(maxError[2], fabs(data.mZ - vox.mZ) / vox.mDz);
782 }
783 mutex.unlock();
784 }
785
786 } else { // no data, take voxel center position
787 data.mCx = 0.;
788 data.mCy = 0.;
789 data.mCz = 0.;
790 data.mX = x;
791 data.mY = vox.mY;
792 data.mZ = vox.mZ;
793 vox.mSmoothingStep = 100; // fill this data point with smoothed values from the neighbours
794 }
795 if (mDebugUseVoxelCenters) { // debug: always use voxel center instead of the mean position
796 data.mY = vox.mY;
797 data.mZ = vox.mZ;
798 }
799 }
800 }
801
802 if (!isDataFound) { // fill everything with 0
803 for (int iy = 0; iy < nY2Xbins; iy++) {
804 for (int iz = 0; iz < nZ2Xbins; iz++) {
805 vRowVoxels[iy * nZ2Xbins + iz].mSmoothingStep = 0;
806 }
807 }
808 }
809 } // complete the voxel data
810
811 // repare the voxel data: fill empty voxels
812
813 int nRepairs = 0;
814
815 for (int ismooth = 1; ismooth <= 2; ismooth++) {
816 for (int iy = 0; iy < nY2Xbins; iy++) {
817 for (int iz = 0; iz < nZ2Xbins; iz++) {
818 auto& data = vSectorData[iSector * nRows + iRow][iy * nZ2Xbins + iz];
819 auto& vox = vRowVoxels[iy * nZ2Xbins + iz];
820 if (vox.mSmoothingStep <= ismooth) { // already filled
821 continue;
822 }
823 nRepairs++;
824 data.mCx = 0.;
825 data.mCy = 0.;
826 data.mCz = 0.;
827 double w = 0.;
828 bool filled = false;
829 auto update = [&](int iy1, int iz1) {
830 auto& data1 = vSectorData[iSector * nRows + iRow][iy1 * nZ2Xbins + iz1];
831 auto& vox1 = vRowVoxels[iy1 * nZ2Xbins + iz1];
832 if (vox1.mSmoothingStep >= ismooth) {
833 return false;
834 }
835 double w1 = 1. / (abs(iy - iy1) + abs(iz - iz1) + 1);
836 data.mCx += w1 * data1.mCx;
837 data.mCy += w1 * data1.mCy;
838 data.mCz += w1 * data1.mCz;
839 w += w1;
840 filled = true;
841 return true;
842 };
843
844 for (int iy1 = iy - 1; iy1 >= 0 && !update(iy1, iz); iy1--) {
845 }
846 for (int iy1 = iy + 1; iy1 < nY2Xbins && !update(iy1, iz); iy1++) {
847 }
848 for (int iz1 = iz - 1; iz1 >= 0 && !update(iy, iz1); iz1--) {
849 }
850 for (int iz1 = iz + 1; iz1 < nZ2Xbins && !update(iy, iz1); iz1++) {
851 }
852
853 if (filled) {
854 data.mCx /= w;
855 data.mCy /= w;
856 data.mCz /= w;
857 vox.mSmoothingStep = ismooth;
858 }
859 } // iz
860 } // iy
861 } // ismooth
862
863 if (nRepairs > 0) {
864 LOG(debug) << "Sector " << iSector << " row " << iRow << ": " << nRepairs << " voxel repairs for " << nY2Xbins * nZ2Xbins << " voxels";
865 }
866
867 // feed the row data to the helper
868
869 auto& info = correction.getRowInfo(iRow);
870 const auto& spline = correction.getSplineForRow(iRow);
871
872 auto addVoxel = [&](int iy, int iz, double weight) {
873 auto& vox = vRowVoxels[iy * nZ2Xbins + iz];
874 if (vox.mSmoothingStep > 2) {
875 LOG(fatal) << "empty voxel is not repared: y " << iy << " z " << iz;
876 }
877 auto& data = vSectorData[iSector * nRows + iRow][iy * nZ2Xbins + iz];
878 map.addCorrectionPoint(iSector, iRow, data.mY, data.mZ, data.mCx, data.mCy, data.mCz, weight);
879 };
880
881 auto addEdge = [&](int iy1, int iz1, int iy2, int iz2, int nPoints) {
882 // add n points on the edge between two voxels excluding the voxel points
883 if (nPoints < 1) {
884 return;
885 }
886 if (iy1 < 0 || iy1 >= nY2Xbins || iz1 < 0 || iz1 >= nZ2Xbins) {
887 return;
888 }
889 if (iy2 < 0 || iy2 >= nY2Xbins || iz2 < 0 || iz2 >= nZ2Xbins) {
890 return;
891 }
892 auto& data1 = vSectorData[iSector * nRows + iRow][iy1 * nZ2Xbins + iz1];
893 auto& vox1 = vRowVoxels[iy1 * nZ2Xbins + iz1];
894 auto& data2 = vSectorData[iSector * nRows + iRow][iy2 * nZ2Xbins + iz2];
895 auto& vox2 = vRowVoxels[iy2 * nZ2Xbins + iz2];
896 double y1 = data1.mY;
897 double z1 = data1.mZ;
898 double cx1 = data1.mCx;
899 double cy1 = data1.mCy;
900 double cz1 = data1.mCz;
901 double y2 = data2.mY;
902 double z2 = data2.mZ;
903 double cx2 = data2.mCx;
904 double cy2 = data2.mCy;
905 double cz2 = data2.mCz;
906
907 for (int is = 1; is <= nPoints; is++) {
908 double s2 = is / (double)(nPoints + 1);
909 double s1 = 1. - s2;
910 double y = s1 * y1 + s2 * y2;
911 double z = s1 * z1 + s2 * z2;
912 double cx = s1 * cx1 + s2 * cx2;
913 double cy = s1 * cy1 + s2 * cy2;
914 double cz = s1 * cz1 + s2 * cz2;
915 map.addCorrectionPoint(iSector, iRow, y, z, cx, cy, cz, 1.);
916 }
917 };
918
919 // original measurements weighted by 8 at each voxel and 8 additional artificial measurements around each voxel
920 //
921 // (y+1, z) 8 1 1 8 (y+1, z+1)
922 // 1 1 1 1 1
923 // 1 1 1 1 1
924 // (y,z) 8 1 1 8 1
925 // 1 1 1 1 1
926
927 for (int iy = 0; iy < nY2Xbins; iy++) {
928 for (int iz = 0; iz < nZ2Xbins; iz++) {
929 addVoxel(iy, iz, 8);
930 addEdge(iy, iz, iy, iz + 1, 2);
931 addEdge(iy, iz, iy + 1, iz, 2);
932 addEdge(iy, iz, iy + 1, iz + 1, 2);
933 addEdge(iy + 1, iz, iy, iz + 1, 2);
934 }
935 }
936
937 } // iRow
938 }; // myThread
939
940 // run n threads
941
942 int nThreads = mNthreads;
943 // nThreads = 1;
944
945 std::vector<std::thread> threads(nThreads);
946
947 for (int i = 0; i < nThreads; i++) {
948 threads[i] = std::thread(myThread, i, nThreads);
949 }
950
951 // wait for the threads to finish
952 for (auto& th : threads) {
953 th.join();
954 }
955 } // iSector
956
957 LOGP(info, "Reading & reparing of the track residuals tooks: {}s", watch3.RealTime());
958
959 LOG(info) << "fast space charge correction helper: create space charge from the map of data points..";
960
961 TStopwatch watch4;
962
963 if (!processingInverseCorrection && fitPointsDirect) {
964 *fitPointsDirect = helper->getCorrectionMap();
965 }
966 if (processingInverseCorrection && fitPointsInverse) {
967 *fitPointsInverse = helper->getCorrectionMap();
968 }
969
970 helper->fillSpaceChargeCorrectionFromMap(correction, processingInverseCorrection);
971
972 LOG(info) << "fast space charge correction helper: creation from the data map took " << watch4.RealTime() << "s";
973
974 } // processingInverseCorrection
975
976 if (voxResTree && !voxResTreeInverse) {
977 LOG(info) << "fast space charge correction helper: init inverse correction from direct correction..";
978 TStopwatch watch4;
979 helper->initInverse(correction, false);
980 LOG(info) << "fast space charge correction helper: init inverse correction took " << watch4.RealTime() << "s";
981 }
982
983 LOGP(info, "Creation from track residuals tooks in total: {}s", watch.RealTime());
984
985 return std::move(correctionPtr);
986
987} // createFromTrackResiduals
988
990{
991 std::vector<o2::gpu::TPCFastSpaceChargeCorrection*> corr{&correction};
992 initInverse(corr, std::vector<float>{1}, prn);
993}
994
995void TPCFastSpaceChargeCorrectionHelper::initInverse(std::vector<o2::gpu::TPCFastSpaceChargeCorrection*>& corrections, const std::vector<float>& scaling, bool prn)
996{
998 TStopwatch watch;
999 LOG(info) << "fast space charge correction helper: init inverse";
1000
1001 if (corrections.size() != scaling.size()) {
1002 LOGP(error, "Input corrections and scaling values have different size");
1003 return;
1004 }
1005
1006 auto& correction = *(corrections.front());
1007
1008 double tpcR2min = mGeo.getRowInfo(0).x - 1.;
1009 tpcR2min = tpcR2min * tpcR2min;
1010 double tpcR2max = mGeo.getRowInfo(mGeo.getNumberOfRows() - 1).x;
1011 tpcR2max = tpcR2max / cos(2 * M_PI / mGeo.getNumberOfSectorsA() / 2) + 1.;
1012 tpcR2max = tpcR2max * tpcR2max;
1013
1014 for (int row = 0; row < mGeo.getNumberOfRows(); row++) {
1015 auto& rowInfo = correction.getRowInfo(row);
1016 rowInfo.gridReal = rowInfo.gridMeasured;
1017 }
1018
1019 for (int sector = 0; sector < mGeo.getNumberOfSectors(); sector++) {
1020 // LOG(info) << "inverse transform for sector " << sector ;
1021
1022 auto myThread = [&](int iThread) {
1023 Spline2DHelper<float> helper;
1024 std::vector<float> splineParameters;
1025
1026 for (int row = iThread; row < mGeo.getNumberOfRows(); row += mNthreads) {
1027 auto& rowInfo = correction.getRowInfo(row);
1028
1029 TPCFastSpaceChargeCorrection::SplineType spline = correction.getSplineForRow(row);
1030 helper.setSpline(spline, 10, 10);
1031
1032 std::vector<double> gridU;
1033 {
1034 const auto& grid = spline.getGridX1();
1035 for (int i = 0; i < grid.getNumberOfKnots(); i++) {
1036 if (i == grid.getNumberOfKnots() - 1) {
1037 gridU.push_back(grid.getKnot(i).u);
1038 break;
1039 }
1040 for (double s = 1.; s > 0.; s -= 0.1) {
1041 gridU.push_back(s * grid.getKnot(i).u + (1. - s) * grid.getKnot(i + 1).u);
1042 }
1043 }
1044 }
1045 std::vector<double> gridV;
1046 {
1047 const auto& grid = spline.getGridX2();
1048 for (int i = 0; i < grid.getNumberOfKnots(); i++) {
1049 if (i == grid.getNumberOfKnots() - 1) {
1050 gridV.push_back(grid.getKnot(i).u);
1051 break;
1052 }
1053 for (double s = 1.; s > 0.; s -= 0.1) {
1054 gridV.push_back(s * grid.getKnot(i).u + (1. - s) * grid.getKnot(i + 1).u);
1055 }
1056 }
1057 }
1058
1059 std::vector<double> dataPointGridU, dataPointGridV, dataPointF, dataPointWeight;
1060 dataPointGridU.reserve(gridU.size() * gridV.size());
1061 dataPointGridV.reserve(gridU.size() * gridV.size());
1062 dataPointF.reserve(3 * gridU.size() * gridV.size());
1063 dataPointWeight.reserve(gridU.size() * gridV.size());
1064
1065 for (int iu = 0; iu < gridU.size(); iu++) {
1066 for (int iv = 0; iv < gridV.size(); iv++) {
1067 float y, z;
1068 correction.convGridToLocal(sector, row, gridU[iu], gridV[iv], y, z);
1069 double dx = 0, dy = 0, dz = 0;
1070
1071 // add corrections
1072 for (int i = 0; i < corrections.size(); ++i) {
1073 float dxTmp, dyTmp, dzTmp;
1074 corrections[i]->getCorrectionLocal(sector, row, y, z, dxTmp, dyTmp, dzTmp);
1075 dx += dxTmp * scaling[i];
1076 dy += dyTmp * scaling[i];
1077 dz += dzTmp * scaling[i];
1078 }
1079 float gridU, gridV, scale;
1080 correction.convRealLocalToGrid(sector, row, y + dy, z + dz, gridU, gridV, scale);
1081 dataPointGridU.push_back(gridU);
1082 dataPointGridV.push_back(gridV);
1083 dataPointF.push_back(scale * dx);
1084 dataPointF.push_back(scale * dy);
1085 dataPointF.push_back(scale * dz);
1086 dataPointWeight.push_back(1.);
1087 }
1088 }
1089
1090 int nDataPoints = dataPointGridU.size();
1091 splineParameters.resize(spline.getNumberOfParameters());
1092
1093 helper.approximateDataPoints(spline, splineParameters.data(), 0., spline.getGridX1().getUmax(),
1094 0., spline.getGridX2().getUmax(),
1095 dataPointGridU.data(), dataPointGridV.data(),
1096 dataPointF.data(), dataPointWeight.data(), nDataPoints);
1097
1098 float* splineX = correction.getCorrectionDataInvX(sector, row);
1099 float* splineUV = correction.getCorrectionDataInvYZ(sector, row);
1100 for (int i = 0; i < spline.getNumberOfParameters() / 3; i++) {
1101 splineX[i] = splineParameters[3 * i + 0];
1102 splineUV[2 * i + 0] = splineParameters[3 * i + 1];
1103 splineUV[2 * i + 1] = splineParameters[3 * i + 2];
1104 }
1105 } // row
1106 }; // thread
1107
1108 std::vector<std::thread> threads(mNthreads);
1109
1110 // run n threads
1111 for (int i = 0; i < mNthreads; i++) {
1112 threads[i] = std::thread(myThread, i);
1113 }
1114
1115 // wait for the threads to finish
1116 for (auto& th : threads) {
1117 th.join();
1118 }
1119
1120 } // sector
1121 float duration = watch.RealTime();
1122 LOGP(info, "Inverse tooks: {}s", duration);
1123}
1124
1126 o2::gpu::TPCFastSpaceChargeCorrection& mainCorrection, double mainScale,
1127 const std::vector<std::pair<const o2::gpu::TPCFastSpaceChargeCorrection*, double>>& additionalCorrections)
1128{
1130 SectorScales mainSectorScale;
1131 mainSectorScale.fill(mainScale);
1132 std::vector<std::pair<const o2::gpu::TPCFastSpaceChargeCorrection*, SectorScales>> additionalSectorScales;
1133 for (const auto& corr : additionalCorrections) {
1134 SectorScales sectorScale;
1135 sectorScale.fill(corr.second);
1136 additionalSectorScales.emplace_back(corr.first, sectorScale);
1137 }
1138
1139 addCorrections(mainCorrection, mainSectorScale, additionalSectorScales);
1140}
1141
1143 o2::gpu::TPCFastSpaceChargeCorrection& mainCorrection, SectorScales mainScale,
1144 const std::vector<std::pair<const o2::gpu::TPCFastSpaceChargeCorrection*, SectorScales>>& additionalCorrections)
1145{
1147
1148 TStopwatch watch;
1149 LOG(info) << "fast space charge correction helper: Add corrections";
1150
1151 const auto& geo = mainCorrection.getGeometry();
1152
1153 for (int sector = 0; sector < geo.getNumberOfSectors(); sector++) {
1154
1155 float secMainScale = mainScale[sector];
1156
1157 auto myThread = [&](int iThread) {
1158 for (int row = iThread; row < geo.getNumberOfRows(); row += mNthreads) {
1159 auto& rowInfo = mainCorrection.getRowInfo(row);
1160 const auto& spline = mainCorrection.getSplineForRow(row);
1161
1162 float* splineParameters = mainCorrection.getCorrectionData(sector, row);
1163 float* splineParametersInvX = mainCorrection.getCorrectionDataInvX(sector, row);
1164 float* splineParametersInvYZ = mainCorrection.getCorrectionDataInvYZ(sector, row);
1165
1166 constexpr int nKnotPar1d = 4;
1167 constexpr int nKnotPar2d = nKnotPar1d * 2;
1168 constexpr int nKnotPar3d = nKnotPar1d * 3;
1169
1170 { // scale the main correction
1171 // a spline is linear in its parameters: scaling the function scales all of them, the mixed derivative included
1172 double parscale[4] = {secMainScale, secMainScale, secMainScale, secMainScale};
1173 for (int iknot = 0, ind = 0; iknot < spline.getNumberOfKnots(); iknot++) {
1174 for (int ipar = 0; ipar < nKnotPar1d; ++ipar) {
1175 for (int idim = 0; idim < 3; idim++, ind++) {
1176 splineParameters[ind] *= parscale[ipar];
1177 }
1178 }
1179 }
1180 for (int iknot = 0, ind = 0; iknot < spline.getNumberOfKnots(); iknot++) {
1181 for (int ipar = 0; ipar < nKnotPar1d; ++ipar) {
1182 for (int idim = 0; idim < 1; idim++, ind++) {
1183 splineParametersInvX[ind] *= parscale[ipar];
1184 }
1185 }
1186 }
1187 for (int iknot = 0, ind = 0; iknot < spline.getNumberOfKnots(); iknot++) {
1188 for (int ipar = 0; ipar < nKnotPar1d; ++ipar) {
1189 for (int idim = 0; idim < 2; idim++, ind++) {
1190 splineParametersInvYZ[ind] *= parscale[ipar];
1191 }
1192 }
1193 }
1194 }
1195
1196 // add the other corrections
1197
1198 const auto& gridU = spline.getGridX1();
1199 const auto& gridV = spline.getGridX2();
1200
1201 for (int icorr = 0; icorr < additionalCorrections.size(); ++icorr) {
1202 const auto& corr = *(additionalCorrections[icorr].first);
1203 double scale = additionalCorrections[icorr].second[sector];
1204 if (scale == 0.) {
1205 continue;
1206 }
1207
1208 auto& linfo = corr.getRowInfo(row);
1209
1210 // chain rule for the derivatives: u = (y - y0) * yScale, v = (z - z0) * zScale => d/du_main = (du_corr / du_main) d/du_corr
1211 double scaleU = linfo.gridMeasured.getYscale() / rowInfo.gridMeasured.getYscale();
1212 double scaleV = linfo.gridMeasured.getZscale() / rowInfo.gridMeasured.getZscale();
1213 double scaleRealU = linfo.gridReal.getYscale() / rowInfo.gridReal.getYscale();
1214 double scaleRealV = linfo.gridReal.getZscale() / rowInfo.gridReal.getZscale();
1215
1216 for (int iu = 0; iu < gridU.getNumberOfKnots(); iu++) {
1217 double u = gridU.getKnot(iu).u;
1218 for (int iv = 0; iv < gridV.getNumberOfKnots(); iv++) {
1219 double v = gridV.getKnot(iv).u;
1220 int knotIndex = spline.getKnotIndex(iu, iv);
1221 float P[nKnotPar3d];
1222
1223 { // direct correction
1224 float y, z;
1225 mainCorrection.convGridToLocal(sector, row, u, v, y, z);
1226 // return values: u, v, scaling factor
1227 float lu, lv, ls;
1228 corr.convLocalToGrid(sector, row, y, z, lu, lv, ls);
1229 ls *= scale;
1230 // knot parameters: {S, dS/dv, dS/du, d2S/dudv}
1231 double parscale[4] = {ls, ls * scaleV, ls * scaleU, ls * scaleU * scaleV};
1232 const auto& spl = corr.getSplineForRow(row);
1233 spl.interpolateParametersAtU(corr.getCorrectionData(sector, row), lu, lv, P);
1234 for (int ipar = 0, ind = 0; ipar < nKnotPar1d; ++ipar) {
1235 for (int idim = 0; idim < 3; idim++, ind++) {
1236 splineParameters[knotIndex * nKnotPar3d + ind] += parscale[ipar] * P[ind];
1237 }
1238 }
1239 }
1240
1241 float y, z;
1242 mainCorrection.convGridToRealLocal(sector, row, u, v, y, z);
1243 // return values: u, v, scaling factor
1244 float lu, lv, ls;
1245 corr.convRealLocalToGrid(sector, row, y, z, lu, lv, ls);
1246 ls *= scale;
1247 double parscale[4] = {ls, ls * scaleRealV, ls * scaleRealU, ls * scaleRealU * scaleRealV};
1248
1249 { // inverse X correction
1250 corr.getSplineInvXforRow(row).interpolateParametersAtU(corr.getCorrectionDataInvX(sector, row), lu, lv, P);
1251 for (int ipar = 0, ind = 0; ipar < nKnotPar1d; ++ipar) {
1252 for (int idim = 0; idim < 1; idim++, ind++) {
1253 splineParametersInvX[knotIndex * nKnotPar1d + ind] += parscale[ipar] * P[ind];
1254 }
1255 }
1256 }
1257
1258 { // inverse YZ correction
1259 corr.getSplineInvYZforRow(row).interpolateParametersAtU(corr.getCorrectionDataInvYZ(sector, row), lu, lv, P);
1260 for (int ipar = 0, ind = 0; ipar < nKnotPar1d; ++ipar) {
1261 for (int idim = 0; idim < 2; idim++, ind++) {
1262 splineParametersInvYZ[knotIndex * nKnotPar2d + ind] += parscale[ipar] * P[ind];
1263 }
1264 }
1265 }
1266
1267 } // iv
1268 } // iu
1269 } // corrections
1270
1271 } // row
1272 }; // thread
1273
1274 std::vector<std::thread> threads(mNthreads);
1275
1276 // run n threads
1277 for (int i = 0; i < mNthreads; i++) {
1278 threads[i] = std::thread(myThread, i);
1279 }
1280
1281 // wait for the threads to finish
1282 for (auto& th : threads) {
1283 th.join();
1284 }
1285
1286 } // sector
1287 double duration = watch.RealTime();
1288 LOGP(info, "Merge of corrections tooks: {}s", duration);
1289}
1290
1292 const o2::gpu::TPCFastSpaceChargeCorrection& sourceCorrection,
1293 const std::vector<int>& sectors)
1294{
1296 TStopwatch watch;
1297 LOG(info) << "fast space charge correction helper: Merge corrections";
1298
1299 const auto& geo = destinationCorrection.getGeometry();
1300
1301 for (int sector : sectors) {
1302 if (sector < 0 || sector >= geo.getNumberOfSectors()) {
1303 LOGP(fatal, "Invalid sector number {}. Valid range is [0, {})", sector, geo.getNumberOfSectors());
1304 continue;
1305 }
1306 auto myThread = [&](int iThread) {
1307 for (int row = iThread; row < geo.getNumberOfRows(); row += mNthreads) {
1308
1309 { // replace the direct correction
1310 const auto& destSpline = destinationCorrection.getSplineForRow(row);
1311 float* destSplineParameters = destinationCorrection.getCorrectionData(sector, row);
1312 const auto& sourceSpline = sourceCorrection.getSplineForRow(row);
1313 const float* sourceSplineParameters = sourceCorrection.getCorrectionData(sector, row);
1314
1315 // ensure the splines are compatible
1316 if (destSpline.getGridX1().getNumberOfKnots() != sourceSpline.getGridX1().getNumberOfKnots() ||
1317 destSpline.getGridX2().getNumberOfKnots() != sourceSpline.getGridX2().getNumberOfKnots()) {
1318 LOGP(error, "Splines for sector {} row {} are not compatible: number of knots in U or V direction do not match", sector, row);
1319 continue;
1320 }
1321 // replace the destination correction with the source correction for this sector and row
1322 memcpy(destSplineParameters, sourceSplineParameters, destSpline.getNumberOfParameters() * sizeof(float));
1323 }
1324
1325 { // replace the inverse correction X
1326 const auto& destSpline = destinationCorrection.getSplineInvXforRow(row);
1327 float* destSplineParameters = destinationCorrection.getCorrectionDataInvX(sector, row);
1328 const auto& sourceSpline = sourceCorrection.getSplineInvXforRow(row);
1329 const float* sourceSplineParameters = sourceCorrection.getCorrectionDataInvX(sector, row);
1330 // ensure the splines are compatible
1331 if (destSpline.getGridX1().getNumberOfKnots() != sourceSpline.getGridX1().getNumberOfKnots() ||
1332 destSpline.getGridX2().getNumberOfKnots() != sourceSpline.getGridX2().getNumberOfKnots()) {
1333 LOGP(error, "Inverse X splines for sector {} row {} are not compatible: number of knots in U or V direction do not match", sector, row);
1334 continue;
1335 }
1336 memcpy(destSplineParameters, sourceSplineParameters, destSpline.getNumberOfParameters() * sizeof(float));
1337 }
1338
1339 { // replace the inverse correction YZ
1340 const auto& destSpline = destinationCorrection.getSplineInvYZforRow(row);
1341 float* destSplineParameters = destinationCorrection.getCorrectionDataInvYZ(sector, row);
1342 const auto& sourceSpline = sourceCorrection.getSplineInvYZforRow(row);
1343 const float* sourceSplineParameters = sourceCorrection.getCorrectionDataInvYZ(sector, row);
1344 // ensure the splines are compatible
1345 if (destSpline.getGridX1().getNumberOfKnots() != sourceSpline.getGridX1().getNumberOfKnots() ||
1346 destSpline.getGridX2().getNumberOfKnots() != sourceSpline.getGridX2().getNumberOfKnots()) {
1347 LOGP(error, "Inverse YZ splines for sector {} row {} are not compatible: number of knots in U or V direction do not match", sector, row);
1348 continue;
1349 }
1350 memcpy(destSplineParameters, sourceSplineParameters, destSpline.getNumberOfParameters() * sizeof(float));
1351 }
1352
1353 // replace the sector row info
1354 auto& destSecRowInfo = destinationCorrection.getRowInfo(row);
1355 const auto& sourceSecRowInfo = sourceCorrection.getRowInfo(row);
1356 destSecRowInfo = sourceSecRowInfo;
1357 } // row
1358 }; // thread
1359
1360 std::vector<std::thread> threads(mNthreads);
1361
1362 // run n threads
1363 for (int i = 0; i < mNthreads; i++) {
1364 threads[i] = std::thread(myThread, i);
1365 }
1366
1367 // wait for the threads to finish
1368 for (auto& th : threads) {
1369 th.join();
1370 }
1371
1372 } // sector
1373 double duration = watch.RealTime();
1374 LOGP(info, "Merge of corrections tooks: {}s", duration);
1375}
1376
1378{
1379 LOG(info) << "fast space charge correction helper: use voxel centers for correction";
1380 mDebugUseVoxelCenters = true;
1381}
1382
1384{
1385 LOG(info) << "fast space charge correction helper: mirror A data to C data";
1386 mDebugMirrorAdata2C = true;
1387}
1388
1389} // namespace tpc
1390} // namespace o2
Definition of ChebyshevFit1D class.
std::ostringstream debug
int32_t i
uint32_t iSector
configStandalone Light OpenGL display display_light Apply smoothing to points Enable Z buffer Highlight links Highlight seeds Highlight sector tracks Highlight final tracks Show individual Show related Highlight grid Show only individual collision Color clusters belonging to track objects Set line Enable drawing TPC data Enable drawing TOF data Enable drawing magnetic field Set field line step count Invert colors configStandalone Heavy OpenGL display display_heavy Show tracks with ITS contribution Show tracks with TOF contribution Enable propagation of loopers Show clusters at nominal x of pad row for early transformed data Split CE tracks when they cross the central Mark clusters Mark adjacent clusters Hide rejected tracks Apply filter on tracks to be displayed Separate sectors
configStandalone Light OpenGL display display_light Apply smoothing to points Enable Z buffer Highlight links Highlight seeds Highlight sector tracks Highlight final tracks Show individual sector
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 s
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
float float float & zMax
float & yMax
char error[CURL_ERROR_SIZE]
float zMin
Definition of the parameter class for the detector.
Definition of the parameter class for the detector electronics.
Definition of the parameter class for the detector gas.
uint32_t j
Definition RawData.h:0
uint32_t c
Definition RawData.h:2
Definition of Spline2DHelper class.
class to create TPC fast space charge correction
Definition of TPCFastTransform class.
Definition of the TrackResiduals class.
int32_t setSpline(const Spline2DContainerBase< DataT, FlatObject > &spline, int32_t nAuxiliaryPointsU1, int32_t nAuxiliaryPointsU2)
_______________ Interface for a step-wise construction of the best-fit spline _______________________...
void approximateDataPoints(Spline2DContainerBase< DataT, FlatObject > &spline, DataT *splineParameters, double x1Min, double x1Max, double x2Min, double x2Max, const double dataPointX1[], const double dataPointX2[], const double dataPointF[], const double dataPointWeight[], int32_t nDataPoints)
Create best-fit spline parameters for a given set of data points.
Forward declaration — specializations below select ClassDefNV based on FlatBase.
Definition Spline2D.h:104
const std::vector< CorrectionPoint > & getPoints(int32_t iSector, int32_t iRow) const
void init(int32_t nSectors, int32_t nRows)
(re-)init the map
void addCorrectionPoint(int32_t iSector, int32_t iRow, double y, double z, double dx, double dy, double dz, double weight)
Starts the construction procedure, reserves temporary memory.
void setSplineScenario(int32_t scenarioIndex, const SplineType &spline)
Sets approximation scenario.
void setRowScenarioID(int32_t iRow, int32_t iScenario)
Initializes a TPC row.
void startConstruction(const TPCFastTransformGeo &geo, int32_t numberOfSplineScenarios)
_______________ Construction interface ________________________
void finishConstruction()
Finishes construction: puts everything to the flat buffer, releases temporary memory.
void finishConstruction()
Finishes initialization: puts everything to the flat buffer, releases temporary memory.
void setTPCzLength(float tpcZlength)
void startConstruction(int32_t numberOfRows)
_______________ Construction interface ________________________
void setTPCrow(int32_t iRow, float x, int32_t nPads, float padWidth)
Initializes a TPC row.
static constexpr int32_t getNumberOfSectorsA()
Gives number of TPC sectors on the A side.
static constexpr int32_t getNumberOfSectors()
_______________ Getters _________________________________
int getNumberOfRows() const
Definition Mapper.h:297
int getNumberOfPadsInRowSector(int row) const
Definition Mapper.h:340
int getGlobalRowOffsetRegion(int region) const
Definition Mapper.h:313
GlobalPadNumber globalPadNumber(const PadPos &globalPadPosition) const
Definition Mapper.h:56
static Mapper & instance(const std::string mappingDir="")
Definition Mapper.h:44
const PadRegionInfo & getPadRegionInfo(const unsigned char region) const
Definition Mapper.h:385
int getNumberOfRowsRegion(int region) const
Definition Mapper.h:309
const PadCentre & padCentre(GlobalPadNumber padNumber) const
Definition Mapper.h:51
static constexpr int MAXSECTOR
Definition Sector.h:44
std::unique_ptr< TPCFastSpaceChargeCorrection > createFromLocalCorrection(std::function< void(int roc, int irow, double y, double z, double &dx, double &dy, double &dz)> correctionLocal, const int nKnotsY=10, const int nKnotsZ=20)
_______________ Main functionality ________________________
std::array< double, TPCFastTransformGeo::getNumberOfSectors()> SectorScales
void initInverse(o2::gpu::TPCFastSpaceChargeCorrection &correction, bool prn)
initialise inverse transformation
std::unique_ptr< o2::gpu::TPCFastSpaceChargeCorrection > createFromTrackResiduals(const o2::tpc::TrackResiduals &trackResiduals, TTree *voxResTree, TTree *voxResTreeInverse, bool useSmoothed, bool invertSigns, TPCFastSpaceChargeCorrectionMap *fitPointsDirect=nullptr, TPCFastSpaceChargeCorrectionMap *fitPointsInverse=nullptr)
void mergeCorrections(o2::gpu::TPCFastSpaceChargeCorrection &destinationCorrection, const o2::gpu::TPCFastSpaceChargeCorrection &sourceCorrection, const std::vector< int > &sectors)
std::unique_ptr< TPCFastSpaceChargeCorrection > createFromGlobalCorrection(std::function< void(int roc, double gx, double gy, double gz, double &dgx, double &dgy, double &dgz)> correctionGlobal, const int nKnotsY=10, const int nKnotsZ=20)
creates TPCFastSpaceChargeCorrection object from a continious space charge correction in global coord...
void testGeometry(const TPCFastTransformGeo &geo) const
void setDebugMirrorAdata2C()
debug: if true, mirror the data from the A side to the C side of the TPC
TPCFastSpaceChargeCorrectionHelper()=default
_____________ Constructors / destructors __________________________
void setNthreads(int n)
_______________ Settings ________________________
void setDebugUseVoxelCenters()
debug: if true, use voxel centers instead of the fitted positions for correction
void setNthreadsToMaximum()
sets number of threads to N cpu cores
static TPCFastSpaceChargeCorrectionHelper * instance()
Singleton.
void addCorrections(o2::gpu::TPCFastSpaceChargeCorrection &mainCorrection, double scale, const std::vector< std::pair< const o2::gpu::TPCFastSpaceChargeCorrection *, double > > &additionalCorrections)
float getZ2X(int iz) const
float getX(int ix) const
float getY2X(int ix, int ip) const
float getDY2XI(int ix, int iy=0) const
float getDXI(int ix) const
@ VoxV
voxel dispersions
float getDZ2X(int iz=0) const
GLdouble n
Definition glcorearb.h:1982
GLint GLenum GLint x
Definition glcorearb.h:403
GLuint GLfloat GLfloat GLfloat GLfloat y1
Definition glcorearb.h:5034
GLsizeiptr size
Definition glcorearb.h:659
const GLdouble * v
Definition glcorearb.h:832
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat s1
Definition glcorearb.h:5034
GLuint GLuint GLfloat weight
Definition glcorearb.h:5477
GLint y
Definition glcorearb.h:270
GLubyte GLubyte GLubyte GLubyte w
Definition glcorearb.h:852
GLdouble GLdouble GLdouble z
Definition glcorearb.h:843
math_utils::Point2D< float > PadCentre
Pad centres as 2D float.
Definition Defs.h:107
unsigned short GlobalPadNumber
global pad number
Definition Defs.h:114
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
o2::InteractionRecord ir(0, 0)
std::vector< int > row
uint64_t const void const *restrict const msg
Definition x9.h:153