Skip to content

Commit 5848cf6

Browse files
TPC SC: review fixes and further fixes of the space-charge calculation
Fixes from the review and of issues found afterwards. The distortions and corrections of the default calculation are unchanged (bit-identical), apart from the sub-um fix of the cartesian getCorrections() (see Other). File IO - no std::string_view::data() for functions expecting a C string: file names, tree names and options are copied to a std::string before they are passed to TFile, TString, gSystem and the column lists of RDataFrame (writeToFile, initFromFile, dumpSlice, dumpInterpolation, dumpToFile, dumpMetaData, readMetaData, DataContainer3D::getVertices, setAnalyticalCorrectionsDistortionsFromFile, calculateElectronDriftPath). A file name given as a view into a longer string created a second file with the longer name in writeToFile() with option UPDATE. - the tree names of the set*FromFile() functions are std::string: they were string_views to the temporary returned by fmt::format(), used after the end of its lifetime in checkGridFromFile() and initFromFile(). Poisson solver - poissonSolver(side) and poissonSolver() use the stopping criterion set for the PoissonSolver (setConvergenceError()) instead of overwriting it with the default; the overloads with a stopping criterion set it as before. Default unchanged: 1e-8 for double, 1e-6 for float. - the factorisation of the periodic tridiagonal systems of the line relaxation in phi (Thomas algorithm + Sherman-Morrison) is calculated once per multi grid level (factorisePhiLines(), PhiLineFactorisation) instead of for each sweep; the coefficients of a level are set in setLevelCoefficients(). Same potential (bit-identical). Poisson solver per side (realistic density, 32 threads): 129x129x1800 1.74 s -> 1.57 s, 257x257x180 and 129x129x180 unchanged. - the parameter of relaxLinePhi() is not shadowed by a local variable. Density from histograms - the fill buffer of the histograms is flushed before the bin array is read: a histogram with entries still in the buffer gave a zero density. - fillChargeDensityFromHisto(hA, hC): a different number of bins of the A- and C-side histograms is an error (the C-side histogram was read outside of its bin array); the density is not filled and the function returns false, also if a histogram is not found in the file. Different ranges of the C-side histogram and a z axis of the A-side histogram not starting at 0 give a warning, the density is unchanged. - one accessor type for the bin content (bin array of TH3F/TH3D or GetBinContent() for other classes): 2 instead of 12 instantiations of the rebinning loop per DataT, same density and time. Tricubic interpolation - the combined interpolation of three containers (interpolate(z, r, phi, other1, other2, ...)) is only used for the same data layout, grid and extrapolation settings, otherwise the three values are interpolated separately: a container with the same number of points but a different shape was read outside of its data at the boundary of the grid. The check is inline (the combined interpolation is ~1% slower than without the check). Same distortions and corrections. Other - cartesian getCorrections(): the phi in [0, 2 pi) is only used for the look up, the corrected position is calculated with the phi of the point. bringTo02PiGen() adds the single precision 2 pi (larger than 2 pi by 1.7e-7), which rotated the corrected position of points with y < 0 by 1.7e-7 rad (up to 0.45 um at the outer radius), also for SpaceCharge<double>. Tests: stopping criterion of poissonSolver(), density with a pending fill buffer, different binning and ranges of the A- and C-side histograms, return value of fillChargeDensityFromHisto(), combined = separate tricubic interpolation for different extrapolation settings, grids and shapes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 71b757c commit 5848cf6

10 files changed

Lines changed: 541 additions & 123 deletions

File tree

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

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -115,15 +115,34 @@ class PoissonSolver
115115
bool lineRelaxPhi{}; ///< use line relaxation in phi (phi strongly coupled, see MGParameters::phiLineRelaxThreshold) instead of point relaxation
116116
};
117117

