Skip to content

Commit 87c833c

Browse files
TPC: Digitizer recalculates the distortions with the derivative corrections without inverting the average map (new default)
Digitizer::recalculateDistortions() (distortion type 2) inverted the average distortions to corrections, added the scaled corrections of the derivative map and inverted the sum back to distortions. The first inversion is the slowest and the least precise step. SpaceCharge::scaleGlobalDistWithDerivativeCorr() applies the scaled derivative corrections directly to the average distortions. With T(x) = x + D(x) the new distortion at the vertex p is D_new(p) = T(x) - p, where x is the fixed point of x = p - scale * C_der(T(x)) (at most 20 iterations, stop at a change of the start point < 2 um). The distorted point y = T(x) then fulfils y + C(y) + scale * C_der(y) = p for the inverse C of D, i.e. the distortions are consistent with the scaled corrections at the distorted point as they are applied in the reconstruction (C_avg + s * C_der at the measured point). Only the derivative distortions are inverted. The r-phi distortion is calculated from the cross and dot product of the vertex and the distorted point: the difference of two angles wrapped with the single precision 2 pi gave an offset of 0.4 um for phi > pi. The function returns false if the global distortions of the object or the global corrections of the derivative map are not set (distortions unchanged); the Digitizer then stops with a fatal instead of continuing with the unscaled distortions. - Digitizer::setRecalcDistortionsMethod() and option --recalculate-distortions-method of the TPC digitizer: 0 = inversion of the scaled corrections (previous method), 1 = new method (default). Other values are a fatal error. The distortions of a digitisation with lumi scaling (distortion type 2) change. - ClassDefNV of the Digitizer 4 -> 5 (new member) Validation with run 559917 (distortion maps and correction splines of the same MC map production): closure with the correction splines of the reconstruction, |c + D(c) - q| for the measured point q and the corrected point c = q + C_avg(q) + s * C_der(q), 99% quantile in local X / Y / Z (5 < |z| < 245 cm, pad rows >= 10): scale s new method previous method no scaling -0.32 0.62/0.94/2.00 mm 1.02/1.39/2.73 mm 0.58/0.96/1.98 mm -2 0.83/1.01/2.17 mm 1.07/1.39/2.74 mm The new method closes as well as the unscaled average map with its own correction splines. The previous method has larger tails (99.9% in r-phi at s = -2: 4.4 mm vs 2.0 mm) and 10x more points with a closure > 1 cm at s = -2 (2396 vs 239). The remaining large residuals of the new method are at the innermost pad rows (IFC). Time of the recalculation (CCDB maps of run 559917, 65x257x720, both sides): 85.9 s -> 27.1 s with 1 thread, 22.9 s -> 7.3 s with 4 threads (default of n-threads-distortions). Tests: SpaceCharge_scaleWithDerivative_test (analytical fields, scale -3, -1 and 2: closure with the scaled corrections, agreement with the inversion of the scaled corrections, return value without distortions or derivative corrections), SpaceCharge_scaleWithDerivativeFloat_test (single precision: r-phi distortion of radial distortions and radial derivative corrections for phi <= pi and phi > pi). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 5848cf6 commit 87c833c

7 files changed

Lines changed: 316 additions & 7 deletions

File tree

‎Detectors/TPC/simulation/include/TPCSimulation/Digitizer.h‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,13 @@ class Digitizer
140140
void setMeanLumiDistortions(float meanLumi);
141141
void setMeanLumiDistortionsDerivative(float meanLumi);
142142

143+
/// method of recalculateDistortions()
144+
/// \param method 0: invert the average and the derivative distortions to corrections and invert the scaled corrections C_avg + s * C_der to distortions,
145+
/// 1: invert only the derivative distortions and apply the scaled derivative corrections directly to the average distortions (SpaceCharge::scaleGlobalDistWithDerivativeCorr).
146+
/// Other values are a fatal error
147+
void setRecalcDistortionsMethod(int method);
148+
int getRecalcDistortionsMethod() const { return mRecalcDistortionsMethod; }
149+
143150
/// in case of scaled distortions, the distortions can be recalculated to ensure consistent distortions and corrections
144151
void recalculateDistortions();
145152

