From a2183b141f43723330bc1202b927b1fbd20f4dda Mon Sep 17 00:00:00 2001 From: Matthias Kleiner Date: Thu, 9 Jan 2025 15:27:48 +0100 Subject: [PATCH] TPC space-charge: Improve GEM frame charging-up distortions Adding: - downsampling of space-charge objects - simulation of n-sectors only - some helper functions - weighted filling of charging-up of GEM frames for smoother potential - set global distortions from function --- .../include/TPCSpaceCharge/DataContainer3D.h | 3 + .../TPCSpaceCharge/PoissonSolverHelpers.h | 3 +- .../include/TPCSpaceCharge/SpaceCharge.h | 58 +++-- .../TPC/spacecharge/src/DataContainer3D.cxx | 31 ++- .../TPC/spacecharge/src/PoissonSolver.cxx | 26 +- Detectors/TPC/spacecharge/src/SpaceCharge.cxx | 231 +++++++++++++----- 6 files changed, 259 insertions(+), 93 deletions(-) diff --git a/Detectors/TPC/spacecharge/include/TPCSpaceCharge/DataContainer3D.h b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/DataContainer3D.h index 79400c3d3d214..73638c5b2982f 100644 --- a/Detectors/TPC/spacecharge/include/TPCSpaceCharge/DataContainer3D.h +++ b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/DataContainer3D.h @@ -186,6 +186,9 @@ struct DataContainer3D { /// print the matrix void print() const; + /// convert a data container to a new datacontainer with different grid definition (e.g. different number of vertices) + DataContainer3D convert(const o2::tpc::RegularGrid3D& gridNew, const o2::tpc::RegularGrid3D& gridRef, const int threads = 1) const; + /// operator overload DataContainer3D& operator*=(const DataT value); DataContainer3D& operator+=(const DataContainer3D& other); diff --git a/Detectors/TPC/spacecharge/include/TPCSpaceCharge/PoissonSolverHelpers.h b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/PoissonSolverHelpers.h index 218bddcf49402..933273d0b5eb9 100644 --- a/Detectors/TPC/spacecharge/include/TPCSpaceCharge/PoissonSolverHelpers.h +++ b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/PoissonSolverHelpers.h @@ -19,6 +19,7 @@ #define ALICEO2_TPC_POISSONSOLVERHELPERS_H_ #include "CommonConstants/MathConstants.h" +#include "DataFormatsTPC/Defs.h" namespace o2 { @@ -55,7 +56,7 @@ struct MGParameters { ///< Parameter inline static int nMGCycle = 200; ///< number of multi grid cycle (V type) inline static int maxLoop = 7; ///< the number of tree-deep of multi grid inline static int gamma = 1; ///< number of iteration at coarsest level !TODO SET TO REASONABLE VALUE! - inline static bool normalizeGridToOneSector = false; ///< the grid in phi direction is squashed from 2 Pi to (2 Pi / SECTORSPERSIDE). This can used to get the potential for phi symmetric sc density or boundary potentials + inline static int normalizeGridToNSector = SECTORSPERSIDE; ///< the grid in phi direction is squashed from 2 Pi to (2 Pi / SECTORSPERSIDE). This can used to get the potential for phi symmetric sc density or boundary potentials }; template diff --git a/Detectors/TPC/spacecharge/include/TPCSpaceCharge/SpaceCharge.h b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/SpaceCharge.h index ad76ec2b6da5b..d0c66d0ff3df1 100644 --- a/Detectors/TPC/spacecharge/include/TPCSpaceCharge/SpaceCharge.h +++ b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/SpaceCharge.h @@ -204,10 +204,13 @@ class SpaceCharge /// simulate only one sector instead of 18 per side. This makes currently only sense for the static distortions (ToDo: simplify usage) /// phi max will be restricted to 2Pi/18 for this instance and for global instance of poisson solver - void setSimOneSector(); + void setSimOneSector() { setSimNSector(1); } + + /// simulate N sectors + void setSimNSector(const int nSectors); /// unsetting simulation of one sector - static void unsetSimOneSector(); + static void unsetSimNSector(); /// setting default potential (same potential for all GEM frames. The default value of 1000V are matched to distortions observed in laser data without X-Ray etc. /// \param side side of the TPC where the potential will be set @@ -308,10 +311,24 @@ class SpaceCharge /// scaling the space-charge density for given stack void scaleChargeDensityStack(const float scalingFactor, const Sector sector, const GEMstack stack); + /// scale the potential by a scaling factor + /// \param scalingFactor factor to scale the potential + /// \param side side for which the potential will be scaled + void scalePotential(const DataT scalingFactor, const Side side) { mPotential[side] *= scalingFactor; } + /// add space charge density from other object (this.mDensity = this.mDensity + other.mDensity) /// \param otherSC other space-charge object, which charge will be added to current object void addChargeDensity(const SpaceCharge& otherSC); + /// add global corrections from other space charge object + void addGlobalCorrections(const SpaceCharge& otherSC, const Side side); + + /// convert space-charge object to new definition of number of vertices + /// \param nZNew new number of vertices in z direction + /// \param nRNew new number of vertices in r direction + /// \param nPhiNew new number of vertices in phi direction + void downSampleObject(const int nZNew, const int nRNew, const int nPhiNew); + /// step 3: calculate the local distortions and corrections with an electric field /// \param type calculate local corrections or local distortions: type = o2::tpc::SpaceCharge<>::Type::Distortions or o2::tpc::SpaceCharge<>::Type::Corrections /// \param formulaStruct struct containing a method to evaluate the electric field Er, Ez, Ephi (analytical formula or by TriCubic interpolator) @@ -415,6 +432,9 @@ class SpaceCharge /// \param phi global phi coordinate DataT getDensityCyl(const DataT z, const DataT r, const DataT phi, const Side side) const; + /// get the potential for list of given coordinate + std::vector getDensityCyl(const std::vector& z, const std::vector& r, const std::vector& phi, const Side side) const; + /// get the potential for given coordinate /// \param z global z coordinate /// \param r global r coordinate @@ -1184,6 +1204,10 @@ class SpaceCharge /// \param gCorr function returning global corrections for given global coordinate void setGlobalCorrections(const std::function& gCorr, const Side side); + /// setting the global distortions directly from input function provided in global coordinates + /// \param gDist function returning global distortions for given global coordinate + void setGlobalDistortions(const std::function& gDist, const Side side); + /// set misalignment of ROC for shift in z /// \param sector sector for which the misalignment in z will be applied (if sector=-1 all sectors are shifted) /// \param type 0=IROC, 1=OROC, 2=IROC+OROC @@ -1229,7 +1253,16 @@ class SpaceCharge /// \param tgl tgl of the track /// \param nPoints number of points used to calculate the DCAr /// \param pcstream if provided debug output is being created - float getDCAr(float tgl, const int nPoints, const float phi, o2::utils::TreeStreamRedirector* pcstream = nullptr) const; + float getDCAr(float tgl, const int nPoints, const float phi, float rStart = -1, o2::utils::TreeStreamRedirector* pcstream = nullptr) const; + + /// \return returns nearest phi vertex for given phi position + size_t getNearestPhiVertex(const DataT phi, const Side side) const { return std::round(phi / getGridSpacingPhi(side)); } + + /// \return returns nearest r vertex for given radius position + size_t getNearestRVertex(const DataT r, const Side side) const { return std::round((r - getRMin(side)) / getGridSpacingR(side) + 0.5); } + + /// \return returns number of bins in phi direction for the gap between sectors and for the GEM frame + size_t getPhiBinsGapFrame(const Side side) const; private: ParamSpaceCharge mParamGrid{}; ///< parameters of the grid on which the calculations are performed @@ -1352,15 +1385,6 @@ class SpaceCharge /// dump the created electron tracks with calculateElectronDriftPath function to a tree void dumpElectronTracksToTree(const std::vector>, std::array>>& electronTracks, const int nSamplingPoints, const char* outFile) const; - /// \return returns nearest phi vertex for given phi position - size_t getNearestPhiVertex(const DataT phi, const Side side) const { return std::round(phi / getGridSpacingPhi(side)); } - - /// \return returns nearest r vertex for given radius position - size_t getNearestRVertex(const DataT r, const Side side) const { return std::round((r - getRMin(side)) / getGridSpacingR(side) + 0.5); } - - /// \return returns number of bins in phi direction for the gap between sectors and for the GEM frame - size_t getPhiBinsGapFrame(const Side side) const; - /// \return setting the boundary potential for given GEM stack void setPotentialBoundaryGEMFrameAlongPhi(const std::function& potentialFunc, const GEMstack stack, const bool bottom, const Side side, const bool outerFrame = false); @@ -1372,16 +1396,16 @@ class SpaceCharge void initAllBuffers(); - void setBoundaryFromIndices(const std::function& potentialFunc, const std::vector& indices, const Side side); + void setBoundaryFromIndices(const std::function& potentialFunc, const std::vector>& indices, const Side side); /// get indices of the GEM frame along r - std::vector getPotentialBoundaryGEMFrameAlongRIndices(const Side side) const; + std::vector> getPotentialBoundaryGEMFrameAlongRIndices(const Side side) const; /// get indices of the GEM frame along phi - std::vector getPotentialBoundaryGEMFrameAlongPhiIndices(const GEMstack stack, const bool bottom, const Side side, const bool outerFrame, const bool noGap = false) const; + std::vector> getPotentialBoundaryGEMFrameAlongPhiIndices(const GEMstack stack, const bool bottom, const Side side, const bool outerFrame, const bool noGap = false) const; void setROCMisalignment(int stackType, int misalignmentType, int sector, const float potMin, const float potMax); - void fillROCMisalignment(const std::vector& indicesTop, const std::vector& indicesBottom, int sector, int misalignmentType, const std::pair& deltaPotPar); + void fillROCMisalignment(const std::vector>& indicesTop, const std::vector>& indicesBottom, int sector, int misalignmentType, const std::pair& deltaPotPar); /// set potentialsdue to ROD misalignment void initRodAlignmentVoltages(const MisalignmentType misalignmentType, const FCType fcType, const int sector, const Side side, const float deltaPot); @@ -1389,6 +1413,8 @@ class SpaceCharge void calcGlobalDistCorrIterative(const DistCorrInterpolator& globCorr, const int maxIter, const DataT approachZ, const DataT approachR, const DataT approachPhi, const DataT diffCorr, const SpaceCharge* scSCale, float scale, const Type type); void calcGlobalDistCorrIterativeLinearCartesian(const DistCorrInterpolator& globCorr, const int maxIter, const DataT approachX, const DataT approachY, const DataT approachZ, const DataT diffCorr, const SpaceCharge* scSCale, float scale, const Type type); + void setGlobalDistCorr(const Type type, const std::function& gFunc, const Side side); + ClassDefNV(SpaceCharge, 6); }; diff --git a/Detectors/TPC/spacecharge/src/DataContainer3D.cxx b/Detectors/TPC/spacecharge/src/DataContainer3D.cxx index cd2802b975fd2..60d7c28b8c74e 100644 --- a/Detectors/TPC/spacecharge/src/DataContainer3D.cxx +++ b/Detectors/TPC/spacecharge/src/DataContainer3D.cxx @@ -331,7 +331,6 @@ void DataContainer3D::dumpSlice(std::string_view treename, std::string_vi ROOT::RDataFrame dFrame(treename, fileIn); auto df = dFrame.Define("slice", [rangeiZ, rangeiR, rangeiPhi](const std::pair>& values, unsigned short nz, unsigned short nr, unsigned short nphi) { - const bool simOneSectorOnly = MGParameters::normalizeGridToOneSector; std::vector ir; std::vector iphi; std::vector iz; @@ -370,12 +369,12 @@ void DataContainer3D::dumpSlice(std::string_view treename, std::string_vi const float rTmp = o2::tpc::GridProperties::getRMin() + o2::tpc::GridProperties::getGridSpacingR(nr) * iRTmp; const float zTmp = o2::tpc::GridProperties::getZMin() + o2::tpc::GridProperties::getGridSpacingZ(nz) * iZTmp; - const float phiTmp = o2::tpc::GridProperties::getPhiMin() + o2::tpc::GridProperties::getGridSpacingPhi(nphi) / (simOneSectorOnly ? SECTORSPERSIDE : 1) * iPhiTmp; + const float phiTmp = o2::tpc::GridProperties::getPhiMin() + o2::tpc::GridProperties::getGridSpacingPhi(nphi) * (MGParameters::normalizeGridToNSector / double(SECTORSPERSIDE)) * iPhiTmp; const float x = rTmp * std::cos(phiTmp); const float y = rTmp * std::sin(phiTmp); const LocalPosition3D pos(x, y, zTmp); - unsigned char secNum = simOneSectorOnly ? 0 : std::floor(phiTmp / SECPHIWIDTH); + unsigned char secNum = std::floor(phiTmp / SECPHIWIDTH); Sector sector(secNum + (pos.Z() < 0) * SECTORSPERSIDE); LocalPosition3D lPosTmp = Mapper::GlobalToLocal(pos, sector); @@ -428,10 +427,9 @@ void DataContainer3D::dumpInterpolation(std::string_view treename, std::s // define grid for interpolation using GridProp = GridProperties; - const RegularGrid3D mGrid3D(GridProp::ZMIN, GridProp::RMIN, GridProp::PHIMIN, GridProp::getGridSpacingZ(nz), GridProp::getGridSpacingR(nr), o2::tpc::GridProperties::getGridSpacingPhi(nphi) / (MGParameters::normalizeGridToOneSector ? SECTORSPERSIDE : 1), ParamSpaceCharge{nr, nz, nphi}); + const RegularGrid3D mGrid3D(GridProp::ZMIN, GridProp::RMIN, GridProp::PHIMIN, GridProp::getGridSpacingZ(nz), GridProp::getGridSpacingR(nr), o2::tpc::GridProperties::getGridSpacingPhi(nphi) * (MGParameters::normalizeGridToNSector / double(SECTORSPERSIDE)), ParamSpaceCharge{nr, nz, nphi}); auto interpolate = [&mGrid3D = std::as_const(mGrid3D), &data = std::as_const(data), rangeR, rangeZ, rangePhi, nR, nZ, nPhi](unsigned int, ULong64_t iPhi) { - const bool simOneSectorOnly = MGParameters::normalizeGridToOneSector; std::vector ir; std::vector iphi; std::vector iz; @@ -473,7 +471,7 @@ void DataContainer3D::dumpInterpolation(std::string_view treename, std::s const float x = rPos * std::cos(phiPos); const float y = rPos * std::sin(phiPos); const LocalPosition3D pos(x, y, zPos); - unsigned char secNum = simOneSectorOnly ? 0 : std::floor(phiPos / SECPHIWIDTH); + unsigned char secNum = std::floor(phiPos / SECPHIWIDTH); // TODO CHECK THIS Sector sector(secNum + (pos.Z() < 0) * SECTORSPERSIDE); LocalPosition3D lPosTmp = Mapper::GlobalToLocal(pos, sector); lPos.emplace_back(lPosTmp); @@ -512,6 +510,27 @@ bool DataContainer3D::getVertices(std::string_view treename, std::string_ return true; } +template +DataContainer3D DataContainer3D::convert(const o2::tpc::RegularGrid3D& gridNew, const o2::tpc::RegularGrid3D& gridRef, const int threads) const +{ + const int nZNew = gridNew.getNZ(); + const int nRNew = gridNew.getNR(); + const int nPhiNew = gridNew.getNPhi(); + DataContainer3D contCont(nZNew, nRNew, nPhiNew); +#pragma omp parallel for num_threads(threads) + for (size_t iPhi = 0; iPhi < nPhiNew; ++iPhi) { + const DataT phi = gridNew.getPhiVertex(iPhi); + for (size_t iR = 0; iR < nRNew; ++iR) { + const DataT radius = gridNew.getRVertex(iR); + for (size_t iZ = 0; iZ < nZNew; ++iZ) { + const DataT z = gridNew.getZVertex(iZ); + contCont(iZ, iR, iPhi) = interpolate(z, radius, phi, gridRef); + } + } + } + return contCont; +} + template class o2::tpc::DataContainer3D; template class o2::tpc::DataContainer3D; diff --git a/Detectors/TPC/spacecharge/src/PoissonSolver.cxx b/Detectors/TPC/spacecharge/src/PoissonSolver.cxx index 952f4b29111ce..d00e268f64125 100644 --- a/Detectors/TPC/spacecharge/src/PoissonSolver.cxx +++ b/Detectors/TPC/spacecharge/src/PoissonSolver.cxx @@ -940,7 +940,7 @@ void PoissonSolver::residue2D(Vector& residue, const Vector& matricesCurr for (int j = 1; j < tnZColumn - 1; ++j) { residue(i, j, iPhi) = ih2 * (coefficient1[i] * matricesCurrentV(i + 1, j, iPhi) + coefficient2[i] * matricesCurrentV(i - 1, j, iPhi) + tempRatio * (matricesCurrentV(i, j + 1, iPhi) + matricesCurrentV(i, j - 1, iPhi)) - inverseTempFourth * matricesCurrentV(i, j, iPhi)) + matricesCurrentCharge(i, j, iPhi); } // end cols - } // end nRRow + } // end nRRow // Boundary points. for (int i = 0; i < tnRRow; ++i) { @@ -997,7 +997,7 @@ void PoissonSolver::residue3D(Vector& residue, const Vector& matricesCurr coefficient3[i] * (signPlus * matricesCurrentV(i, j, mp1) + signMinus * matricesCurrentV(i, j, mm1)) - inverseCoefficient4[i] * matricesCurrentV(i, j, m)) + matricesCurrentCharge(i, j, m); } // end cols - } // end mParamGrid.NRVertices + } // end mParamGrid.NRVertices } } @@ -1263,9 +1263,9 @@ void PoissonSolver::relax3D(Vector& matricesCurrentV, const Vector& matri for (int i = isw; i < tnRRow - 1; i += 2) { (matricesCurrentV)(i, j, m) = (coefficient2[i] * (matricesCurrentV)(i - 1, j, m) + tempRatioZ * ((matricesCurrentV)(i, j - 1, m) + (matricesCurrentV)(i, j + 1, m)) + coefficient1[i] * (matricesCurrentV)(i + 1, j, m) + coefficient3[i] * (signPlus * (matricesCurrentV)(i, j, mp1) + signMinus * (matricesCurrentV)(i, j, mm1)) + (h2 * (matricesCurrentCharge)(i, j, m))) * coefficient4[i]; } // end cols - } // end mParamGrid.NRVertices - } // end phi - } // end sweep + } // end mParamGrid.NRVertices + } // end phi + } // end sweep } else if (MGParameters::relaxType == RelaxType::Jacobi) { // for each slice for (int m = 0; m < iPhi; ++m) { @@ -1306,8 +1306,8 @@ void PoissonSolver::relax3D(Vector& matricesCurrentV, const Vector& matri for (int i = 1; i < tnRRow - 1; ++i) { (matricesCurrentV)(i, j, m) = (coefficient2[i] * (matricesCurrentV)(i - 1, j, m) + tempRatioZ * ((matricesCurrentV)(i, j - 1, m) + (matricesCurrentV)(i, j + 1, m)) + coefficient1[i] * (matricesCurrentV)(i + 1, j, m) + coefficient3[i] * (signPlus * (matricesCurrentV)(i, j, mp1) + signMinus * (matricesCurrentV)(i, j, mm1)) + (h2 * (matricesCurrentCharge)(i, j, m))) * coefficient4[i]; } // end cols - } // end mParamGrid.NRVertices - } // end phi + } // end mParamGrid.NRVertices + } // end phi } else { // Case weighted Jacobi // TODO @@ -1329,15 +1329,15 @@ void PoissonSolver::relax2D(Vector& matricesCurrentV, const Vector& matri matricesCurrentV(i, j, iPhi) = tempFourth * (coefficient1[i] * matricesCurrentV(i + 1, j, iPhi) + coefficient2[i] * matricesCurrentV(i - 1, j, iPhi) + tempRatio * (matricesCurrentV(i, j + 1, iPhi) + matricesCurrentV(i, j - 1, iPhi)) + (h2 * matricesCurrentCharge(i, j, iPhi))); } // end cols - } // end mParamGrid.NRVertices - } // end pass red-black + } // end mParamGrid.NRVertices + } // end pass red-black } else if (MGParameters::relaxType == RelaxType::Jacobi) { for (int j = 1; j < tnZColumn - 1; ++j) { for (int i = 1; i < tnRRow - 1; ++i) { matricesCurrentV(i, j, iPhi) = tempFourth * (coefficient1[i] * matricesCurrentV(i + 1, j, iPhi) + coefficient2[i] * matricesCurrentV(i - 1, j, iPhi) + tempRatio * (matricesCurrentV(i, j + 1, iPhi) + matricesCurrentV(i, j - 1, iPhi)) + (h2 * matricesCurrentCharge(i, j, iPhi))); } // end cols - } // end mParamGrid.NRVertices + } // end mParamGrid.NRVertices } else if (MGParameters::relaxType == RelaxType::WeightedJacobi) { // Weighted Jacobi // TODO @@ -1421,7 +1421,7 @@ void PoissonSolver::restrict3D(Vector& matricesCurrentCharge, const Vecto matricesCurrentCharge(i, j, m) = residue(ii, jj, mm) / 8 + s1 / 16 + s2 / 32 + s3 / 64; } // end cols - } // end mParamGrid.NRVertices + } // end mParamGrid.NRVertices // for boundary for (int j = 0, jj = 0; j < tnZColumn; ++j, jj += 2) { @@ -1460,7 +1460,7 @@ void PoissonSolver::restrict2D(Vector& matricesCurrentCharge, const Vecto (residue(iip1, jjp1, iphi) + residue(iim1, jjp1, iphi) + residue(iip1, jjm1, iphi) + residue(iim1, jjm1, iphi)) / 16; } } // end cols - } // end mParamGrid.NRVertices + } // end mParamGrid.NRVertices // boundary // for boundary for (int j = 0, jj = 0; j < tnZColumn; ++j, jj += 2) { @@ -1520,7 +1520,7 @@ void PoissonSolver::calcCoefficients2D(unsigned int from, unsigned int to template DataT PoissonSolver::getGridSizePhiInv() { - return MGParameters::normalizeGridToOneSector ? (INVTWOPI * SECTORSPERSIDE) : INVTWOPI; + return INVTWOPI * SECTORSPERSIDE / MGParameters::normalizeGridToNSector; } template class o2::tpc::PoissonSolver; diff --git a/Detectors/TPC/spacecharge/src/SpaceCharge.cxx b/Detectors/TPC/spacecharge/src/SpaceCharge.cxx index 07101bac15c23..44200bad29bad 100644 --- a/Detectors/TPC/spacecharge/src/SpaceCharge.cxx +++ b/Detectors/TPC/spacecharge/src/SpaceCharge.cxx @@ -43,6 +43,7 @@ #include "TStopwatch.h" #include "ROOT/RDataFrame.hxx" #include "THnSparse.h" +#include "TRandom.h" #include @@ -246,6 +247,10 @@ void SpaceCharge::setDefaultStaticDistortionsGEMFrameChargeUp(const Side template size_t SpaceCharge::getPhiBinsGapFrame(const Side side) const { + if (MGParameters::normalizeGridToNSector == 1) { + return 0; + } + const auto& regInf = Mapper::instance().getPadRegionInfo(0); const float localYEdgeIROC = regInf.getPadsInRowRegion(0) / 2 * regInf.getPadWidth(); const auto globalPosGap = Mapper::LocalToGlobal(LocalPosition2D(regInf.getRadiusFirstRow(), -(localYEdgeIROC + GEMFrameParameters::WIDTHFRAME)), Sector(0)); @@ -268,16 +273,15 @@ void SpaceCharge::setPotentialBoundaryGEMFrameAlongR(const std::function< } template -std::vector SpaceCharge::getPotentialBoundaryGEMFrameAlongRIndices(const Side side) const +std::vector> SpaceCharge::getPotentialBoundaryGEMFrameAlongRIndices(const Side side) const { - const bool simOneSectorOnly = MGParameters::normalizeGridToOneSector; const auto radiusStart = std::sqrt(std::pow(GEMFrameParameters::LENGTHFRAMEIROCBOTTOM / 2, 2) + std::pow(GEMFrameParameters::POSBOTTOM[0], 2)); const auto rStart = getNearestRVertex(radiusStart, side); const auto radiusEnd = std::sqrt(std::pow(GEMFrameParameters::LENGTHFRAMEOROC3TOP / 2, 2) + std::pow(GEMFrameParameters::POSTOP[3], 2)); const auto rEnd = getNearestRVertex(radiusEnd, side); // mParamGrid.NRVertices - 1 - const int verticesPerSector = simOneSectorOnly ? mParamGrid.NPhiVertices : mParamGrid.NPhiVertices / SECTORSPERSIDE; + const int verticesPerSector = mParamGrid.NPhiVertices / MGParameters::normalizeGridToNSector; const auto& regInf = Mapper::instance().getPadRegionInfo(0); const float localYEdgeIROC = regInf.getPadsInRowRegion(0) / 2 * regInf.getPadWidth(); @@ -300,7 +304,8 @@ std::vector SpaceCharge::getPotentialBoundaryGEMFrameAlongRIndice radii.emplace_back(std::sqrt(std::pow(GEMFrameParameters::POSTOP[stack], 2) + std::pow(localYEdge, 2))); } - std::vector potentialInd; + std::vector> potentialInd; + const float weight = 1; for (size_t iR = rStart; iR < rEnd; ++iR) { const DataT radius = getRVertex(iR, side); auto const it = std::lower_bound(radii.begin(), radii.end(), radius); @@ -315,13 +320,13 @@ std::vector SpaceCharge::getPotentialBoundaryGEMFrameAlongRIndice break; } - for (int sector = 0; sector < (simOneSectorOnly ? 1 : SECTORSPERSIDE); ++sector) { + for (int sector = 0; sector < MGParameters::normalizeGridToNSector; ++sector) { const size_t iPhiLeft = sector * verticesPerSector + iPhiTmp; const size_t iZ = mParamGrid.NZVertices - 1; - potentialInd.emplace_back(mPotential[side].getDataIndex(iZ, iR, iPhiLeft)); + potentialInd.emplace_back(mPotential[side].getDataIndex(iZ, iR, iPhiLeft), weight); if (iPhiTmp > 0) { const size_t iPhiRight = (sector + 1) * verticesPerSector - iPhiTmp; - potentialInd.emplace_back(mPotential[side].getDataIndex(iZ, iR, iPhiRight)); + potentialInd.emplace_back(mPotential[side].getDataIndex(iZ, iR, iPhiRight), weight); } } } @@ -339,37 +344,35 @@ void SpaceCharge::setPotentialBoundaryGEMFrameAlongPhi(const std::functio } template -void SpaceCharge::setBoundaryFromIndices(const std::function& potentialFunc, const std::vector& indices, const Side side) +void SpaceCharge::setBoundaryFromIndices(const std::function& potentialFunc, const std::vector>& indices, const Side side) { - for (const auto& index : indices) { + /* + make check for the weights + Loop over bins in the radial direction + Check for duplicates and use the one with larger weight + */ + + for (const auto& indexw : indices) { + const int index = indexw.first; const int iZ = mPotential[side].getIndexZ(index); const int iR = mPotential[side].getIndexR(index); const int iPhi = mPotential[side].getIndexPhi(index); const DataT radius = getRVertex(iR, side); - mPotential[side](iZ, iR, iPhi) = potentialFunc(radius); + const float weight = indexw.second; + const float pot = mPotential[side](iZ, iR, iPhi); + const float potNew = weight * potentialFunc(radius); + if (std::abs(potNew) > std::abs(pot)) { + mPotential[side](iZ, iR, iPhi) = potNew; + } } } template -std::vector SpaceCharge::getPotentialBoundaryGEMFrameAlongPhiIndices(const GEMstack stack, const bool bottom, const Side side, const bool outerFrame, const bool noGap) const +std::vector> SpaceCharge::getPotentialBoundaryGEMFrameAlongPhiIndices(const GEMstack stack, const bool bottom, const Side side, const bool outerFrame, const bool noGap) const { - const bool simOneSectorOnly = MGParameters::normalizeGridToOneSector; - // to avoid double counting auto indices = getPotentialBoundaryGEMFrameAlongRIndices(side); - if (!bottom && outerFrame) { - // if OROC3 to OFC check outer GEM frame from OROC3! - const auto indicesOROC3 = getPotentialBoundaryGEMFrameAlongPhiIndices(GEMstack::OROC3gem, false, side, false); - indices.insert(indices.end(), indicesOROC3.begin(), indicesOROC3.end()); - std::sort(indices.begin(), indices.end()); - } else if (bottom && outerFrame) { - // if IROC to IFC check inner GEM frame from IROC - const auto indicesIROC = getPotentialBoundaryGEMFrameAlongPhiIndices(GEMstack::IROCgem, true, side, false); - indices.insert(indices.end(), indicesIROC.begin(), indicesIROC.end()); - std::sort(indices.begin(), indices.end()); - } - int region = 0; float offsStart = 0; float offsEnd = 0; @@ -415,10 +418,10 @@ std::vector SpaceCharge::getPotentialBoundaryGEMFrameAlongPhiIndi nVerticesR = 1; } - std::vector potentialInd; - const int verticesPerSector = simOneSectorOnly ? mParamGrid.NPhiVertices : mParamGrid.NPhiVertices / SECTORSPERSIDE; - const auto nBinsPhi = (outerFrame || noGap) ? 0 : (simOneSectorOnly ? 0 : getPhiBinsGapFrame(side)); - for (int sector = 0; sector < (simOneSectorOnly ? 1 : SECTORSPERSIDE); ++sector) { + std::vector> potentialInd; // index, weight + const int verticesPerSector = mParamGrid.NPhiVertices / MGParameters::normalizeGridToNSector; + const auto nBinsPhi = (outerFrame || noGap) ? 0 : getPhiBinsGapFrame(side); + for (int sector = 0; sector < MGParameters::normalizeGridToNSector; ++sector) { const auto offsetPhi = sector * verticesPerSector + verticesPerSector / 2; for (size_t iPhiLocal = 0; iPhiLocal <= (verticesPerSector / 2 - nBinsPhi); ++iPhiLocal) { const auto iPhiLeft = offsetPhi + iPhiLocal; @@ -432,31 +435,62 @@ std::vector SpaceCharge::getPotentialBoundaryGEMFrameAlongPhiIndi // end at gem frame if ((outerFrame && (stack == GEMstack::IROCgem))) { - nREnd = (radiusBottom - getRVertex(1, side)) / getGridSpacingR(side) + 2; // 2 safety margin + // TODO: remove this? + const float marginCM = 0; // 0.4; + const int nMargingBins = marginCM / getGridSpacingR(side) + 0.5; + nREnd = (radiusBottom - getRVertex(1, side) + 0.5) / getGridSpacingR(side) + nMargingBins; // 2 safety margin + radiusMax = 3 + getRVertex(getNearestRVertex(radiusBottom + getGridSpacingR(side) * nMargingBins, side), side); } - if (rStart == 0) { + rStart -= 1; + nREnd += 1; + if (rStart <= 0) { rStart = 1; } + if (nREnd >= mParamGrid.NRVertices) { + nREnd = mParamGrid.NRVertices - 1; + } + + float lxMin = radiusStart; + if ((outerFrame && (stack == GEMstack::IROCgem))) { + lxMin = 0; + } + const float lxMax = (nREnd == mParamGrid.NRVertices - 1) ? 9999 : radiusMax; for (size_t iR = rStart; iR < nREnd; ++iR) { const size_t iZ = mParamGrid.NZVertices - 1; + float weight = 1; if (iPhiLeft < getNPhiVertices()) { - if (noGap || !std::binary_search(indices.begin(), indices.end(), mPotential[side].getDataIndex(iZ, iR, iPhiLeft))) { - potentialInd.emplace_back(mPotential[side].getDataIndex(iZ, iR, iPhiLeft)); + if (noGap || !std::binary_search(indices.begin(), indices.end(), std::make_pair(mPotential[side].getDataIndex(iZ, iR, iPhiLeft), 0.0f), [](const auto& a, const auto& b) { return (a.first < b.first); })) { + + // check how much of the bin is in the lx range and assign weigth + const int nIterPoints = 1000; + int nPointsGood = 0; + for (int i = 0; i < nIterPoints; ++i) { + const float radius = getRVertex(iR, side) + getGridSpacingR(side) * gRandom->Uniform(-0.5, 0.5); + const float phi = getGridSpacingPhi(side) * gRandom->Uniform(-0.5, 0.5); + const DataT lx = radius * std::cos(phi + localphi); + if ((lx >= lxMin) && (lx <= lxMax)) { + ++nPointsGood; + } + } + weight = nPointsGood / double(nIterPoints); + potentialInd.emplace_back(mPotential[side].getDataIndex(iZ, iR, iPhiLeft), weight); } } - if (iPhiLocal && (noGap || !std::binary_search(indices.begin(), indices.end(), mPotential[side].getDataIndex(iZ, iR, iPhiRight)))) { - potentialInd.emplace_back(mPotential[side].getDataIndex(iZ, iR, iPhiRight)); + if (iPhiLocal && (noGap || !std::binary_search(indices.begin(), indices.end(), std::make_pair(mPotential[side].getDataIndex(iZ, iR, iPhiRight), 0.0f), [](const auto& a, const auto& b) { return (a.first < b.first); }))) { + potentialInd.emplace_back(mPotential[side].getDataIndex(iZ, iR, iPhiRight), weight); } } } } // remove duplicate entries - std::unordered_set set(potentialInd.begin(), potentialInd.end()); - potentialInd.assign(set.begin(), set.end()); std::sort(potentialInd.begin(), potentialInd.end()); + + // Remove duplicates + potentialInd.erase(std::unique(potentialInd.begin(), potentialInd.end()), potentialInd.end()); + return potentialInd; } @@ -1809,6 +1843,18 @@ DataT SpaceCharge::getDensityCyl(const DataT z, const DataT r, const Data return mInterpolatorDensity[side](z, r, phi); } +template +std::vector SpaceCharge::getDensityCyl(const std::vector& z, const std::vector& r, const std::vector& phi, const Side side) const +{ + const auto nPoints = z.size(); + std::vector density(nPoints); +#pragma omp parallel for num_threads(sNThreads) + for (size_t i = 0; i < nPoints; ++i) { + density[i] = getDensityCyl(z[i], r[i], phi[i], side); + } + return density; +} + template DataT SpaceCharge::getPotentialCyl(const DataT z, const DataT r, const DataT phi, const Side side) const { @@ -1885,7 +1931,8 @@ void SpaceCharge::getCorrections(const DataT x, const DataT y, const Data } else { // convert cartesian to polar const DataT radius = getRadiusFromCartesian(x, y); - const DataT phi = getPhiFromCartesian(x, y); + DataT phi = getPhiFromCartesian(x, y); + o2::math_utils::detail::bringTo02PiGen(phi); DataT corrR{}; DataT corrRPhi{}; @@ -2421,7 +2468,7 @@ void SpaceCharge::makeElectronDriftPathGif(const char* inpFile, TH2F& hDu template void SpaceCharge::dumpToTree(const char* outFileName, const Side side, const int nZPoints, const int nRPoints, const int nPhiPoints, const bool randomize) const { - const DataT phiSpacing = GridProp::getGridSpacingPhi(nPhiPoints) / (MGParameters::normalizeGridToOneSector ? SECTORSPERSIDE : 1); + const DataT phiSpacing = GridProp::getGridSpacingPhi(nPhiPoints) * (MGParameters::normalizeGridToNSector / double(SECTORSPERSIDE)); const DataT rSpacing = GridProp::getGridSpacingR(nRPoints); const DataT zSpacing = side == Side::A ? GridProp::getGridSpacingZ(nZPoints) : -GridProp::getGridSpacingZ(nZPoints); @@ -2459,6 +2506,7 @@ void SpaceCharge::dumpToTree(const char* outFileName, const Side side, co std::vector> lPosOut(nPhiPoints); std::vector> sectorOut(nPhiPoints); std::vector> globalIdxOut(nPhiPoints); + std::vector> isOnPadPlane(nPhiPoints); #pragma omp parallel for num_threads(sNThreads) for (int iPhi = 0; iPhi < nPhiPoints; ++iPhi) { @@ -2494,6 +2542,7 @@ void SpaceCharge::dumpToTree(const char* outFileName, const Side side, co lPosOut[iPhi].reserve(nPoints); sectorOut[iPhi].reserve(nPoints); globalIdxOut[iPhi].reserve(nPoints); + isOnPadPlane[iPhi].reserve(nPoints); std::mt19937 rng(std::random_device{}()); DataT phiPos = iPhi * phiSpacing; @@ -2603,6 +2652,12 @@ void SpaceCharge::dumpToTree(const char* outFileName, const Side side, co sectorOut[iPhi].emplace_back(sector); const size_t idx = (iZ + nZPoints * (iR + iPhi * nRPoints)); globalIdxOut[iPhi].emplace_back(idx); + + const float xDist = getXFromPolar(radiusDistorted, phiDistorted); + const float yDist = getYFromPolar(radiusDistorted, phiDistorted); + GlobalPosition3D posTmp(xDist, yDist, zPos); + const DigitPos digiPadPos = o2::tpc::Mapper::instance().findDigitPosFromGlobalPosition(posTmp); + isOnPadPlane[iPhi].emplace_back(digiPadPos.isValid()); } } } @@ -2645,6 +2700,7 @@ void SpaceCharge::dumpToTree(const char* outFileName, const Side side, co dfStore = dfStore.DefineSlotEntry("bZ", [&bZOut = bZOut](unsigned int, ULong64_t entry) { return bZOut[entry]; }); dfStore = dfStore.DefineSlotEntry("bPhi", [&bPhiOut = bPhiOut](unsigned int, ULong64_t entry) { return bPhiOut[entry]; }); dfStore = dfStore.DefineSlotEntry("globalIndex", [&globalIdxOut = globalIdxOut](unsigned int, ULong64_t entry) { return globalIdxOut[entry]; }); + dfStore = dfStore.DefineSlotEntry("isOnPadPlane", [&isOnPadPlane = isOnPadPlane](unsigned int, ULong64_t entry) { return isOnPadPlane[entry]; }); dfStore.Snapshot("tree", outFileName); timer.Print("u"); } @@ -3356,20 +3412,20 @@ void SpaceCharge::readMetaData(std::string_view file) } template -void SpaceCharge::setSimOneSector() +void SpaceCharge::setSimNSector(const int nSectors) { LOGP(warning, "Use this feature only if you know what you are doing!"); - o2::tpc::MGParameters::normalizeGridToOneSector = true; - RegularGrid gridTmp[FNSIDES]{{GridProp::ZMIN, GridProp::RMIN, GridProp::PHIMIN, getSign(Side::A) * GridProp::getGridSpacingZ(mParamGrid.NZVertices), GridProp::getGridSpacingR(mParamGrid.NRVertices), GridProp::getGridSpacingPhi(mParamGrid.NPhiVertices) / SECTORSPERSIDE, mParamGrid}, - {GridProp::ZMIN, GridProp::RMIN, GridProp::PHIMIN, getSign(Side::C) * GridProp::getGridSpacingZ(mParamGrid.NZVertices), GridProp::getGridSpacingR(mParamGrid.NRVertices), GridProp::getGridSpacingPhi(mParamGrid.NPhiVertices) / SECTORSPERSIDE, mParamGrid}}; + o2::tpc::MGParameters::normalizeGridToNSector = nSectors; + RegularGrid gridTmp[FNSIDES]{{GridProp::ZMIN, GridProp::RMIN, GridProp::PHIMIN, getSign(Side::A) * GridProp::getGridSpacingZ(mParamGrid.NZVertices), GridProp::getGridSpacingR(mParamGrid.NRVertices), GridProp::getGridSpacingPhi(mParamGrid.NPhiVertices) / SECTORSPERSIDE * nSectors, mParamGrid}, + {GridProp::ZMIN, GridProp::RMIN, GridProp::PHIMIN, getSign(Side::C) * GridProp::getGridSpacingZ(mParamGrid.NZVertices), GridProp::getGridSpacingR(mParamGrid.NRVertices), GridProp::getGridSpacingPhi(mParamGrid.NPhiVertices) / SECTORSPERSIDE * nSectors, mParamGrid}}; mGrid3D[0] = gridTmp[0]; mGrid3D[1] = gridTmp[1]; } template -void SpaceCharge::unsetSimOneSector() +void SpaceCharge::unsetSimNSector() { - o2::tpc::MGParameters::normalizeGridToOneSector = false; + o2::tpc::MGParameters::normalizeGridToNSector = SECTORSPERSIDE; } template @@ -3424,6 +3480,20 @@ void SpaceCharge::addChargeDensity(const SpaceCharge& otherSC) mDensity[Side::C] += otherSC.mDensity[Side::C]; } +template +void SpaceCharge::addGlobalCorrections(const SpaceCharge& otherSC, const Side side) +{ + const bool sameGrid = (getNPhiVertices() == otherSC.getNPhiVertices()) && (getNRVertices() == otherSC.getNRVertices()) && (getNZVertices() == otherSC.getNZVertices()); + if (!sameGrid) { + LOGP(warning, "Space charge objects have different grid definition"); + return; + } + + mGlobalCorrdR[side] += otherSC.mGlobalCorrdR[side]; + mGlobalCorrdZ[side] += otherSC.mGlobalCorrdZ[side]; + mGlobalCorrdRPhi[side] += otherSC.mGlobalCorrdRPhi[side]; +} + template void SpaceCharge::fillChargeDensityFromHisto(const char* file, const char* nameA, const char* nameC) { @@ -3739,9 +3809,27 @@ void SpaceCharge::setIFCChargeUpFallingPot(const float deltaPot, const fl template void SpaceCharge::setGlobalCorrections(const std::function& gCorr, const Side side) { - initContainer(mGlobalCorrdR[side], true); - initContainer(mGlobalCorrdZ[side], true); - initContainer(mGlobalCorrdRPhi[side], true); + setGlobalDistCorr(Type::Corrections, gCorr, side); +} + +template +void SpaceCharge::setGlobalDistortions(const std::function& gDist, const Side side) +{ + setGlobalDistCorr(Type::Distortions, gDist, side); +} + +template +void SpaceCharge::setGlobalDistCorr(const Type type, const std::function& gFunc, const Side side) +{ + if (type == Type::Distortions) { + initContainer(mGlobalDistdR[side], true); + initContainer(mGlobalDistdZ[side], true); + initContainer(mGlobalDistdRPhi[side], true); + } else { + initContainer(mGlobalCorrdR[side], true); + initContainer(mGlobalCorrdZ[side], true); + initContainer(mGlobalCorrdRPhi[side], true); + } #pragma omp parallel for num_threads(sNThreads) for (unsigned int iPhi = 0; iPhi < mParamGrid.NPhiVertices; ++iPhi) { @@ -3761,7 +3849,7 @@ void SpaceCharge::setGlobalCorrections(const std::function::setGlobalCorrections(const std::function::setROCMisalignment(int stackType, int misalignmentType, } template -void SpaceCharge::fillROCMisalignment(const std::vector& indicesTop, const std::vector& indicesBottom, int sector, int misalignmentType, const std::pair& deltaPotPar) +void SpaceCharge::fillROCMisalignment(const std::vector>& indicesTop, const std::vector>& indicesBottom, int sector, int misalignmentType, const std::pair& deltaPotPar) { - for (const auto& index : indicesTop) { + for (const auto& indexw : indicesTop) { + const int index = indexw.first; const int iZ = DataContainer3D::getIndexZ(index, getNZVertices(), getNRVertices(), getNPhiVertices()); const int iRStart = DataContainer3D::getIndexR(index, getNZVertices(), getNRVertices(), getNPhiVertices()); const int iPhi = DataContainer3D::getIndexPhi(index, getNZVertices(), getNRVertices(), getNPhiVertices()); @@ -3853,7 +3948,8 @@ void SpaceCharge::fillROCMisalignment(const std::vector& indicesT for (size_t iR = iRStart; iR > 0; --iR) { const size_t currInd = (iZ + getNZVertices() * (iR + iPhi * getNRVertices())); - const bool foundVertexBottom = std::binary_search(indicesBottom.begin(), indicesBottom.end(), currInd); + const bool foundVertexBottom = std::binary_search(indicesBottom.begin(), indicesBottom.end(), std::make_pair(currInd, 0.0f), [](const auto& a, const auto& b) { return (a.first < b.first); }); + if (foundVertexBottom) { break; } @@ -3982,7 +4078,7 @@ void SpaceCharge::initAfterReadingFromFile() } template -float SpaceCharge::getDCAr(float tgl, const int nPoints, const float phi, o2::utils::TreeStreamRedirector* pcstream) const +float SpaceCharge::getDCAr(float tgl, const int nPoints, const float phi, float rStart, o2::utils::TreeStreamRedirector* pcstream) const { const float rmin = getRMin(o2::tpc::Side::A); std::vector dRphi; @@ -3990,7 +4086,7 @@ float SpaceCharge::getDCAr(float tgl, const int nPoints, const float phi, dRphi.reserve(nPoints); r.reserve(nPoints); for (int i = 0; i < nPoints; ++i) { - float radius = rmin + i; + float radius = (rStart > 0) ? (rStart + i) : (rmin + i); float z = tgl * radius; DataT distZ = 0; DataT distR = 0; @@ -4030,6 +4126,7 @@ float SpaceCharge::getDCAr(float tgl, const int nPoints, const float phi, << "r=" << r << "dRphi=" << dRphi << "tgl=" << tgl + << "phi=" << phi << "dca=" << dca << "rInterpol=" << rInterpol << "dRPhiInterpol=" << dRPhiInterpol @@ -4047,6 +4144,26 @@ void SpaceCharge::setPotential(int iz, int ir, int iphi, Side side, float mPotential[side](iz, ir, iphi) = val; } +template +void SpaceCharge::downSampleObject(const int nZNew, const int nRNew, const int nPhiNew) +{ + o2::tpc::SpaceCharge scNew(getBField(), nZNew, nRNew, nPhiNew); + for (int iside = 0; iside < 2; ++iside) { + const o2::tpc::Side side = (iside == 0) ? o2::tpc::Side::A : o2::tpc::Side::C; + const std::vector> dataRef{mLocalDistdR[iside], mLocalDistdZ[iside], mLocalDistdRPhi[iside], mLocalVecDistdR[iside], mLocalVecDistdZ[iside], mLocalVecDistdRPhi[iside], mLocalCorrdR[iside], mLocalCorrdZ[iside], mLocalCorrdRPhi[iside], mGlobalDistdR[iside], mGlobalDistdZ[iside], mGlobalDistdRPhi[iside], mGlobalCorrdR[iside], mGlobalCorrdZ[iside], mGlobalCorrdRPhi[iside], mDensity[iside], mPotential[iside], mElectricFieldEr[iside], mElectricFieldEz[iside], mElectricFieldEphi[iside]}; + const std::vector> dataNew{scNew.mLocalDistdR[iside], scNew.mLocalDistdZ[iside], scNew.mLocalDistdRPhi[iside], scNew.mLocalVecDistdR[iside], scNew.mLocalVecDistdZ[iside], scNew.mLocalVecDistdRPhi[iside], scNew.mLocalCorrdR[iside], scNew.mLocalCorrdZ[iside], scNew.mLocalCorrdRPhi[iside], scNew.mGlobalDistdR[iside], scNew.mGlobalDistdZ[iside], scNew.mGlobalDistdRPhi[iside], scNew.mGlobalCorrdR[iside], scNew.mGlobalCorrdZ[iside], scNew.mGlobalCorrdRPhi[iside], scNew.mDensity[iside], scNew.mPotential[iside], scNew.mElectricFieldEr[iside], scNew.mElectricFieldEz[iside], scNew.mElectricFieldEphi[iside]}; + for (int i = 0; i < dataRef.size(); ++i) { + const auto& objRef = dataRef[i].get(); + if (objRef.getNDataPoints()) { + auto& objNew = dataNew[i].get(); + scNew.initContainer(objNew, true); + objNew = objRef.convert(scNew.mGrid3D[iside], mGrid3D[iside], sNThreads); + } + } + } + *this = std::move(scNew); +} + using DataTD = double; template class o2::tpc::SpaceCharge;