118-
/// relaxation of the given level: line relaxation in phi (MGLevel::lineRelaxPhi) or point relaxation (relax3D())
119-
void relaxLevel(const MGLevel& level, Vector& matricesCurrentV, const Vector& matricesCurrentCharge, const DataT h2, const DataT tempRatioZ,
118+
/// factorisation of the periodic tridiagonal systems of the line relaxation in phi of one level (Thomas algorithm and Sherman-Morrison correction).
119+
/// The matrix depends only on the radial index i (and on the level): storage of the vectors with n = nphi entries per i: index = i + nr * m
120+
struct PhiLineFactorisation {
121+
std::vector<DataT> cPrime{}; ///< modified super diagonal of the Thomas algorithm
122+
std::vector<DataT> invDen{}; ///< inverse of the modified diagonal of the Thomas algorithm
123+
std::vector<DataT> zSM{}; ///< solution of T z = u for the Sherman-Morrison correction
124+
std::vector<DataT> factDenInv{}; ///< 1 / (1 + z_0 + b/a * z_{n-1}) for each i
125+
std::vector<DataT> bOverA{}; ///< b / a for each i
126+
std::vector<DataT> bCoeff{}; ///< b (coupling in phi) for each i
127+
};
128+
129+
/// set the coefficients of the relaxation for given level
130+
/// \return returns h^2 with h the grid spacing in r of the level
131+
DataT setLevelCoefficients(const MGLevel& level, const DataT ratioZ, std::vector<DataT>& coefficient1, std::vector<DataT>& coefficient2, std::vector<DataT>& coefficient3, std::vector<DataT>& coefficient4) const;
132+
133+
/// \return returns the factorisation of the periodic tridiagonal systems of the line relaxation in phi for given level and coefficients of the level
134+
PhiLineFactorisation factorisePhiLines(const MGLevel& level, const std::vector<DataT>& coefficient3, const std::vector<DataT>& coefficient4) const;
135+
136+
/// relaxation of the given level: line relaxation in phi (MGLevel::lineRelaxPhi) with the factorisation phiLineFact of the level or point relaxation (relax3D())
137+
void relaxLevel(const MGLevel& level, const PhiLineFactorisation& phiLineFact, Vector& matricesCurrentV, const Vector& matricesCurrentCharge, const DataT h2, const DataT tempRatioZ,
120138
const std::vector<DataT>& coefficient1, const std::vector<DataT>& coefficient2, const std::vector<DataT>& coefficient3, const std::vector<DataT>& coefficient4) const;
121139

122140
/// red-black Gauss-Seidel line relaxation in phi direction: the potential of all phi slices on one line (r, z) is solved exactly (periodic tridiagonal system)
123141
/// for given potential on the neighbouring lines. The lines with (ir + iz) even and odd are relaxed one after the other.
124142
/// Robust if the phi direction is strongly coupled compared to r and z (fine grid in phi), where the point relaxation smooths the error badly.
143+
/// \param phiLineFact factorisation of the periodic tridiagonal systems of the level (see factorisePhiLines())
125144
void relaxLinePhi(Vector& matricesCurrentV, const Vector& matricesCurrentCharge, const int tnRRow, const int tnZColumn, const int iPhi, const DataT h2, const DataT tempRatioZ,
126-
const std::vector<DataT>& coefficient1, const std::vector<DataT>& coefficient2, const std::vector<DataT>& coefficient3, const std::vector<DataT>& coefficient4) const;
145+
const std::vector<DataT>& coefficient1, const std::vector<DataT>& coefficient2, const PhiLineFactorisation& phiLineFact) const;
127146

128147
/// \returns the grids of the multi grid levels. From one level to the next the r and z direction are coarsened and the phi direction is coarsened (if the number of slices is even)
129148
/// only if it is not weakly coupled compared to r and z (see MGParameters::phiCoarseningThreshold). Line relaxation in phi is used on the levels where phi is strongly coupled (see MGParameters::phiLineRelaxThreshold)
@@ -273,6 +292,7 @@ class PoissonSolver
273292
/// \param nPost number of smoothing after coarsening
274293
/// \param ratioZ ratio between square of grid r and grid z
275294
/// \param levels grids of the multi grid levels
295+
/// \param phiLineFact factorisation of the line relaxation in phi of the levels (empty for the levels without line relaxation)
276296
/// \param tvArrayV vector of V potential in different grids
277297
/// \param tvCharge vector of charge distribution in different grids
278298
/// \param tvResidue vector of residue calculation in different grids
@@ -281,9 +301,9 @@ class PoissonSolver
281301
/// \param coefficient3 coefficient for relaxation (ratio r/z)
282302
/// \param coefficient4 coefficient for relaxation (ratio for grid_r)
283303
/// \param inverseCoefficient4 coefficient for relaxation (inverse coefficient4)
284-
void vCycle3D(const int gridFrom, const int gridTo, const int nPre, const int nPost, const DataT ratioZ, const std::vector<MGLevel>& levels, std::vector<Vector>& tvArrayV, std::vector<Vector>& tvCharge,
285-
std::vector<Vector>& tvResidue, std::vector<DataT>& coefficient1, std::vector<DataT>& coefficient2, std::vector<DataT>& coefficient3,
286-
std::vector<DataT>& coefficient4, std::vector<DataT>& inverseCoefficient4) const;
304+
void vCycle3D(const int gridFrom, const int gridTo, const int nPre, const int nPost, const DataT ratioZ, const std::vector<MGLevel>& levels, const std::vector<PhiLineFactorisation>& phiLineFact,
305+
std::vector<Vector>& tvArrayV, std::vector<Vector>& tvCharge, std::vector<Vector>& tvResidue, std::vector<DataT>& coefficient1, std::vector<DataT>& coefficient2,
306+
std::vector<DataT>& coefficient3, std::vector<DataT>& coefficient4, std::vector<DataT>& inverseCoefficient4) const;
287307

