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);
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)
334 LOG(info) <<
"fast space charge correction helper: create correction with irregular knots using " << mNthreads <<
" threads";
336 if (!mIsInitialized) {
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");
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]);
353 if (!(minDistance > 0.)) {
354 LOG(
error) <<
"fast space charge correction helper: the knots in " << (dim == 0 ?
"y" :
"z") <<
" must be strictly increasing";
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));
368 for (
int row = 0;
row < mGeo.getNumberOfRows();
row++) {
372 spline.recreate(knotsInt[0].
size(), knotsInt[0].
data(), knotsInt[1].
size(), knotsInt[1].
data());
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);
387 samplingPoints[dim].push_back(1.);
390 LOG(info) <<
"fast space charge correction helper: fill data points from an input SP correction function";
393 const int nRows = mGeo.getNumberOfRows();
394 mCorrectionMap.
init(nSectors, nRows);
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]) {
409 correctionLocal(
iSector, iRow,
y,
z, dx, dy, dz);
416 std::vector<std::thread> threads(mNthreads);
419 for (
int i = 0;
i < mNthreads;
i++) {
420 threads[
i] = std::thread(myThread,
i);
424 for (
auto& th : threads) {
430 fillSpaceChargeCorrectionFromMap(correction,
false);
433 return correctionPtr;
445 LOG(fatal) <<
"Wrong number of rows :" << geo.getNumberOfRows() <<
" instead of " << mapper.
getNumberOfRows() << std::endl;
448 double maxDx = 0, maxDy = 0;
450 for (
int row = 0;
row < geo.getNumberOfRows();
row++) {
452 const int nPads = geo.getRowInfo(
row).maxPad + 1;
458 const double x = geo.getRowInfo(
row).x;
462 for (
int pad = 0; pad < nPads; pad++) {
466 geo.convPadDriftLengthToLocal(0,
row, pad, 0.,
y,
z);
467 const double dx =
x -
c.X();
468 const double dy =
y - (-
c.Y());
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;
475 if (fabs(maxDx) < fabs(dx)) {
478 if (fabs(maxDy) < fabs(dy)) {
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;
491 const o2::tpc::TrackResiduals& trackResiduals, TTree* voxResTree, TTree* voxResTreeInverse,
bool useSmoothed,
bool invertSigns,
497 LOG(info) <<
"fast space charge correction helper: create correction from track residuals using " << mNthreads <<
" threads";
499 TStopwatch watch, watch1;
511 std::vector<double> knotsDouble[2];
513 knotsDouble[0].reserve(nY2Xbins);
514 knotsDouble[1].reserve(nZ2Xbins);
518 for (
int i = 0,
j = nY2Xbins - 1;
i <=
j;
i += 2,
j -= 2) {
519 knotsDouble[0].push_back(trackResiduals.
getY2X(0,
i));
521 knotsDouble[0].push_back(trackResiduals.
getY2X(0,
j));
525 for (
int i = 0,
j = nZ2Xbins - 1;
i <=
j;
i += 2,
j -= 2) {
526 knotsDouble[1].push_back(trackResiduals.
getZ2X(
i));
528 knotsDouble[1].push_back(trackResiduals.
getZ2X(
j));
532 std::vector<int> knotsInt[2];
534 for (
int dim = 0; dim < 2; dim++) {
535 auto& knotsD = knotsDouble[dim];
536 std::sort(knotsD.begin(), knotsD.end());
538 double pitch = knotsD[1] - knotsD[0];
539 for (
int i = 2;
i < knotsD.size();
i++) {
540 double d = knotsD[
i] - knotsD[
i - 1];
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) {
554 int iu = int(u / pitch + 0.5);
555 knotsI.push_back(iu);
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;
562 if (knotsI.size() < 2) {
569 auto& yKnotsInt = knotsInt[0];
570 auto& zKnotsInt = knotsInt[1];
572 int nKnotsY = yKnotsInt.size();
573 int nKnotsZ = zKnotsInt.size();
578 const int nRows = geo.getNumberOfRows();
583 const int nCorrectionScenarios = 1;
588 for (
int row = 0;
row < geo.getNumberOfRows();
row++) {
594 spline.recreate(nKnotsY, &yKnotsInt[0], nKnotsZ, &zKnotsInt[0]);
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);
607 double zMin = rowX * trackResiduals.
getZ2X(0);
609 double zOut = std::min(
zMax, (
double)geo.getTPCzLength());
610 info.gridMeasured.set(yMin, spline.getGridX1().getUmax() / (
yMax - yMin),
612 zOut, geo.getTPCzLength());
614 info.gridReal = info.gridMeasured;
620 LOG(info) <<
"fast space charge correction helper: preparation took " << watch1.RealTime() <<
"s";
622 for (
int processingInverseCorrection = 0; processingInverseCorrection <= 1; processingInverseCorrection++) {
624 TTree* currentTree = (processingInverseCorrection) ? voxResTreeInverse : voxResTree;
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.. ";
648 std::vector<VoxelData> vSectorData[nRows * nSectors];
649 for (
int ir = 0;
ir < nRows * nSectors;
ir++) {
650 vSectorData[
ir].resize(nY2Xbins * nZ2Xbins);
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()) {
661 if (iSector < 0 || iSector >= nSectors) {
662 LOG(fatal) << errMsg <<
"Sector number " <<
iSector <<
" is out of range";
666 if (iRow < 0 || iRow >= nRows) {
667 LOG(fatal) << errMsg <<
"Row number " << iRow <<
" is out of range";
669 double rowX = trackResiduals.
getX(iRow);
672 auto&
data = vSectorData[
iSector * nRows + iRow][iy * nZ2Xbins + iz];
687 processor.Process(myThread);
692 if (mDebugMirrorAdata2C) {
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];
700 dataC.mZ = -dataC.mZ;
701 dataC.mCz = -dataC.mCz;
708 double maxError[3] = {0., 0., 0.};
715 auto myThread = [&](
int iThread,
int nTreads) {
719 int mSmoothingStep{100};
722 std::vector<Voxel> vRowVoxels(nY2Xbins * nZ2Xbins);
724 for (
int iRow = iThread; iRow < nRows; iRow += nTreads) {
730 double x = trackResiduals.
getX(xBin);
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];
738 double y2x = trackResiduals.
getY2X(xBin, iy);
740 double z2x = trackResiduals.
getZ2X(iz);
743 vox.mDy =
x / trackResiduals.
getDY2XI(xBin, iy);
744 vox.mDz =
x * trackResiduals.
getDZ2X(iz);
748 if (
data.mNentries > 0) {
749 vox.mSmoothingStep = 0;
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;
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;
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;
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;
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
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);
793 vox.mSmoothingStep = 100;
795 if (mDebugUseVoxelCenters) {
803 for (
int iy = 0; iy < nY2Xbins; iy++) {
804 for (
int iz = 0; iz < nZ2Xbins; iz++) {
805 vRowVoxels[iy * nZ2Xbins + iz].mSmoothingStep = 0;
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) {
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) {
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;
844 for (
int iy1 = iy - 1; iy1 >= 0 && !update(iy1, iz); iy1--) {
846 for (
int iy1 = iy + 1; iy1 < nY2Xbins && !update(iy1, iz); iy1++) {
848 for (
int iz1 = iz - 1; iz1 >= 0 && !update(iy, iz1); iz1--) {
850 for (
int iz1 = iz + 1; iz1 < nZ2Xbins && !update(iy, iz1); iz1++) {
857 vox.mSmoothingStep = ismooth;
864 LOG(
debug) <<
"Sector " <<
iSector <<
" row " << iRow <<
": " << nRepairs <<
" voxel repairs for " << nY2Xbins * nZ2Xbins <<
" voxels";
869 auto& info = correction.getRowInfo(iRow);
870 const auto& spline = correction.getSplineForRow(iRow);
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;
877 auto&
data = vSectorData[
iSector * nRows + iRow][iy * nZ2Xbins + iz];
881 auto addEdge = [&](
int iy1,
int iz1,
int iy2,
int iz2,
int nPoints) {
886 if (iy1 < 0 || iy1 >= nY2Xbins || iz1 < 0 || iz1 >= nZ2Xbins) {
889 if (iy2 < 0 || iy2 >= nY2Xbins || iz2 < 0 || iz2 >= nZ2Xbins) {
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;
907 for (
int is = 1; is <= nPoints; is++) {
908 double s2 = is / (double)(nPoints + 1);
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;
927 for (
int iy = 0; iy < nY2Xbins; iy++) {
928 for (
int iz = 0; iz < nZ2Xbins; iz++) {
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);
942 int nThreads = mNthreads;
945 std::vector<std::thread> threads(nThreads);
947 for (
int i = 0;
i < nThreads;
i++) {
948 threads[
i] = std::thread(myThread,
i, nThreads);
952 for (
auto& th : threads) {
957 LOGP(info,
"Reading & reparing of the track residuals tooks: {}s", watch3.RealTime());
959 LOG(info) <<
"fast space charge correction helper: create space charge from the map of data points..";
963 if (!processingInverseCorrection && fitPointsDirect) {
964 *fitPointsDirect = helper->getCorrectionMap();
966 if (processingInverseCorrection && fitPointsInverse) {
967 *fitPointsInverse = helper->getCorrectionMap();
970 helper->fillSpaceChargeCorrectionFromMap(correction, processingInverseCorrection);
972 LOG(info) <<
"fast space charge correction helper: creation from the data map took " << watch4.RealTime() <<
"s";
976 if (voxResTree && !voxResTreeInverse) {
977 LOG(info) <<
"fast space charge correction helper: init inverse correction from direct correction..";
979 helper->initInverse(correction,
false);
980 LOG(info) <<
"fast space charge correction helper: init inverse correction took " << watch4.RealTime() <<
"s";
983 LOGP(info,
"Creation from track residuals tooks in total: {}s", watch.RealTime());
985 return std::move(correctionPtr);
991 std::vector<o2::gpu::TPCFastSpaceChargeCorrection*> corr{&correction};
999 LOG(info) <<
"fast space charge correction helper: init inverse";
1001 if (corrections.size() != scaling.size()) {
1002 LOGP(
error,
"Input corrections and scaling values have different size");
1006 auto& correction = *(corrections.front());
1008 double tpcR2min = mGeo.getRowInfo(0).x - 1.;
1009 tpcR2min = tpcR2min * tpcR2min;
1010 double tpcR2max = mGeo.getRowInfo(mGeo.getNumberOfRows() - 1).
x;
1012 tpcR2max = tpcR2max * tpcR2max;
1014 for (
int row = 0;
row < mGeo.getNumberOfRows();
row++) {
1015 auto& rowInfo = correction.getRowInfo(
row);
1016 rowInfo.gridReal = rowInfo.gridMeasured;
1022 auto myThread = [&](
int iThread) {
1024 std::vector<float> splineParameters;
1026 for (
int row = iThread;
row < mGeo.getNumberOfRows();
row += mNthreads) {
1027 auto& rowInfo = correction.getRowInfo(
row);
1032 std::vector<double> gridU;
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);
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);
1045 std::vector<double> gridV;
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);
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);
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());
1065 for (
int iu = 0; iu < gridU.size(); iu++) {
1066 for (
int iv = 0; iv < gridV.size(); iv++) {
1068 correction.convGridToLocal(
sector,
row, gridU[iu], gridV[iv],
y,
z);
1069 double dx = 0, dy = 0, dz = 0;
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];
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.);
1090 int nDataPoints = dataPointGridU.size();
1091 splineParameters.resize(spline.getNumberOfParameters());
1094 0., spline.getGridX2().getUmax(),
1095 dataPointGridU.data(), dataPointGridV.data(),
1096 dataPointF.data(), dataPointWeight.data(), nDataPoints);
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];
1108 std::vector<std::thread> threads(mNthreads);
1111 for (
int i = 0;
i < mNthreads;
i++) {
1112 threads[
i] = std::thread(myThread,
i);
1116 for (
auto& th : threads) {
1121 float duration = watch.RealTime();
1122 LOGP(info,
"Inverse tooks: {}s", duration);
1127 const std::vector<std::pair<const o2::gpu::TPCFastSpaceChargeCorrection*, double>>& additionalCorrections)
1131 mainSectorScale.fill(mainScale);
1132 std::vector<std::pair<const o2::gpu::TPCFastSpaceChargeCorrection*, SectorScales>> additionalSectorScales;
1133 for (
const auto& corr : additionalCorrections) {
1135 sectorScale.fill(corr.second);
1136 additionalSectorScales.emplace_back(corr.first, sectorScale);
1139 addCorrections(mainCorrection, mainSectorScale, additionalSectorScales);
1144 const std::vector<std::pair<const o2::gpu::TPCFastSpaceChargeCorrection*, SectorScales>>& additionalCorrections)
1149 LOG(info) <<
"fast space charge correction helper: Add corrections";
1151 const auto& geo = mainCorrection.getGeometry();
1155 float secMainScale = mainScale[
sector];
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);
1162 float* splineParameters = mainCorrection.getCorrectionData(
sector,
row);
1163 float* splineParametersInvX = mainCorrection.getCorrectionDataInvX(
sector,
row);
1164 float* splineParametersInvYZ = mainCorrection.getCorrectionDataInvYZ(
sector,
row);
1166 constexpr int nKnotPar1d = 4;
1167 constexpr int nKnotPar2d = nKnotPar1d * 2;
1168 constexpr int nKnotPar3d = nKnotPar1d * 3;
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];
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];
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];
1198 const auto& gridU = spline.getGridX1();
1199 const auto& gridV = spline.getGridX2();
1201 for (
int icorr = 0; icorr < additionalCorrections.size(); ++icorr) {
1202 const auto& corr = *(additionalCorrections[icorr].first);
1203 double scale = additionalCorrections[icorr].second[
sector];
1208 auto& linfo = corr.getRowInfo(
row);
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();
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];
1225 mainCorrection.convGridToLocal(
sector,
row, u,
v,
y,
z);
1228 corr.convLocalToGrid(
sector,
row,
y,
z, lu, lv, ls);
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];
1242 mainCorrection.convGridToRealLocal(
sector,
row, u,
v,
y,
z);
1245 corr.convRealLocalToGrid(
sector,
row,
y,
z, lu, lv, ls);
1247 double parscale[4] = {ls, ls * scaleRealV, ls * scaleRealU, ls * scaleRealU * scaleRealV};
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];
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];
1274 std::vector<std::thread> threads(mNthreads);
1277 for (
int i = 0;
i < mNthreads;
i++) {
1278 threads[
i] = std::thread(myThread,
i);
1282 for (
auto& th : threads) {
1287 double duration = watch.RealTime();
1288 LOGP(info,
"Merge of corrections tooks: {}s", duration);
1293 const std::vector<int>&
sectors)
1297 LOG(info) <<
"fast space charge correction helper: Merge corrections";
1299 const auto& geo = destinationCorrection.getGeometry();
1302 if (sector < 0 || sector >= geo.getNumberOfSectors()) {
1303 LOGP(fatal,
"Invalid sector number {}. Valid range is [0, {})",
sector, geo.getNumberOfSectors());
1306 auto myThread = [&](
int iThread) {
1307 for (
int row = iThread;
row < geo.getNumberOfRows();
row += mNthreads) {
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);
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);
1322 memcpy(destSplineParameters, sourceSplineParameters, destSpline.getNumberOfParameters() *
sizeof(
float));
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);
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);
1336 memcpy(destSplineParameters, sourceSplineParameters, destSpline.getNumberOfParameters() *
sizeof(
float));
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);
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);
1350 memcpy(destSplineParameters, sourceSplineParameters, destSpline.getNumberOfParameters() *
sizeof(
float));
1354 auto& destSecRowInfo = destinationCorrection.getRowInfo(
row);
1355 const auto& sourceSecRowInfo = sourceCorrection.getRowInfo(
row);
1356 destSecRowInfo = sourceSecRowInfo;
1360 std::vector<std::thread> threads(mNthreads);
1363 for (
int i = 0;
i < mNthreads;
i++) {
1364 threads[
i] = std::thread(myThread,
i);
1368 for (
auto& th : threads) {
1373 double duration = watch.RealTime();
1374 LOGP(info,
"Merge of corrections tooks: {}s", duration);
1379 LOG(info) <<
"fast space charge correction helper: use voxel centers for correction";
1380 mDebugUseVoxelCenters =
true;
1385 LOG(info) <<
"fast space charge correction helper: mirror A data to C data";
1386 mDebugMirrorAdata2C =
true;
Definition of ChebyshevFit1D class.
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
char error[CURL_ERROR_SIZE]
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