29#include <fairlogger/Logger.h>
31#include "TStopwatch.h"
32#include "TTreeReader.h"
33#include "TTreeReaderValue.h"
34#include "ROOT/TTreeProcessorMT.hxx"
45TPCFastSpaceChargeCorrectionHelper* TPCFastSpaceChargeCorrectionHelper::sInstance =
nullptr;
52 sInstance->initGeometry();
57void TPCFastSpaceChargeCorrectionHelper::initGeometry()
71 for (
int iRow = 0; iRow < mGeo.getNumberOfRows(); iRow++) {
81 float padWidth = region.getPadWidth();
85 float xRow = padCentre.X();
87 mGeo.
setTPCrow(iRow, xRow, nPads, padWidth);
100 LOG(info) <<
"fast space charge correction helper: use " <<
n << ((
n > 1) ?
" cpu threads" :
" cpu thread");
101 mNthreads = (
n > 0) ?
n : 1;
108 mNthreads = std::thread::hardware_concurrency();
110 LOG(info) <<
"fast space charge correction helper: use " << mNthreads << ((mNthreads > 1) ?
" cpu threads" :
" cpu thread");
117void TPCFastSpaceChargeCorrectionHelper::fillSpaceChargeCorrectionFromMap(
TPCFastSpaceChargeCorrection& correction,
bool processingInverseCorrection)
124 if (!mIsInitialized) {
129 correction.setNoCorrection();
133 LOG(info) <<
"fast space charge correction helper: init from data points";
135 for (
int sector = 0; sector < correction.getGeometry().getNumberOfSectors(); sector++) {
137 auto myThread = [&](
int iThread) {
138 for (
int row = iThread;
row < correction.getGeometry().getNumberOfRows();
row += mNthreads) {
142 std::vector<float> splineParameters;
143 splineParameters.resize(spline.getNumberOfParameters());
145 const std::vector<o2::gpu::TPCFastSpaceChargeCorrectionMap::CorrectionPoint>&
data = mCorrectionMap.
getPoints(sector,
row);
146 int nDataPoints =
data.size();
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);
153 for (
int i = 0;
i < nDataPoints; ++
i) {
157 correction.convLocalToGrid(sector,
row, p.mY, p.mZ, gu, gv, scale);
158 if (scale - 1.f > 1.e-6) {
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;
168 helper.
approximateDataPoints(spline, splineParameters.data(), 0., spline.getGridX1().getUmax(), 0., spline.getGridX2().getUmax(), pointGU.data(),
169 pointGV.data(), pointCorr.data(), pointWeight.data(), nDataPoints);
171 for (
int i = 0;
i < spline.getNumberOfParameters();
i++) {
172 splineParameters[
i] = 0.f;
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];
185 float* splineXYZ = correction.getCorrectionData(sector,
row);
186 for (
int i = 0;
i < spline.getNumberOfParameters();
i++) {
187 splineXYZ[
i] = splineParameters[
i];
193 std::vector<std::thread> threads(mNthreads);
196 for (
int i = 0;
i < mNthreads;
i++) {
197 threads[
i] = std::thread(myThread,
i);
201 for (
auto& th : threads) {
209 LOGP(info,
"Space charge correction tooks: {}s", watch.RealTime());
213 std::function<
void(
int sector,
double gx,
double gy,
double gz,
214 double& dgx,
double& dgy,
double& dgz)>
216 const int nKnotsY,
const int nKnotsZ)
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;
224 mGeo.convLocalToGlobal(sector, lx, ly, lz, gx, gy, gz);
225 double dgx, dgy, dgz;
226 correctionGlobal(sector, gx, gy, gz, dgx, dgy, dgz);
228 mGeo.convGlobalToLocal(sector, gx + dgx, gy + dgy, gz + dgz, lx1, ly1, lz1);
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)
242 LOG(info) <<
"fast space charge correction helper: create correction using " << mNthreads <<
" threads";
249 if (!mIsInitialized) {
253 const int nRows = mGeo.getNumberOfRows();
254 const int nCorrectionScenarios = nRows / 10 + 1;
259 for (
int row = 0;
row < mGeo.getNumberOfRows();
row++) {
260 int scenario =
row / 10;
261 if (scenario >= nCorrectionScenarios) {
262 scenario = nCorrectionScenarios - 1;
267 for (
int scenario = 0; scenario < nCorrectionScenarios; scenario++) {
268 int row = scenario * 10;
270 spline.recreate(nKnotsY, nKnotsZ);
276 LOG(info) <<
"fast space charge correction helper: fill data points from an input SP correction function";
283 int nRows = mGeo.getNumberOfRows();
284 mCorrectionMap.
init(nSectors, nRows);
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) {
296 mGeo.convPadDriftLengthToLocal(
iSector, iRow, pad, l,
y,
z);
298 correctionLocal(
iSector, iRow,
y,
z, dx, dy, dz);
300 y,
z, dx, dy, dz, 1.);
306 std::vector<std::thread> threads(mNthreads);
309 for (
int i = 0;
i < mNthreads;
i++) {
310 threads[
i] = std::thread(myThread,
i);
314 for (
auto& th : threads) {
320 fillSpaceChargeCorrectionFromMap(correction,
false);
324 return std::move(correctionPtr);
336 LOG(fatal) <<
"Wrong number of rows :" << geo.getNumberOfRows() <<
" instead of " << mapper.
getNumberOfRows() << std::endl;
339 double maxDx = 0, maxDy = 0;
341 for (
int row = 0;
row < geo.getNumberOfRows();
row++) {
343 const int nPads = geo.getRowInfo(
row).maxPad + 1;
349 const double x = geo.getRowInfo(
row).x;
353 for (
int pad = 0; pad < nPads; pad++) {
357 geo.convPadDriftLengthToLocal(0,
row, pad, 0.,
y,
z);
358 const double dx =
x -
c.X();
359 const double dy =
y - (-
c.Y());
361 if (fabs(dx) >= 1.e-6 || fabs(dy) >= 1.e-5) {
362 LOG(warning) <<
"wrong calculated pad position:"
363 <<
" row " <<
row <<
" pad " << pad <<
" x calc " <<
x <<
" x in map " <<
c.X() <<
" dx " << (
x -
c.X())
364 <<
" y calc " <<
y <<
" y in map " << -
c.Y() <<
" dy " << dy << std::endl;
366 if (fabs(maxDx) < fabs(dx)) {
369 if (fabs(maxDy) < fabs(dy)) {
375 if (fabs(maxDx) >= 1.e-4 || fabs(maxDy) >= 1.e-4) {
376 LOG(fatal) <<
"wrong calculated pad position:"
377 <<
" max Dx " << maxDx <<
" max Dy " << maxDy << std::endl;
382 const o2::tpc::TrackResiduals& trackResiduals, TTree* voxResTree, TTree* voxResTreeInverse,
bool useSmoothed,
bool invertSigns,
388 LOG(info) <<
"fast space charge correction helper: create correction from track residuals using " << mNthreads <<
" threads";
390 TStopwatch watch, watch1;
402 std::vector<double> knotsDouble[2];
404 knotsDouble[0].reserve(nY2Xbins);
405 knotsDouble[1].reserve(nZ2Xbins);
409 for (
int i = 0,
j = nY2Xbins - 1;
i <=
j;
i += 2,
j -= 2) {
410 knotsDouble[0].push_back(trackResiduals.
getY2X(0,
i));
412 knotsDouble[0].push_back(trackResiduals.
getY2X(0,
j));
416 for (
int i = 0,
j = nZ2Xbins - 1;
i <=
j;
i += 2,
j -= 2) {
417 knotsDouble[1].push_back(trackResiduals.
getZ2X(
i));
419 knotsDouble[1].push_back(trackResiduals.
getZ2X(
j));
423 std::vector<int> knotsInt[2];
425 for (
int dim = 0; dim < 2; dim++) {
426 auto& knotsD = knotsDouble[dim];
427 std::sort(knotsD.begin(), knotsD.end());
429 double pitch = knotsD[1] - knotsD[0];
430 for (
int i = 2;
i < knotsD.size();
i++) {
431 double d = knotsD[
i] - knotsD[
i - 1];
439 auto& knotsI = knotsInt[dim];
440 knotsI.reserve(knotsD.size());
441 double u0 = knotsD[0];
442 double u1 = knotsD[knotsD.size() - 1];
443 for (
auto& u : knotsD) {
445 int iu =
int(u / pitch + 0.5);
446 knotsI.push_back(iu);
448 double uorig = u / (u1 - u0) * 2 - 1.;
449 u = (iu * pitch) / (u1 - u0) * 2 - 1.;
450 LOG(info) <<
"TPC SC splines: convert " << (dim == 0 ?
"y" : (dim == 1 ?
"z" :
"-z")) <<
" bin to the knot: " << uorig <<
" -> " << u <<
" -> " << iu;
453 if (knotsI.size() < 2) {
460 auto& yKnotsInt = knotsInt[0];
461 auto& zKnotsInt = knotsInt[1];
463 int nKnotsY = yKnotsInt.size();
464 int nKnotsZ = zKnotsInt.size();
469 const int nRows = geo.getNumberOfRows();
474 const int nCorrectionScenarios = 1;
479 for (
int row = 0;
row < geo.getNumberOfRows();
row++) {
485 spline.recreate(nKnotsY, &yKnotsInt[0], nKnotsZ, &zKnotsInt[0]);
492 for (
int iRow = 0; iRow < geo.getNumberOfRows(); iRow++) {
493 auto& info = correction.getRowInfo(iRow);
494 const auto& spline = correction.getSplineForRow(iRow);
495 double rowX = geo.getRowInfo(iRow).x;
496 double yMin = rowX * trackResiduals.
getY2X(iRow, 0);
498 double zMin = rowX * trackResiduals.
getZ2X(0);
500 double zOut = std::min(
zMax, (
double)geo.getTPCzLength());
501 info.gridMeasured.set(yMin, spline.getGridX1().getUmax() / (
yMax - yMin),
503 zOut, geo.getTPCzLength());
505 info.gridReal = info.gridMeasured;
511 LOG(info) <<
"fast space charge correction helper: preparation took " << watch1.RealTime() <<
"s";
513 for (
int processingInverseCorrection = 0; processingInverseCorrection <= 1; processingInverseCorrection++) {
515 TTree* currentTree = (processingInverseCorrection) ? voxResTreeInverse : voxResTree;
520 const char* directionName = (processingInverseCorrection) ?
"inverse" :
"direct";
521 LOG(info) <<
"\n fast space charge correction helper: Process " << directionName
522 <<
" correction: fill data points from track residuals.. ";
539 std::vector<VoxelData> vSectorData[nRows * nSectors];
540 for (
int ir = 0;
ir < nRows * nSectors;
ir++) {
541 vSectorData[
ir].resize(nY2Xbins * nZ2Xbins);
546 ROOT::TTreeProcessorMT processor(*currentTree, mNthreads);
547 std::string errMsg = std::string(
"Error reading ") + directionName +
" track residuals: ";
548 auto myThread = [&](TTreeReader& readerSubRange) {
549 TTreeReaderValue<o2::tpc::TrackResiduals::VoxRes>
v(readerSubRange,
"voxRes");
550 while (readerSubRange.Next()) {
552 if (iSector < 0 || iSector >= nSectors) {
553 LOG(fatal) << errMsg <<
"Sector number " <<
iSector <<
" is out of range";
557 if (iRow < 0 || iRow >= nRows) {
558 LOG(fatal) << errMsg <<
"Row number " << iRow <<
" is out of range";
560 double rowX = trackResiduals.
getX(iRow);
563 auto&
data = vSectorData[
iSector * nRows + iRow][iy * nZ2Xbins + iz];
578 processor.Process(myThread);
583 if (mDebugMirrorAdata2C) {
585 for (
int iRow = 0; iRow < nRows; iRow++) {
586 for (
int iy = 0; iy < nY2Xbins; iy++) {
587 for (
int iz = 0; iz < nZ2Xbins; iz++) {
588 auto& dataA = vSectorData[
iSector * nRows + iRow][iy * nZ2Xbins + iz];
591 dataC.mZ = -dataC.mZ;
592 dataC.mCz = -dataC.mCz;
599 double maxError[3] = {0., 0., 0.};
606 auto myThread = [&](
int iThread,
int nTreads) {
610 int mSmoothingStep{100};
613 std::vector<Voxel> vRowVoxels(nY2Xbins * nZ2Xbins);
615 for (
int iRow = iThread; iRow < nRows; iRow += nTreads) {
621 double x = trackResiduals.
getX(xBin);
622 double dx = 1. / trackResiduals.
getDXI(xBin);
623 bool isDataFound =
false;
624 for (
int iy = 0; iy < nY2Xbins; iy++) {
625 for (
int iz = 0; iz < nZ2Xbins; iz++) {
626 auto&
data = vSectorData[
iSector * nRows + iRow][iy * nZ2Xbins + iz];
627 auto& vox = vRowVoxels[iy * nZ2Xbins + iz];
629 double y2x = trackResiduals.
getY2X(xBin, iy);
631 double z2x = trackResiduals.
getZ2X(iz);
634 vox.mDy =
x / trackResiduals.
getDY2XI(xBin, iy);
635 vox.mDz =
x * trackResiduals.
getDZ2X(iz);
639 if (
data.mNentries > 0) {
640 vox.mSmoothingStep = 0;
644 std::stringstream
msg;
645 if (fabs(
x -
data.mX) > mVoxelMeanValidityRange * dx / 2.) {
646 msg <<
"\n x: center " <<
x <<
" dx " <<
data.mX -
x <<
" half bin size " << dx / 2;
649 if (fabs(vox.mY -
data.mY) > mVoxelMeanValidityRange * vox.mDy / 2.) {
650 msg <<
"\n y: center " << vox.mY <<
" dy " <<
data.mY - vox.mY <<
" half bin size " << vox.mDy / 2;
654 if (fabs(vox.mZ -
data.mZ) > mVoxelMeanValidityRange * vox.mDz / 2.) {
655 msg <<
"\n z: center " << vox.mZ <<
" dz " <<
data.mZ - vox.mZ <<
" half bin size " << vox.mDz / 2;
659 if (!
msg.str().empty()) {
660 bool isMaxErrorExceeded = (fabs(
data.mX -
x) / dx > maxError[0]) ||
661 (fabs(
data.mY - vox.mY) / vox.mDy > maxError[1]) ||
662 (fabs(
data.mZ - vox.mZ) / vox.mDz > maxError[2]);
663 static std::mutex mutex;
666 if (nErrors < 20 || isMaxErrorExceeded) {
667 LOG(warning) << directionName <<
" correction: error N " << nErrors <<
"fitted voxel position is outside the voxel: "
668 <<
" sector " <<
iSector <<
" row " << iRow <<
" bin y " << iy <<
" bin z " << iz
670 maxError[0] = GPUCommonMath::Max<double>(maxError[0], fabs(
data.mX -
x) / dx);
671 maxError[1] = GPUCommonMath::Max<double>(maxError[1], fabs(
data.mY - vox.mY) / vox.mDy);
672 maxError[2] = GPUCommonMath::Max<double>(maxError[2], fabs(
data.mZ - vox.mZ) / vox.mDz);
684 vox.mSmoothingStep = 100;
686 if (mDebugUseVoxelCenters) {
694 for (
int iy = 0; iy < nY2Xbins; iy++) {
695 for (
int iz = 0; iz < nZ2Xbins; iz++) {
696 vRowVoxels[iy * nZ2Xbins + iz].mSmoothingStep = 0;
706 for (
int ismooth = 1; ismooth <= 2; ismooth++) {
707 for (
int iy = 0; iy < nY2Xbins; iy++) {
708 for (
int iz = 0; iz < nZ2Xbins; iz++) {
709 auto&
data = vSectorData[
iSector * nRows + iRow][iy * nZ2Xbins + iz];
710 auto& vox = vRowVoxels[iy * nZ2Xbins + iz];
711 if (vox.mSmoothingStep <= ismooth) {
720 auto update = [&](
int iy1,
int iz1) {
721 auto& data1 = vSectorData[
iSector * nRows + iRow][iy1 * nZ2Xbins + iz1];
722 auto& vox1 = vRowVoxels[iy1 * nZ2Xbins + iz1];
723 if (vox1.mSmoothingStep >= ismooth) {
726 double w1 = 1. / (abs(iy - iy1) + abs(iz - iz1) + 1);
727 data.mCx += w1 * data1.mCx;
728 data.mCy += w1 * data1.mCy;
729 data.mCz += w1 * data1.mCz;
735 for (
int iy1 = iy - 1; iy1 >= 0 && !update(iy1, iz); iy1--) {
737 for (
int iy1 = iy + 1; iy1 < nY2Xbins && !update(iy1, iz); iy1++) {
739 for (
int iz1 = iz - 1; iz1 >= 0 && !update(iy, iz1); iz1--) {
741 for (
int iz1 = iz + 1; iz1 < nZ2Xbins && !update(iy, iz1); iz1++) {
748 vox.mSmoothingStep = ismooth;
755 LOG(
debug) <<
"Sector " <<
iSector <<
" row " << iRow <<
": " << nRepairs <<
" voxel repairs for " << nY2Xbins * nZ2Xbins <<
" voxels";
760 auto& info = correction.getRowInfo(iRow);
761 const auto& spline = correction.getSplineForRow(iRow);
763 auto addVoxel = [&](
int iy,
int iz,
double weight) {
764 auto& vox = vRowVoxels[iy * nZ2Xbins + iz];
765 if (vox.mSmoothingStep > 2) {
766 LOG(fatal) <<
"empty voxel is not repared: y " << iy <<
" z " << iz;
768 auto&
data = vSectorData[
iSector * nRows + iRow][iy * nZ2Xbins + iz];
772 auto addEdge = [&](
int iy1,
int iz1,
int iy2,
int iz2,
int nPoints) {
777 if (iy1 < 0 || iy1 >= nY2Xbins || iz1 < 0 || iz1 >= nZ2Xbins) {
780 if (iy2 < 0 || iy2 >= nY2Xbins || iz2 < 0 || iz2 >= nZ2Xbins) {
783 auto& data1 = vSectorData[
iSector * nRows + iRow][iy1 * nZ2Xbins + iz1];
784 auto& vox1 = vRowVoxels[iy1 * nZ2Xbins + iz1];
785 auto& data2 = vSectorData[
iSector * nRows + iRow][iy2 * nZ2Xbins + iz2];
786 auto& vox2 = vRowVoxels[iy2 * nZ2Xbins + iz2];
787 double y1 = data1.mY;
788 double z1 = data1.mZ;
789 double cx1 = data1.mCx;
790 double cy1 = data1.mCy;
791 double cz1 = data1.mCz;
792 double y2 = data2.mY;
793 double z2 = data2.mZ;
794 double cx2 = data2.mCx;
795 double cy2 = data2.mCy;
796 double cz2 = data2.mCz;
798 for (
int is = 1; is <= nPoints; is++) {
799 double s2 = is / (double)(nPoints + 1);
801 double y =
s1 *
y1 + s2 * y2;
802 double z =
s1 * z1 + s2 * z2;
803 double cx =
s1 * cx1 + s2 * cx2;
804 double cy =
s1 * cy1 + s2 * cy2;
805 double cz =
s1 * cz1 + s2 * cz2;
818 for (
int iy = 0; iy < nY2Xbins; iy++) {
819 for (
int iz = 0; iz < nZ2Xbins; iz++) {
821 addEdge(iy, iz, iy, iz + 1, 2);
822 addEdge(iy, iz, iy + 1, iz, 2);
823 addEdge(iy, iz, iy + 1, iz + 1, 2);
824 addEdge(iy + 1, iz, iy, iz + 1, 2);
833 int nThreads = mNthreads;
836 std::vector<std::thread> threads(nThreads);
838 for (
int i = 0;
i < nThreads;
i++) {
839 threads[
i] = std::thread(myThread,
i, nThreads);
843 for (
auto& th : threads) {
848 LOGP(info,
"Reading & reparing of the track residuals tooks: {}s", watch3.RealTime());
850 LOG(info) <<
"fast space charge correction helper: create space charge from the map of data points..";
854 if (!processingInverseCorrection && fitPointsDirect) {
855 *fitPointsDirect = helper->getCorrectionMap();
857 if (processingInverseCorrection && fitPointsInverse) {
858 *fitPointsInverse = helper->getCorrectionMap();
861 helper->fillSpaceChargeCorrectionFromMap(correction, processingInverseCorrection);
863 LOG(info) <<
"fast space charge correction helper: creation from the data map took " << watch4.RealTime() <<
"s";
867 if (voxResTree && !voxResTreeInverse) {
868 LOG(info) <<
"fast space charge correction helper: init inverse correction from direct correction..";
870 helper->initInverse(correction,
false);
871 LOG(info) <<
"fast space charge correction helper: init inverse correction took " << watch4.RealTime() <<
"s";
874 LOGP(info,
"Creation from track residuals tooks in total: {}s", watch.RealTime());
876 return std::move(correctionPtr);
882 std::vector<o2::gpu::TPCFastSpaceChargeCorrection*> corr{&correction};
890 LOG(info) <<
"fast space charge correction helper: init inverse";
892 if (corrections.size() != scaling.size()) {
893 LOGP(error,
"Input corrections and scaling values have different size");
897 auto& correction = *(corrections.front());
899 double tpcR2min = mGeo.getRowInfo(0).x - 1.;
900 tpcR2min = tpcR2min * tpcR2min;
901 double tpcR2max = mGeo.getRowInfo(mGeo.getNumberOfRows() - 1).
x;
903 tpcR2max = tpcR2max * tpcR2max;
905 for (
int row = 0;
row < mGeo.getNumberOfRows();
row++) {
906 auto& rowInfo = correction.getRowInfo(
row);
907 rowInfo.gridReal = rowInfo.gridMeasured;
913 auto myThread = [&](
int iThread) {
915 std::vector<float> splineParameters;
917 for (
int row = iThread;
row < mGeo.getNumberOfRows();
row += mNthreads) {
918 auto& rowInfo = correction.getRowInfo(
row);
923 std::vector<double> gridU;
925 const auto& grid = spline.getGridX1();
926 for (
int i = 0;
i < grid.getNumberOfKnots();
i++) {
927 if (
i == grid.getNumberOfKnots() - 1) {
928 gridU.push_back(grid.getKnot(
i).u);
931 for (
double s = 1.; s > 0.; s -= 0.1) {
932 gridU.push_back(s * grid.getKnot(
i).u + (1. - s) * grid.getKnot(
i + 1).u);
936 std::vector<double> gridV;
938 const auto& grid = spline.getGridX2();
939 for (
int i = 0;
i < grid.getNumberOfKnots();
i++) {
940 if (
i == grid.getNumberOfKnots() - 1) {
941 gridV.push_back(grid.getKnot(
i).u);
944 for (
double s = 1.; s > 0.; s -= 0.1) {
945 gridV.push_back(s * grid.getKnot(
i).u + (1. - s) * grid.getKnot(
i + 1).u);
950 std::vector<double> dataPointGridU, dataPointGridV, dataPointF, dataPointWeight;
951 dataPointGridU.reserve(gridU.size() * gridV.size());
952 dataPointGridV.reserve(gridU.size() * gridV.size());
953 dataPointF.reserve(3 * gridU.size() * gridV.size());
954 dataPointWeight.reserve(gridU.size() * gridV.size());
956 for (
int iu = 0; iu < gridU.size(); iu++) {
957 for (
int iv = 0; iv < gridV.size(); iv++) {
959 correction.convGridToLocal(sector,
row, gridU[iu], gridV[iv],
y,
z);
960 double dx = 0, dy = 0, dz = 0;
963 for (
int i = 0;
i < corrections.size(); ++
i) {
964 float dxTmp, dyTmp, dzTmp;
965 corrections[
i]->getCorrectionLocal(sector,
row,
y,
z, dxTmp, dyTmp, dzTmp);
966 dx += dxTmp * scaling[
i];
967 dy += dyTmp * scaling[
i];
968 dz += dzTmp * scaling[
i];
970 float gridU, gridV, scale;
971 correction.convRealLocalToGrid(sector,
row,
y + dy,
z + dz, gridU, gridV, scale);
972 dataPointGridU.push_back(gridU);
973 dataPointGridV.push_back(gridV);
974 dataPointF.push_back(scale * dx);
975 dataPointF.push_back(scale * dy);
976 dataPointF.push_back(scale * dz);
977 dataPointWeight.push_back(1.);
981 int nDataPoints = dataPointGridU.size();
982 splineParameters.resize(spline.getNumberOfParameters());
985 0., spline.getGridX2().getUmax(),
986 dataPointGridU.data(), dataPointGridV.data(),
987 dataPointF.data(), dataPointWeight.data(), nDataPoints);
989 float* splineX = correction.getCorrectionDataInvX(sector,
row);
990 float* splineUV = correction.getCorrectionDataInvYZ(sector,
row);
991 for (
int i = 0;
i < spline.getNumberOfParameters() / 3;
i++) {
992 splineX[
i] = splineParameters[3 *
i + 0];
993 splineUV[2 *
i + 0] = splineParameters[3 *
i + 1];
994 splineUV[2 *
i + 1] = splineParameters[3 *
i + 2];
999 std::vector<std::thread> threads(mNthreads);
1002 for (
int i = 0;
i < mNthreads;
i++) {
1003 threads[
i] = std::thread(myThread,
i);
1007 for (
auto& th : threads) {
1012 float duration = watch.RealTime();
1013 LOGP(info,
"Inverse tooks: {}s", duration);
1018 const std::vector<std::pair<const o2::gpu::TPCFastSpaceChargeCorrection*, double>>& additionalCorrections)
1022 mainSectorScale.fill(mainScale);
1023 std::vector<std::pair<const o2::gpu::TPCFastSpaceChargeCorrection*, SectorScales>> additionalSectorScales;
1024 for (
const auto& corr : additionalCorrections) {
1026 sectorScale.fill(corr.second);
1027 additionalSectorScales.emplace_back(corr.first, sectorScale);
1030 addCorrections(mainCorrection, mainSectorScale, additionalSectorScales);
1035 const std::vector<std::pair<const o2::gpu::TPCFastSpaceChargeCorrection*, SectorScales>>& additionalCorrections)
1040 LOG(info) <<
"fast space charge correction helper: Add corrections";
1042 const auto& geo = mainCorrection.getGeometry();
1044 for (
int sector = 0; sector < geo.getNumberOfSectors(); sector++) {
1046 float secMainScale = mainScale[sector];
1048 auto myThread = [&](
int iThread) {
1049 for (
int row = iThread;
row < geo.getNumberOfRows();
row += mNthreads) {
1050 auto& rowInfo = mainCorrection.getRowInfo(
row);
1051 const auto& spline = mainCorrection.getSplineForRow(
row);
1053 float* splineParameters = mainCorrection.getCorrectionData(sector,
row);
1054 float* splineParametersInvX = mainCorrection.getCorrectionDataInvX(sector,
row);
1055 float* splineParametersInvYZ = mainCorrection.getCorrectionDataInvYZ(sector,
row);
1057 constexpr int nKnotPar1d = 4;
1058 constexpr int nKnotPar2d = nKnotPar1d * 2;
1059 constexpr int nKnotPar3d = nKnotPar1d * 3;
1062 double parscale[4] = {secMainScale, secMainScale, secMainScale, secMainScale * secMainScale};
1063 for (
int iknot = 0, ind = 0; iknot < spline.getNumberOfKnots(); iknot++) {
1064 for (
int ipar = 0; ipar < nKnotPar1d; ++ipar) {
1065 for (
int idim = 0; idim < 3; idim++, ind++) {
1066 splineParameters[ind] *= parscale[ipar];
1070 for (
int iknot = 0, ind = 0; iknot < spline.getNumberOfKnots(); iknot++) {
1071 for (
int ipar = 0; ipar < nKnotPar1d; ++ipar) {
1072 for (
int idim = 0; idim < 1; idim++, ind++) {
1073 splineParametersInvX[ind] *= parscale[ipar];
1077 for (
int iknot = 0, ind = 0; iknot < spline.getNumberOfKnots(); iknot++) {
1078 for (
int ipar = 0; ipar < nKnotPar1d; ++ipar) {
1079 for (
int idim = 0; idim < 2; idim++, ind++) {
1080 splineParametersInvYZ[ind] *= parscale[ipar];
1088 const auto& gridU = spline.getGridX1();
1089 const auto& gridV = spline.getGridX2();
1091 for (
int icorr = 0; icorr < additionalCorrections.size(); ++icorr) {
1092 const auto& corr = *(additionalCorrections[icorr].first);
1093 double scale = additionalCorrections[icorr].second[sector];
1098 auto& linfo = corr.getRowInfo(
row);
1100 double scaleU = rowInfo.gridMeasured.getYscale() / linfo.gridMeasured.getYscale();
1101 double scaleV = rowInfo.gridMeasured.getZscale() / linfo.gridMeasured.getZscale();
1102 double scaleRealU = rowInfo.gridReal.getYscale() / linfo.gridReal.getYscale();
1103 double scaleRealV = rowInfo.gridReal.getZscale() / linfo.gridReal.getZscale();
1105 for (
int iu = 0; iu < gridU.getNumberOfKnots(); iu++) {
1106 double u = gridU.getKnot(iu).u;
1107 for (
int iv = 0; iv < gridV.getNumberOfKnots(); iv++) {
1108 double v = gridV.getKnot(iv).u;
1109 int knotIndex = spline.getKnotIndex(iu, iv);
1110 float P[nKnotPar3d];
1114 mainCorrection.convGridToLocal(sector,
row, u,
v,
y,
z);
1117 corr.convLocalToGrid(sector,
row,
y,
z, lu, lv, ls);
1119 double parscale[4] = {ls, ls * scaleU, ls * scaleV, ls * ls * scaleU * scaleV};
1120 const auto& spl = corr.getSplineForRow(
row);
1121 spl.interpolateParametersAtU(corr.getCorrectionData(sector,
row), lu, lv,
P);
1122 for (
int ipar = 0, ind = 0; ipar < nKnotPar1d; ++ipar) {
1123 for (
int idim = 0; idim < 3; idim++, ind++) {
1124 splineParameters[knotIndex * nKnotPar3d + ind] += parscale[ipar] *
P[ind];
1130 mainCorrection.convGridToRealLocal(sector,
row, u,
v,
y,
z);
1133 corr.convRealLocalToGrid(sector,
row,
y,
z, lu, lv, ls);
1135 double parscale[4] = {ls, ls * scaleRealU, ls * scaleRealV, ls * ls * scaleRealU * scaleRealV};
1138 corr.getSplineInvXforRow(
row).interpolateParametersAtU(corr.getCorrectionDataInvX(sector,
row), lu, lv,
P);
1139 for (
int ipar = 0, ind = 0; ipar < nKnotPar1d; ++ipar) {
1140 for (
int idim = 0; idim < 1; idim++, ind++) {
1141 splineParametersInvX[knotIndex * nKnotPar1d + ind] += parscale[ipar] *
P[ind];
1147 corr.getSplineInvYZforRow(
row).interpolateParametersAtU(corr.getCorrectionDataInvYZ(sector,
row), lu, lv,
P);
1148 for (
int ipar = 0, ind = 0; ipar < nKnotPar1d; ++ipar) {
1149 for (
int idim = 0; idim < 2; idim++, ind++) {
1150 splineParametersInvYZ[knotIndex * nKnotPar2d + ind] += parscale[ipar] *
P[ind];
1162 std::vector<std::thread> threads(mNthreads);
1165 for (
int i = 0;
i < mNthreads;
i++) {
1166 threads[
i] = std::thread(myThread,
i);
1170 for (
auto& th : threads) {
1175 double duration = watch.RealTime();
1176 LOGP(info,
"Merge of corrections tooks: {}s", duration);
1181 const std::vector<int>& sectors)
1185 LOG(info) <<
"fast space charge correction helper: Merge corrections";
1187 const auto& geo = destinationCorrection.getGeometry();
1189 for (
int sector : sectors) {
1190 if (sector < 0 || sector >= geo.getNumberOfSectors()) {
1191 LOGP(fatal,
"Invalid sector number {}. Valid range is [0, {})", sector, geo.getNumberOfSectors());
1194 auto myThread = [&](
int iThread) {
1195 for (
int row = iThread;
row < geo.getNumberOfRows();
row += mNthreads) {
1198 const auto& destSpline = destinationCorrection.getSplineForRow(
row);
1199 float* destSplineParameters = destinationCorrection.getCorrectionData(sector,
row);
1200 const auto& sourceSpline = sourceCorrection.getSplineForRow(
row);
1201 const float* sourceSplineParameters = sourceCorrection.getCorrectionData(sector,
row);
1204 if (destSpline.getGridX1().getNumberOfKnots() != sourceSpline.getGridX1().getNumberOfKnots() ||
1205 destSpline.getGridX2().getNumberOfKnots() != sourceSpline.getGridX2().getNumberOfKnots()) {
1206 LOGP(error,
"Splines for sector {} row {} are not compatible: number of knots in U or V direction do not match", sector,
row);
1210 memcpy(destSplineParameters, sourceSplineParameters, destSpline.getNumberOfParameters() *
sizeof(
float));
1214 const auto& destSpline = destinationCorrection.getSplineInvXforRow(
row);
1215 float* destSplineParameters = destinationCorrection.getCorrectionDataInvX(sector,
row);
1216 const auto& sourceSpline = sourceCorrection.getSplineInvXforRow(
row);
1217 const float* sourceSplineParameters = sourceCorrection.getCorrectionDataInvX(sector,
row);
1219 if (destSpline.getGridX1().getNumberOfKnots() != sourceSpline.getGridX1().getNumberOfKnots() ||
1220 destSpline.getGridX2().getNumberOfKnots() != sourceSpline.getGridX2().getNumberOfKnots()) {
1221 LOGP(error,
"Inverse X splines for sector {} row {} are not compatible: number of knots in U or V direction do not match", sector,
row);
1224 memcpy(destSplineParameters, sourceSplineParameters, destSpline.getNumberOfParameters() *
sizeof(
float));
1228 const auto& destSpline = destinationCorrection.getSplineInvYZforRow(
row);
1229 float* destSplineParameters = destinationCorrection.getCorrectionDataInvYZ(sector,
row);
1230 const auto& sourceSpline = sourceCorrection.getSplineInvYZforRow(
row);
1231 const float* sourceSplineParameters = sourceCorrection.getCorrectionDataInvYZ(sector,
row);
1233 if (destSpline.getGridX1().getNumberOfKnots() != sourceSpline.getGridX1().getNumberOfKnots() ||
1234 destSpline.getGridX2().getNumberOfKnots() != sourceSpline.getGridX2().getNumberOfKnots()) {
1235 LOGP(error,
"Inverse YZ splines for sector {} row {} are not compatible: number of knots in U or V direction do not match", sector,
row);
1238 memcpy(destSplineParameters, sourceSplineParameters, destSpline.getNumberOfParameters() *
sizeof(
float));
1242 auto& destSecRowInfo = destinationCorrection.getRowInfo(
row);
1243 const auto& sourceSecRowInfo = sourceCorrection.getRowInfo(
row);
1244 destSecRowInfo = sourceSecRowInfo;
1248 std::vector<std::thread> threads(mNthreads);
1251 for (
int i = 0;
i < mNthreads;
i++) {
1252 threads[
i] = std::thread(myThread,
i);
1256 for (
auto& th : threads) {
1261 double duration = watch.RealTime();
1262 LOGP(info,
"Merge of corrections tooks: {}s", duration);
1267 LOG(info) <<
"fast space charge correction helper: use voxel centers for correction";
1268 mDebugUseVoxelCenters =
true;
1273 LOG(info) <<
"fast space charge correction helper: mirror A data to C data";
1274 mDebugMirrorAdata2C =
true;
Definition of ChebyshevFit1D class.
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.
Definition of Spline2DHelper class.
class to create TPC fast space charge correction
Definition of the TrackResiduals class.
static const ParameterDetector & Instance()
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.
const std::vector< CorrectionPoint > & getPoints(int32_t iSector, int32_t iRow) const
bool isInitialized() 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.
int getNumberOfRows() const
int getNumberOfPadsInRowSector(int row) const
int getGlobalRowOffsetRegion(int region) const
GlobalPadNumber globalPadNumber(const PadPos &globalPadPosition) const
static Mapper & instance(const std::string mappingDir="")
const PadRegionInfo & getPadRegionInfo(const unsigned char region) const
int getNumberOfRowsRegion(int region) const
const PadCentre & padCentre(GlobalPadNumber padNumber) const
static constexpr int MAXSECTOR
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 > §ors)
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 getY2X(int ix, int ip) const
float getDY2XI(int ix, int iy=0) const
float getDXI(int ix) const
float getDZ2X(int iz=0) const
GLuint GLfloat GLfloat GLfloat GLfloat y1
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat s1
GLuint GLuint GLfloat weight
GLubyte GLubyte GLubyte GLubyte w
GLdouble GLdouble GLdouble z
math_utils::Point2D< float > PadCentre
Pad centres as 2D float.
unsigned short GlobalPadNumber
global pad number
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
The struct contains necessary info for TPC padrow.
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
o2::InteractionRecord ir(0, 0)
uint64_t const void const *restrict const msg