288308
/// Residue3D
289309
///

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

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -136,16 +136,21 @@ class SpaceCharge
136136
/// \param hisSCDensity3D histogram for the space charge density
137137
void fillChargeDensityFromHisto(const TH3& hisSCDensity3D);
138138

139-
/// step 0: set the space-charge density from two TH3 histograms containing the space-charge density for A and C side seperately
139+
/// step 0: set the space-charge density from two TH3 histograms containing the space-charge density for A and C side seperately.
140+
/// Both histograms need the same number of bins (phi, r, z), the C-side histogram is expected in phi and r with the same ranges as the A-side histogram
141+
/// and in z with the mirrored range (e.g. A side: z in [0, zMax], C side: z in [-zMax, 0]). The z axis of the A-side histogram is expected to start at 0.
142+
/// Different ranges give a warning: the bins are used with the expected ranges
140143
/// \param hisSCDensity3D_A histogram for the space charge density for A-side
141144
/// \param hisSCDensity3D_C histogram for the space charge density for C-side
142-
void fillChargeDensityFromHisto(const TH3& hisSCDensity3D_A, const TH3& hisSCDensity3D_C);
145+
/// \return true if the density was filled, false if the number of bins of the histograms differs (the density is not changed)
146+
bool fillChargeDensityFromHisto(const TH3& hisSCDensity3D_A, const TH3& hisSCDensity3D_C);
143147

144148
/// step 0: set the space-charge density from two TH3 histograms containing the space charge density for A and C side separately which are stored in a ROOT file
145149
/// \param file path to root file containing the space-charge density
146150
/// \param nameA name of the space-charge density histogram for the A-side
147151
/// \param nameC name of the space-charge density histogram for the C-side
148-
void fillChargeDensityFromHisto(const char* file, const char* nameA, const char* nameC);
152+
/// \return true if the density was filled, false if a histogram was not found or the number of bins of the histograms differs (the density is not changed)
153+
bool fillChargeDensityFromHisto(const char* file, const char* nameA, const char* nameC);
149154

150155
/// step 0: set the space charge density from std::vector<CalDet> containing the space charge density. Each entry in the object corresponds to one z slice
151156
/// \param calSCDensity3D pad-by-pad CalDet for the space charge density
@@ -282,14 +287,25 @@ class SpaceCharge
282287
/// \param Side of the TPC
283288
void setPotentialBoundaryOuterRadius(const std::function<DataT(DataT)>& potentialFunc, const Side side);
284289

285-
/// step 1: use the O2TPCPoissonSolver class to numerically calculate the potential with set space charge density and boundary conditions from potential
290+
/// step 1: use the O2TPCPoissonSolver class to numerically calculate the potential with set space charge density and boundary conditions from potential.
291+
/// The stopping criterion of the poisson solver is the one set with PoissonSolver<DataT>::setConvergenceError() (default: PoissonSolver<DataT>::getDefaultConvergenceError())
286292
/// \param side side of the TPC
287-
/// \param stoppingConvergence stopping criterion used in the poisson solver (relative change of the potential between two multi grid V cycles, see MGParameters::relativeConvergence; default 1e-8 for double, 1e-6 for float)
288-
void poissonSolver(const Side side, const DataT stoppingConvergence = std::is_same_v<DataT, float> ? DataT(1e-6) : DataT(1e-8));
293+
void poissonSolver(const Side side);
289294

