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);
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);
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*, float>>& additionalCorrections,
bool )
1023 LOG(info) <<
"fast space charge correction helper: Merge corrections";
1025 const auto& geo = mainCorrection.getGeometry();
1027 for (
int sector = 0; sector < geo.getNumberOfSectors(); sector++) {
1029 auto myThread = [&](
int iThread) {
1030 for (
int row = iThread;
row < geo.getNumberOfRows();
row += mNthreads) {
1031 auto& rowInfo = mainCorrection.getRowInfo(
row);
1032 const auto& spline = mainCorrection.getSplineForRow(
row);
1034 float* splineParameters = mainCorrection.getCorrectionData(sector,
row);
1035 float* splineParametersInvX = mainCorrection.getCorrectionDataInvX(sector,
row);
1036 float* splineParametersInvYZ = mainCorrection.getCorrectionDataInvYZ(sector,
row);
1038 constexpr int nKnotPar1d = 4;
1039 constexpr int nKnotPar2d = nKnotPar1d * 2;
1040 constexpr int nKnotPar3d = nKnotPar1d * 3;
1044 double parscale[4] = {mainScale, mainScale, mainScale, mainScale * mainScale};
1045 for (
int iknot = 0, ind = 0; iknot < spline.getNumberOfKnots(); iknot++) {
1046 for (
int ipar = 0; ipar < nKnotPar1d; ++ipar) {
1047 for (
int idim = 0; idim < 3; idim++, ind++) {
1048 splineParameters[ind] *= parscale[ipar];
1052 for (
int iknot = 0, ind = 0; iknot < spline.getNumberOfKnots(); iknot++) {
1053 for (
int ipar = 0; ipar < nKnotPar1d; ++ipar) {
1054 for (
int idim = 0; idim < 1; idim++, ind++) {
1055 splineParametersInvX[ind] *= parscale[ipar];
1059 for (
int iknot = 0, ind = 0; iknot < spline.getNumberOfKnots(); iknot++) {
1060 for (
int ipar = 0; ipar < nKnotPar1d; ++ipar) {
1061 for (
int idim = 0; idim < 2; idim++, ind++) {
1062 splineParametersInvYZ[ind] *= parscale[ipar];
1070 const auto& gridU = spline.getGridX1();
1071 const auto& gridV = spline.getGridX2();
1073 for (
int icorr = 0; icorr < additionalCorrections.size(); ++icorr) {
1074 const auto& corr = *(additionalCorrections[icorr].first);
1075 double scale = additionalCorrections[icorr].second;
1076 auto& linfo = corr.getRowInfo(
row);
1078 double scaleU = rowInfo.gridMeasured.getYscale() / linfo.gridMeasured.getYscale();
1079 double scaleV = rowInfo.gridMeasured.getZscale() / linfo.gridMeasured.getZscale();
1080 double scaleRealU = rowInfo.gridReal.getYscale() / linfo.gridReal.getYscale();
1081 double scaleRealV = rowInfo.gridReal.getZscale() / linfo.gridReal.getZscale();
1083 for (
int iu = 0; iu < gridU.getNumberOfKnots(); iu++) {
1084 double u = gridU.getKnot(iu).u;
1085 for (
int iv = 0; iv < gridV.getNumberOfKnots(); iv++) {
1086 double v = gridV.getKnot(iv).u;
1087 int knotIndex = spline.getKnotIndex(iu, iv);
1088 float P[nKnotPar3d];
1092 mainCorrection.convGridToLocal(sector,
row, u,
v,
y,
z);
1095 corr.convLocalToGrid(sector,
row,
y,
z, lu, lv, ls);
1097 double parscale[4] = {ls, ls * scaleU, ls * scaleV, ls * ls * scaleU * scaleV};
1098 const auto& spl = corr.getSplineForRow(
row);
1099 spl.interpolateParametersAtU(corr.getCorrectionData(sector,
row), lu, lv,
P);
1100 for (
int ipar = 0, ind = 0; ipar < nKnotPar1d; ++ipar) {
1101 for (
int idim = 0; idim < 3; idim++, ind++) {
1102 splineParameters[knotIndex * nKnotPar3d + ind] += parscale[ipar] *
P[ind];
1108 mainCorrection.convGridToRealLocal(sector,
row, u,
v,
y,
z);
1111 corr.convRealLocalToGrid(sector,
row,
y,
z, lu, lv, ls);
1113 double parscale[4] = {ls, ls * scaleRealU, ls * scaleRealV, ls * ls * scaleRealU * scaleRealV};
1116 corr.getSplineInvXforRow(
row).interpolateParametersAtU(corr.getCorrectionDataInvX(sector,
row), lu, lv,
P);
1117 for (
int ipar = 0, ind = 0; ipar < nKnotPar1d; ++ipar) {
1118 for (
int idim = 0; idim < 1; idim++, ind++) {
1119 splineParametersInvX[knotIndex * nKnotPar1d + ind] += parscale[ipar] *
P[ind];
1125 corr.getSplineInvYZforRow(
row).interpolateParametersAtU(corr.getCorrectionDataInvYZ(sector,
row), lu, lv,
P);
1126 for (
int ipar = 0, ind = 0; ipar < nKnotPar1d; ++ipar) {
1127 for (
int idim = 0; idim < 2; idim++, ind++) {
1128 splineParametersInvYZ[knotIndex * nKnotPar2d + ind] += parscale[ipar] *
P[ind];
1140 std::vector<std::thread> threads(mNthreads);
1143 for (
int i = 0;
i < mNthreads;
i++) {
1144 threads[
i] = std::thread(myThread,
i);
1148 for (
auto& th : threads) {
1153 float duration = watch.RealTime();
1154 LOGP(info,
"Merge of corrections tooks: {}s", duration);