Repository navigation
TPC SC: faster and more robust space-charge distortion calculation - #16070
Open
matthias-kleiner wants to merge 1 commit into
Open
matthias-kleiner wants to merge 1 commit into
matthias-kleiner wants to merge 1 commit into
Conversation
Major improvements: - Poisson solver: anisotropy aware multigrid with phi line relaxation. Fine phi grids converge now (1800 phi bins did not converge before), relative stopping criterion independent of units and grid size, parallelised - tricubic interpolation without Vc, the three components of the fields and of the distortions/corrections are interpolated together - fix of the global distortions (standard method) at the readout and several other bug fixes in SpaceCharge, no crash or endless loop for invalid interpolation positions - faster filling of the density from histograms and faster reading of dumped files (ZSTD) - SpaceCharge objects read from a file are directly usable (the grid is set up by a read rule, initAfterReadingFromFile() is not needed anymore) - the slow space-charge correction is removed from TPCFastTransform - closure test of the global distortions and corrections Time per TPC side for calculateDistortionsCorrections() (default: fast global distortions), realistic space-charge density, 32 threads (AMD EPYC 7702P): grid (z x r x phi) old new speed-up 129 x 129 x 180 4.4 s 0.49 s 9x 257 x 257 x 180 50 s 1.6 s 32x 129 x 129 x 1800 68 s (*) 4.7 s 15x (*) Poisson solver not converged after 200 V-cycles Poisson solver only old new 129 x 129 x 180 3.4 s 0.11 s 257 x 257 x 180 47 s 0.33 s 129 x 129 x 1800 62 s (*) 1.55 s other old new density from TH3 (3600x257x257 -> 129x129x180) 11.7 s 0.3 s read all dumped containers of one side (1800 phi) 2.7 s 1.0 s The global distortions and corrections agree with the old code to a few um rms (max. ~30 um for r < 248 cm). Larger differences are only at the outer radial edge (extrapolation) and from the fixes listed below. Poisson solver - phi is only coarsened if its coupling (h_rz / (r_min * h_phi))^2 is not weak compared to r/z (MGParameters::phiCoarseningThreshold, default 0.5). On levels where the phi coupling dominates, each phi ring is solved exactly (periodic tridiagonal system, Thomas algorithm + Sherman-Morrison) instead of the point-wise relaxation, which does not smooth the error there (MGParameters::phiLineRelaxThreshold, default 4, 0 disables). The levels are described by a list of MGLevel. All levels converge in 3-15 V-cycles. - relative stopping criterion (MGParameters::relativeConvergence, default true): relative change of the potential between two V-cycles < 1e-8 (1e-6 for float, which stays at ~1e-7 because of the rounding). The previous absolute criterion (sum of dV^2 < 1e-6) depended on the units of the potential and on the grid size. The error w.r.t. the converged solution is now ~3e-9 of max|V|. relativeConvergence = false with setConvergenceError(1e-6) reproduces the previous criterion. - parallelised: red-black relaxation over phi, line relaxation (factorisation and blocks of lines), restriction, interpolation, convergence error, allocation of the level buffers and the copies between the V-cycles - removed unused options (available in the git history): 2D solver, 3D solver with constant number of phi slices, W cycle (not functional), Jacobi relaxation, half weighting, the symmetry argument (diverged; also removed from SpaceCharge::poissonSolver() and calcEField()) and the MGParameters isFull3D, cycleType, gtType, relaxType, maxLoop, gamma with their enums - merged duplicated code (interpolate3D<Add>(), restrictBoundary2D()) - fixes: minimum radius of the grid instead of the hard coded IFC radius for the coefficients, Vector3D constructor (list initialisation gave a single element) and size() Tricubic interpolation - separable Catmull-Rom interpolation (z, then r, then phi) with the values taken directly from the grid. The cell (position, index, weights) is calculated once for the three components (new TriCubicInterpolator::interpolate(z, r, phi, other1, other2, ...), used by NumericalFields::evalFields() and DistCorrInterpolator::evalDistCorr()) and the three sums are done in one pass. The results change at the rounding level (< 2.3e-14 of max|V| for double). - the cells at the boundary use a generic extrapolation of the stencil points outside of the grid instead of 26 hand-written cases. This fixes the extrapolation at the (first z, last r) and (last z, first r) edges, which was done along the wrong diagonal. - no Vc: Vector.h is removed (std::array in RegularGrid3D), the public headers and the dictionary do not include Vc anymore and TPCSpaceCharge does not link Vc - fixes: without extrapolation, queries at or beyond the last vertex in r or z returned the value one grid spacing inwards; the move constructor did not take over the extrapolation setting - non-finite query positions return NaN instead of reading outside of the grid, and positions far outside of the grid in phi are wrapped with fmod instead of loops which did not terminate for huge values SpaceCharge - calcGlobalDistortions(): in the last step before the readout the local distortions were scaled to the remaining drift length, but the correction for the missing distance assumed a full z bin, and std::abs turned the correction for electrons which passed the readout into an addition. The z bias of the standard method w.r.t. a direct integration of the fields changes from -83 um to -12 um, the closure with the global corrections in z from 110 um to 2.7 um rms. Standard and fast method agree in z to ~2 um. - correctElectron(): the z coordinate of the corrected point was set to y + corrY - calcGlobalDistCorrIterativeLinearCartesian(): the scaled contribution was lost for corrections with a scaling object; float precision of the angles - calcGlobalDistCorrIterative*(): reference to a member of scSCale also for scSCale == nullptr (undefined behaviour) - calcGlobalCorrections() with type 1: dR was copied to dRPhi and dZ for the vertices out of the volume - scaleChargeDensityStack(): phi of the vertex taken from the radial index - fillChargeDensityFromHisto(file, ...): nullptr dereference if a histogram is missing - getStepWidth(): integer division - DataContainer3D: the element-wise operators did not check the grid of the other container (out-of-bounds read) - reading from a file: the grid properties are transient and were only set up for the default number of vertices. Without calling initAfterReadingFromFile() an object with a different grid gave wrong values or read outside of the data. A read rule in the LinkDef now calls it for every SpaceCharge object read from a file (calling it explicitly, e.g. in the Digitizer, is still possible). - warning if the global distortions/corrections contain non-finite values (e.g. for unphysically large fields) - the functions processing both sides used only two threads (nested OpenMP regions are not active); the sides are processed one after the other with all threads, and the number of threads is passed to the Poisson solver - the density is rebinned from the histograms of both sides directly, without a merged copy (1.9 GB for 3600x257x257 bins) - iterative Simpson integration: the fields at the start point are evaluated once per step instead of once per iteration - calculateDistortionsCorrections() and initAllBuffers() allocate the containers concurrently: the first access to the memory of a large container by the single thread zeroing it took ~1.2 s per side at 1800 phi - the dumped containers use ZSTD instead of ZLIB compression: same file size (the data are hardly compressible), ~2.7 times faster reading. Old files are read as before. GPU TPCFastTransform - TPCSlowSpaceChargeCorrection held a SpaceCharge<float> object with the global corrections (setSlowTPCSCCorrection()), which replaced the spline correction in TransformLocal() on the CPU. It is not used anymore (the reconstruction uses the POD correction object) and is removed together with setSlowTPCSCCorrection(), getCorrectionSlow() and the dependency of TPCFastTransformation on O2::TPCSpaceCharge (O2::DataFormatsTPC is linked directly instead). - the persistent member mCorrectionSlow is removed: ClassDefNV of TPCFastTransform is increased to 6 (schema evolution). An empty dictionary-only stub of TPCSlowSpaceChargeCorrection is kept, so that reading existing objects (ClassDef <= 5, e.g. the CCDB maps) does not print a missing-dictionary warning. It can be removed once old maps are no longer read. Tests - Poisson solver: phi coarsening, phi line relaxation (720 phi bins) - tricubic interpolation: interpolation at all vertices, combined vs. separate interpolation of three components, phi spacing of the query points - new SpaceCharge test: closure of the global distortions (standard and fast method) with the global corrections from analytical fields on both sides, large fields and queries at invalid positions, reading of a stored object with a non-default grid (float and double) - createResidualDistortionObject.C is tested compiled only (COMPILE_ONLY, as the other space-charge macros and as documented in the macro): loading it in the interpreter crashed while auto-loading the CommonUtils dictionary (DebugStreamer's map of unique_ptr<TreeStreamRedirector> with an incomplete type), independent of this change Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
matthias-kleiner
requested review from
davidrohr,
sgorbuno,
shahor02 and
wiechula
as code owners
November 6, 2026 22:00
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Major improvements:
Time per TPC side for calculateDistortionsCorrections() (default: fast global distortions), realistic space-charge density, 32 threads (AMD EPYC 7702P):
grid (z x r x phi) old new speed-up
129 x 129 x 180 4.4 s 0.49 s 9x
257 x 257 x 180 50 s 1.6 s 32x
129 x 129 x 1800 68 s (*) 4.7 s 15x
(*) Poisson solver not converged after 200 V-cycles
Poisson solver only old new
129 x 129 x 180 3.4 s 0.11 s
257 x 257 x 180 47 s 0.33 s
129 x 129 x 1800 62 s (*) 1.55 s
other old new
density from TH3 (3600x257x257 -> 129x129x180) 11.7 s 0.3 s
read all dumped containers of one side (1800 phi) 2.7 s 1.0 s
The global distortions and corrections agree with the old code to a few um rms (max. ~30 um for r < 248 cm). Larger differences are only at the outer radial edge (extrapolation) and from the fixes listed below.
Poisson solver
Tricubic interpolation
SpaceCharge
GPU TPCFastTransform
Tests