290-
/// step 1: use the O2TPCPoissonSolver class to numerically calculate the potential with set space charge density and boundary conditions from potential for A and C side in parallel
291-
/// \param stoppingConvergence stopping criterion used in the poisson solver (relative change of the potential between two multi grid V cycles, see MGParameters::relativeConvergence; default 1e-8 for double, 1e-6 for float)
292-
void poissonSolver(const DataT stoppingConvergence = std::is_same_v<DataT, float> ? DataT(1e-6) : DataT(1e-8));
295+
/// step 1: use the O2TPCPoissonSolver class to numerically calculate the potential with set space charge density and boundary conditions from potential
296+
/// \param side side of the TPC
297+
/// \param stoppingConvergence stopping criterion used in the poisson solver (relative change of the potential between two multi grid V cycles, see MGParameters::relativeConvergence).
298+
/// It is set with PoissonSolver<DataT>::setConvergenceError() and is also used by later calls
299+
void poissonSolver(const Side side, const DataT stoppingConvergence);
300+
301+
/// step 1: use the O2TPCPoissonSolver class to numerically calculate the potential with set space charge density and boundary conditions from potential for the A and C side (one after the other).
302+
/// The stopping criterion of the poisson solver is the one set with PoissonSolver<DataT>::setConvergenceError() (default: PoissonSolver<DataT>::getDefaultConvergenceError())
303+
void poissonSolver();
304+
305+
/// step 1: use the O2TPCPoissonSolver class to numerically calculate the potential with set space charge density and boundary conditions from potential for the A and C side (one after the other)
306+
/// \param stoppingConvergence stopping criterion used in the poisson solver (relative change of the potential between two multi grid V cycles, see MGParameters::relativeConvergence).
307+
/// It is set with PoissonSolver<DataT>::setConvergenceError() and is also used by later calls
308+
void poissonSolver(const DataT stoppingConvergence);
293309

294310
/// step 2: calculate numerically the electric field from the potential
295311
/// \param side side of the TPC

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ class TriCubicInterpolator
8383

8484
/// interpolate the data of this and of two other interpolators at the same coordinate (e.g. the three components of a vector field).
8585
/// The position in the grid and the weights of the interpolation are calculated only once, which is faster than three separate interpolations.
86-
/// The other interpolators have to use the same grid. The extrapolation settings of this interpolator are used for all three.
86+
/// If the data layout, the grid or the extrapolation settings of the other interpolators are different, the three values are interpolated separately.
8787
/// \param z z coordinate
8888
/// \param r r coordinate
8989
/// \param phi phi coordinate
@@ -117,6 +117,9 @@ class TriCubicInterpolator
117117
ExtrapolationType mExtrapolationType = ExtrapolationType::Parabola; ///< sets which type of extrapolation for missing points at boundary is used
118118
bool mExtraPolateValues = true; ///< extrapolating values outside of the grid or restricting query to inside of the grid
119119

120+
/// \return returns whether the other interpolator (with a different grid object) has the same data layout and grid (number of vertices, minimum and spacing)
121+
bool isSameGrid(const TriCubicInterpolator<DataT>& other) const;
122+
120123
/// \return returns the cell and the weights of the interpolation for given coordinate
121124
TriCubicCell<DataT> getCell(const DataT z, const DataT r, const DataT phi) const;
122125

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -252,8 +252,8 @@ DataT DataContainer3D<DataT>::interpolate(const DataT z, const DataT r, const Da
252252
template <typename DataT>
253253
bool DataContainer3D<DataT>::getVertices(std::string_view treename, std::string_view fileIn, unsigned short& nR, unsigned short& nZ, unsigned short& nPhi)
254254
{
255-
TFile fTmp(fileIn.data(), "READ");
256-
TTree* tree = (TTree*)fTmp.Get(treename.data());
255+
TFile fTmp(std::string(fileIn).c_str(), "READ");
256+
TTree* tree = (TTree*)fTmp.Get(std::string(treename).c_str());
257257
if (!tree) {
258258
LOGP(warning, "Tree {} not found in input file {}", treename, fileIn);
259259
return false;

0 commit comments

Comments
 (0)