@@ -192,7 +199,8 @@ class Digitizer
192199
int mDistortionScaleType = 0; ///< type=0: no scaling of distortions, type=1 distortions without any scaling, type=2 distortions scaling with lumi
193200
float mLumiScaleFactor = 0; ///< value used to scale the derivative map
194201
bool mUseScaledDistortions = false; ///< whether the distortions are already scaled
195-
ClassDefNV(Digitizer, 4);
202+
int mRecalcDistortionsMethod = 1; ///< method of recalculateDistortions(): 0 = inversion of the scaled corrections, 1 = derivative corrections applied to the distortions
203+
ClassDefNV(Digitizer, 5);
196204
};
197205
} // namespace tpc
198206
} // namespace o2

‎Detectors/TPC/simulation/src/Digitizer.cxx‎

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,14 @@ void Digitizer::setMeanLumiDistortionsDerivative(float meanLumi)
351351
mSpaceChargeDer->setMeanLumi(meanLumi);
352352
}
353353

354+
void Digitizer::setRecalcDistortionsMethod(int method)
355+
{
356+
if ((method != 0) && (method != 1)) {
357+
LOGP(fatal, "Unknown method {} for the recalculation of the distortions (0: inversion of the scaled corrections, 1: scaled derivative corrections applied to the distortions)", method);
358+
}
359+
mRecalcDistortionsMethod = method;
360+
}
361+
354362
void Digitizer::recalculateDistortions()
355363
{
356364
if (!mSpaceCharge || !mSpaceChargeDer) {
@@ -362,15 +370,24 @@ void Digitizer::recalculateDistortions()
362370
if (mSpaceCharge->getMeanLumi() != CorrMapParam::Instance().lumiInst) {
363371
for (int iside = 0; iside < 2; ++iside) {
364372
const o2::tpc::Side side = (iside == 0) ? Side::A : Side::C;
365-
// this needs to be done only once
366-
LOGP(info, "Calculating corrections for average distortions");
367-
mSpaceCharge->calcGlobalCorrWithGlobalDistIterative(side, nullptr, 0);
368-
369373
LOGP(info, "Calculating corrections for derivative distortions");
370374
mSpaceChargeDer->calcGlobalCorrWithGlobalDistIterative(side, nullptr, 0);
371375

372-
LOGP(info, "Calculating scaled distortions with scaling factor {}", mLumiScaleFactor);
373-
mSpaceCharge->calcGlobalDistWithGlobalCorrIterativeLinearCartesian(side, mSpaceChargeDer, mLumiScaleFactor);
376+
if (mRecalcDistortionsMethod == 0) {
377+
// invert the average distortions, then invert the scaled corrections C_avg + s * C_der
378+
LOGP(info, "Calculating corrections for average distortions");
379+
mSpaceCharge->calcGlobalCorrWithGlobalDistIterative(side, nullptr, 0);
380+
381+
LOGP(info, "Calculating scaled distortions with scaling factor {}", mLumiScaleFactor);
382+
mSpaceCharge->calcGlobalDistWithGlobalCorrIterativeLinearCartesian(side, mSpaceChargeDer, mLumiScaleFactor);
383+
} else {
384+
// apply the scaled derivative corrections directly to the average distortions (no inversion of the average distortions)
385+
LOGP(info, "Scaling distortions with the derivative corrections and scaling factor {}", mLumiScaleFactor);
386+
if (!mSpaceCharge->scaleGlobalDistWithDerivativeCorr(side, *mSpaceChargeDer, mLumiScaleFactor)) {
387+
// continuing would use the unscaled average distortions for the rest of the job
388+
LOGP(fatal, "Distortions of side {} could not be scaled with the derivative map", static_cast<int>(side));
389+
}
390+
}
374391
}
375392
// set new lumi of avg map
376393
mSpaceCharge->setMeanLumi(CorrMapParam::Instance().lumiInst);

‎Detectors/TPC/spacecharge/include/TPCSpaceCharge/SpaceCharge.h‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,24 @@ class SpaceCharge
409409
void calcGlobalDistWithGlobalCorrIterativeLinearCartesian(const Side side, const SpaceCharge<DataT>* scSCale = nullptr, float scale = 0, const int maxIter = 100, const DataT approachX = 1, const DataT approachY = 1, const DataT approachZ = 1, const DataT diffCorr = 50e-6);
410410
void calcGlobalDistWithGlobalCorrIterativeLinearCartesian(const SpaceCharge<DataT>* scSCale = nullptr, float scale = 0, const int maxIter = 100, const DataT approachX = 1, const DataT approachY = 1, const DataT approachZ = 1, const DataT diffCorr = 50e-6);
411411

412+
/// Scale the global distortions with the global corrections of a derivative map, consistent with the scaled corrections C(y) + scale * C_der(y) at the
413+
/// distorted point y (as done in the tracking), without inverting the global distortions of this object.
414+
/// With T(x) = x + D(x) (the global distortions of this object), the new distortion at the vertex p is D_new(p) = T(x) - p, where x is the fixed point of
415+
/// x = p - scale * C_der(T(x)) (iterated starting at x = p). Then y = T(x) fulfils y + C(y) = x for the inverse C of D, and y + C(y) + scale * C_der(y) = p.
416+
/// The iteration converges fast since C_der is small and smooth (each step reduces the error by about |scale| * |dC_der/dy|).
417+
/// \param side side of the TPC
418+
/// \param scDer derivative map: its global corrections must be set (e.g. with calcGlobalCorrWithGlobalDistIterative())
419+
/// \param scale scaling factor of the derivative map
420+
/// \param maxIter maximum number of fixed-point iterations per vertex
421+
/// \param diffCorr the iteration stops when the start point x changes by less than this value (cm) in x, y and z. The default (2 um) is above the
422+
/// rounding noise of the cartesian corrections in single precision (O(0.5 um) at the outer radius) times the scaling factor
423+
/// \return true if the distortions were scaled, false if the global distortions of this object or the global corrections of the derivative map are not set (the distortions are not changed)
424+
bool scaleGlobalDistWithDerivativeCorr(const Side side, const SpaceCharge<DataT>& scDer, const float scale, const int maxIter = 20, const DataT diffCorr = 2e-4);
425+
426+
/// Scale the global distortions of both sides with the global corrections of a derivative map (see the function for one side)
427+
/// \return true if the distortions of both sides were scaled, false if at least one side was not scaled
428+
bool scaleGlobalDistWithDerivativeCorr(const SpaceCharge<DataT>& scDer, const float scale, const int maxIter = 20, const DataT diffCorr = 2e-4);
429+
412430
/// Calculate global corrections using the global distortions by scaling with second distortion object, which will be consistent with scaled corrections in cartesian coordinates
413431
/// \param side Side of the TPC
414432
/// \param scSCale possible second sc object

‎Detectors/TPC/spacecharge/src/SpaceCharge.cxx‎

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -943,6 +943,105 @@ void SpaceCharge<DataT>::calcGlobalDistWithGlobalCorrIterativeLinearCartesian(co
943943
}
944944
}
945945

946+
template <typename DataT>
947+
bool SpaceCharge<DataT>::scaleGlobalDistWithDerivativeCorr(const Side side, const SpaceCharge<DataT>& scDer, const float scale, const int maxIter, const DataT diffCorr)
948+
{
949+
if (!mGlobalDistdR[side].getNDataPoints() || !scDer.mGlobalCorrdR[side].getNDataPoints()) {
950+
LOGP(error, "Global distortions or global corrections of the derivative map not set for side {}: distortions are not scaled", static_cast<int>(side));
951+
return false;
952+
}
953+
954+
// the new distortions are written to separate containers, since the current distortions are interpolated during the calculation
955+
DataContainer distdR(mParamGrid.NZVertices, mParamGrid.NRVertices, mParamGrid.NPhiVertices);
956+
DataContainer distdZ(mParamGrid.NZVertices, mParamGrid.NRVertices, mParamGrid.NPhiVertices);
957+
DataContainer distdRPhi(mParamGrid.NZVertices, mParamGrid.NRVertices, mParamGrid.NPhiVertices);
958+
unsigned long nNotConverged = 0;
959+
960+
#pragma omp parallel for num_threads(sNThreads) reduction(+ : nNotConverged)
961+
for (unsigned int iPhi = 0; iPhi < mParamGrid.NPhiVertices; ++iPhi) {
962+
const DataT phi = getPhiVertex(iPhi, side);
963+
for (unsigned int iR = 0; iR < mParamGrid.NRVertices; ++iR) {
964+
const DataT radius = getRVertex(iR, side);
965+
const DataT px = getXFromPolar(radius, phi);
966+
const DataT py = getYFromPolar(radius, phi);
967+
for (unsigned int iZ = 0; iZ < mParamGrid.NZVertices; ++iZ) {
968+
const DataT pz = getZVertex(iZ, side);
969+
970+
// distorted point y = T(p) of the vertex p, from the distortions at the vertex
971+
const DataT radiusDist = radius + mGlobalDistdR[side](iZ, iR, iPhi);
972+
const DataT phiDist = phi + mGlobalDistdRPhi[side](iZ, iR, iPhi) / radius;
973+
DataT yX = getXFromPolar(radiusDist, phiDist);
974+
DataT yY = getYFromPolar(radiusDist, phiDist);
975+
DataT yZ = pz + mGlobalDistdZ[side](iZ, iR, iPhi);
976+
977+
// fixed-point iteration for the start point x = p - scale * C_der(T(x)), starting at x = p
978+
DataT xX = px;
979+
DataT xY = py;
980+
DataT xZ = pz;
981+
bool converged = false;
982+
for (int iter = 0; iter < maxIter; ++iter) {
983+
DataT corrX = 0;
984+
DataT corrY = 0;
985+
DataT corrZ = 0;
986+
scDer.getCorrections(yX, yY, yZ, side, corrX, corrY, corrZ);
987+
const DataT xXNew = px - scale * corrX;
988+
const DataT xYNew = py - scale * corrY;
989+
const DataT xZNew = pz - scale * corrZ;
990+
const DataT diff = std::max(std::abs(xXNew - xX), std::max(std::abs(xYNew - xY), std::abs(xZNew - xZ)));
991+
xX = xXNew;
992+
xY = xYNew;
993+
xZ = xZNew;
994+
995+
// distorted point of the new start point
996+
DataT distX = 0;
997+
DataT distY = 0;
998+
DataT distZ = 0;
999+
getDistortions(xX, xY, xZ, side, distX, distY, distZ);
1000+
yX = xX + distX;
1001+
yY = xY + distY;
1002+
yZ = xZ + distZ;
1003+
if (diff < diffCorr) {
1004+
converged = true;
1005+
break;
1006+
}
1007+
}
1008+
nNotConverged += !converged;
1009+
1010+
// new distortion of the vertex: y - p in r, r phi (at the radius of the vertex) and z. The angle between p and y is calculated from the cross
1011+
// and the dot product: the difference of the two angles is close to -2 pi for phi > pi and has to be wrapped, which in single precision gives
1012+
// an offset (2 pi in single precision is larger than 2 pi by 1.7e-7) and a larger rounding error
1013+
const DataT deltaPhi = std::atan2(px * yY - py * yX, px * yX + py * yY);
1014+
distdR(iZ, iR, iPhi) = getRadiusFromCartesian(yX, yY) - radius;
1015+
distdRPhi(iZ, iR, iPhi) = deltaPhi * radius;
1016+
distdZ(iZ, iR, iPhi) = yZ - pz;
1017+
}
1018+
}
1019+
}
1020+
1021+
// swap the data: the interpolators of the global distortions keep the address of the containers
1022+
mGlobalDistdR[side].getData().swap(distdR.getData());
1023+
mGlobalDistdZ[side].getData().swap(distdZ.getData());
1024+
mGlobalDistdRPhi[side].getData().swap(distdRPhi.getData());
1025+
1026+
if (nNotConverged) {
1027+
LOGP(warning, "Scaling of the global distortions with the derivative map (scale {}): {} of {} vertices on side {} not converged after {} iterations", scale, nNotConverged, mGlobalDistdR[side].getNDataPoints(), static_cast<int>(side), maxIter);
1028+
}
1029+
warnNonFinite(mGlobalDistdR[side], mGlobalDistdRPhi[side], mGlobalDistdZ[side], "global distortions scaled with the derivative map", side);
1030+
return true;
1031+
}
1032+
1033+
template <typename DataT>
1034+
bool SpaceCharge<DataT>::scaleGlobalDistWithDerivativeCorr(const SpaceCharge<DataT>& scDer, const float scale, const int maxIter, const DataT diffCorr)
1035+
{
1036+
// the sides are processed one after the other: the calculation for each side is parallelised
1037+
bool scaled = true;
1038+
for (int iside = 0; iside < FNSIDES; ++iside) {
1039+
const o2::tpc::Side side = (iside == 0) ? Side::A : Side::C;
1040+
scaled &= scaleGlobalDistWithDerivativeCorr(side, scDer, scale, maxIter, diffCorr);
1041+
}
1042+
return scaled;
1043+
}
1044+
9461045
template <typename DataT>
9471046
void SpaceCharge<DataT>::calcGlobalCorrWithGlobalDistIterativeLinearCartesian(const Side side, const SpaceCharge<DataT>* scSCale, float scale, const int maxIter, const DataT approachX, const DataT approachY, const DataT approachZ, const DataT diffCorr)
9481047
{

0 commit comments

Comments
 (0)