diff --git a/Detectors/TPC/CMakeLists.txt b/Detectors/TPC/CMakeLists.txt index ccc16941d64b3..a07460ecae1c0 100644 --- a/Detectors/TPC/CMakeLists.txt +++ b/Detectors/TPC/CMakeLists.txt @@ -15,3 +15,4 @@ add_subdirectory(simulation) add_subdirectory(monitor) add_subdirectory(workflow) add_subdirectory(qc) +add_subdirectory(spacecharge) diff --git a/Detectors/TPC/reconstruction/CMakeLists.txt b/Detectors/TPC/reconstruction/CMakeLists.txt index e7456f146032a..c97696459ce53 100644 --- a/Detectors/TPC/reconstruction/CMakeLists.txt +++ b/Detectors/TPC/reconstruction/CMakeLists.txt @@ -123,7 +123,7 @@ o2_add_test_root_macro(macro/createTPCSpaceChargeCorrection.C PUBLIC_LINK_LIBRARIES O2::TPCReconstruction O2::CommonConstants O2::CommonUtils - O2::TPCSpaceChargeBase + O2::TPCSpaceCharge LABELS tpc) o2_add_test_root_macro(macro/findKrBoxCluster.C diff --git a/Detectors/TPC/reconstruction/macro/createTPCSpaceChargeCorrection.C b/Detectors/TPC/reconstruction/macro/createTPCSpaceChargeCorrection.C index 7c0c46d1e2a65..afd75f772ab10 100644 --- a/Detectors/TPC/reconstruction/macro/createTPCSpaceChargeCorrection.C +++ b/Detectors/TPC/reconstruction/macro/createTPCSpaceChargeCorrection.C @@ -18,7 +18,7 @@ /eos/user/e/ehellbar/SpaceCharge/data/RUN3/InputSCDensityHistograms Run macro: - root -l -b -q $O2_SRC/Detectors/TPC/reconstruction/macro/createTPCSpaceChargeCorrection.C+\(180,65,65,\"InputSCDensityHistograms_8000events.root\",\"inputSCDensity3D_8000_0\",\"tpctransformSCcorrection.root\"\) + root -l -b -q $O2_SRC/Detectors/TPC/reconstruction/macro/createTPCSpaceChargeCorrection.C+\(\"InputSCDensityHistograms_8000events.root\",\"inputSCDensity3D_8000_0\",\"tpctransformSCcorrection.root\"\) Test macro compilation: .L $O2_SRC/Detectors/TPC/reconstruction/macro/createTPCSpaceChargeCorrection.C+ @@ -31,9 +31,8 @@ #include "TFile.h" #include "TH3.h" #include "TLatex.h" -#include "TMatrixD.h" -#include "AliTPCSpaceCharge3DCalc.h" +#include "TPCSpaceCharge/SpaceCharge.h" #include "CommonConstants/MathConstants.h" #include "CommonUtils/TreeStreamRedirector.h" @@ -43,39 +42,36 @@ using namespace o2; using namespace tpc; using namespace gpu; -std::unique_ptr spaceCharge = nullptr; +/// \param nPhi number of phi bins of original correction lookup table +/// \param nR number of r bins of original correction lookup table, has to be 2^n + 1 +/// \param nZ number of z bins of original correction lookup table, has to be 2^n + 1 +const int nPhi = 180; +const int nR = 129; +const int nZ = 129; +using SC = o2::tpc::SpaceCharge; +std::unique_ptr spaceCharge; void getSpaceChargeCorrection(const int roc, const double XYZ[3], double dXdYdZ[3]); -void initSpaceCharge(const int nPhi, const int nR, const int nZ, const int interpolationOrder, - const char* histoFileName, const char* histoName); +void initSpaceCharge(const char* histoFileName, const char* histoName); void DumpFlatObjectToFile(const TPCFastTransform* obj, const char* file); std::unique_ptr ReadFlatObjectFromFile(const char* file); -void debugInterpolation(utils::TreeStreamRedirector& pcstream, - const o2::gpu::TPCFastTransformGeo& geo, - TPCFastTransform* fastTransform); -void debugGridpoints(utils::TreeStreamRedirector& pcstream, - const o2::gpu::TPCFastTransformGeo& geo, - TPCFastTransform* fastTransform); +void debugInterpolation(utils::TreeStreamRedirector& pcstream, const o2::gpu::TPCFastTransformGeo& geo, TPCFastTransform* fastTransform); +void debugGridpoints(utils::TreeStreamRedirector& pcstream, const o2::gpu::TPCFastTransformGeo& geo, TPCFastTransform* fastTransform); -/// Creates TPCFastTransform object for TPC space-charge correction, stores it in a file and provides a deub tree if requested -/// \param nPhi number of phi bins of original correction lookup table -/// \param nR number of r bins of original correction lookup table, has to be 2^n + 1 -/// \param nZ number of z bins of original correction lookup table, has to be 2^n + 1 +/// Creates TPCFastTransform object for TPC space-charge correction, stores it in a file and provides a debug tree if requested /// \param histoFileName path and name to the root file containing input space-charge density histograms /// \param histoName name of the input space-charge density histogram /// \param outputFileName name of the output file to store the TPCFastTransform object in /// \param debug create debug tree comparing original corrections and spline interpolations from TPCFastTransform (1 = on the spline interpolation grid, 2 = on the original lookup table grid) void createTPCSpaceChargeCorrection( - const int nPhi = 180, const int nR = 65, const int nZ = 65, const char* histoFileName = "InputSCDensityHistograms_10000events.root", const char* histoName = "inputSCDensity3D_10000_avg", const char* outputFileName = "tpctransform.root", const int debug = 0) { - const int interpolationOrder = 2.; - initSpaceCharge(nPhi, nR, nZ, interpolationOrder, histoFileName, histoName); + initSpaceCharge(histoFileName, histoName); TPCFastTransformHelperO2::instance()->setSpaceChargeCorrection(getSpaceChargeCorrection); std::unique_ptr fastTransform(TPCFastTransformHelperO2::instance()->create(0)); @@ -84,7 +80,7 @@ void createTPCSpaceChargeCorrection( if (debug > 0) { const o2::gpu::TPCFastTransformGeo& geo = fastTransform->getGeometry(); - utils::TreeStreamRedirector pcstream(TString::Format("fastTransformUnitTest_debug%d_gridsize%d-%d-%d_order%d.root", debug, nPhi, nR, nZ, interpolationOrder).Data(), "recreate"); + utils::TreeStreamRedirector pcstream(TString::Format("fastTransformUnitTest_debug%d_gridsize%d-%d-%d.root", debug, nPhi, nR, nZ).Data(), "recreate"); switch (debug) { case 1: debugInterpolation(pcstream, geo, fastTransform.get()); @@ -100,54 +96,35 @@ void createTPCSpaceChargeCorrection( } /// Initialize calculation of original correction lookup tables -/// \param nPhi number of phi bins of original correction lookup table -/// \param nR number of r bins of original correction lookup table, has to be 2^n + 1 -/// \param nZ number of z bins of original correction lookup table, has to be 2^n + 1 -/// \param interpolationOrder interpolation method to use for original correction lookup tables (1 = linear interpolation, 2 = polynomial interpolation of 2nd order, >2 = cubic spline interpolation of higher orders) /// \param histoFileName path and name to the root file containing input space-charge density histograms /// \param histoName name of the input space-charge density histogram -void initSpaceCharge(const int nPhi, const int nR, const int nZ, const int interpolationOrder, - const char* histoFileName, const char* histoName) +void initSpaceCharge(const char* histoFileName, const char* histoName) { // get histogram with space-charge density std::unique_ptr histoFile = std::unique_ptr(TFile::Open(histoFileName)); std::unique_ptr scHisto = std::unique_ptr((TH3*)histoFile->Get(histoName)); // initialize space-charge object - spaceCharge = std::make_unique(nR, nZ, nPhi, interpolationOrder, 3, 0); + spaceCharge = std::make_unique(); // input charge - std::unique_ptr[]> mMatrixChargeA = std::make_unique[]>(nPhi); - std::unique_ptr[]> mMatrixChargeC = std::make_unique[]>(nPhi); - for (int iphi = 0; iphi < nPhi; ++iphi) { - mMatrixChargeA[iphi] = std::make_unique(nR, nZ); - mMatrixChargeC[iphi] = std::make_unique(nR, nZ); - } - spaceCharge->GetChargeDensity((TMatrixD**)mMatrixChargeA.get(), (TMatrixD**)mMatrixChargeC.get(), (TH3*)scHisto.get(), nR, nZ, nPhi); - spaceCharge->SetInputSpaceChargeA((TMatrixD**)mMatrixChargeA.get()); - spaceCharge->SetInputSpaceChargeC((TMatrixD**)mMatrixChargeC.get()); + spaceCharge->fillChargeDensityFromHisto(*scHisto.get()); // further parameters - spaceCharge->SetOmegaTauT1T2(0.32, 1.f, 1.f); - spaceCharge->SetCorrectionType(0); // 0: use regular spaced LUTs, 1: use irregular LUTs (not recommended at the time as it is slower and results have to be verified) - spaceCharge->SetIntegrationStrategy(0); // 0: use default integration along drift lines, 1: use fast integration (not recommended at the time as results have to be verified) + spaceCharge->setOmegaTauT1T2(0.32, 1.f, 1.f); - // calculate LUTs - spaceCharge->ForceInitSpaceCharge3DPoissonIntegralDz(nR, nZ, nPhi, 300, 1e-8); + // start calculation of lookup tables (takes some time) + spaceCharge->calculateDistortionsCorrections(Side::A); + spaceCharge->calculateDistortionsCorrections(Side::C); } /// Function to get corrections from original lookup tables -/// \param roc readout chamber index (0 - 71) /// \param XYZ array with x, y and z position /// \param dXdYdZ array with correction dx, dy and dz void getSpaceChargeCorrection(const int roc, const double XYZ[3], double dXdYdZ[3]) { - const float xyzf[3] = {static_cast(XYZ[0]), static_cast(XYZ[1]), static_cast(XYZ[2])}; - float dxdydzf[3] = {0.f, 0.f, 0.f}; - spaceCharge->GetCorrection(xyzf, roc, dxdydzf); - dXdYdZ[0] = static_cast(dxdydzf[0]); - dXdYdZ[1] = static_cast(dxdydzf[1]); - dXdYdZ[2] = static_cast(dxdydzf[2]); + Side side = roc < 18 ? Side::A : Side::C; + spaceCharge->getCorrections(XYZ[0], XYZ[1], XYZ[2], side, dXdYdZ[0], dXdYdZ[1], dXdYdZ[2]); } /// Save TPCFastTransform to a file @@ -203,15 +180,12 @@ void debugInterpolation(utils::TreeStreamRedirector& pcstream, for (int slice = 0; slice < geo.getNumberOfSlices(); slice += 1) { // for (int slice = 21; slice < 22; slice += 1) { std::cout << "debug slice " << slice << " ... " << std::endl; - const o2::gpu::TPCFastTransformGeo::SliceInfo& sliceInfo = geo.getSliceInfo(slice); for (int row = 0; row < geo.getNumberOfRows(); row++) { - int nPads = geo.getRowInfo(row).maxPad + 1; for (int pad = 0; pad < nPads; pad++) { - for (float time = 0; time < 500; time += 10) { // non-corrected point @@ -232,35 +206,32 @@ void debugInterpolation(utils::TreeStreamRedirector& pcstream, // the original correction double gxyz[3] = {gx, gy, gz}; double gdC[3] = {0, 0, 0}; - getSpaceChargeCorrection(slice, gxyz, gdC); + Side side = slice < geo.getNumberOfSlicesA() ? Side::A : Side::C; + spaceCharge->getCorrections(gxyz[0], gxyz[1], gxyz[2], side, gdC[0], gdC[1], gdC[2]); float ldxC, ldyC, ldzC; - geo.convGlobalToLocal(slice, gdC[0], gdC[1], gdC[2], ldxC, ldyC, - ldzC); + geo.convGlobalToLocal(slice, gdC[0], gdC[1], gdC[2], ldxC, ldyC, ldzC); - float rC = std::sqrt((gx + gdC[0]) * (gx + gdC[0]) + - (gy + gdC[1]) * (gy + gdC[1])); + float rC = std::sqrt((gx + gdC[0]) * (gx + gdC[0]) + (gy + gdC[1]) * (gy + gdC[1])); // calculate distortion for the xyz0 - float ldD[3] = {0.0, 0.0, 0.0}; - float gdD[3] = {0.0, 0.0, 0.0}; + double ldD[3] = {0.0, 0.0, 0.0}; + double gdD[3] = {0.0, 0.0, 0.0}; float gxyzf[3] = {gx, gy, gz}; - float pointCyl[3] = {r, phi, gz}; + float pointCyl[3] = {gz, r, phi}; double efield[3] = {0.0, 0.0, 0.0}; - double charge = spaceCharge->GetChargeCylAC(pointCyl, slice); - double potential = spaceCharge->GetPotentialCylAC(pointCyl, slice); - spaceCharge->GetElectricFieldCyl(pointCyl, slice, efield); - spaceCharge->GetLocalDistortionCyl(pointCyl, slice, ldD); - spaceCharge->GetDistortion(gxyzf, slice, gdD); + double charge = spaceCharge->getChargeCyl(pointCyl[0], pointCyl[1], pointCyl[2], side); + double potential = spaceCharge->getPotentialCyl(pointCyl[0], pointCyl[1], pointCyl[2], side); + spaceCharge->getElectricFieldsCyl(pointCyl[0], pointCyl[1], pointCyl[2], side, efield[0], efield[1], efield[2]); + spaceCharge->getLocalDistortionsCyl(pointCyl[0], pointCyl[1], pointCyl[2], side, ldD[0], ldD[1], ldD[2]); + spaceCharge->getDistortions(gxyzf[0], gxyzf[1], gxyzf[2], side, gdD[0], gdD[1], gdD[2]); double gD[3] = {gx + gdD[0], gy + gdD[1], gz + gdD[2]}; - float rD = std::sqrt(gD[0] * gD[0] + gD[1] * gD[1]); // correction for the distorted point - double gdDC[3] = {0, 0, 0}; - getSpaceChargeCorrection(slice, gD, gdDC); + spaceCharge->getCorrections(gD[0], gD[1], gD[2], side, gdDC[0], gdDC[1], gdDC[2]); pcstream << "fastTransform" // internal coordinates @@ -313,9 +284,9 @@ void debugInterpolation(utils::TreeStreamRedirector& pcstream, // << "charge=" << charge << "potential=" << potential - << "Er=" << efield[0] - << "Ephi=" << efield[1] - << "Ez=" << efield[2] + << "Ez=" << efield[0] + << "Er=" << efield[1] + << "Ephi=" << efield[2] << "\n"; } } @@ -327,51 +298,33 @@ void debugInterpolation(utils::TreeStreamRedirector& pcstream, /// \param pcstream output stream /// \param geo TPCFastTransformGeo object /// \param fastTransform TPCFastTransform object -void debugGridpoints(utils::TreeStreamRedirector& pcstream, - const o2::gpu::TPCFastTransformGeo& geo, - TPCFastTransform* fastTransform) +void debugGridpoints(utils::TreeStreamRedirector& pcstream, const o2::gpu::TPCFastTransformGeo& geo, TPCFastTransform* fastTransform) { - int nR = spaceCharge->GetNRRows(); - int nZ = spaceCharge->GetNZColumns(); - int nPhi = spaceCharge->GetNPhiSlices(); - - float deltaR = - (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / - (nR - 1); - float deltaZ = AliTPCPoissonSolver::fgkTPCZ0 / (nZ - 1); - float deltaPhi = o2::constants::math::TwoPI / nPhi; - for (int iside = 0; iside < 2; ++iside) { - + const Side side = iside == 0 ? Side::A : Side::C; for (int iphi = 0; iphi < nPhi; ++iphi) { - float phi = deltaPhi * iphi; - int sector = iside == 0 ? phi / o2::constants::math::TwoPI * 18 - : phi / o2::constants::math::TwoPI * 18 + 18; + float phi = spaceCharge->getPhiVertex(iphi, side); + int sector = iside == 0 ? phi / o2::constants::math::TwoPI * 18 : phi / o2::constants::math::TwoPI * 18 + 18; for (int ir = 0; ir < nR; ++ir) { - float radius = AliTPCPoissonSolver::fgkIFCRadius + deltaR * ir; + float radius = spaceCharge->getRVertex(ir, side); float gx0 = radius * std::cos(phi); float gy0 = radius * std::sin(phi); for (int iz = 0; iz < nZ; ++iz) { - float gz0 = iside == 0 ? deltaZ * iz - : -1 * deltaZ * iz; - + float gz0 = spaceCharge->getZVertex(iz, side); float x0 = 0.f, y0 = 0.f, z0 = 0.f; geo.convGlobalToLocal(sector, gx0, gy0, gz0, x0, y0, z0); if (x0 < geo.getRowInfo(0).x - 0.375) { continue; } - if (x0 >= (geo.getRowInfo(62).x + 0.375) && - x0 < (geo.getRowInfo(63).x - 0.5)) { + if (x0 >= (geo.getRowInfo(62).x + 0.375) && x0 < (geo.getRowInfo(63).x - 0.5)) { continue; } - if (x0 >= (geo.getRowInfo(96).x + 0.5) && - x0 < (geo.getRowInfo(97).x - 0.6)) { + if (x0 >= (geo.getRowInfo(96).x + 0.5) && x0 < (geo.getRowInfo(97).x - 0.6)) { continue; } - if (x0 >= (geo.getRowInfo(126).x + 0.6) && - x0 < (geo.getRowInfo(127).x - 0.75)) { + if (x0 >= (geo.getRowInfo(126).x + 0.6) && x0 < (geo.getRowInfo(127).x - 0.75)) { continue; } if (x0 > (geo.getRowInfo(151).x + 0.75)) { @@ -417,19 +370,18 @@ void debugGridpoints(utils::TreeStreamRedirector& pcstream, double gcorr[3] = {0, 0, 0}; getSpaceChargeCorrection(sector, xyz, gcorr); float lcorr[3]; - geo.convGlobalToLocal(sector, gcorr[0], gcorr[1], gcorr[2], lcorr[0], - lcorr[1], lcorr[2]); + geo.convGlobalToLocal(sector, gcorr[0], gcorr[1], gcorr[2], lcorr[0], lcorr[1], lcorr[2]); float fxyz[3] = {gx0, gy0, gz0}; - float pointCyl[3] = {radius, phi, gz0}; + float pointCyl[3] = {gz0, radius, phi}; double efield[3] = {0.0, 0.0, 0.0}; - float distLocal[3] = {0.0, 0.0, 0.0}; - float dist[3] = {0.0, 0.0, 0.0}; - double charge = spaceCharge->GetChargeCylAC(pointCyl, sector); - double potential = spaceCharge->GetPotentialCylAC(pointCyl, sector); - spaceCharge->GetElectricFieldCyl(pointCyl, sector, efield); - spaceCharge->GetLocalDistortionCyl(pointCyl, sector, distLocal); - spaceCharge->GetDistortion(fxyz, sector, dist); + double distLocal[3] = {0.0, 0.0, 0.0}; + double dist[3] = {0.0, 0.0, 0.0}; + double charge = spaceCharge->getChargeCyl(pointCyl[0], pointCyl[1], pointCyl[2], side); + double potential = spaceCharge->getPotentialCyl(pointCyl[0], pointCyl[1], pointCyl[2], side); + spaceCharge->getElectricFieldsCyl(pointCyl[0], pointCyl[1], pointCyl[2], side, efield[0], efield[1], efield[2]); + spaceCharge->getLocalDistortionsCyl(pointCyl[0], pointCyl[1], pointCyl[2], side, distLocal[0], distLocal[1], distLocal[2]); + spaceCharge->getDistortions(fxyz[0], fxyz[1], fxyz[2], side, dist[0], dist[1], dist[2]); pcstream << "fastTransform" // internal coordinates @@ -472,9 +424,9 @@ void debugGridpoints(utils::TreeStreamRedirector& pcstream, // << "charge=" << charge << "potential=" << potential - << "Er=" << efield[0] - << "Ephi=" << efield[1] - << "Ez=" << efield[2] + << "Ez=" << efield[0] + << "Er=" << efield[1] + << "Ephi=" << efield[2] << "\n"; } } @@ -485,7 +437,7 @@ void debugGridpoints(utils::TreeStreamRedirector& pcstream, /// Make a simple QA plot of the debug stream void makeQAplot() { - TFile f("fastTransformUnitTest_debug1_gridsize180-65-65_order2.root"); + TFile f("fastTransformUnitTest_debug1_gridsize180-129-129.root"); TTree* tree = (TTree*)f.Get("fastTransform"); tree->SetMarkerSize(0.2); tree->SetMarkerStyle(21); diff --git a/Detectors/TPC/simulation/CMakeLists.txt b/Detectors/TPC/simulation/CMakeLists.txt index 1420c3ce08cce..d4d4af7b46702 100644 --- a/Detectors/TPC/simulation/CMakeLists.txt +++ b/Detectors/TPC/simulation/CMakeLists.txt @@ -21,9 +21,8 @@ o2_add_library(TPCSimulation src/PadResponse.cxx src/Point.cxx src/SAMPAProcessing.cxx - src/SpaceCharge.cxx PUBLIC_LINK_LIBRARIES O2::DetectorsBase O2::SimulationDataFormat - O2::TPCBase O2::TPCSpaceChargeBase + O2::TPCBase O2::TPCSpaceCharge ROOT::Physics) o2_target_root_dictionary(TPCSimulation @@ -38,8 +37,7 @@ o2_target_root_dictionary(TPCSimulation include/TPCSimulation/GEMAmplification.h include/TPCSimulation/PadResponse.h include/TPCSimulation/Point.h - include/TPCSimulation/SAMPAProcessing.h - include/TPCSimulation/SpaceCharge.h) + include/TPCSimulation/SAMPAProcessing.h) o2_add_executable(digits-to-rawzs COMPONENT_NAME tpc @@ -57,13 +55,6 @@ if(BUILD_TESTING) O2::SimulationDataFormat LABELS tpc) - o2_add_test_root_macro(macro/createResidualDistortionObject.C - PUBLIC_LINK_LIBRARIES O2::TPCSpaceChargeBase - O2::CommonUtils - O2::CommonConstants - O2::TPCSimulation - LABELS tpc) - o2_add_test_root_macro(macro/laserTrackGenerator.C PUBLIC_LINK_LIBRARIES FairRoot::Base O2::DataFormatsTPC diff --git a/Detectors/TPC/simulation/include/TPCSimulation/Digitizer.h b/Detectors/TPC/simulation/include/TPCSimulation/Digitizer.h index c0a565857eedd..cb6d74c43555a 100644 --- a/Detectors/TPC/simulation/include/TPCSimulation/Digitizer.h +++ b/Detectors/TPC/simulation/include/TPCSimulation/Digitizer.h @@ -18,7 +18,7 @@ #include "TPCSimulation/DigitContainer.h" #include "TPCSimulation/PadResponse.h" #include "TPCSimulation/Point.h" -#include "TPCSimulation/SpaceCharge.h" +#include "TPCSpaceCharge/SpaceCharge.h" #include "TPCBase/Mapper.h" @@ -52,6 +52,8 @@ class DigitContainer; class Digitizer { public: + using SC = SpaceCharge; + /// Default constructor Digitizer() = default; @@ -109,20 +111,24 @@ class Digitizer /// \param nZSlices number of grid points in z, must be (2**N)+1 /// \param nPhiBins number of grid points in phi /// \param nRBins number of grid points in r, must be (2**N)+1 - void setUseSCDistortions(SpaceCharge::SCDistortionType distortionType, const TH3* hisInitialSCDensity, int nRBins, int nPhiBins, int nZSlices); + void setUseSCDistortions(SC::SCDistortionType distortionType, const TH3* hisInitialSCDensity); /// Enable the use of space-charge distortions and provide SpaceCharge object as input /// \param spaceCharge unique pointer to spaceCharge object - void setUseSCDistortions(SpaceCharge* spaceCharge); + void setUseSCDistortions(SC* spaceCharge); + + /// Enable the use of space-charge distortions by providing global distortions and global corrections stored in a ROOT file + /// The storage of the values should be done by the methods provided in the SpaceCharge class + /// \param TFile file containing distortions and corrections + void setUseSCDistortions(TFile& finp); private: - DigitContainer mDigitContainer; ///< Container for the Digits - std::unique_ptr mSpaceCharge; ///< Handler of space-charge distortions - Sector mSector = -1; ///< ID of the currently processed sector - float mEventTime = 0.f; ///< Time of the currently processed event + DigitContainer mDigitContainer; ///< Container for the Digits + std::unique_ptr mSpaceCharge; ///< Handler of space-charge distortions + Sector mSector = -1; ///< ID of the currently processed sector + float mEventTime = 0.f; ///< Time of the currently processed event // FIXME: whats the reason for hving this static? static bool mIsContinuous; ///< Switch for continuous readout bool mUseSCDistortions = false; ///< Flag to switch on the use of space-charge distortions - ClassDefNV(Digitizer, 1); }; } // namespace tpc diff --git a/Detectors/TPC/simulation/include/TPCSimulation/SpaceCharge.h b/Detectors/TPC/simulation/include/TPCSimulation/SpaceCharge.h deleted file mode 100644 index 8b68c733b3766..0000000000000 --- a/Detectors/TPC/simulation/include/TPCSimulation/SpaceCharge.h +++ /dev/null @@ -1,244 +0,0 @@ -// Copyright CERN and copyright holders of ALICE O2. This software is -// distributed under the terms of the GNU General Public License v3 (GPL -// Version 3), copied verbatim in the file "COPYING". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file SpaceCharge.h -/// \brief Definition of the handler for the ALICE TPC space-charge distortions calculations -/// \author Ernst Hellbär, Goethe-Universität Frankfurt, ernst.hellbar@cern.ch - -/* - * TODO: - * - modifiy TPCSpaceChargeBase classes - * - create light-weight 2D matrix class as matrix containers - * - std::vector instead of TMatrixD** - * - bring to O2 conventions - * - fix constants (more precise values, export into TPCBase/Constants) - * - granularity in r, rphi, z? - * - accumulate and add next slice - * - event based: propagate charge(ievent-1), add charge(ievent) - * - time based: mTime0, mEffectiveTime - * addIons(eventtime, drifttime, r, phi) - * time in us, 50 kHz = - * if (ev.time+dr.time-mTime0 < mLengthTimebin) => add2NextSlice - * if (mLengthTimebin < ev.time+dr.time-mTime0 < mLengthTimebin+100us) add2NextToNextSlice - * - apply updated distortions to ions in NextToNextSlice when space charge is propagated; need to store exact positions (e.g. std::vector>)! - * - ion transport along the E field -> Continuity equation - * - Validate results by comparison to single ion transport - * - what about primary ionization? - * - irregular bin sizes in r and rphi - */ - -#ifndef ALICEO2_TPC_SPACECHARGE_H -#define ALICEO2_TPC_SPACECHARGE_H - -#include "AliTPCSpaceCharge3DCalc.h" -#include "DataFormatsTPC/Defs.h" -#include "MathUtils/RandomRing.h" - -class TH3; - -namespace o2 -{ -namespace tpc -{ - -class SpaceCharge -{ - public: - /// Enumerator for setting the space-charge distortion mode - enum class SCDistortionType : int { - SCDistortionsConstant = 0, // space-charge distortions constant over time - SCDistortionsRealistic = 1 // realistic evolution of space-charge distortions over time - }; - - // Constructors - /// Default constructor using a grid size of (129 z bins, 180 phi bins, 129 r bins) - SpaceCharge(); - /// Constructor with grid size specified by user - /// \param nRBins number of grid points in r, must be (2**N)+1 - /// \param nPhiBins number of grid points in phi - /// \param nZSlices number of grid points in z, must be (2**N)+1 - SpaceCharge(int nRBins, int nPhiBins, int nZSlices); - /// Constructor with grid size and interpolation order specified by user - /// \param nRBins number of grid points in r, must be (2**N)+1 - /// \param nPhiBins number of grid points in phi - /// \param nZSlices number of grid points in z, must be (2**N)+1 - /// \param interpolationOrder order used for interpolation of lookup tables - SpaceCharge(int nRBins, int nPhiBins, int nZSlices, int interpolationOrder); - - // Destructor - ~SpaceCharge() = default; - - /// Calculate lookup tables if initial space-charge density is provided - void init(); - - /// Calculate distortion and correction lookup tables using AliTPCSpaceChargeCalc class - /// \return real time for the calculation of the electron lookup tables - float calculateLookupTables(); - /// Update distortion and correction lookup tables by current space-charge density - /// \param eventTime time of current event - /// \return real time for the re-calculation of the electron lookup tables - float updateLookupTables(float eventTime); - - /// Set omega*tau and T1, T2 tensor terms in Langevin-equation solution - /// \param omegaTau omega*tau - /// \param t1 T1 tensor term - /// \param t2 T2 tensor term - void setOmegaTauT1T2(float omegaTau, float t1, float t2); - /// Set an initial space-charge density - /// \param hisSCDensity 3D space-charge density histogram, expected format (phi,r,z) and units C / cm^3 / epsilon0 - void setInitialSpaceChargeDensity(const TH3* hisSCDensity); - /// Add primary ions to space-charge density - /// \param r global radius - /// \param phi global phi position - /// \param z z position - /// \param nIons number of ions - void fillPrimaryIons(double r, double phi, double z, int nIons); - /// Add charge to space-charge density - /// \param r global radius - /// \param phi global phi position - /// \param z z position - /// \param charge charge in C/cm^3/epsilon0 - void fillPrimaryCharge(double r, double phi, double z, float charge); - /// Add ion backflow to space-charge density - /// \param r global radius - /// \param phi global phi position - /// \param side A or C side - /// \param nIons number of ions - void fillIBFIons(double r, double phi, Side side, int nIons); - /// Add ion backflow to space-charge density - /// \param r global radius - /// \param phi global phi position - /// \param side A or C side - /// \param charge charge in C/cm^3/epsilon0 - void fillIBFCharge(double r, double phi, Side side, float charge); - /// Get ion drift vector along electric field - /// \param r global radius - /// \param phi global phi position - /// \param z z position - /// \param dr return drift in radial direction - /// \param drphi return drift in azimuthal (rphi) direction - /// \param dz return drift in z direction - void getIonDrift(Side side, double r, double phi, double z, double& dr, double& drphi, double& dz) const; - /// Propagate space-charge density along electric field by one time slice - void propagateSpaceCharge(); - /// Convert space-charge density to distribution of ions, propagate them along the electric field and convert back to space-charge density - void propagateIons(); - - /// Correct electron position using correction lookup tables - /// \param point 3D coordinates of the electron - void correctElectron(GlobalPosition3D& point); - /// Distort electron position using distortion lookup tables - /// \param point 3D coordinates of the electron - void distortElectron(GlobalPosition3D& point) const; - - /// Interpolate the space-charge density from lookup tables in mLookUpTableCalculator - /// \param point Position at which to calculate the space-charge density - /// \return space-charge density at given point in C/cm^3/epsilon0 - double getChargeDensity(Side side, const GlobalPosition3D& point) const; - /// Get the space-charge density stored in the - /// \param iphi phi bin - /// \param ir r bin - /// \param iz z bin - /// \return space-charge density in given bin in C/cm^3/epsilon0 - float getChargeDensity(Side side, int ir, int iphi, int iz) const; - - /// Set the space-charge distortions model - /// \param distortionType distortion type (constant or realistic) - void setSCDistortionType(SCDistortionType distortionType) { mSCDistortionType = distortionType; } - /// Get the space-charge distortions model - SCDistortionType getSCDistortionType() const { return mSCDistortionType; } - - /// Return the ion drift time for one z bin - double getDriftTimeZSlice() const { return mDriftTimeVoxel; } - double getNPhi() const { return mNPhi; } - double getNR() const { return mNR; } - double getNZ() const { return mNZ; } - double getVoxelSizePhi() const { return mVoxelSizePhi; } - double getVoxelSizeR() const { return mVoxelSizeR; } - double getVoxelSizeZ() const { return mVoxelSizeZ; } - std::vector const& getCoordinatesPhi() const { return mCoordPhi; } - std::vector const& getCoordinatesR() const { return mCoordR; } - std::vector const& getCoordinatesZ() const { return mCoordZ; } - - void setUseIrregularLUTs(int useIrrLUTs); - void setUseFastDistIntegration(int useFastInt); - - void setDistortionLookupTables(TMatrixD** matrixIntDistDrA, TMatrixD** matrixIntDistDrphiA, TMatrixD** matrixIntDistDzA, TMatrixD** matrixIntDistDrC, TMatrixD** matrixIntDistDrphiC, TMatrixD** matrixIntDistDzC); - - private: - /// Allocate memory for data members - void allocateMemory(); - void setVoxelCoordinates(); - - /// Convert amount of ions into charge density C/cm^3/epsilon0 - /// \param nIons number of ions - /// \return space-charge density (C/cm^3/epsilon0) - float ions2Charge(int rBin, int nIons); - - static constexpr float DvDEoverv0 = 0.0025; //!2==cubic spline - - const int mNZ; ///< number of z slices used in lookup tables - const int mNPhi; ///< number of phi bins used in lookup tables - const int mNR; ///< number of r bins used in lookup tables - const double mVoxelSizeZ; ///< length of one z bin (cm) - const double mDriftTimeVoxel; ///< ion drift time for one z slice (us) - const double mVoxelSizePhi; ///< width of one phi bin (radians) - const double mVoxelSizeR; ///< length of one r bin (cm) - - std::vector mCoordZ; ///< vector with coodinates of the z bins - std::vector mCoordPhi; ///< vector with coodinates of the phi bins - std::vector mCoordR; ///< vector with coodinates of the r bins - - bool mUseInitialSCDensity; ///< Flag for the use of an initial space-charge density at the beginning of the simulation - bool mInitLookUpTables; ///< Flag to indicate if lookup tables have been calculated - float mTimeInit; ///< time of last update of lookup tables - SCDistortionType mSCDistortionType; ///< Type of space-charge distortions - - AliTPCSpaceCharge3DCalc mLookUpTableCalculator; ///< object to calculate and store correction and distortion lookup tables - - /// TODO: What are the coordinates of the bins? They are defined in AliTPCSpaceCharge3DCalc::GetChargeDensity and are different from mCoordZ, mCoordPhi, mCoordR used for local ion drift lookup table! Use consistent convention? Lookup table instead of vector? - std::vector mSpaceChargeDensityA; ///< space-charge density on the A side, stored in C/cm^3/epsilon0, z ordering: z=[0,250], [iphi*mNR*mNZ + ir*mNZ + iz] - std::vector mSpaceChargeDensityC; ///< space-charge density on the C side, stored in C/cm^3/epsilon0, z ordering: z=[0,-250], [iphi*mNR*mNZ + ir*mNZ + iz] - - /// Ion drift vectors after time deltaT = mLengthZSlice / v_driftIon - /// nominal E field only in z direction, distortions in r, phi, z due to space charge - /// d = (dr, drphi, mVoxelSizeZ + dz) - /// TODO: Eliminate the need for these matrices as members, they should be owned by AliTPCLookUpTable3DInterpolatorD. AliTPCLookUpTable3DInterpolatorD needs getters for the matrices and the constructor has to be modified. - std::unique_ptr[]> mMatrixIonDriftZA; //![]> mMatrixIonDriftZC; //![]> mMatrixIonDriftRPhiA; //![]> mMatrixIonDriftRPhiC; //![]> mMatrixIonDriftRA; //![]> mMatrixIonDriftRC; //! mLookUpIonDriftA; ///< lookup table for ion drift along E field on A side in cm - std::unique_ptr mLookUpIonDriftC; ///< lookup table for ion drift along E field on C side in cm - bool mMemoryAllocated; - - math_utils::RandomRing<> mRandomFlat; //! - -#include "TFile.h" -#include "TH3.h" -#include "TMatrixD.h" -#include "TString.h" - -#include "AliTPCSpaceCharge3DCalc.h" -#include "CommonConstants/MathConstants.h" -#include "CommonUtils/TreeStreamRedirector.h" -#include "TPCSimulation/SpaceCharge.h" -#include "MathUtils/Cartesian.h" - -using namespace o2::tpc; - -// function declarations -AliTPCSpaceCharge3DCalc* createSpaceCharge(TH3* hisSCDensity, int bSign, int nR, int nPhi, int nZ); -void fillDistortionLookupMatrices(AliTPCSpaceCharge3DCalc* spaceChargeCalc, AliTPCSpaceCharge3DCalc* spaceChargeCalcAvg, TMatrixD** matrixDistDrA, TMatrixD** matrixDistDrphiA, TMatrixD** matrixDistDzA, TMatrixD** matrixDistDrC, TMatrixD** matrixDistDrphiC, TMatrixD** matrixDistDzC); -void makeDebugTreeResiduals(AliTPCSpaceCharge3DCalc* calcFluc, AliTPCSpaceCharge3DCalc* calcAvg, SpaceCharge* spaceChargeRes); - -/// Create a SpaceCharge object with residual distortions from a fluctuating and an average space-charge density histogram and write it to a file. -/// \param bSign sign of the B-field: -1 = negative, 0 = no B-field, 1 = positive -/// \param nR number of bins in r -/// \param nPhi number of bins in phi -/// \param nZ number of bins in z -/// \param pathToHistoFile path to a file with the fluctuating and average histograms -/// \param histoFlucName name of the fluctuating histogram -/// \param histoAvgName name of the average histogram -void createResidualDistortionObject(int bSign, int nR = 129, int nPhi = 144, int nZ = 129, const char* pathToHistoFile = "InputSCDensityHistograms_8000events.root", const char* histoFlucName = "inputSCDensity3D_8000_0", const char* histoAvgName = "inputSCDensity3D_8000_avg", bool debug = false) -{ - /* - Usage: - root -l -b -q createResidualDistortionObject.C+\(-1,129,144,129,\"InputSCDensityHistograms_8000events.root\",\"inputSCDensity3D_8000_0\",\"inputSCDensity3D_8000_avg\",true\) - */ - TFile* fileHistos = TFile::Open(pathToHistoFile); - auto histoFluc = fileHistos->Get(histoFlucName); - auto histoAvg = fileHistos->Get(histoAvgName); - - TFile* fileOutput = TFile::Open("ResidualDistortions.root", "recreate"); - - // Calculate fluctuating and average distortion and correction lookup tables - AliTPCSpaceCharge3DCalc* scCalcFluc = createSpaceCharge(histoFluc, bSign, nR, nPhi, nZ); - AliTPCSpaceCharge3DCalc* scCalcAvg = createSpaceCharge(histoAvg, bSign, nR, nPhi, nZ); - - // Create matrices and fill them with residual distortions. Create SpaceCharge object, assign residual distortion matrices to it and store it in the output file. - TMatrixD** matrixResDistDrA = new TMatrixD*[nPhi]; - TMatrixD** matrixResDistDrphiA = new TMatrixD*[nPhi]; - TMatrixD** matrixResDistDzA = new TMatrixD*[nPhi]; - TMatrixD** matrixResDistDrC = new TMatrixD*[nPhi]; - TMatrixD** matrixResDistDrphiC = new TMatrixD*[nPhi]; - TMatrixD** matrixResDistDzC = new TMatrixD*[nPhi]; - for (int iphi = 0; iphi < nPhi; ++iphi) { - matrixResDistDrA[iphi] = new TMatrixD(nR, nZ); - matrixResDistDrphiA[iphi] = new TMatrixD(nR, nZ); - matrixResDistDzA[iphi] = new TMatrixD(nR, nZ); - matrixResDistDrC[iphi] = new TMatrixD(nR, nZ); - matrixResDistDrphiC[iphi] = new TMatrixD(nR, nZ); - matrixResDistDzC[iphi] = new TMatrixD(nR, nZ); - } - fillDistortionLookupMatrices(scCalcFluc, scCalcAvg, matrixResDistDrA, matrixResDistDrphiA, matrixResDistDzA, matrixResDistDrC, matrixResDistDrphiC, matrixResDistDzC); - SpaceCharge spaceChargeRes(nR, nPhi, nZ); - spaceChargeRes.setDistortionLookupTables(matrixResDistDrA, matrixResDistDrphiA, matrixResDistDzA, matrixResDistDrC, matrixResDistDrphiC, matrixResDistDzC); - fileOutput->WriteObject(&spaceChargeRes, "spaceChargeRes"); - - if (debug) { - makeDebugTreeResiduals(scCalcFluc, scCalcAvg, &spaceChargeRes); - } -} - -/// Create AliTPCSpaceCharge3DCalc object from a space-charge density histogram -/// \param hisSCDensity input space-charge density histogram -/// \param bSign sign of the B-field: -1 = negative, 0 = no B-field, 1 = positive -/// \param nR number of bins in r -/// \param nPhi number of bins in phi -/// \param nZ number of bins in z -/// \return pointer to AliTPCSpaceCharge3DCalc object -AliTPCSpaceCharge3DCalc* createSpaceCharge(TH3* hisSCDensity, int bSign, int nR, int nPhi, int nZ) -{ - AliTPCSpaceCharge3DCalc* spaceCharge = new AliTPCSpaceCharge3DCalc(nR, nZ, nPhi, 2, 3, 0); - std::unique_ptr[]> mMatrixChargeA = std::make_unique[]>(nPhi); - std::unique_ptr[]> mMatrixChargeC = std::make_unique[]>(nPhi); - for (int iphi = 0; iphi < nPhi; ++iphi) { - mMatrixChargeA[iphi] = std::make_unique(nR, nZ); - mMatrixChargeC[iphi] = std::make_unique(nR, nZ); - } - spaceCharge->GetChargeDensity((TMatrixD**)mMatrixChargeA.get(), (TMatrixD**)mMatrixChargeC.get(), hisSCDensity, nR, nZ, nPhi); - spaceCharge->SetInputSpaceChargeA((TMatrixD**)mMatrixChargeA.get()); - spaceCharge->SetInputSpaceChargeC((TMatrixD**)mMatrixChargeC.get()); - float omegaTau = -0.32 * bSign; - spaceCharge->SetOmegaTauT1T2(omegaTau, 1.f, 1.f); - spaceCharge->SetCorrectionType(0); - spaceCharge->SetIntegrationStrategy(0); - spaceCharge->ForceInitSpaceCharge3DPoissonIntegralDz(nR, nZ, nPhi, 300, 1e-8); - return spaceCharge; -} - -/// Store distortions from spaceChargeCalcFluc in the matrices provided. If providing an object with average space-charge distortions spaceChargeCalcAvg, the residual distortions (dist_fluctuation(xyzTrue) + corr_average(xyzDistorted)) will be stored. -/// \param spaceChargeCalcFluc fluctuating distortions object -/// \param spaceChargeCalcAvg average distortions object -/// \param matrixDistDrA matrix to store radial distortions on the A side -/// \param matrixDistDrphiA matrix to store rphi distortions on the A side -/// \param matrixDistDzA matrix to store z distortions on the A side -/// \param matrixDistDrC matrix to store radial distortions on the C side -/// \param matrixDistDrphiC matrix to store rphi distortions on the C side -/// \param matrixDistDzC matrix to store z distortions on the C side -void fillDistortionLookupMatrices(AliTPCSpaceCharge3DCalc* spaceChargeCalcFluc, AliTPCSpaceCharge3DCalc* spaceChargeCalcAvg, TMatrixD** matrixDistDrA, TMatrixD** matrixDistDrphiA, TMatrixD** matrixDistDzA, TMatrixD** matrixDistDrC, TMatrixD** matrixDistDrphiC, TMatrixD** matrixDistDzC) -{ - const int nR = spaceChargeCalcFluc->GetNRRows(); - const int nPhi = spaceChargeCalcFluc->GetNPhiSlices(); - const int nZ = spaceChargeCalcFluc->GetNZColumns(); - const float mVoxelSizeR = (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (nR - 1); - const float mVoxelSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (nZ - 1); - const float mVoxelSizePhi = o2::constants::math::TwoPI / nPhi; - - for (int iphi = 0; iphi < nPhi; ++iphi) { - float phi = iphi * mVoxelSizePhi; - - for (int ir = 0; ir < nR; ++ir) { - float r = AliTPCPoissonSolver::fgkIFCRadius + ir * mVoxelSizeR; - - for (int iz = 0; iz < nZ; ++iz) { - float absz = iz * mVoxelSizeZ; - - float xCylA[3] = {r, phi, absz}; - float xCylC[3] = {r, phi, -1 * absz}; - int roc = 0; - float distFlucA[3] = {0.f, 0.f, 0.f}; - float distFlucC[3] = {0.f, 0.f, 0.f}; - - // get fluctuating distortions - spaceChargeCalcFluc->GetDistortionCylAC(xCylA, roc, distFlucA); - spaceChargeCalcFluc->GetDistortionCylAC(xCylC, roc + 18, distFlucC); - float distA[3] = {distFlucA[0], distFlucA[1], distFlucA[2]}; - float distC[3] = {distFlucC[0], distFlucC[1], distFlucC[2]}; - - // get average corrections if provided and add them to the fluctuating distortions - if (spaceChargeCalcAvg) { - float xDistCylA[3] = {xCylA[0] + distFlucA[0], xCylA[1] + distFlucA[1] / xCylA[0], xCylA[2] + distFlucA[2]}; - float xDistCylC[3] = {xCylC[0] + distFlucC[0], xCylC[1] + distFlucC[1] / xCylC[0], xCylC[2] + distFlucC[2]}; - float corrAvgA[3] = {0.f, 0.f, 0.f}; - float corrAvgC[3] = {0.f, 0.f, 0.f}; - spaceChargeCalcAvg->GetCorrectionCylAC(xDistCylA, roc, corrAvgA); - spaceChargeCalcAvg->GetCorrectionCylAC(xDistCylC, roc + 18, corrAvgC); - distA[0] += corrAvgA[0]; - distA[1] += (corrAvgA[1] * xCylA[0] / xDistCylA[0]); - distA[2] += corrAvgA[2]; - distC[0] += corrAvgC[0]; - distC[1] += (corrAvgC[1] * xCylC[0] / xDistCylC[0]); - distC[2] += corrAvgC[2]; - } - - // store (residual) distortions in the matrices - (*matrixDistDrA[iphi])(ir, iz) = distA[0]; - (*matrixDistDrphiA[iphi])(ir, iz) = distA[1]; - (*matrixDistDzA[iphi])(ir, iz) = distA[2]; - (*matrixDistDrC[iphi])(ir, iz) = distC[0]; - (*matrixDistDrphiC[iphi])(ir, iz) = distC[1]; - (*matrixDistDzC[iphi])(ir, iz) = -1 * distC[2]; - } - } - } -} - -/// Calculate and stream residual distortions from spaceChargeRes and from calcFluc and calcAvg for comparison. -/// \param calcFluc AliTPCSpaceCharge3DCalc object with fluctuation distortions -/// \param calcAvg AliTPCSpaceCharge3DCalc object with average distortions -/// \param spaceChargeRes SpaceCharge object with residual distortions -void makeDebugTreeResiduals(AliTPCSpaceCharge3DCalc* calcFluc, AliTPCSpaceCharge3DCalc* calcAvg, SpaceCharge* spaceChargeRes) -{ - const int nR = calcFluc->GetNRRows(); - const int nPhi = calcFluc->GetNPhiSlices(); - const int nZ = calcFluc->GetNZColumns(); - const float mVoxelSizeR = (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (nR - 1); - const float mVoxelSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (nZ - 1); - const float mVoxelSizePhi = o2::constants::math::TwoPI / nPhi; - - o2::utils::TreeStreamRedirector pcstream("debugResidualDistortions.root", "recreate"); - for (int iside = 0; iside < 2; ++iside) { - int roc = iside == 0 ? 0 : 18; - for (int iphi = 0; iphi < nPhi; ++iphi) { - float phi = iphi * mVoxelSizePhi; - for (int ir = 0; ir < nR; ++ir) { - float r = AliTPCPoissonSolver::fgkIFCRadius + ir * mVoxelSizeR; - float x = r * std::cos(phi); - float y = r * std::sin(phi); - for (int iz = 1; iz < nZ; ++iz) { - float z = iside == 0 ? iz * mVoxelSizeZ : -1 * iz * mVoxelSizeZ; - - GlobalPosition3D posDistRes(x, y, z); - spaceChargeRes->distortElectron(posDistRes); - float xyzDistRes[3] = {posDistRes.x(), posDistRes.y(), posDistRes.z()}; - float distRes[3] = {posDistRes.x() - x, posDistRes.y() - y, posDistRes.z() - z}; - - float xyz[3] = {x, y, z}; - float distFluc[3] = {0.f, 0.f, 0.f}; - calcFluc->GetDistortion(xyz, roc, distFluc); - float xyzDist[3] = {xyz[0] + distFluc[0], xyz[1] + distFluc[1], xyz[2] + distFluc[2]}; - float corrAvg[3] = {0.f, 0.f, 0.f}; - calcAvg->GetCorrection(xyzDist, roc, corrAvg); - float xyzDistResTrue[3] = {xyzDist[0] + corrAvg[0], xyzDist[1] + corrAvg[1], xyzDist[2] + corrAvg[2]}; - float distResTrue[3] = {distFluc[0] + corrAvg[0], distFluc[1] + corrAvg[1], distFluc[2] + corrAvg[2]}; - - pcstream << "debug" - << "iside=" << iside - << "iphi=" << iphi - << "ir=" << ir - << "iz=" << iz - // original position - << "phi=" << phi - << "r=" << r - << "x=" << x - << "y=" << y - << "z=" << z - // position of distorted points - << "xRes=" << xyzDistRes[0] - << "yRes=" << xyzDistRes[1] - << "zRes=" << xyzDistRes[2] - // true position of distorted points - << "xResTrue=" << xyzDistResTrue[0] - << "yResTrue=" << xyzDistResTrue[1] - << "zResTrue=" << xyzDistResTrue[2] - // residual distortions - << "distX=" << distRes[0] - << "distY=" << distRes[1] - << "distZ=" << distRes[2] - // true residual distortions - << "distXTrue=" << distResTrue[0] - << "distYTrue=" << distResTrue[1] - << "distZTrue=" << distResTrue[2] - // - << "\n"; - } - } - } - } - pcstream.Close(); -} \ No newline at end of file diff --git a/Detectors/TPC/simulation/src/Digitizer.cxx b/Detectors/TPC/simulation/src/Digitizer.cxx index 241159f14fe88..c12ecdec9be15 100644 --- a/Detectors/TPC/simulation/src/Digitizer.cxx +++ b/Detectors/TPC/simulation/src/Digitizer.cxx @@ -164,20 +164,33 @@ void Digitizer::flush(std::vector& digits, mDigitContainer.fillOutputContainer(digits, labels, commonModeOutput, mSector, sampaProcessing.getTimeBinFromTime(mEventTime), mIsContinuous, finalFlush); } -void Digitizer::setUseSCDistortions(SpaceCharge::SCDistortionType distortionType, const TH3* hisInitialSCDensity, int nRBins, int nPhiBins, int nZSlices) +void Digitizer::setUseSCDistortions(SC::SCDistortionType distortionType, const TH3* hisInitialSCDensity) { mUseSCDistortions = true; if (!mSpaceCharge) { - mSpaceCharge = std::make_unique(nRBins, nPhiBins, nZSlices); + mSpaceCharge = std::make_unique(); } mSpaceCharge->setSCDistortionType(distortionType); if (hisInitialSCDensity) { - mSpaceCharge->setInitialSpaceChargeDensity(hisInitialSCDensity); + mSpaceCharge->fillChargeDensityFromHisto(*hisInitialSCDensity); + mSpaceCharge->setUseInitialSCDensity(true); } } -void Digitizer::setUseSCDistortions(SpaceCharge* spaceCharge) +void Digitizer::setUseSCDistortions(SC* spaceCharge) { mUseSCDistortions = true; mSpaceCharge.reset(spaceCharge); } + +void Digitizer::setUseSCDistortions(TFile& finp) +{ + mUseSCDistortions = true; + if (!mSpaceCharge) { + mSpaceCharge = std::make_unique(); + } + mSpaceCharge->setGlobalDistortionsFromFile(finp, Side::A); + mSpaceCharge->setGlobalDistortionsFromFile(finp, Side::C); + mSpaceCharge->setGlobalCorrectionsFromFile(finp, Side::A); + mSpaceCharge->setGlobalCorrectionsFromFile(finp, Side::C); +} diff --git a/Detectors/TPC/simulation/src/SpaceCharge.cxx b/Detectors/TPC/simulation/src/SpaceCharge.cxx deleted file mode 100644 index 2020d258d2e59..0000000000000 --- a/Detectors/TPC/simulation/src/SpaceCharge.cxx +++ /dev/null @@ -1,653 +0,0 @@ -// Copyright CERN and copyright holders of ALICE O2. This software is -// distributed under the terms of the GNU General Public License v3 (GPL -// Version 3), copied verbatim in the file "COPYING". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file SpaceCharge.cxx -/// \brief Implementation of the interface for the ALICE TPC space-charge distortions calculations -/// \author Ernst Hellbär, Goethe-Universität Frankfurt, ernst.hellbar@cern.ch - -#include "TGeoGlobalMagField.h" -#include "TH3.h" -#include "TMath.h" -#include "TMatrixD.h" -#include "TStopwatch.h" - -#include "FairLogger.h" - -#include "DataFormatsTPC/Constants.h" -#include "DataFormatsTPC/Defs.h" -#include "Field/MagneticField.h" -#include "MathUtils/Utils.h" -#include "TPCBase/ParameterGas.h" -#include "TPCSimulation/SpaceCharge.h" - -using namespace o2::tpc; -using namespace o2::tpc::constants; -using namespace o2::math_utils; - -const float o2::tpc::SpaceCharge::sEzField = (AliTPCPoissonSolver::fgkCathodeV - AliTPCPoissonSolver::fgkGG) / AliTPCPoissonSolver::fgkTPCZ0; - -SpaceCharge::SpaceCharge() - : mNZ(MaxNZ), - mNPhi(MaxNPhi), - mNR(MAXGLOBALPADROW), - mVoxelSizeZ(AliTPCPoissonSolver::fgkTPCZ0 / (MaxNZ - 1)), - mDriftTimeVoxel(IonDriftTime / (MaxNZ - 1)), - mVoxelSizePhi(TWOPI / MaxNPhi), - mVoxelSizeR((AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (MAXGLOBALPADROW - 1)), - mCoordZ(MaxNZ), - mCoordPhi(MaxNPhi), - mCoordR(MAXGLOBALPADROW), - mInterpolationOrder(2), - mUseInitialSCDensity(false), - mInitLookUpTables(false), - mMemoryAllocated(false), - mTimeInit(-1), - mSCDistortionType(SpaceCharge::SCDistortionType::SCDistortionsRealistic), - mLookUpTableCalculator(MAXGLOBALPADROW, MaxNZ, MaxNPhi, 2, 3, 0), - mSpaceChargeDensityA(MaxNPhi * MAXGLOBALPADROW * MaxNZ), - mSpaceChargeDensityC(MaxNPhi * MAXGLOBALPADROW * MaxNZ), - mRandomFlat(RandomRing<>::RandomType::Flat) -{ - mLookUpTableCalculator.SetCorrectionType(0); - mLookUpTableCalculator.SetIntegrationStrategy(0); - setVoxelCoordinates(); -} - -SpaceCharge::SpaceCharge(int nRBins, int nPhiBins, int nZSlices) - : mNZ(nZSlices), - mNPhi(nPhiBins), - mNR(nRBins), - mVoxelSizeZ(AliTPCPoissonSolver::fgkTPCZ0 / (nZSlices - 1)), - mDriftTimeVoxel(IonDriftTime / (nZSlices - 1)), - mVoxelSizePhi(TWOPI / nPhiBins), - mVoxelSizeR((AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (nRBins - 1)), - mCoordZ(nZSlices), - mCoordPhi(nPhiBins), - mCoordR(nRBins), - mInterpolationOrder(2), - mUseInitialSCDensity(false), - mInitLookUpTables(false), - mMemoryAllocated(false), - mTimeInit(-1), - mSCDistortionType(SpaceCharge::SCDistortionType::SCDistortionsRealistic), - mLookUpTableCalculator(nRBins, nZSlices, nPhiBins, 2, 3, 0), - mSpaceChargeDensityA(nPhiBins * nRBins * nZSlices), - mSpaceChargeDensityC(nPhiBins * nRBins * nZSlices), - mRandomFlat(RandomRing<>::RandomType::Flat) -{ - mLookUpTableCalculator.SetCorrectionType(0); - mLookUpTableCalculator.SetIntegrationStrategy(0); - setVoxelCoordinates(); -} - -SpaceCharge::SpaceCharge(int nRBins, int nPhiBins, int nZSlices, int interpolationOrder) - : mNZ(nZSlices), - mNPhi(nPhiBins), - mNR(nRBins), - mVoxelSizeZ(AliTPCPoissonSolver::fgkTPCZ0 / (nZSlices - 1)), - mDriftTimeVoxel(IonDriftTime / (nZSlices - 1)), - mVoxelSizePhi(TWOPI / nPhiBins), - mVoxelSizeR((AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (nRBins - 1)), - mCoordZ(nZSlices), - mCoordPhi(nPhiBins), - mCoordR(nRBins), - mInterpolationOrder(interpolationOrder), - mUseInitialSCDensity(false), - mInitLookUpTables(false), - mMemoryAllocated(false), - mTimeInit(-1), - mSCDistortionType(SpaceCharge::SCDistortionType::SCDistortionsRealistic), - mLookUpTableCalculator(nRBins, nZSlices, nPhiBins, interpolationOrder, 3, 0), - mSpaceChargeDensityA(nPhiBins * nRBins * nZSlices), - mSpaceChargeDensityC(nPhiBins * nRBins * nZSlices), - mRandomFlat(RandomRing<>::RandomType::Flat) -{ - mLookUpTableCalculator.SetCorrectionType(0); - mLookUpTableCalculator.SetIntegrationStrategy(0); - setVoxelCoordinates(); -} - -void SpaceCharge::setVoxelCoordinates() -{ - for (int iz = 0; iz < mNZ; ++iz) { - mCoordZ[iz] = iz * mVoxelSizeZ; - } - for (int iphi = 0; iphi < mNPhi; ++iphi) { - mCoordPhi[iphi] = iphi * mVoxelSizePhi; - } - for (int ir = 0; ir < mNR; ++ir) { - mCoordR[ir] = AliTPCPoissonSolver::fgkIFCRadius + ir * mVoxelSizeR; - } -} - -void SpaceCharge::allocateMemory() -{ - mMatrixIonDriftZA = std::make_unique[]>(mNPhi); - mMatrixIonDriftRPhiA = std::make_unique[]>(mNPhi); - mMatrixIonDriftRA = std::make_unique[]>(mNPhi); - mMatrixIonDriftZC = std::make_unique[]>(mNPhi); - mMatrixIonDriftRPhiC = std::make_unique[]>(mNPhi); - mMatrixIonDriftRC = std::make_unique[]>(mNPhi); - for (int iphi = 0; iphi < mNPhi; ++iphi) { - mMatrixIonDriftZA[iphi] = std::make_unique(mNR, mNZ); - mMatrixIonDriftRPhiA[iphi] = std::make_unique(mNR, mNZ); - mMatrixIonDriftRA[iphi] = std::make_unique(mNR, mNZ); - mMatrixIonDriftZC[iphi] = std::make_unique(mNR, mNZ); - mMatrixIonDriftRPhiC[iphi] = std::make_unique(mNR, mNZ); - mMatrixIonDriftRC[iphi] = std::make_unique(mNR, mNZ); - } - mLookUpIonDriftA = std::make_unique(mNR, (TMatrixD**)mMatrixIonDriftRA.get(), mCoordR.data(), mNPhi, (TMatrixD**)mMatrixIonDriftRPhiA.get(), mCoordPhi.data(), mNZ, (TMatrixD**)mMatrixIonDriftZA.get(), mCoordZ.data(), mInterpolationOrder); - mLookUpIonDriftC = std::make_unique(mNR, (TMatrixD**)mMatrixIonDriftRC.get(), mCoordR.data(), mNPhi, (TMatrixD**)mMatrixIonDriftRPhiC.get(), mCoordPhi.data(), mNZ, (TMatrixD**)mMatrixIonDriftZC.get(), mCoordZ.data(), mInterpolationOrder); - mMemoryAllocated = true; -} - -void SpaceCharge::init() -{ - if (!mInitLookUpTables) { - auto o2field = static_cast(TGeoGlobalMagField::Instance()->GetField()); - const float bzField = o2field->solenoidField(); // magnetic field in kGauss - /// TODO is there a faster way to get the drift velocity - auto& gasParam = ParameterGas::Instance(); - float vDrift = gasParam.DriftV; // drift velocity in cm/us - /// TODO fix hard coded values (ezField, t1, t2): export to Constants.h or get from somewhere? - const float t1 = 1.; - const float t2 = 1.; - /// TODO use this parameterization or fixed value(s) from Magboltz calculations? - const float omegaTau = -10. * bzField * vDrift / std::abs(sEzField); - setOmegaTauT1T2(omegaTau, t1, t2); - if (mUseInitialSCDensity) { - calculateLookupTables(); - } - } -} - -float SpaceCharge::calculateLookupTables() -{ - // Potential, E field and electron distortion and correction lookup tables - TStopwatch timer; - mLookUpTableCalculator.ForceInitSpaceCharge3DPoissonIntegralDz(mNR, mNZ, mNPhi, 300, 1e-8); - float tRealCalc = static_cast(timer.RealTime()); - - // Lookup tables for local ion drift along E field - if (mSCDistortionType == SCDistortionType::SCDistortionsRealistic) { - if (!mMemoryAllocated) { - allocateMemory(); - } - TMatrixD* matrixDriftZ = nullptr; - TMatrixD* matrixDriftRPhi = nullptr; - TMatrixD* matrixDriftR = nullptr; - for (int iside = 0; iside < 2; ++iside) { - const int sign = 1 - iside * 2; - for (int iphi = 0; iphi < mNPhi; ++iphi) { - const float phi = static_cast(mCoordPhi[iphi]); - if (iside == 0) { - matrixDriftZ = mMatrixIonDriftZA[iphi].get(); - matrixDriftRPhi = mMatrixIonDriftRPhiA[iphi].get(); - matrixDriftR = mMatrixIonDriftRA[iphi].get(); - } else { - matrixDriftZ = mMatrixIonDriftZC[iphi].get(); - matrixDriftRPhi = mMatrixIonDriftRPhiC[iphi].get(); - matrixDriftR = mMatrixIonDriftRC[iphi].get(); - } - int roc = iside == 0 ? o2::math_utils::angle2Sector(phi) : o2::math_utils::angle2Sector(phi) + 18; - for (int ir = 0; ir < mNR; ++ir) { - const float radius = static_cast(mCoordR[ir]); - /// TODO: what is the electric field stored in the LUTs at iz=0 and iz=mNZSlices-1 - for (int iz = 0; iz < mNZ; ++iz) { - const float z = static_cast(mCoordZ[iz]); - float x0[3] = {radius, phi, sign * (z + static_cast(mVoxelSizeZ))}; // iphi, ir, iz+1 - float x1[3] = {radius, phi, sign * z}; // iphi, ir, iz - if (iside == 1) { - x0[2] *= -1; - x1[2] *= -1; - } - double eVector0[3] = {0., 0., 0.}; - double eVector1[3] = {0., 0., 0.}; - mLookUpTableCalculator.GetElectricFieldCyl(x0, roc, eVector0); // returns correct sign for Ez - mLookUpTableCalculator.GetElectricFieldCyl(x1, roc, eVector1); // returns correct sign for Ez - - // drift of ions along E field - (*matrixDriftR)(ir, iz) = -1 * sign * mVoxelSizeZ * 0.5 * (eVector0[0] + eVector1[0]) / (sign * sEzField + eVector0[2]); - (*matrixDriftRPhi)(ir, iz) = -1 * sign * mVoxelSizeZ * 0.5 * (eVector0[1] + eVector1[1]) / (sign * sEzField + eVector0[2]); - (*matrixDriftZ)(ir, iz) = -1 * sign * mVoxelSizeZ + DvDEoverv0 * mVoxelSizeZ * 0.5 * (eVector0[2] + eVector1[2]); - } - } - } - if (iside == 0) { - mLookUpIonDriftA->CopyFromMatricesToInterpolator(); - } else { - mLookUpIonDriftC->CopyFromMatricesToInterpolator(); - } - } - - // TODO: Propagate current SC density along E field by one time bin for next update - // propagateSpaceCharge(); - } - - mInitLookUpTables = true; - return tRealCalc; -} - -float SpaceCharge::updateLookupTables(float eventTime) -{ - // TODO: only update after update time interval - // if (mTimeInit < 0.) { - // mTimeInit = eventTime; // set the time of first initialization - // } - // if (std::abs(eventTime - mTimeInit) < mDriftTimeVoxel) { - // return 0.f; // update only after one time bin has passed - // } - // mTimeInit = eventTime; - - std::unique_ptr[]> spaceChargeA = std::make_unique[]>(mNPhi); - std::unique_ptr[]> spaceChargeC = std::make_unique[]>(mNPhi); - for (int iphi = 0; iphi < mNPhi; ++iphi) { - spaceChargeA[iphi] = std::make_unique(mNR, mNZ); - spaceChargeC[iphi] = std::make_unique(mNR, mNZ); - } - for (int iside = 0; iside < 2; ++iside) { - for (int iphi = 0; iphi < mNPhi; ++iphi) { - TMatrixD& chargeDensity = iside == 0 ? *spaceChargeA[iphi] : *spaceChargeC[iphi]; - for (int ir = 0; ir < mNR; ++ir) { - for (int iz = 0; iz < mNZ; ++iz) { - if (iside == 0) { - chargeDensity(ir, iz) = mSpaceChargeDensityA[iphi * mNR * mNZ + ir * mNZ + iz]; - } else { - chargeDensity(ir, iz) = mSpaceChargeDensityC[iphi * mNR * mNZ + ir * mNZ + iz]; - } - } - } - } - } - mLookUpTableCalculator.SetInputSpaceChargeA((TMatrixD**)spaceChargeA.get()); - mLookUpTableCalculator.SetInputSpaceChargeC((TMatrixD**)spaceChargeC.get()); - float tRealCalc = calculateLookupTables(); - return tRealCalc; -} - -void SpaceCharge::setOmegaTauT1T2(float omegaTau, float t1, float t2) -{ - mLookUpTableCalculator.SetOmegaTauT1T2(omegaTau, t1, t2); -} - -void SpaceCharge::setInitialSpaceChargeDensity(const TH3* hisSCDensity) -{ - std::unique_ptr[]> spaceChargeA = std::make_unique[]>(mNPhi); - std::unique_ptr[]> spaceChargeC = std::make_unique[]>(mNPhi); - for (int iphi = 0; iphi < mNPhi; ++iphi) { - spaceChargeA[iphi] = std::make_unique(mNR, mNZ); - spaceChargeC[iphi] = std::make_unique(mNR, mNZ); - } - mLookUpTableCalculator.GetChargeDensity((TMatrixD**)spaceChargeA.get(), (TMatrixD**)spaceChargeC.get(), hisSCDensity, mNR, mNZ, mNPhi); - for (int iside = 0; iside < 2; ++iside) { - for (int iphi = 0; iphi < mNPhi; ++iphi) { - TMatrixD& chargeDensity = iside == 0 ? *spaceChargeA[iphi] : *spaceChargeC[iphi]; - for (int ir = 0; ir < mNR; ++ir) { - for (int iz = 0; iz < mNZ; ++iz) { - if (iside == 0) { - mSpaceChargeDensityA[iphi * mNR * mNZ + ir * mNZ + iz] = chargeDensity(ir, iz); - } else { - mSpaceChargeDensityC[iphi * mNR * mNZ + ir * mNZ + iz] = chargeDensity(ir, iz); - } - } - } - } - } - mLookUpTableCalculator.SetInputSpaceChargeA((TMatrixD**)spaceChargeA.get()); - mLookUpTableCalculator.SetInputSpaceChargeC((TMatrixD**)spaceChargeC.get()); - mUseInitialSCDensity = true; -} - -void SpaceCharge::fillPrimaryIons(double r, double phi, double z, int nIons) -{ - Side side = z > 0 ? Side::A : Side::C; - double dr = 0.; - double drphi = 0.; - double dz = 0.; - getIonDrift(side, r, phi, z, dr, drphi, dz); - double rdist = r + dr; - double phidist = phi + drphi / r; - double zdist = z + dz; - const int zBin = TMath::BinarySearch(mNZ, mCoordZ.data(), std::abs(zdist)); - const int phiBin = TMath::BinarySearch(mNPhi, mCoordPhi.data(), phidist); - const int rBin = TMath::BinarySearch(mNR, mCoordR.data(), rdist); - /// TODO: protection against ions ending up outside the volume - if (z > 0 && zdist > 0) { - mSpaceChargeDensityA[phiBin * mNR * mNZ + rBin * mNZ + zBin] += ions2Charge(rBin, nIons); - } else if (z < 0 && zdist < 0) { - mSpaceChargeDensityC[phiBin * mNR * mNZ + rBin * mNZ + zBin] += ions2Charge(rBin, nIons); - } -} - -void SpaceCharge::fillPrimaryCharge(double r, double phi, double z, float charge) -{ - Side side = z > 0 ? Side::A : Side::C; - double dr = 0.; - double drphi = 0.; - double dz = 0.; - getIonDrift(side, r, phi, z, dr, drphi, dz); - double rdist = r + dr; - double phidist = phi + drphi / r; - double zdist = z + dz; - const int zBin = TMath::BinarySearch(mNZ, mCoordZ.data(), std::abs(zdist)); - const int phiBin = TMath::BinarySearch(mNPhi, mCoordPhi.data(), phidist); - const int rBin = TMath::BinarySearch(mNR, mCoordR.data(), rdist); - /// TODO: protection against ions ending up outside the volume - if (z > 0 && zdist > 0) { - mSpaceChargeDensityA[phiBin * mNR * mNZ + rBin * mNZ + zBin] += charge; - } else if (z < 0 && zdist < 0) { - mSpaceChargeDensityC[phiBin * mNR * mNZ + rBin * mNZ + zBin] += charge; - } -} - -void SpaceCharge::fillIBFIons(double r, double phi, Side side, int nIons) -{ - const int phiBin = TMath::BinarySearch(mNPhi, mCoordPhi.data(), phi); - const int rBin = TMath::BinarySearch(mNR, mCoordR.data(), r); - const int zBin = mNZ - 1; - /// TODO: distribution of amplification ions instead of placing all of them in one point - /// TODO: protection against ions ending up outside the volume - if (side == Side::A) { - mSpaceChargeDensityA[phiBin * mNR * mNZ + rBin * mNZ + zBin] += ions2Charge(rBin, nIons); - } else { - mSpaceChargeDensityC[phiBin * mNR * mNZ + rBin * mNZ + zBin] += ions2Charge(rBin, nIons); - } -} - -void SpaceCharge::fillIBFCharge(double r, double phi, Side side, float charge) -{ - const int phiBin = TMath::BinarySearch(mNPhi, mCoordPhi.data(), phi); - const int rBin = TMath::BinarySearch(mNR, mCoordR.data(), r); - const int zBin = mNZ - 1; - /// TODO: distribution of amplification ions instead of placing all of them in one point - /// TODO: protection against ions ending up outside the volume - if (side == Side::A) { - mSpaceChargeDensityA[phiBin * mNR * mNZ + rBin * mNZ + zBin] += charge; - } else { - mSpaceChargeDensityC[phiBin * mNR * mNZ + rBin * mNZ + zBin] += charge; - } -} - -void SpaceCharge::propagateSpaceCharge() -{ - // - // Continuity equation, assuming that the charge is conserved: - // delta(rho_0) / delta(t) = - div(flux) = - div(rho_0 * u) = - ( rho_0 * div(u) + u * grad(rho_0) ) - // u: velocity vector of space-charge density - // rho_0: space-charge density at time t0 - // - // - // Calculate the change of the space-charge density rho_0 at time t0 after time delta(t) = mVoxelSizeZ / v_driftIon: - // delta(rho_0) = - ( rho_0 * div(u) + u * grad(rho_0) ) * delta(t) - // = - ( rho_0 * div(d) + d * grad(rho_0) ) - // d: drift vector (dr, dphi, mVoxelSizeZ + dz) - // - // div(d) = 1/r del(r*d_r)/del(r) + 1/r del(d_phi)/del(phi) + del(d_z)/del(z) - // = d_r/r + del(d_r)/del(r) + 1/r del(d_phi)/del(phi) + del(d_z)/del(z) - // - // grad(rho_0) = del(rho_0)/del(r) * e_r + 1/r * del(rho_0)/del(phi) * e_phi + del(rho_0)/del(z) * e_z - // e_i: unit vectors in i = {r, phi, z} direction - // - - // Finite difference interpolation coefficients - const float coeffCent40 = 1.f / 12.f; - const float coeffCent41 = -2.f / 3.f; - const float coeffCent42 = -1.f * coeffCent41; - const float coeffCent43 = -1.f * coeffCent40; - const float coeffFwd30 = -11.f / 6.f; - const float coeffFwd31 = 3.f; - const float coeffFwd32 = -1.5; - const float coeffFwd33 = 1.f / 3.f; - const float coeffFwd20 = -1.5; - const float coeffFwd21 = 2.f; - const float coeffFwd22 = -0.5; - - std::vector* scDensity = nullptr; - TMatrixD** matrixDriftZ = nullptr; - TMatrixD** matrixDriftRPhi = nullptr; - TMatrixD** matrixDriftR = nullptr; - for (int iside = 0; iside < 2; ++iside) { - const int signZ = iside == 0 ? 1 : -1; - if (iside == 0) { - scDensity = &mSpaceChargeDensityA; - matrixDriftZ = (TMatrixD**)mMatrixIonDriftZA.get(); - matrixDriftRPhi = (TMatrixD**)mMatrixIonDriftRPhiA.get(); - matrixDriftR = (TMatrixD**)mMatrixIonDriftRA.get(); - } else { - scDensity = &mSpaceChargeDensityC; - matrixDriftZ = (TMatrixD**)mMatrixIonDriftZC.get(); - matrixDriftRPhi = (TMatrixD**)mMatrixIonDriftRPhiC.get(); - matrixDriftR = (TMatrixD**)mMatrixIonDriftRC.get(); - } - /// TODO: is there a better way than to create a copy for the new SC density? - std::vector newSCDensity(mNPhi * mNR * mNZ); - for (int iphi0 = 0; iphi0 < mNPhi; ++iphi0) { - - for (int ir0 = 0; ir0 < mNR; ++ir0) { - const float r0 = static_cast(mCoordR[ir0]); - - for (int iz0 = 0; iz0 < mNZ; ++iz0) { - - // rho_0 * div(d) - float ddrdr = 0.f; - if (ir0 > 1 && ir0 < mNR - 2) { - ddrdr = (coeffCent40 * (*matrixDriftR[iphi0])(ir0 - 2, iz0) + coeffCent41 * (*matrixDriftR[iphi0])(ir0 - 1, iz0) + coeffCent42 * (*matrixDriftR[iphi0])(ir0 + 1, iz0) + coeffCent43 * (*matrixDriftR[iphi0])(ir0 + 2, iz0)) / static_cast(mVoxelSizeR); - } else if (ir0 < 2) { - ddrdr = (coeffFwd30 * (*matrixDriftR[iphi0])(ir0, iz0) + coeffFwd31 * (*matrixDriftR[iphi0])(ir0 + 1, iz0) + coeffFwd32 * (*matrixDriftR[iphi0])(ir0 + 2, iz0) + coeffFwd33 * (*matrixDriftR[iphi0])(ir0 + 3, iz0)) / static_cast(mVoxelSizeR); - } else if (ir0 > (mNR - 3)) { - ddrdr = -1 * (coeffFwd30 * (*matrixDriftR[iphi0])(ir0, iz0) + coeffFwd31 * (*matrixDriftR[iphi0])(ir0 - 1, iz0) + coeffFwd32 * (*matrixDriftR[iphi0])(ir0 - 2, iz0) + coeffFwd33 * (*matrixDriftR[iphi0])(ir0 - 3, iz0)) / static_cast(mVoxelSizeR); - } - - const int iphiCent0 = iphi0 - 2 + (mNPhi) * (iphi0 < 2); - const int iphiCent1 = iphi0 - 1 + (mNPhi) * (iphi0 < 1); - const int iphiCent3 = iphi0 + 1 - (mNPhi) * (iphi0 > (mNPhi - 2)); - const int iphiCent4 = iphi0 + 2 - (mNPhi) * (iphi0 > (mNPhi - 3)); - const float ddphidphi = (coeffCent40 * (*matrixDriftRPhi[iphiCent0])(ir0, iz0) + coeffCent41 * (*matrixDriftRPhi[iphiCent1])(ir0, iz0) + coeffCent42 * (*matrixDriftRPhi[iphiCent3])(ir0, iz0) + coeffCent43 * (*matrixDriftRPhi[iphiCent4])(ir0, iz0)) / static_cast(mVoxelSizePhi); - - float ddzdz = 0.f; - if (iz0 > 1 && iz0 < mNZ - 2) { - ddzdz = signZ * (coeffCent40 * (*matrixDriftZ[iphi0])(ir0, iz0 - 2) + coeffCent41 * (*matrixDriftZ[iphi0])(ir0, iz0 - 1) + coeffCent42 * (*matrixDriftZ[iphi0])(ir0, iz0 + 1) + coeffCent43 * (*matrixDriftZ[iphi0])(ir0, iz0 + 2)) / static_cast(mVoxelSizeR); - } else if (iz0 == 1 || iz0 == (mNZ - 2)) { - ddzdz = signZ * (-0.5 * (*matrixDriftZ[iphi0])(ir0, iz0 - 1) + 0.5 * (*matrixDriftZ[iphi0])(ir0, iz0 + 1)) / static_cast(mVoxelSizeR); - } else if (iz0 == 0) { - ddzdz = signZ * (coeffFwd20 * (*matrixDriftZ[iphi0])(ir0, iz0) + coeffFwd21 * (*matrixDriftZ[iphi0])(ir0, iz0 + 1) + coeffFwd22 * (*matrixDriftZ[iphi0])(ir0, iz0 + 2)) / static_cast(mVoxelSizeR); - } else if (iz0 == (mNZ - 1)) { - ddzdz = -1 * signZ * (coeffFwd20 * (*matrixDriftZ[iphi0])(ir0, iz0) + coeffFwd21 * (*matrixDriftZ[iphi0])(ir0, iz0 - 1) + coeffFwd22 * (*matrixDriftZ[iphi0])(ir0, iz0 - 2)) / static_cast(mVoxelSizeR); - } - - const float qdivd = (*scDensity)[iphi0 * mNR * mNZ + ir0 * mNZ + iz0] * (((*matrixDriftR[iphi0])(ir0, iz0) + ddphidphi) / r0 + ddrdr + ddzdz); - - // - d * grad(rho_0) = - d_drift * grad(rho_0(x)) + d_dist * grad(rho_0(x+d_drift)) = (charge0 - charge1) + d_dist * grad(rho_0(x-d_drift)) - if (iz0 < (mNZ - 1)) { - const float dr = (*matrixDriftR[iphi0])(ir0, iz0); - const float drphi = (*matrixDriftRPhi[iphi0])(ir0, iz0); - const float dz = (*matrixDriftZ[iphi0])(ir0, iz0) + mVoxelSizeZ * signZ; - - const int ir1 = dr < 0 ? ir0 - 1 * (ir0 == (mNR - 1)) : ir0 - 1 + 1 * (ir0 == 0); - const int ir2 = dr < 0 ? ir0 + 1 - 1 * (ir0 == (mNR - 1)) : ir0 + 1 * (ir0 == 0); - const int iphi1 = drphi < 0 ? iphi0 : iphi0 - 1 + (mNPhi) * (iphi0 == 0); - const int iphi2 = drphi < 0 ? iphi0 + 1 - (mNPhi) * (iphi0 == (mNPhi - 1)) : iphi0; - const int iz1 = dz < 0 ? iz0 + 1 - 1 * (iside == 0) * (iz0 == (mNZ - 2)) : iz0 + 2 * (iside == 1) - 1 * (iside == 1) * (iz0 == (mNZ - 2)); - const int iz2 = dz < 0 ? iz0 + 2 - 2 * (iside == 1) - 1 * (iside == 0) * (iz0 == (mNZ - 2)) : iz0 + 1 - 1 * (iside == 1) * (iz0 == (mNZ - 2)); - - const float dqdr = ((*scDensity)[iphi0 * mNR * mNZ + ir2 * mNZ + (iz0 + 1)] - (*scDensity)[iphi0 * mNR * mNZ + ir1 * mNZ + (iz0 + 1)]) / static_cast(mVoxelSizeR); - const float dqdphi = ((*scDensity)[iphi2 * mNR * mNZ + ir0 * mNZ + (iz0 + 1)] - (*scDensity)[iphi1 * mNR * mNZ + ir0 * mNZ + (iz0 + 1)]) / static_cast(mVoxelSizePhi); - const float dqdz = ((*scDensity)[iphi0 * mNR * mNZ + ir0 * mNZ + iz2] - (*scDensity)[iphi0 * mNR * mNZ + ir0 * mNZ + iz1]) / static_cast(mVoxelSizeZ); - - const float dgradq = dr * dqdr + drphi / r0 * dqdphi + dz * dqdz; - - newSCDensity[iphi0 * mNR * mNZ + ir0 * mNZ + iz0] = (*scDensity)[iphi0 * mNR * mNZ + ir0 * mNZ + (iz0 + 1)] - (qdivd + dgradq); - } else { - newSCDensity[iphi0 * mNR * mNZ + ir0 * mNZ + iz0] = -qdivd; - } - - if (newSCDensity[iphi0 * mNR * mNZ + ir0 * mNZ + iz0] < 0.f) { - newSCDensity[iphi0 * mNR * mNZ + ir0 * mNZ + iz0] = 0.f; - } - } - } - } - (*scDensity).swap(newSCDensity); - } -} - -void SpaceCharge::propagateIons() -{ - std::vector* scDensity = nullptr; - AliTPCLookUpTable3DInterpolatorD* lookUpIonDrift = nullptr; - for (int iside = 0; iside < 2; ++iside) { - const int signZ = iside == 0 ? 1 : -1; - if (iside == 0) { - scDensity = &mSpaceChargeDensityA; - lookUpIonDrift = mLookUpIonDriftA.get(); - } else { - scDensity = &mSpaceChargeDensityC; - lookUpIonDrift = mLookUpIonDriftC.get(); - } - std::vector newSCDensity(mNPhi * mNR * mNZ); - - for (int iphi0 = 0; iphi0 < mNPhi; ++iphi0) { - const double phi0 = static_cast(mCoordPhi[iphi0]); - - for (int ir0 = 0; ir0 < mNR; ++ir0) { - const double r0 = static_cast(mCoordR[ir0]); - const double r1 = r0 + mVoxelSizeR; - - for (int iz0 = 0; iz0 < mNZ; ++iz0) { - const double z0 = mCoordZ[iz0] * signZ; - - const float ionDensity = (*scDensity)[iphi0 * mNR * mNZ + ir0 * mNZ + iz0] * AliTPCPoissonSolver::fgke0 / TMath::Qe(); // #ions / cm^3 - const float nIons = std::round(ionDensity * mVoxelSizeZ * 0.5 * mVoxelSizePhi * (r1 * r1 - r0 * r0)); // absolute #ions in the voxel - - for (int iion = 0; iion < nIons; ++iion) { - double phiIon = phi0 + mRandomFlat.getNextValue() * mVoxelSizePhi; - double rIon = r0 + mRandomFlat.getNextValue() * mVoxelSizeR; - double zIon = z0 + mRandomFlat.getNextValue() * mVoxelSizeZ * signZ; - - double drphi = 0.f; - double dr = 0.f; - double dz = 0.f; - lookUpIonDrift->GetValue(rIon, phiIon, std::abs(zIon), dr, drphi, dz); - float phiIonF = static_cast(phiIon + (drphi / rIon)); - o2::math_utils::bringTo02PiGen(phiIonF); - rIon += dr; - zIon += dz; - - // continue if ion is outside the TPC boundaries in r or z - if (rIon > (mCoordR[mNR - 1] + mVoxelSizeR) || rIon < mCoordR[0]) { - continue; - } - if ((zIon * signZ) > (mCoordZ[mNZ - 1] + mVoxelSizeZ) || (zIon * signZ) < mCoordZ[0]) { - continue; - } - - const int iphiDrift = TMath::BinarySearch(mNPhi, mCoordPhi.data(), static_cast(phiIonF)); - const int irDrift = TMath::BinarySearch(mNR, mCoordR.data(), rIon); - const int izDrift = TMath::BinarySearch(mNZ, mCoordZ.data(), std::abs(zIon)); - newSCDensity[iphiDrift * mNR * mNZ + irDrift * mNZ + izDrift] += ions2Charge(irDrift, 1); - } - } - } - } - (*scDensity).swap(newSCDensity); - } -} - -void SpaceCharge::getIonDrift(Side side, double r, double phi, double z, double& dr, double& drphi, double& dz) const -{ - if (!mInitLookUpTables) { - return; - } - if (side == Side::A) { - mLookUpIonDriftA->GetValue(r, phi, z, dr, drphi, dz); - } else if (side == Side::C) { - mLookUpIonDriftC->GetValue(r, phi, -1 * z, dr, drphi, dz); - } else { - LOG(INFO) << "TPC side undefined! Cannot calculate local ion drift correction..."; - } -} - -void SpaceCharge::correctElectron(GlobalPosition3D& point) -{ - if (!mInitLookUpTables) { - return; - } - const float x[3] = {point.X(), point.Y(), point.Z()}; - float dx[3] = {0.f, 0.f, 0.f}; - float phi = point.phi(); - o2::math_utils::bringTo02PiGen(phi); - int roc = o2::math_utils::angle2Sector(phi); - /// FIXME: which side when z==0? - if (x[2] < 0) { - roc += 18; - } - mLookUpTableCalculator.GetCorrection(x, roc, dx); - point.SetXYZ(x[0] + dx[0], x[1] + dx[1], x[2] + dx[2]); -} - -void SpaceCharge::distortElectron(GlobalPosition3D& point) const -{ - if (!mInitLookUpTables) { - return; - } - const float x[3] = {point.X(), point.Y(), point.Z()}; - float dx[3] = {0.f, 0.f, 0.f}; - float phi = point.phi(); - o2::math_utils::bringTo02PiGen(phi); - int roc = o2::math_utils::angle2Sector(phi); - /// FIXME: which side when z==0? - if (x[2] < 0) { - roc += 18; - } - mLookUpTableCalculator.GetDistortion(x, roc, dx); - point.SetXYZ(x[0] + dx[0], x[1] + dx[1], x[2] + dx[2]); -} - -double SpaceCharge::getChargeDensity(Side side, const GlobalPosition3D& point) const -{ - Float_t x[3] = {point.rho(), point.phi(), point.z()}; - o2::math_utils::bringTo02PiGen(x[1]); - const int roc = side == Side::A ? o2::math_utils::angle2Sector(x[1]) : o2::math_utils::angle2Sector(x[1]) + 18; - return mLookUpTableCalculator.GetChargeCylAC(x, roc); -} - -float SpaceCharge::getChargeDensity(Side side, int ir, int iphi, int iz) const -{ - if (side == Side::A) { - return mSpaceChargeDensityA[iphi * mNR * mNZ + ir * mNZ + iz]; - } else if (side == Side::C) { - return mSpaceChargeDensityC[iphi * mNR * mNZ + ir * mNZ + iz]; - } else { - return -1.f; - } -} - -void SpaceCharge::setUseIrregularLUTs(int useIrrLUTs) -{ - mLookUpTableCalculator.SetCorrectionType(useIrrLUTs); -} - -void SpaceCharge::setUseFastDistIntegration(int useFastInt) -{ - mLookUpTableCalculator.SetIntegrationStrategy(useFastInt); -} - -void SpaceCharge::setDistortionLookupTables(TMatrixD** matrixIntDistDrA, TMatrixD** matrixIntDistDrphiA, TMatrixD** matrixIntDistDzA, TMatrixD** matrixIntDistDrC, TMatrixD** matrixIntDistDrphiC, TMatrixD** matrixIntDistDzC) -{ - mLookUpTableCalculator.SetDistortionLookupTables(matrixIntDistDrA, matrixIntDistDrphiA, matrixIntDistDzA, matrixIntDistDrC, matrixIntDistDrphiC, matrixIntDistDzC); - mInitLookUpTables = true; -} - -float SpaceCharge::ions2Charge(int rBin, int nIons) -{ - float rInner = mCoordR[rBin]; - float rOuter = mCoordR[rBin] + mVoxelSizeR; - return nIons * TMath::Qe() / (mVoxelSizeZ * 0.5 * mVoxelSizePhi * (rOuter * rOuter - rInner * rInner)) / AliTPCPoissonSolver::fgke0; -} diff --git a/Detectors/TPC/simulation/src/TPCSimulationLinkDef.h b/Detectors/TPC/simulation/src/TPCSimulationLinkDef.h index 48edbf58b0277..8a727c250f44d 100644 --- a/Detectors/TPC/simulation/src/TPCSimulationLinkDef.h +++ b/Detectors/TPC/simulation/src/TPCSimulationLinkDef.h @@ -32,8 +32,6 @@ #pragma link C++ class std::vector < o2::tpc::ElementalHit> + ; #pragma link C++ class o2::tpc::HitGroup + ; #pragma link C++ class o2::tpc::SAMPAProcessing + ; -#pragma link C++ class o2::tpc::SpaceCharge + ; -#pragma link C++ enum o2::tpc::SpaceCharge::SCDistortionType + ; #pragma link C++ class std::vector < o2::tpc::HitGroup> + ; diff --git a/Detectors/TPC/spacecharge/CMakeLists.txt b/Detectors/TPC/spacecharge/CMakeLists.txt new file mode 100644 index 0000000000000..a1eb18a71242d --- /dev/null +++ b/Detectors/TPC/spacecharge/CMakeLists.txt @@ -0,0 +1,50 @@ +# Copyright CERN and copyright holders of ALICE O2. This software is distributed +# under the terms of the GNU General Public License v3 (GPL Version 3), copied +# verbatim in the file "COPYING". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization or +# submit itself to any jurisdiction. + +o2_add_library(TPCSpaceCharge + TARGETVARNAME targetName + SOURCES src/SpaceCharge.cxx + src/PoissonSolver.cxx + PUBLIC_LINK_LIBRARIES O2::TPCBase + Vc::Vc + ROOT::Core) + +o2_target_root_dictionary(TPCSpaceCharge + HEADERS include/TPCSpaceCharge/PoissonSolver.h + include/TPCSpaceCharge/SpaceCharge.h + include/TPCSpaceCharge/RegularGrid3D.h + include/TPCSpaceCharge/DataContainer3D.h + include/TPCSpaceCharge/PoissonSolverHelpers.h + include/TPCSpaceCharge/SpaceChargeHelpers.h + include/TPCSpaceCharge/TriCubic.h + include/TPCSpaceCharge/Vector.h + include/TPCSpaceCharge/Vector3D.h + LINKDEF src/TPCSpacechargeLinkDef.h) + +o2_add_test_root_macro(macro/calculateDistortionsCorrections.C + PUBLIC_LINK_LIBRARIES O2::TPCSpaceCharge + LABELS tpc COMPILE_ONLY) + +o2_add_test_root_macro(macro/createResidualDistortionObject.C + PUBLIC_LINK_LIBRARIES O2::TPCSpaceCharge + O2::CommonUtils + LABELS tpc) + +o2_add_test(PoissonSolver + COMPONENT_NAME spacecharge + PUBLIC_LINK_LIBRARIES O2::TPCSpaceCharge + SOURCES test/testO2TPCPoissonSolver.cxx + ENVIRONMENT O2_ROOT=${CMAKE_BINARY_DIR}/stage + LABELS tpc) + +if (OpenMP_CXX_FOUND) + target_compile_definitions(${targetName} PRIVATE WITH_OPENMP) + target_link_libraries(${targetName} PRIVATE OpenMP::OpenMP_CXX) +endif() diff --git a/Detectors/TPC/spacecharge/include/TPCSpaceCharge/DataContainer3D.h b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/DataContainer3D.h new file mode 100644 index 0000000000000..a641a78598336 --- /dev/null +++ b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/DataContainer3D.h @@ -0,0 +1,204 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file DataContainer3D.h +/// \brief This class provides a simple method to store values on a large 3-Dim grid with ROOT io functionality +/// +/// \author Matthias Kleiner +/// \date Aug 21, 2020 + +#ifndef ALICEO2_TPC_DATACONTAINER3D_H_ +#define ALICEO2_TPC_DATACONTAINER3D_H_ + +#include +#include "TFile.h" +#include "Rtypes.h" +#include "Framework/Logger.h" +#include + +namespace o2 +{ +namespace tpc +{ + +/// \class DataContainer3D +/// The DataContainer3D class represents a simple method to store values on a large 3-Dim grid with ROOT io functionality. + +/// \tparam DataT the type of data which is used during the calculations +/// \tparam Nx number of values in x direction +/// \tparam Ny number of values in y direction +/// \tparam Nz number of values in z direction +template +struct DataContainer3D { + + ///< default constructor + DataContainer3D() : mData(FN){}; + + /// operator to directly access the values + const DataT& operator[](size_t i) const { return mData[i]; } + DataT& operator[](size_t i) { return mData[i]; } + + const auto& getData() const { return mData; } + auto& getData() { return mData; } + + /// \param ix index in x dimension + /// \param iy index in y dimension + /// \param iz index in z dimension + /// \return returns the stored value + const DataT& operator()(size_t ix, size_t iy, size_t iz) const + { + const size_t ind = getDataIndex(ix, iy, iz); + return mData[ind]; + } + + /// \param ix index in x dimension + /// \param iy index in y dimension + /// \param iz index in z dimension + /// \return returns the stored value + DataT& operator()(size_t ix, size_t iy, size_t iz) + { + const size_t ind = getDataIndex(ix, iy, iz); + return mData[ind]; + } + + /// \param ix index in x dimension + /// \param iy index in y dimension + /// \param iz index in z dimension + /// \return returns the index to the data + static constexpr size_t getDataIndex(const size_t ix, const size_t iy, const size_t iz) + { + const size_t index = ix + Nx * (iy + iz * Ny); + return index; + } + + /// \return returns the number of values stored + static constexpr size_t getNDataPoints() { return FN; } + + /// \return returns the number of x vertices + static constexpr size_t getNX() { return Nx; } + + /// \return returns the number of y vertices + static constexpr size_t getNY() { return Ny; } + + /// \return returns the number of z vertices + static constexpr size_t getNZ() { return Nz; } + + /// write this object to a file + /// \param outf object is written to this file + /// \param name object is saved with this name + int writeToFile(TFile& outf, const char* name = "data") const; + + /// set values from file + bool initFromFile(TFile& inpf, const char* name = "data"); + + /// get pointer to object from file + inline static DataContainer3D* loadFromFile(TFile& inpf, const char* name = "data"); + + /// print the matrix + void print() const; + + private: + static constexpr size_t FN{Nx * Ny * Nz}; ///< number of values stored in the container + std::vector mData; ///< storage for the data + + ClassDefNV(DataContainer3D, 1) +}; + +/// +/// ======================================================================================================== +/// Inline implementations of some methods +/// ======================================================================================================== +/// + +template +int DataContainer3D::writeToFile(TFile& outf, const char* name) const +{ + if (outf.IsZombie()) { + LOGP(ERROR, "Failed to write to file: {}", outf.GetName()); + return -1; + } + outf.WriteObjectAny(this, DataContainer3D::Class(), name); + return 0; +} + +/// set values from file +template +bool DataContainer3D::initFromFile(TFile& inpf, const char* name) +{ + if (inpf.IsZombie()) { + LOGP(ERROR, "Failed to read from file: {}", inpf.GetName()); + return false; + } + DataContainer3D* dataCont{nullptr}; + + dataCont = reinterpret_cast*>(inpf.GetObjectChecked(name, DataContainer3D::Class())); + if (!dataCont) { + LOGP(ERROR, "Failed to load {} from {}", name, inpf.GetName()); + return false; + } + mData = dataCont->mData; + return true; +} + +template +DataContainer3D* DataContainer3D::loadFromFile(TFile& inpf, const char* name) +{ + if (inpf.IsZombie()) { + LOGP(ERROR, "Failed to read from file {}", inpf.GetName()); + return nullptr; + } + DataContainer3D* dataCont{nullptr}; + + dataCont = reinterpret_cast*>(inpf.GetObjectChecked(name, DataContainer3D::Class())); + if (!dataCont) { + LOGP(ERROR, "Failed to load {} from {}", name, inpf.GetName()); + return nullptr; + } + return dataCont; +} + +template +void DataContainer3D::print() const +{ + std::stringstream stream; + stream.precision(3); + auto&& w = std::setw(9); + stream << std::endl; + + for (unsigned int iz = 0; iz < Nz; ++iz) { + stream << "z layer: " << iz << "\n"; + // print top x row + stream << "⎡" << w << (*this)(0, 0, iz); + for (unsigned int ix = 1; ix < Nx; ++ix) { + stream << ", " << w << (*this)(ix, 0, iz); + } + stream << " ⎤ \n"; + + for (unsigned int iy = 1; iy < Ny - 1; ++iy) { + stream << "⎢" << w << (*this)(0, iy, iz); + for (unsigned int ix = 1; ix < Nx; ++ix) { + stream << ", " << w << (*this)(ix, iy, iz); + } + stream << " ⎥ \n"; + } + + stream << "⎣" << w << (*this)(0, Ny - 1, iz); + for (unsigned int ix = 1; ix < Nx; ++ix) { + stream << ", " << w << (*this)(ix, Ny - 1, iz); + } + stream << " ⎦ \n \n"; + } + LOGP(info, "{} \n \n", stream.str()); +} + +} // namespace tpc +} // namespace o2 + +#endif diff --git a/Detectors/TPC/spacecharge/include/TPCSpaceCharge/PoissonSolver.h b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/PoissonSolver.h new file mode 100644 index 0000000000000..c708ea88c1fe2 --- /dev/null +++ b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/PoissonSolver.h @@ -0,0 +1,488 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file PoissonSolver.h +/// \brief This class provides implementation of Poisson equation +/// solver by MultiGrid Method +/// Original version of this class can be found in AliTPCPoissonSolver.h +/// +/// +/// \author Matthias Kleiner +/// \date Aug 21, 2020 + +#ifndef ALICEO2_TPC_POISSONSOLVER_H_ +#define ALICEO2_TPC_POISSONSOLVER_H_ + +#include "TPCSpaceCharge/DataContainer3D.h" +#include "TPCSpaceCharge/PoissonSolverHelpers.h" +#include "TPCSpaceCharge/RegularGrid3D.h" +#include "TPCSpaceCharge/Vector3D.h" +#include "CommonConstants/MathConstants.h" + +namespace o2 +{ +namespace tpc +{ + +/// \class PoissonSolver +/// The PoissonSolver class represents methods to solve the poisson equation. +/// Original version with more methods can be found in AliTPCPoissonSolver. +/// Following methods are implemented: poissonSolver3D, poissonSolver3D2D, poissonSolver2D + +/// \tparam DataT the type of data which is used during the calculations +/// \tparam Nr number of vertices in r direction (2^N + 1) +/// \tparam Nz number of vertices in z direction (2^M + 1) +/// \tparam Nphi number of vertices in phi direction +template +class PoissonSolver +{ + public: + using RegularGrid = RegularGrid3D; + using DataContainer = DataContainer3D; + using Vector = Vector3D; + + /// default constructor + PoissonSolver(const RegularGrid& gridProperties) : mGrid3D{gridProperties} {}; + + /// Provides poisson solver in Cylindrical 3D (TPC geometry) + /// + /// Strategy based on parameter settings (mMgParameters)provided + /// * Cascaded multi grid with S.O.R + /// * Geometric MultiGrid + /// * Cycles: V, W, Full + /// * Relaxation: Jacobi, Weighted-Jacobi, Gauss-Seidel + /// * Grid transfer operators: Full, Half + /// * Spectral Methods (TODO) + /// + /// \param matricesV potential in 3D + /// \param matricesCharge charge density in 3D (side effect) + /// \param symmetry symmetry or not + /// + /// \pre Charge density distribution in **matricesCharge** is known and boundary values for **matricesV** are set + /// \post Numerical solution for potential distribution is calculated and stored in each rod at **matricesV** + void poissonSolver3D(DataContainer& matricesV, const DataContainer& matricesCharge, const int symmetry); + + /// Provides poisson solver in 2D + /// + /// Based on the strategy (multi grid) + /// + /// \param matricesV potential in matrix + /// \param matricesCharge charge density in matrix (side effect + void poissonSolver2D(DataContainer& matricesV, const DataContainer& matricesCharge); + + DataT getSpacingZ() const { return mGrid3D.getSpacingX(); } + DataT getSpacingR() const { return mGrid3D.getSpacingY(); } + DataT getSpacingPhi() const { return mGrid3D.getSpacingZ(); } + + static void setConvergenceError(const DataT error) { sConvergenceError = error; } + + static DataT getConvergenceError() { return sConvergenceError; } + + /// get the number of threads used for some of the calculations + static int getNThreads() { return sNThreads; } + + /// set the number of threads used for some of the calculations + static void setNThreads(int nThreads) { sNThreads = nThreads; } + + private: + const RegularGrid& mGrid3D{}; ///< grid properties + inline static DataT sConvergenceError{1e-6}; ///< Error tolerated + static constexpr DataT INVTWOPI = 1. / o2::constants::math::TwoPI; ///< inverse of 2*pi + inline static int sNThreads{4}; ///< number of threads which are used during some of the calculations (increasing this number has no big impact) + + /// Relative error calculation: comparison with exact solution + /// + /// \param matricesCurrentV current potential (numerical solution) + /// \param prevArrayV content from matricesCurrentV from previous iteration + DataT getConvergenceError(const Vector& matricesCurrentV, Vector& prevArrayV) const; + + /// 3D - Solve Poisson's Equation in 3D by MultiGrid with constant phi slices + /// + /// NOTE: In order for this algorithm to work, the number of Nr and Nz must be a power of 2 plus one. + /// The number of Nr and Z Column can be different. + /// + /// R Row == 2**M + 1 + /// Z Column == 2**N + 1 + /// Phi Slice == Arbitrary but greater than 3 + /// + /// Solving: \f$ \nabla^{2}V(r,\phi,z) = - f(r,\phi,z) \f$ + /// + /// Algorithm for MultiGrid Full Cycle (FMG) + /// - Relax on the coarsest grid + /// - Do from coarsest to finest + /// - Interpolate potential from coarse -> fine + /// - Do V-Cycle to the current coarse level to the coarsest + /// - Stop if converged + /// + /// DeltaPhi in Radians + /// \param matricesV potential in 3D matrix \f$ V(r,\phi,z) \f$ + /// \param matricesCharge charge density in 3D matrix (side effect) \f$ - f(r,\phi,z) \f$ + /// \param symmetry symmetry (TODO for symmetry = 1) + // + /// SYMMETRY = 0 if no phi symmetries, and no phi boundary condition + /// = 1 if we have reflection symmetry at the boundaries (eg. sector symmetry or half sector symmetries). + void poissonMultiGrid3D2D(DataContainer& matricesV, const DataContainer& matricesCharge, const int symmetry); + + /// 3D - Solve Poisson's Equation in 3D in all direction by MultiGrid + /// + /// NOTE: In order for this algorithm to work, the number of nRRow and nZColumn must be a power of 2 plus one. + /// The number of nRRow and Z Column can be different. + /// + /// R Row == 2**M + 1 + /// Z Column == 2**N + 1 + /// Phi Slices == Arbitrary but greater than 3 + /// + /// Solving: \f$ \nabla^{2}V(r,\phi,z) = - f(r,\phi,z) \f$ + /// + /// Algorithm for MultiGrid Full Cycle (FMG) + /// - Relax on the coarsest grid + /// - Do from coarsest to finest + /// - Interpolate potential from coarse -> fine + /// - Do V-Cycle to the current coarse level to the coarsest + /// - Stop if converged + /// + /// \param matricesV potential in 3D matrix + /// \param matricesCharge charge density in 3D matrix (side effect) + /// \param symmetry symmetry or not: symmetry = 0 if no phi symmetries, and no phi boundary condition. + /// symmetry = 1 if we have reflection symmetry at the boundaries (eg. sector symmetry or half sector symmetries). + void poissonMultiGrid3D(DataContainer& matricesV, const DataContainer& matricesCharge, const int symmetry); + + /// Solve Poisson's Equation by MultiGrid Technique in 2D (assuming cylindrical symmetry) + /// + /// NOTE: In order for this algorithm to work, the number of nRRow and nZColumn must be a power of 2 plus one. + /// So nRRow == 2**M + 1 and nZColumn == 2**N + 1. The number of nRRow and nZColumn can be different. + /// + /// \param matricesV potential in matrix + /// \param matricesCharge charge density in matrix (side effect + /// \param iPhi phi vertex + void poissonMultiGrid2D(DataContainer& matricesV, const DataContainer& matricesCharge, const int iPhi = 0); + + /// Restrict2D + /// + /// Grid transfer operator, restrict from fine -> coarse grid + /// provide full-half weighting + /// + /// \[ \frac{1}{16}\left( \begin{array}{ccc} + /// 1 & 2 & 1 \\ + /// 2 & 4 & 2 \\ + /// 1 & 2 & 1 \end{array} \right) \] + /// + /// \param matricesCurrentCharge coarse grid (2h) + /// \param residue fine grid (h) + /// \param tnRRow number of vertices in r direction of TPC + /// \param tnZColumn number of vertices in z direction of TPC + /// \param iphi phi vertex + void restrict2D(Vector& matricesCurrentCharge, const Vector& residue, const int tnRRow, const int tnZColumn, const int iphi) const; + + /// Restriction in 3D + /// + /// Restriction is a map from fine grid (h) to coarse grid (2h) + /// + /// In case of 3D + /// Full weighting: + /// \f[ (R u)_{i,j,k} = \frac{1}{2} u_{2i,2j,2k} + \frac{1}{4} S_{1} + \frac{1}{8} S_{2} + \frac{1}{16} S_{3}\f] + /// + /// + /// Restriction in all direction r-phi-z + /// restriction in phi only if oldPhi == 2*newPhi + /// \param matricesCurrentCharge coarser grid 2h + /// \param residue fine grid h + /// \param tnRRow number of grid in Nr (in r-direction) for coarser grid should be 2^N + 1, finer grid in 2^{N+1} + 1 + /// \param tnZColumn number of grid in Nz (in z-direction) for coarser grid should be 2^M + 1, finer grid in 2^{M+1} + 1 + /// \param newPhiSlice number of Nphi (in phi-direction) for coarser grid + /// \param oldPhiSlice number of Nphi (in phi-direction) for finer grid + void restrict3D(Vector& matricesCurrentCharge, const Vector& residue, const int tnRRow, const int tnZColumn, const int newPhiSlice, const int oldPhiSlice) const; + + /// Restrict Boundary in 3D + /// + /// Pass boundary information to coarse grid + /// + /// \param matricesCurrentCharge coarser grid 2h + /// \param residue fine grid h + /// \param tnRRow number of grid in Nr (in r-direction) for coarser grid should be 2^N + 1, finer grid in 2^{N+1} + 1 + /// \param tnZColumn number of grid in Nz (in z-direction) for coarser grid should be 2^M + 1, finer grid in 2^{M+1} + 1 + /// \param newPhiSlice number of Nphi (in phi-direction) for coarser grid + /// \param oldPhiSlice number of Nphi (in phi-direction) for finer grid + void restrictBoundary3D(Vector& matricesCurrentCharge, const Vector& residue, const int tnRRow, const int tnZColumn, const int newPhiSlice, const int oldPhiSlice) const; + + /// Relaxation operation for multiGrid + /// relaxation used 7 stencil in cylindrical coordinate + /// + /// Using the following equations + /// \f$ U_{i,j,k} = (1 + \frac{1}{r_{i}h_{r}}) U_{i+1,j,k} + (1 - \frac{1}{r_{i}h_{r}}) U_{i+1,j,k} \f$ + /// + /// \param matricesCurrentV potential in 3D (matrices of matrix) + /// \param matricesCurrentCharge charge in 3D + /// \param tnRRow number of grid in in r-direction for coarser grid should be 2^N + 1, finer grid in 2^{N+1} + 1 + /// \param tnZColumn number of grid in in z-direction for coarser grid should be 2^M + 1, finer grid in 2^{M+1} + 1 + /// \param iPhi phi vertex + /// \param symmetry is the cylinder has symmetry + /// \param h2 \f$ h_{r}^{2} \f$ + /// \param tempRatioZ ration between grid size in z-direction and r-direction + /// \param coefficient1 coefficients for \f$ V_{x+1,y,z} \f$ + /// \param coefficient2 coefficients for \f$ V_{x-1,y,z} \f$ + /// \param coefficient3 coefficients for z + /// \param coefficient4 coefficients for f(r,\phi,z) + void relax3D(Vector& matricesCurrentV, const Vector& matricesCurrentCharge, const int tnRRow, const int tnZColumn, const int iPhi, const int symmetry, const DataT h2, const DataT tempRatioZ, + const std::array& coefficient1, const std::array& coefficient2, const std::array& coefficient3, const std::array& coefficient4) const; + + /// Relax2D + /// + /// Relaxation operation for multiGrid + /// relaxation used 5 stencil in cylindrical coordinate + /// + /// Using the following equations + /// \f$ U_{i,j,k} = (1 + \frac{1}{r_{i}h_{r}}) U_{i+1,j,k} + (1 - \frac{1}{r_{i}h_{r}}) U_{i+1,j,k} \f$ + /// + /// \param matricesCurrentV potential in 3D (matrices of matrix) + /// \param matricesCurrentCharge charge in 3D + /// \param tnRRow number of vertices in r direction of TPC + /// \param tnZColumn number of vertices in z direction of TPC + /// \param h2 \f$ h_{r}^{2} \f$ + /// \param tempFourth coefficient for h + /// \param tempRatio ratio between grid size in z-direction and r-direction + /// \param coefficient1 coefficient for \f$ V_{x+1,y,z} \f$ + /// \param coefficient2 coefficient for \f$ V_{x-1,y,z} \f$ + void relax2D(Vector& matricesCurrentV, const Vector& matricesCurrentCharge, const int tnRRow, const int tnZColumn, const DataT h2, const DataT tempFourth, const DataT tempRatio, + std::vector& coefficient1, std::vector& coefficient2); + + /// Interpolation/Prolongation in 2D + /// + /// Interpolation is a map from coarse grid (h) to fine grid (2h) + /// + /// In case of 2D + /// Full weighting: + /// \f[ (R u)_{i,j,k} = \frac{1}{2} u_{2i,2j,2k} + \frac{1}{4} S_{1} + \frac{1}{8} S_{2} + \frac{1}{16} S_{3}\f] + /// + /// + /// Restriction in all direction r-phi-z + /// \param matricesCurrentV finer grid h + /// \param matricesCurrentVC coarse grid 2h + /// \param tnRRow number of grid in r-direction for coarser grid should be 2^N + 1, finer grid in 2^{N+1} + 1 + /// \param tnZColumn number of grid in z-direction for coarser grid should be 2^M + 1, finer grid in 2^{M+1} + 1 + /// \param iphi phi vertex + void interp2D(Vector& matricesCurrentV, const Vector& matricesCurrentVC, const int tnRRow, const int tnZColumn, const int iphi) const; + + /// Interpolation/Prolongation in 3D + /// + /// Interpolation is a map from coarse grid (h) to fine grid (2h) + /// + /// In case of 3D + /// Full weighting: + /// \f[ (R u)_{i,j,k} = \frac{1}{2} u_{2i,2j,2k} + \frac{1}{4} S_{1} + \frac{1}{8} S_{2} + \frac{1}{16} S_{3}\f] + /// + /// + /// Restriction in all direction r-phi-z + /// restriction in phi only if oldPhi == 2*newPhi + /// \param matricesCurrentV finer grid h + /// \param matricesCurrentVC coarse grid 2h + /// \param tnRRow number of vertices in r-direction for coarser grid should be 2^N + 1, finer grid in 2^{N+1} + 1 + /// \param tnZColumn number of vertices in z-direction for coarser grid should be 2^M + 1, finer grid in 2^{M+1} + 1 + /// \param newPhiSlice number of vertices in phi-direction for coarser grid + /// \param oldPhiSlice number of vertices in phi-direction for finer grid + void interp3D(Vector& matricesCurrentV, const Vector& matricesCurrentVC, const int tnRRow, const int tnZColumn, const int newPhiSlice, const int oldPhiSlice) const; + + /// Prolongation with Addition for 3D + /// + /// Interpolation with addition from coarse level (2h) --> fine level (h) + /// + /// Interpolation in all direction r-phi-z + /// Interpolation in phi only if oldPhi == 2*newPhi + /// \param matricesCurrentV fine grid h + /// \param matricesCurrentVC coarse grid 2h + /// \param tnRRow number of vertices in r-direction for coarser grid should be 2^N + 1, finer grid in 2^{N+1} + 1 + /// \param tnZColumn number of vertices in z-direction for coarser grid should be 2^M + 1, finer grid in 2^{M+1} + 1a + /// \param newPhiSlice number of vertices in phi-direction for coarser grid + /// \param oldPhiSlice number of vertices in phi-direction for finer grid + void addInterp3D(Vector& matricesCurrentV, const Vector& matricesCurrentVC, const int tnRRow, const int tnZColumn, const int newPhiSlice, const int oldPhiSlice) const; + + /// Prolongation with Addition for 2D + /// + /// Interpolation with addition from coarse level (2h) --> fine level (h) + /// + /// Interpolation in all direction r-phi-z + /// \param matricesCurrentV fine grid h + /// \param matricesCurrentVC coarse grid 2h + /// \param tnRRow number of vertices in r-direction for coarser grid should be 2^N + 1, finer grid in 2^{N+1} + 1 + /// \param tnZColumn number of vertices in z-direction for coarser grid should be 2^M + 1, finer grid in 2^{M+1} + 1a + /// \param tnPhi phi vertices + void addInterp2D(Vector& matricesCurrentV, const Vector& matricesCurrentVC, const int tnRRow, const int tnZColumn, const int tnPhi) const; + + /// VCycle 3D2D, V Cycle 3D in multiGrid with constant Nphi + /// fine-->coarsest-->fine, propagating the residue to correct initial guess of V + /// + /// Algorithm: + /// + /// NOTE: In order for this algorithm to work, the number of Nr and Nz must be a power of 2 plus one. + /// The number of Nr and Z Column can be different. + /// + /// R Row == 2**M + 1 + /// Z Column == 2**N + 1 + /// Phi Slice == Arbitrary but greater than 3 + /// + /// DeltaPhi in Radians + /// \param gridFrom finest level of grid + /// \param gridTo coarsest level of grid + /// \param nPre number of smoothing before coarsening + /// \param nPost number of smoothing after coarsening + /// \param ratioZ ratio between square of grid r and grid z (OPTION, recalculate) + /// \param ratioPhi ratio between square of grid r and grid phi (OPTION, recalculate) + /// \param tvArrayV vector of V potential in different grids + /// \param tvCharge vector of charge distribution in different grids + /// \param tvResidue vector of residue calculation in different grids + /// \param coefficient1 coefficient for relaxation (r direction) + /// \param coefficient2 coefficient for relaxation (r direction) + /// \param coefficient3 coefficient for relaxation (ratio r/z) + /// \param coefficient4 coefficient for relaxation (ratio for grid_r) + /// \param inverseCoefficient4 coefficient for relaxation (inverse coefficient4) + void vCycle3D2D(const int symmetry, const int gridFrom, const int gridTo, const int nPre, const int nPost, const DataT ratioZ, const DataT ratioPhi, std::vector& tvArrayV, + std::vector& tvCharge, std::vector& tvResidue, std::array& coefficient1, std::array& coefficient2, std::array& coefficient3, + std::array& coefficient4, std::array& inverseCoefficient4) const; + + /// VCycle 3D, V Cycle in multiGrid, fine-->coarsest-->fine, propagating the residue to correct initial guess of V + /// + /// NOTE: In order for this algorithm to work, the number of nRRow and nZColumn must be a power of 2 plus one. + /// The number of nRRow and Z Column can be different. + /// + /// R Row == 2**M + 1 + /// Z Column == 2**N + 1 + /// Phi Slice == Arbitrary but greater than 3 + /// + /// DeltaPhi in Radians + /// + /// \param symmetry symmetry or not + /// \param gridFrom finest level of grid + /// \param gridTo coarsest level of grid + /// \param nPre number of smoothing before coarsening + /// \param nPost number of smoothing after coarsening + /// \param ratioZ ratio between square of grid r and grid z (OPTION, recalculate) + /// \param tvArrayV vector of V potential in different grids + /// \param tvCharge vector of charge distribution in different grids + /// \param tvResidue vector of residue calculation in different grids + /// \param coefficient1 coefficient for relaxation (r direction) + /// \param coefficient2 coefficient for relaxation (r direction) + /// \param coefficient3 coefficient for relaxation (ratio r/z) + /// \param coefficient4 coefficient for relaxation (ratio for grid_r) + /// \param inverseCoefficient4 coefficient for relaxation (inverse coefficient4) + void vCycle3D(const int symmetry, const int gridFrom, const int gridTo, const int nPre, const int nPost, const DataT ratioZ, std::vector& tvArrayV, std::vector& tvCharge, + std::vector& tvResidue, std::array& coefficient1, std::array& coefficient2, std::array& coefficient3, + std::array& coefficient4, std::array& inverseCoefficient4) const; + + /// V-Cycle 2D + /// + /// Implementation non-recursive V-cycle for 2D + /// + /// Algorithms: + /// + /// \param gridFrom finest level of grid + /// \param gridTo coarsest level of grid + /// \param nPre number of smoothing before coarsening + /// \param nPost number of smoothing after coarsening + /// \param gridSizeR grid size in r direction (OPTION, recalculate) + /// \param ratio ratio between square of grid r and grid z (OPTION, recalculate) + /// \param tvArrayV vector of V potential in different grids + /// \param tvCharge vector of charge distribution in different grids + /// \param tvResidue vector of residue calculation in different grids + void vCycle2D(const int gridFrom, const int gridTo, const int nPre, const int nPost, const DataT gridSizeR, const DataT ratio, std::vector& tvArrayV, + std::vector& tvCharge, std::vector& tvResidue); + + /// W-Cycle 2D + /// + /// Implementation non-recursive W-cycle for 2D + /// + /// Algorithms: + /// + /// \param gridFrom finest level of grid + /// \param gridTo coarsest level of grid + /// \param gamma number of iterations at coarsest level + /// \param nPre number of smoothing before coarsening + /// \param nPost number of smoothing after coarsening + /// \param gridSizeR grid size in r direction (OPTION, recalculate) + /// \param ratio ratio between square of grid r and grid z (OPTION, recalculate) + /// \param tvArrayV vector of V potential in different grids + /// \param tvCharge vector of charge distribution in different grids + /// \param tvResidue vector of residue calculation in different grids + void wCycle2D(const int gridFrom, const int gridTo, const int gamma, const int nPre, const int nPost, const DataT gridSizeR, const DataT ratio, + std::vector& tvArrayV, std::vector& tvCharge, std::vector& tvResidue); + + /// Residue3D + /// + /// Compute residue from V(.) where V(.) is numerical potential and f(.). + /// residue used 7 stencil in cylindrical coordinate + /// + /// Using the following equations + /// \f$ U_{i,j,k} = (1 + \frac{1}{r_{i}h_{r}}) U_{i+1,j,k} + (1 - \frac{1}{r_{i}h_{r}}) U_{i+1,j,k} \f$ + /// + /// \param residue residue in 3D (matrices of matrix) + /// \param matricesCurrentV potential in 3D (matrices of matrix) + /// \param matricesCurrentCharge charge in 3D + /// \param tnRRow number of vertices in the r direction of TPC + /// \param tnZColumn number of vertices in z direction of TPC + /// \param tnPhi number of vertices in phi direction of TPC + /// \param symmetry if the cylinder has symmetry + /// \param ih2 \f$ 1/ h_{r}^{2} \f$ + /// \param tempRatioZ ration between grid size in z-direction and r-direction + /// \param coefficient1 coefficient for \f$ V_{x+1,y,z} \f$ + /// \param coefficient2 coefficient for \f$ V_{x-1,y,z} \f$ + /// \param coefficient3 coefficient for z + /// \param inverseCoefficient4 inverse coefficient for f(r,\phi,z) + void residue3D(Vector& residue, const Vector& matricesCurrentV, const Vector& matricesCurrentCharge, const int tnRRow, const int tnZColumn, const int tnPhi, const int symmetry, const DataT ih2, const DataT tempRatioZ, + const std::array& coefficient1, const std::array& coefficient2, const std::array& coefficient3, const std::array& inverseCoefficient4) const; + + /// Residue2D + /// + /// Compute residue from V(.) where V(.) is numerical potential and f(.). + /// residue used 5 stencil in cylindrical coordinate + /// + /// Using the following equations + /// \f$ U_{i,j,k} = (1 + \frac{1}{r_{i}h_{r}}) U_{i+1,j,k} + (1 - \frac{1}{r_{i}h_{r}}) U_{i+1,j,k} \f$ + /// + /// \param residue potential in 2D + /// \param matricesCurrentV potential in 2D + /// \param matricesCurrentCharge charge in 2D + /// \param nRRow number of nRRow in the r direction of TPC + /// \param nZColumn number of nZColumn in z direction of TPC + /// \param ih2 \f$ h_{r}^{2} \f$ + /// \param iTempFourth coefficient for h + /// \param tempRatio ratio between grid size in z-direction and r-direction + /// \param coefficient1 coefficient for \f$ V_{x+1,y,z} \f$ + /// \param coefficient2 coefficient for \f$ V_{x-1,y,z} \f$ + void residue2D(Vector& residue, const Vector& matricesCurrentV, const Vector& matricesCurrentCharge, const int tnRRow, const int tnZColumn, const DataT ih2, const DataT inverseTempFourth, + const DataT tempRatio, std::vector& coefficient1, std::vector& coefficient2); + + /// Boundary transfer restrict from fine -> coarse grid + /// + /// \param matricesCurrentCharge coarse grid (2h) + /// \param residue fine grid (h) + /// \param tnRRow number of vertices in the r direction of TPC + /// \param tnZColumn number of vertices in z direction of TPC + /// \param tnPhi phi vertices + void restrictBoundary2D(Vector& matricesCurrentCharge, const Vector& residue, const int tnRRow, const int tnZColumn, const int tnPhi) const; + + // calculate coefficients + void calcCoefficients(unsigned int from, unsigned int to, const DataT h, const DataT tempRatioZ, const DataT tempRatioPhi, std::array& coefficient1, + std::array& coefficient2, std::array& coefficient3, std::array& coefficient4) const; + + // calculate coefficients for 2D poisson solver + void calcCoefficients2D(unsigned int from, unsigned int to, const DataT h, std::vector& coefficient1, std::vector& coefficient2) const; + + /// Helper function to check if the integer is equal to a power of two + /// \param i the number + /// \return 1 if it is a power of two, else 0 + bool isPowerOfTwo(const int i) const + { + return ((i > 0) && !(i & (i - 1))); + }; +}; + +} // namespace tpc +} // namespace o2 + +#endif diff --git a/Detectors/TPC/spacecharge/include/TPCSpaceCharge/PoissonSolverHelpers.h b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/PoissonSolverHelpers.h new file mode 100644 index 0000000000000..044a5564e519b --- /dev/null +++ b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/PoissonSolverHelpers.h @@ -0,0 +1,88 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file O2TPCPoissonSolverHelpers.h +/// \brief This file provides all the necessary structs which are used in the poisson solver +/// +/// \author Matthias Kleiner +/// \date Aug 21, 2020 + +#ifndef ALICEO2_TPC_POISSONSOLVERHELPERS_H_ +#define ALICEO2_TPC_POISSONSOLVERHELPERS_H_ + +#include "CommonConstants/MathConstants.h" + +namespace o2 +{ +namespace tpc +{ + +///< Enumeration of Cycles Type +enum class CycleType { + VCycle = 0, ///< V Cycle + WCycle = 1, ///< W Cycle (TODO) + FCycle = 2 ///< Full Cycle +}; + +///< Fine -> Coarse Grid transfer operator types +enum class GridTransferType { + Half = 0, ///< Half weighting + Full = 1, ///< Full weighting +}; + +///< Smoothing (Relax) operator types +enum class RelaxType { + Jacobi = 0, ///< Jacobi (5 Stencil 2D, 7 Stencil 3D_ + WeightedJacobi = 1, ///< (TODO) + GaussSeidel = 2 ///< Gauss Seidel 2D (2 Color, 5 Stencil), 3D (7 Stencil) +}; + +struct MGParameters { ///< Parameters choice for MultiGrid algorithm + inline static bool isFull3D = true; ///< TRUE: full coarsening, FALSE: semi coarsening + inline static CycleType cycleType = CycleType::FCycle; ///< cycleType follow CycleType + inline static GridTransferType gtType = GridTransferType::Full; ///< gtType grid transfer type follow GridTransferType + inline static RelaxType relaxType = RelaxType::GaussSeidel; ///< relaxType follow RelaxType + inline static int nPre = 2; ///< number of iteration for pre smoothing + inline static int nPost = 2; ///< number of iteration for post smoothing + 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! +}; + +template +struct TPCParameters { + static constexpr DataT TPCZ0{249.525}; ///< nominal G1T position + static constexpr DataT IFCRADIUS{83.5}; ///< Mean Radius of the Inner Field Cage ( 82.43 min, 83.70 max) (cm) + static constexpr DataT OFCRADIUS{254.5}; ///< Mean Radius of the Outer Field Cage (252.55 min, 256.45 max) (cm) + static constexpr DataT ZOFFSET{0.2}; ///< Offset from CE: calculate all distortions closer to CE as if at this point + static constexpr DataT DVDE{0.0024}; ///< [cm/V] drift velocity dependency on the E field (from Magboltz for NeCO2N2 at standard environment) + static constexpr DataT EM{-1.602176487e-19 / 9.10938215e-31}; ///< charge/mass in [C/kg] + static constexpr DataT E0{8.854187817e-12}; ///< vacuum permittivity [A·s/(V·m)] + inline static DataT cathodev{-103070.0}; ///< Cathode Voltage [V] (for 400 V/cm) + inline static DataT vg1t{-3260}; ///< GEM 1 Top voltage. (setting with reduced ET1,2,4 = 3.5kV/cm) +}; + +template +struct GridProperties { + static constexpr DataT RMIN{TPCParameters::IFCRADIUS}; ///< min radius + static constexpr DataT ZMIN{0}; ///< min z coordinate + static constexpr DataT PHIMIN{0}; ///< min phi coordinate + static constexpr DataT RMAX{TPCParameters::OFCRADIUS}; ///< max radius + static constexpr DataT ZMAX{TPCParameters::TPCZ0}; ///< max z coordinate + static constexpr DataT PHIMAX{static_cast(o2::constants::math::TwoPI)}; ///< max phi coordinate + static constexpr DataT GRIDSPACINGR{(RMAX - RMIN) / (Nr - 1)}; ///< grid spacing in r direction + static constexpr DataT GRIDSPACINGZ{(ZMAX - ZMIN) / (Nz - 1)}; ///< grid spacing in z direction + static constexpr DataT GRIDSPACINGPHI{PHIMAX / Nphi}; ///< grid spacing in phi direction +}; + +} // namespace tpc +} // namespace o2 + +#endif diff --git a/Detectors/TPC/spacecharge/include/TPCSpaceCharge/RegularGrid3D.h b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/RegularGrid3D.h new file mode 100644 index 0000000000000..f9a5948ece0ad --- /dev/null +++ b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/RegularGrid3D.h @@ -0,0 +1,245 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file RegularGrid3D.h +/// \brief Definition of RegularGrid3D class +/// +/// \author Matthias Kleiner + +#ifndef ALICEO2_TPC_REGULARGRID3D_H_ +#define ALICEO2_TPC_REGULARGRID3D_H_ + +#include "TPCSpaceCharge/Vector.h" +#include "Rtypes.h" // for ClassDefNV + +namespace o2 +{ +namespace tpc +{ + +/// \class RegularGrid3D +/// This class implements basic properties of a regular 3D-Grid like the spacing for each dimension and min and max coordinates. + +/// \tparam DataT the type of data which is used during the calculations +/// \tparam Nx number of vertices in x direction +/// \tparam Ny number of vertices in y direction +/// \tparam Nz number of vertices in z direction +template +struct RegularGrid3D { + + public: + RegularGrid3D(const DataT xmin, const DataT ymin, const DataT zmin, const DataT spacingX, const DataT spacingY, const DataT spacingZ) : mMin{{xmin, ymin, zmin}}, mMax{{xmin + (Nx - 1) * spacingX, ymin + (Ny - 1) * spacingY, zmin + (Nz - 1) * spacingZ}}, mSpacing{{spacingX, spacingY, spacingZ}}, mInvSpacing{{static_cast(1 / spacingX), static_cast(1 / spacingY), static_cast(1 / spacingZ)}} + { + initLists(); + } + + /// \param deltaX delta x index + /// \return returns the delta index (where the data is stored) for given deltaX + int getDeltaXDataIndex(const int deltaX) const { return deltaX; } + + /// \param deltaY delta y index + /// \return returns the delta index (where the data is stored) for given deltaY + int getDeltaYDataIndex(const int deltaY) const { return Nx * deltaY; } + + /// \param deltaZ delta z index + /// \return returns the delta index (where the data is stored) for given deltaZ + int getDeltaZDataIndex(const int deltaZ) const { return deltaZ * Ny * Nx; } + + // same as above + /// \param delta delta index + /// \param dim dimension of interest + /// \return returns the delta index (where the data is stored) for given delta and dim + int getDeltaDataIndex(const int delta, const int dim) const; + + // check if the specified index for given dimension lies in the grid + /// \param index query index + /// \return returns if the index lies in the grid + bool isIndexInGrid(const int index, const unsigned int dim) const { return index < 0 ? false : (index > (sNdim[dim] - 1) ? false : true); } + + /// \param dim dimension of interest + /// \return returns the number of vertices for given dimension for the grid + static constexpr size_t getN(unsigned int dim) { return sNdim[dim]; } + static constexpr size_t getNX() { return sNdim[FX]; } + static constexpr size_t getNY() { return sNdim[FY]; } + static constexpr size_t getNZ() { return sNdim[FZ]; } + + static constexpr unsigned int getDim() { return FDIM; } /// \return returns number of dimensions of the grid (3) + static constexpr unsigned int getFX() { return FX; } /// \return returns the index for dimension x (0) + static constexpr unsigned int getFY() { return FY; } /// \return returns the index for dimension y (1) + static constexpr unsigned int getFZ() { return FZ; } /// \return returns the index for dimension z (2) + + const Vector& getGridMin() const { return mMin; } /// \return returns the minimum coordinates of the grid in all dimensions + DataT getGridMinX() const { return mMin[FX]; } /// \return returns the minimum coordinate of the grid in x dimension + DataT getGridMinY() const { return mMin[FY]; } /// \return returns the minimum coordinate of the grid in y dimension + DataT getGridMinZ() const { return mMin[FZ]; } /// \return returns the minimum coordinate of the grid in z dimension + + DataT getGridMaxX() const { return mMax[FX]; } + DataT getGridMaxY() const { return mMax[FY]; } + DataT getGridMaxZ() const { return mMax[FZ]; } + + /// \return returns the inversed spacing of the grid for all dimensions + const Vector& getInvSpacing() const { return mInvSpacing; } + DataT getInvSpacingX() const { return mInvSpacing[FX]; } + DataT getInvSpacingY() const { return mInvSpacing[FY]; } + DataT getInvSpacingZ() const { return mInvSpacing[FZ]; } + + DataT getSpacingX() const { return mSpacing[FX]; } + DataT getSpacingY() const { return mSpacing[FY]; } + DataT getSpacingZ() const { return mSpacing[FZ]; } + + // clamp coordinates to the grid (not circular) + /// \param pos query position which will be clamped + /// \return returns clamped coordinate coordinate + DataT clampToGrid(const DataT pos, const unsigned int dim) const; + + // clamp coordinates to the grid (not circular) + /// \param pos relative query position in grid which will be clamped + /// \return returns clamped coordinate coordinate + DataT clampToGridRel(const DataT pos, const unsigned int dim) const; + + // clamp coordinates to the grid circular + /// \param pos query position which will be clamped + /// \return returns clamped coordinate coordinate + DataT clampToGridCircular(DataT pos, const unsigned int dim) const; + + // clamp coordinates to the grid circular + /// \param pos relative query position in grid which will be clamped + /// \return returns clamped coordinate coordinate + DataT clampToGridCircularRel(DataT pos, const unsigned int dim) const; + + void checkStability(Vector& relPos, const Vector& circular) const; + + /// \param vertexX in x dimension + /// \return returns the x positon for given vertex + DataT getXVertex(const size_t vertexX) const { return mXVertices[vertexX]; } + + /// \param vertexY in y dimension + /// \return returns the y positon for given vertex + DataT getYVertex(const size_t vertexY) const { return mYVertices[vertexY]; } + + /// \param vertexZ in z dimension + /// \return returns the z positon for given vertex + DataT getZVertex(const size_t vertexZ) const { return mZVertices[vertexZ]; } + + const Vector& getMaxIndices() const { return sMaxIndex; } /// get max indices for all dimensions + + DataT getMaxIndexX() const { return sMaxIndex[0]; } /// get max index in x direction + DataT getMaxIndexY() const { return sMaxIndex[1]; } /// get max index in y direction + DataT getMaxIndexZ() const { return sMaxIndex[2]; } /// get max index in z direction + + private: + static constexpr unsigned int FDIM = 3; ///< dimensions of the grid (only 3 supported) + static constexpr unsigned int FX = 0; ///< index for x coordinate + static constexpr unsigned int FY = 1; ///< index for y coordinate + static constexpr unsigned int FZ = 2; ///< index for z coordinate + const Vector mMin{}; ///< min vertices positions of the grid + const Vector mMax{}; ///< max vertices positions of the grid + const Vector mSpacing{}; ///< spacing of the grid + const Vector mInvSpacing{}; ///< inverse spacing of grid + const inline static Vector sMaxIndex{{Nx - 1, Ny - 1, Nz - 1}}; ///< max index which is on the grid in all dimensions + inline static Vector sNdim{{Nx, Ny, Nz}}; ///< number of vertices for each dimension + DataT mXVertices[Nx]{}; ///< positions of vertices in x direction + DataT mYVertices[Ny]{}; ///< positions of vertices in y direction + DataT mZVertices[Nz]{}; ///< positions of vertices in z direction + + void initLists(); + + ClassDefNV(RegularGrid3D, 1) +}; + +/// +/// ======================================================================================================== +/// Inline implementations of some methods +/// ======================================================================================================== +/// + +template +DataT RegularGrid3D::clampToGrid(const DataT pos, const unsigned int dim) const +{ + if (mMin[dim] < mMax[dim]) { + if (pos < mMin[dim]) { + return mMin[dim]; + } else if (pos > mMax[dim]) { + return mMax[dim]; + } + } else { + if (pos > mMin[dim]) { + return mMin[dim]; + } else if (pos < mMax[dim]) { + return mMax[dim]; + } + } + return pos; +} + +template +DataT RegularGrid3D::clampToGridRel(const DataT pos, const unsigned int dim) const +{ + if (pos < 0) { + return 0; + } else if (pos >= sMaxIndex[dim]) { + return sMaxIndex[dim] - 1; // -1 return second last index. otherwise two additional points have to be extrapolated for tricubic interpolation + } + return pos; +} + +template +DataT RegularGrid3D::clampToGridCircular(DataT pos, const unsigned int dim) const +{ + while (pos < mMin[dim]) { + pos += mMax[dim] - mMin[dim] + mSpacing[dim]; + } + while (pos >= mMax[dim] + mSpacing[dim]) { + pos -= mMax[dim] + mSpacing[dim] - mMin[dim]; + } + return pos; +} + +template +DataT RegularGrid3D::clampToGridCircularRel(DataT pos, const unsigned int dim) const +{ + while (pos < 0) { + pos += sNdim[dim]; + } + while (pos > sNdim[dim]) { + pos -= sNdim[dim]; + } + if (pos == sNdim[dim]) { + pos = 0; + } + return pos; +} + +template +void RegularGrid3D::initLists() +{ + for (size_t i = 0; i < Nx; ++i) { + mXVertices[i] = mMin[FX] + i * mSpacing[FX]; + } + for (size_t i = 0; i < Ny; ++i) { + mYVertices[i] = mMin[FY] + i * mSpacing[FY]; + } + for (size_t i = 0; i < Nz; ++i) { + mZVertices[i] = mMin[FZ] + i * mSpacing[FZ]; + } +} + +template +int RegularGrid3D::getDeltaDataIndex(const int delta, const int dim) const +{ + const unsigned int offset[FDIM]{1, Nx, Ny * Nx}; + const int deltaIndex = delta * offset[dim]; + return deltaIndex; +} + +} // namespace tpc +} // namespace o2 + +#endif diff --git a/Detectors/TPC/spacecharge/include/TPCSpaceCharge/SpaceCharge.h b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/SpaceCharge.h new file mode 100644 index 0000000000000..4d4376b758d2b --- /dev/null +++ b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/SpaceCharge.h @@ -0,0 +1,901 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file SpaceCharge.h +/// \brief This class contains the algorithms for calculation the distortions and corrections +/// +/// \author Matthias Kleiner +/// \date Aug 21, 2020 + +#ifndef ALICEO2_TPC_SPACECHARGE_H_ +#define ALICEO2_TPC_SPACECHARGE_H_ + +#include "TPCSpaceCharge/TriCubic.h" +#include "TPCSpaceCharge/PoissonSolver.h" +#include "TPCSpaceCharge/SpaceChargeHelpers.h" +#include "TPCSpaceCharge/RegularGrid3D.h" +#include "TPCSpaceCharge/DataContainer3D.h" + +#include "TPCBase/ParameterGas.h" +#include "Field/MagneticField.h" +#include "TGeoGlobalMagField.h" + +#include "DataFormatsTPC/Defs.h" +#include "Framework/Logger.h" + +// Root includes +#include "TF1.h" /// for numerical intergration only +#include "TH3.h" + +namespace o2 +{ +namespace tpc +{ + +/// \class SpaceCharge +/// this class provides the algorithms for calculating the global distortions and corrections from the space charge density. +/// The calculations can be done by a realistic space charge histogram as an input or by an analytical formula. +/// An example of of the usage can be found in 'macro/calculateDistortionsCorrections.C' + +/// \tparam DataT the data type which is used during the calculations +/// \tparam Nz number of vertices in z direction +/// \tparam Nr number of vertices in r direction +/// \tparam Nphi number of vertices in phi direction +template +class SpaceCharge +{ + using RegularGrid = RegularGrid3D; + using DataContainer = DataContainer3D; + using GridProp = GridProperties; + using TriCubic = TriCubicInterpolator; + + public: + /// default constructor + SpaceCharge() = default; + + /// Enumerator for setting the space-charge distortion mode + enum class SCDistortionType : int { + SCDistortionsConstant = 0, // space-charge distortions constant over time + SCDistortionsRealistic = 1 // realistic evolution of space-charge distortions over time + }; + + /// numerical integration strategys + enum class IntegrationStrategy { Trapezoidal = 0, ///< trapezoidal integration (https://en.wikipedia.org/wiki/Trapezoidal_rule). straight electron drift line assumed: z0->z1, r0->r0, phi0->phi0 + Simpson = 1, ///< simpon integration. see: https://en.wikipedia.org/wiki/Simpson%27s_rule. straight electron drift line assumed: z0->z1, r0->r0, phi0->phi0 + Root = 2, ///< Root integration. straight electron drift line assumed: z0->z1, r0->r0, phi0->phi0 + SimpsonIterative = 3 ///< simpon integration, but using an iterative method to approximate the drift path. No straight electron drift line assumed: z0->z1, r0->r1, phi0->phi1 + }; + + enum class Type { + Distortions = 0, ///< distortions + Corrections = 1 ///< corrections + }; + + enum class GlobalDistType { + Standard = 0, ///< classical method (start calculation of global distortion at each voxel in the tpc and follow electron drift to readout -slow-) + Fast = 1, ///< interpolation of global corrections (use the global corrections to apply an iterative approach to obtain the global distortions -fast-) + None = 2 ///< dont calculate global distortions + }; + + enum class GlobalDistCorrMethod { + LocalDistCorr, ///< using local dis/corr interpolator for calculation of global distortions/corrections + ElectricalField ///< using electric field for calculation of global distortions/corrections + }; + + /// step 0: set the charge density from TH3 histogram containing the space charge density + /// \param hisSCDensity3D histogram for the space charge density + void fillChargeDensityFromHisto(const TH3& hisSCDensity3D); + + /// step 0: set the charge density from TH3 histogram containing the space charge density + /// \param fInp input file containing a histogram for the space charge density + /// \param name the name of the space charge density histogram in the file + void fillChargeDensityFromFile(TFile& fInp, const char* name); + + /// \param side side of the TPC + /// \param globalDistType the algorithm which is used to calculate the global distortions + /// \param globalDistCorrMethod the setting if local distortions/corrections or the electrical field will be used for the calculation of the global distortions/corrections + void calculateDistortionsCorrections(const o2::tpc::Side side); + + /// step 0: this function fills the internal storage for the charge density using an analytical formula + /// \param formulaStruct struct containing a method to evaluate the density + void setChargeDensityFromFormula(const AnalyticalFields& formulaStruct); + + /// step 0: this function fills the boundary of the potential using an analytical formula. The boundary is used in the PoissonSolver. + /// \param formulaStruct struct containing a method to evaluate the potential + void setPotentialBoundaryFromFormula(const AnalyticalFields& formulaStruct); + + /// step 0: this function fills the potential using an analytical formula + /// \param formulaStruct struct containing a method to evaluate the potential + void setPotentialFromFormula(const AnalyticalFields& formulaStruct); + + /// step 1: use the O2TPCPoissonSolver class to numerically calculate the potential with set space charge density and boundary conditions from potential + /// \param side side of the TPC + /// \param maxIteration maximum number of iterations used in the poisson solver + /// \param stoppingConvergence stopping criterion used in the poisson solver + /// \param symmetry use symmetry or not in the poisson solver + void poissonSolver(const Side side, const int maxIteration = 300, const DataT stoppingConvergence = 1e-6, const int symmetry = 0); + + /// step 2: calculate numerically the electric field from the potential + /// \param side side of the TPC + void calcEField(const Side side); + + /// step 2a: set the electric field from an analytical formula + /// \param formulaStruct struct containing a method to evaluate the electric fields + void setEFieldFromFormula(const AnalyticalFields& formulaStruct); + + /// 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) + template > + void calcLocalDistortionsCorrections(const Type type, const ElectricFields& formulaStruct); + + /// step 4: calculate global corrections by using the electric field or the local corrections + /// \param formulaStruct struct containing a method to evaluate the electric field Er, Ez, Ephi or the local corrections + template > + void calcGlobalCorrections(const Fields& formulaStruct); + + /// step 5: calculate global distortions by using the electric field or the local distortions (SLOW) + /// \param formulaStruct struct containing a method to evaluate the electric field Er, Ez, Ephi or the local distortions + template > + void calcGlobalDistortions(const Fields& formulaStruct); + + void init(); + + /// step 5: calculate global distortions using the global corrections (FAST) + /// \param globCorr interpolator for global corrections + /// \param maxIter maximum iterations per global distortion + /// \param approachZ when the difference between the desired z coordinate and the position of the global correction is deltaZ, approach the desired z coordinate by deltaZ * \p approachZ. + /// \param approachR when the difference between the desired r coordinate and the position of the global correction is deltaR, approach the desired r coordinate by deltaR * \p approachR. + /// \param approachPhi when the difference between the desired phi coordinate and the position of the global correction is deltaPhi, approach the desired phi coordinate by deltaPhi * \p approachPhi. + /// \param diffCorr if the absolute differences from the interpolated values for the global corrections from the last iteration compared to the current iteration is smaller than this value, set converged to true for current global distortion + void calcGlobalDistWithGlobalCorrIterative(const DistCorrInterpolator& globCorr, const int maxIter = 100, const DataT approachZ = 0.5, const DataT approachR = 0.5, const DataT approachPhi = 0.5, const DataT diffCorr = 1e-6); + + /// get the global correction for given coordinate + /// \param z global z coordinate + /// \param r global r coordinate + /// \param phi global phi coordinate + DataT getChargeCyl(const DataT z, const DataT r, const DataT phi, const Side side) const; + + /// get the global correction for given coordinate + /// \param z global z coordinate + /// \param r global r coordinate + /// \param phi global phi coordinate + DataT getPotentialCyl(const DataT z, const DataT r, const DataT phi, const Side side) const; + + /// get the global correction for given coordinate + /// \param z global z coordinate + /// \param r global r coordinate + /// \param phi global phi coordinate + /// \param eZ returns correction in z direction + /// \param eR returns correction in r direction + /// \param ePhi returns correction in phi direction + void getElectricFieldsCyl(const DataT z, const DataT r, const DataT phi, const Side side, DataT& eZ, DataT& eR, DataT& ePhi) const; + + /// get the global correction for given coordinate + /// \param z global z coordinate + /// \param r global r coordinate + /// \param phi global phi coordinate + /// \param lcorrZ returns local correction in z direction + /// \param lcorrR returns local correction in r direction + /// \param lcorrRPhi returns local correction in rphi direction + void getLocalCorrectionsCyl(const DataT z, const DataT r, const DataT phi, const Side side, DataT& lcorrZ, DataT& lcorrR, DataT& lcorrRPhi) const; + + /// get the global correction for given coordinate + /// \param z global z coordinate + /// \param r global r coordinate + /// \param phi global phi coordinate + /// \param corrZ returns correction in z direction + /// \param corrR returns correction in r direction + /// \param corrRPhi returns correction in rphi direction + void getCorrectionsCyl(const DataT z, const DataT r, const DataT phi, const Side side, DataT& corrZ, DataT& corrR, DataT& corrRPhi) const; + + /// get the global corrections for given coordinate + /// \param x global x coordinate + /// \param y global y coordinate + /// \param z global z coordinate + /// \param corrX returns corrections in x direction + /// \param corrY returns corrections in y direction + /// \param corrZ returns corrections in z direction + void getCorrections(const DataT x, const DataT y, const DataT z, const Side side, DataT& corrX, DataT& corrY, DataT& corrZ) const; + + /// get the global distortions for given coordinate + /// \param z global z coordinate + /// \param r global r coordinate + /// \param phi global phi coordinate + /// \param ldistZ returns local distortion in z direction + /// \param ldistR returns local distortion in r direction + /// \param ldistRPhi returns local distortion in rphi direction + void getLocalDistortionsCyl(const DataT z, const DataT r, const DataT phi, const Side side, DataT& ldistZ, DataT& ldistR, DataT& ldistRPhi) const; + + /// get the global distortions for given coordinate + /// \param z global z coordinate + /// \param r global r coordinate + /// \param phi global phi coordinate + /// \param distZ returns distortion in z direction + /// \param distR returns distortion in r direction + /// \param distRPhi returns distortion in rphi direction + void getDistortionsCyl(const DataT z, const DataT r, const DataT phi, const Side side, DataT& distZ, DataT& distR, DataT& distRPhi) const; + + /// get the global distortions for given coordinate + /// \param x global x coordinate + /// \param y global y coordinate + /// \param z global z coordinate + /// \param distX returns distortion in x direction + /// \param distY returns distortion in y direction + /// \param distZ returns distortion in z direction + void getDistortions(const DataT x, const DataT y, const DataT z, const Side side, DataT& distX, DataT& distY, DataT& distZ) const; + + /// convert x and y coordinates from cartesian to the radius in polar coordinates + static DataT getRadiusFromCartesian(const DataT x, const DataT y) { return std::sqrt(x * x + y * y); } + + /// convert x and y coordinates from cartesian to phi in polar coordinates + static DataT getPhiFromCartesian(const DataT x, const DataT y) { return std::atan2(y, x); } + + /// convert radius and phi coordinates from polar coordinates to x cartesian coordinates + static DataT getXFromPolar(const DataT r, const DataT phi) { return r * std::cos(phi); } + + /// convert radius and phi coordinates from polar coordinates to y cartesian coordinate + static DataT getYFromPolar(const DataT r, const DataT phi) { return r * std::sin(phi); } + + /// Correct electron position using correction lookup tables + /// \param point 3D coordinates of the electron + void correctElectron(GlobalPosition3D& point); + + /// Distort electron position using distortion lookup tables + /// \param point 3D coordinates of the electron + void distortElectron(GlobalPosition3D& point) const; + + /// set the distortions directly from a look up table + /// \param distdZ distortions in z direction + /// \param distdR distortions in r direction + /// \param distdRPhi distortions in rphi direction + /// \param side side of the TPC + void setDistortionLookupTables(const DataContainer& distdZ, const DataContainer& distdR, const DataContainer& distdRPhi, const Side side); + + /// set the density, potential, electric fields, local distortions/corrections, global distortions/corrections from a file. Missing objects in the file are ignored. + /// \file file containing the stored values for the density, potential, electric fields, local distortions/corrections, global distortions/corrections + /// \param side side of the TPC + void setFromFile(TFile& file, const Side side); + + /// Get grid spacing in r direction + DataT getGridSpacingR(const Side side) const { return mGrid3D[side].getSpacingY(); } + + /// Get grid spacing in z direction + DataT getGridSpacingZ(const Side side) const { return mGrid3D[side].getSpacingX(); } + + /// Get grid spacing in phi direction + DataT getGridSpacingPhi(const Side side) const { return mGrid3D[side].getSpacingZ(); } + + /// Get constant electric field + static constexpr DataT getEzField(const Side side) { return getSign(side) * (TPCParameters::cathodev - TPCParameters::vg1t) / TPCParameters::TPCZ0; } + + /// Get inner radius of tpc + DataT getRMin(const Side side) const { return mGrid3D[side].getGridMinY(); } + + /// Get min z position which is used during the calaculations + DataT getZMin(const Side side) const { return mGrid3D[side].getGridMinX(); } + + /// Get min phi + DataT getPhiMin(const Side side) const { return mGrid3D[side].getGridMinZ(); } + + /// Get max r + DataT getRMax(const Side side) const { return mGrid3D[side].getGridMaxY(); }; + + /// Get max z + DataT getZMax(const Side side) const { return mGrid3D[side].getGridMaxX(); } + + /// Get max phi + DataT getPhiMax(const Side side) const { return mGrid3D[side].getGridMaxZ(); } + + // get side of TPC for z coordinate TODO rewrite this + static Side getSide(const DataT z) { return ((z >= 0) ? Side::A : Side::C); } + + /// Get the grid object + const RegularGrid& getGrid3D(const Side side) const { return mGrid3D[side]; } + + /// Get struct containing interpolators for the electrical fields + /// \param side side of the TPC + NumericalFields getElectricFieldsInterpolator(const Side side) const; + + /// Get struct containing interpolators for local distortions dR, dZ, dPhi + /// \param side side of the TPC + DistCorrInterpolator getLocalDistInterpolator(const Side side) const; + + /// Get struct containing interpolators for local corrections dR, dZ, dPhi + /// \param side side of the TPC + DistCorrInterpolator getLocalCorrInterpolator(const Side side) const; + + /// Get struct containing interpolators for global distortions dR, dZ, dPhi + /// \param side side of the TPC + DistCorrInterpolator getGlobalDistInterpolator(const Side side) const; + + /// Get struct containing interpolators for global corrections dR, dZ, dPhi + /// \param side side of the TPC + DistCorrInterpolator getGlobalCorrInterpolator(const Side side) const; + + /// \param vertex in iz dimension + /// \param vertex in ir dimension + /// \param vertex in iphi dimension + /// \param side side of the TPC + /// \return returns local distortion dR for given vertex + DataT getLocalDistR(const size_t iz, const size_t ir, const size_t iphi, const Side side) const { return mLocalDistdR[side](iz, ir, iphi); } + + /// \param vertex in iz dimension + /// \param vertex in ir dimension + /// \param vertex in iphi dimension + /// \param side side of the TPC + /// \return returns local distortion dZ for given vertex + DataT getLocalDistZ(const size_t iz, const size_t ir, const size_t iphi, const Side side) const { return mLocalDistdZ[side](iz, ir, iphi); } + + /// \param vertex in iz dimension + /// \param vertex in ir dimension + /// \param vertex in iphi dimension + /// \param side side of the TPC + /// \return returns local distortion dRPhi for given vertex + DataT getLocalDistRPhi(const size_t iz, const size_t ir, const size_t iphi, const Side side) const { return mLocalDistdRPhi[side](iz, ir, iphi); } + + /// \param vertex in iz dimension + /// \param vertex in ir dimension + /// \param vertex in iphi dimension + /// \param side side of the TPC + /// \return returns local correction dR for given vertex + DataT getLocalCorrR(const size_t iz, const size_t ir, const size_t iphi, const Side side) const { return mLocalCorrdR[side](iz, ir, iphi); } + + /// \param vertex in iz dimension + /// \param vertex in ir dimension + /// \param vertex in iphi dimension + /// \param side side of the TPC + /// \return returns local correction dZ for given vertex + DataT getLocalCorrZ(const size_t iz, const size_t ir, const size_t iphi, const Side side) const { return mLocalCorrdZ[side](iz, ir, iphi); } + + /// \param vertex in iz dimension + /// \param vertex in ir dimension + /// \param vertex in iphi dimension + /// \param side side of the TPC + /// \return returns local correction dRPhi for given vertex + DataT getLocalCorrRPhi(const size_t iz, const size_t ir, const size_t iphi, const Side side) const { return mLocalCorrdRPhi[side](iz, ir, iphi); } + + /// Get global distortion dR for vertex + /// \param vertex in iz dimension + /// \param vertex in ir dimension + /// \param vertex in iphi dimension + /// \param side side of the TPC + DataT getGlobalDistR(const size_t iz, const size_t ir, const size_t iphi, const Side side) const { return mGlobalDistdR[side](iz, ir, iphi); } + + /// Get global distortion dZ for vertex + /// \param vertex in iz dimension + /// \param vertex in ir dimension + /// \param vertex in iphi dimension + /// \param side side of the TPC + DataT getGlobalDistZ(const size_t iz, const size_t ir, const size_t iphi, const Side side) const { return mGlobalDistdZ[side](iz, ir, iphi); } + + /// Get global distortion dRPhi for vertex + /// \param vertex in iz dimension + /// \param vertex in ir dimension + /// \param vertex in iphi dimension + /// \param side side of the TPC + DataT getGlobalDistRPhi(const size_t iz, const size_t ir, const size_t iphi, const Side side) const { return mGlobalDistdRPhi[side](iz, ir, iphi); } + + /// Get global correction dR for vertex + /// \param vertex in iz dimension + /// \param vertex in ir dimension + /// \param vertex in iphi dimension + /// \param side side of the TPC + DataT getGlobalCorrR(const size_t iz, const size_t ir, const size_t iphi, const Side side) const { return mGlobalCorrdR[side](iz, ir, iphi); } + + /// Get global correction dZ for vertex + /// \param vertex in iz dimension + /// \param vertex in ir dimension + /// \param vertex in iphi dimension + /// \param side side of the TPC + DataT getGlobalCorrZ(const size_t iz, const size_t ir, const size_t iphi, const Side side) const { return mGlobalCorrdZ[side](iz, ir, iphi); } + + /// Get global correction dRPhi for vertex + /// \param vertex in iz dimension + /// \param vertex in ir dimension + /// \param vertex in iphi dimension + /// \param side side of the TPC + DataT getGlobalCorrRPhi(const size_t iz, const size_t ir, const size_t iphi, const Side side) const { return mGlobalCorrdRPhi[side](iz, ir, iphi); } + + /// Get global electric Field Er for vertex + /// \param vertex in iz dimension + /// \param vertex in ir dimension + /// \param vertex in iphi dimension + /// \param side side of the TPC + DataT getEr(const size_t iz, const size_t ir, const size_t iphi, const Side side) const { return mElectricFieldEr[side](iz, ir, iphi); } + + /// Get global electric Field Ez for vertex + /// \param vertex in iz dimension + /// \param vertex in ir dimension + /// \param vertex in iphi dimension + /// \param side side of the TPC + DataT getEz(const size_t iz, const size_t ir, const size_t iphi, const Side side) const { return mElectricFieldEz[side](iz, ir, iphi); } + + /// Get global electric Field Ephi for vertex + /// \param vertex in iz dimension + /// \param vertex in ir dimension + /// \param vertex in iphi dimension + /// \param side side of the TPC + DataT getEphi(const size_t iz, const size_t ir, const size_t iphi, const Side side) const { return mElectricFieldEphi[side](iz, ir, iphi); } + + /// Get density for vertex + /// \param vertex in iz dimension + /// \param vertex in ir dimension + /// \param vertex in iphi dimension + /// \param side side of the TPC + DataT getDensity(const size_t iz, const size_t ir, const size_t iphi, const Side side) const { return mDensity[side](iz, ir, iphi); } + + /// Get potential for vertex + /// \param vertex in iz dimension + /// \param vertex in ir dimension + /// \param vertex in iphi dimension + /// \param side side of the TPC + DataT getPotential(const size_t iz, const size_t ir, const size_t iphi, const Side side) const { return mPotential[side](iz, ir, iphi); } + + /// Get the step width which is used for the calculation of the correction/distortions in units of the z-bin + static int getStepWidth() { return 1 / sSteps; } + + /// Get phi vertex position for index in phi direction + /// \param indexPhi index in phi direction + DataT getPhiVertex(const size_t indexPhi, const Side side) const { return mGrid3D[side].getZVertex(indexPhi); } + + /// Get r vertex position for index in r direction + /// \param indexR index in r direction + DataT getRVertex(const size_t indexR, const Side side) const { return mGrid3D[side].getYVertex(indexR); } + + /// Get z vertex position for index in z direction + /// \param indexZ index in z direction + DataT getZVertex(const size_t indexZ, const Side side) const { return mGrid3D[side].getXVertex(indexZ); } + + /// \param omegaTau \omega \tau value + /// \param t1 value for t1 see: ??? + /// \param t2 value for t2 see: ??? + void setOmegaTauT1T2(const DataT omegaTau, const DataT t1, const DataT t2) + { + const DataT wt0 = t2 * omegaTau; + mC0 = 1 / (1 + wt0 * wt0); + const DataT wt1 = t1 * omegaTau; + mC1 = wt1 / (1 + wt1 * wt1); + }; + + /// \param c0 coefficient C0 (compare Jim Thomas's notes for definitions) + /// \param c1 coefficient C1 (compare Jim Thomas's notes for definitions) + void setC0C1(const DataT c0, const DataT c1) + { + mC0 = c0; + mC1 = c1; + } + + /// set number of steps used for calculation of distortions/corrections per z bin + /// \param nSteps number of steps per z bin + static void setNStep(const int nSteps) { sSteps = nSteps; } + + static int getNStep() { return sSteps; } + + /// get the number of threads used for some of the calculations + static int getNThreads() { return sNThreads; } + + /// set the number of threads used for some of the calculations + static void setNThreads(const int nThreads) + { + sNThreads = nThreads; + o2::tpc::TriCubicInterpolator::setNThreads(nThreads); + } + + /// set which kind of numerical integration is used for calcution of the integrals int Er/Ez dz, int Ephi/Ez dz, int Ez dz + /// \param strategy numerical integration strategy. see enum IntegrationStrategy for the different types + static void setNumericalIntegrationStrategy(const IntegrationStrategy strategy) { sNumericalIntegrationStrategy = strategy; } + static IntegrationStrategy getNumericalIntegrationStrategy() { return sNumericalIntegrationStrategy; } + + static void setGlobalDistType(const GlobalDistType globalDistType) { sGlobalDistType = globalDistType; } + static GlobalDistType getGlobalDistType() { return sGlobalDistType; } + + static void setGlobalDistCorrMethod(const GlobalDistCorrMethod globalDistCorrMethod) { sGlobalDistCorrCalcMethod = globalDistCorrMethod; } + static GlobalDistCorrMethod getGlobalDistCorrMethod() { return sGlobalDistCorrCalcMethod; } + + static void setSimpsonNIteratives(const int nIter) { sSimpsonNIteratives = nIter; } + static int getSimpsonNIteratives() { return sSimpsonNIteratives; } + + /// Set the space-charge distortions model + /// \param distortionType distortion type (constant or realistic) + static void setSCDistortionType(SCDistortionType distortionType) { sSCDistortionType = distortionType; } + /// Get the space-charge distortions model + static SCDistortionType getSCDistortionType() { return sSCDistortionType; } + + void setUseInitialSCDensity(const bool useInitialSCDensity) { mUseInitialSCDensity = useInitialSCDensity; } + + /// write electric fields to root file + /// \param outf output file where the electrical fields will be written to + /// \side side of the TPC + int dumpElectricFields(TFile& outf, const Side side) const; + + /// set electric field from root file + /// \param inpf input file where the electrical fields are stored + /// \side side of the TPC + void setElectricFieldsFromFile(TFile& inpf, const Side side); + + /// write potential to root file + /// \param outf output file where the potential will be written to + /// \side side of the TPC + int dumpPotential(TFile& outf, const Side side) const { return mPotential[side].writeToFile(outf, Form("potential_side%s", getSideName(side).data())); } + + /// set potential from root file + /// \param inpf input file where the potential is stored + /// \side side of the TPC + void setPotentialFromFile(TFile& inpf, const Side side) { mPotential[side].initFromFile(inpf, Form("potential_side%s", getSideName(side).data())); } + + /// write potential to root file + /// \param outf output file where the charge density will be written to + /// \side side of the TPC + int dumpDensity(TFile& outf, const Side side) const { return mDensity[side].writeToFile(outf, Form("density_side%s", getSideName(side).data())); } + + /// set potential from root file + /// \param inpf input file where the charge density is stored + /// \side side of the TPC + void setDensityFromFile(TFile& inpf, const Side side) { mDensity[side].initFromFile(inpf, Form("density_side%s", getSideName(side).data())); } + + /// write global distortions to root file + /// \param outf output file where the global distortions will be written to + /// \side side of the TPC + int dumpGlobalDistortions(TFile& outf, const Side side) const; + + /// set global distortions from root file + /// \param inpf input file where the global distortions are stored + /// \side side of the TPC + void setGlobalDistortionsFromFile(TFile& inpf, const Side side); + + /// write global corrections to root file + /// \param outf output file where the global corrections will be written to + /// \side side of the TPC + int dumpGlobalCorrections(TFile& outf, const Side side) const; + + /// set global corrections from root file + /// \param inpf input file where the global corrections are stored + /// \side side of the TPC + void setGlobalCorrectionsFromFile(TFile& inpf, const Side side); + + /// write local corrections to root file + /// \param outf output file where the local corrections will be written to + /// \side side of the TPC + int dumpLocalCorrections(TFile& outf, const Side side) const; + + /// set local corrections from root file + /// \param inpf input file where the local corrections are stored + /// \side side of the TPC + void setLocalCorrectionsFromFile(TFile& inpf, const Side side); + + /// write local distortions to root file + /// \param outf output file where the local distortions will be written to + /// \side side of the TPC + int dumpLocalDistortions(TFile& outf, const Side side) const; + + /// set local distortions from root file + /// \param inpf input file where the local distortions are stored + /// \side side of the TPC + void setLocalDistortionsFromFile(TFile& inpf, const Side side); + + /// set z coordinate between min z max z + /// \param posZ z position which will be regulated if needed + DataT regulateZ(const DataT posZ, const Side side) const { return mGrid3D[side].clampToGrid(posZ, 0); } + + /// set r coordinate between 'RMIN - 4 * GRIDSPACINGR' and 'RMAX + 2 * GRIDSPACINGR'. the r coordinate is not clamped to RMIN and RMAX to ensure correct interpolation at the borders of the grid. + DataT regulateR(const DataT posR, const Side side) const; + + /// set phi coordinate between min phi max phi + DataT regulatePhi(const DataT posPhi, const Side side) const { return mGrid3D[side].clampToGridCircular(posPhi, 2); } + + private: + using ASolv = o2::tpc::PoissonSolver; + + inline static int sNThreads{omp_get_max_threads()}; ///< number of threads which are used during the calculations + + inline static IntegrationStrategy sNumericalIntegrationStrategy{IntegrationStrategy::SimpsonIterative}; ///< numerical integration strategy of integration of the E-Field: 0: trapezoidal, 1: Simpson, 2: Root (only for analytical formula case) + inline static int sSimpsonNIteratives{3}; ///< number of iterations which are performed in the iterative simpson calculation of distortions/corrections + inline static int sSteps{1}; ///< during the calculation of the corrections/distortions it is assumed that the electron drifts on a line from deltaZ = z0 -> z1. The value sets the deltaZ width: 1: deltaZ=zBin/1, 5: deltaZ=zBin/5 + inline static GlobalDistType sGlobalDistType{GlobalDistType::Fast}; ///< setting for global distortions: 0: standard method, 1: interpolation of global corrections + inline static GlobalDistCorrMethod sGlobalDistCorrCalcMethod{GlobalDistCorrMethod::LocalDistCorr}; ///< setting for global distortions/corrections: 0: using electric field, 1: using local dis/corr interpolator + inline static SCDistortionType sSCDistortionType{SCDistortionType::SCDistortionsConstant}; ///< Type of space-charge distortions + + DataT mC0 = 0; ///< coefficient C0 (compare Jim Thomas's notes for definitions) + DataT mC1 = 0; ///< coefficient C1 (compare Jim Thomas's notes for definitions) + + static constexpr int FNSIDES = SIDES; ///< number of sides of the TPC + bool mIsEfieldSet[FNSIDES]{}; ///< flag if E-fields are set + bool mIsLocalCorrSet[FNSIDES]{}; ///< flag if local corrections are set + bool mIsLocalDistSet[FNSIDES]{}; ///< flag if local distortions are set + bool mIsGlobalCorrSet[FNSIDES]{}; ///< flag if global corrections are set + bool mIsGlobalDistSet[FNSIDES]{}; ///< flag if global distortions are set + bool mIsChargeSet[FNSIDES]{}; ///< flag if the charge + + bool mUseInitialSCDensity{false}; ///< Flag for the use of an initial space-charge density at the beginning of the simulation + bool mInitLookUpTables{false}; ///< Flag to indicate if lookup tables have been calculated + + const RegularGrid mGrid3D[FNSIDES]{ + {GridProp::ZMIN, GridProp::RMIN, GridProp::PHIMIN, getSign(Side::A) * GridProp::GRIDSPACINGZ, GridProp::GRIDSPACINGR, GridProp::GRIDSPACINGPHI}, + {GridProp::ZMIN, GridProp::RMIN, GridProp::PHIMIN, getSign(Side::C) * GridProp::GRIDSPACINGZ, GridProp::GRIDSPACINGR, GridProp::GRIDSPACINGPHI}}; ///< grid properties + + DataContainer mLocalDistdR[FNSIDES]{}; ///< data storage for local distortions dR + DataContainer mLocalDistdZ[FNSIDES]{}; ///< data storage for local distortions dZ + DataContainer mLocalDistdRPhi[FNSIDES]{}; ///< data storage for local distortions dRPhi + + DataContainer mLocalCorrdR[FNSIDES]{}; ///< data storage for local corrections dR + DataContainer mLocalCorrdZ[FNSIDES]{}; ///< data storage for local corrections dZ + DataContainer mLocalCorrdRPhi[FNSIDES]{}; ///< data storage for local corrections dRPhi + + DataContainer mGlobalDistdR[FNSIDES]{}; ///< data storage for global distortions dR + DataContainer mGlobalDistdZ[FNSIDES]{}; ///< data storage for global distortions dZ + DataContainer mGlobalDistdRPhi[FNSIDES]{}; ///< data storage for global distortions dRPhi + + DataContainer mGlobalCorrdR[FNSIDES]{}; ///< data storage for global corrections dR + DataContainer mGlobalCorrdZ[FNSIDES]{}; ///< data storage for global corrections dZ + DataContainer mGlobalCorrdRPhi[FNSIDES]{}; ///< data storage for global corrections dRPhi + + DataContainer mDensity[FNSIDES]{}; ///< data storage for space charge density + DataContainer mPotential[FNSIDES]{}; ///< data storage for the potential + + DataContainer mElectricFieldEr[FNSIDES]{}; ///< data storage for the electric field Er + DataContainer mElectricFieldEz[FNSIDES]{}; ///< data storage for the electric field Ez + DataContainer mElectricFieldEphi[FNSIDES]{}; ///< data storage for the electric field Ephi + + TriCubic mInterpolatorPotential[FNSIDES]{ + {mPotential[Side::A], mGrid3D[Side::A]}, + {mPotential[Side::C], mGrid3D[Side::C]}}; ///< interpolator for the potenial + + TriCubic mInterpolatorDensity[FNSIDES]{ + {mDensity[Side::A], mGrid3D[Side::A]}, + {mDensity[Side::C], mGrid3D[Side::C]}}; ///< interpolator for the charge + + DistCorrInterpolator mInterpolatorGlobalCorr[FNSIDES]{ + {mGlobalCorrdR[Side::A], mGlobalCorrdZ[Side::A], mGlobalCorrdRPhi[Side::A], mGrid3D[Side::A], Side::A}, + {mGlobalCorrdR[Side::C], mGlobalCorrdZ[Side::C], mGlobalCorrdRPhi[Side::C], mGrid3D[Side::C], Side::C}}; ///< interpolator for the global corrections + + DistCorrInterpolator mInterpolatorLocalCorr[FNSIDES]{ + {mLocalCorrdR[Side::A], mLocalCorrdZ[Side::A], mLocalCorrdRPhi[Side::A], mGrid3D[Side::A], Side::A}, + {mLocalCorrdR[Side::C], mLocalCorrdZ[Side::C], mLocalCorrdRPhi[Side::C], mGrid3D[Side::C], Side::C}}; ///< interpolator for the local corrections + + DistCorrInterpolator mInterpolatorGlobalDist[FNSIDES]{ + {mGlobalDistdR[Side::A], mGlobalDistdZ[Side::A], mGlobalDistdRPhi[Side::A], mGrid3D[Side::A], Side::A}, + {mGlobalDistdR[Side::C], mGlobalDistdZ[Side::C], mGlobalDistdRPhi[Side::C], mGrid3D[Side::C], Side::C}}; ///< interpolator for the global distortions + + DistCorrInterpolator mInterpolatorLocalDist[FNSIDES]{ + {mLocalDistdR[Side::A], mLocalDistdZ[Side::A], mLocalDistdRPhi[Side::A], mGrid3D[Side::A], Side::A}, + {mLocalDistdR[Side::C], mLocalDistdZ[Side::C], mLocalDistdRPhi[Side::C], mGrid3D[Side::C], Side::C}}; ///< interpolator for the local distortions + + NumericalFields mInterpolatorEField[FNSIDES]{ + {mElectricFieldEr[Side::A], mElectricFieldEz[Side::A], mElectricFieldEphi[Side::A], mGrid3D[Side::A], Side::A}, + {mElectricFieldEr[Side::C], mElectricFieldEz[Side::C], mElectricFieldEphi[Side::C], mGrid3D[Side::C], Side::C}}; ///< interpolator for the electric fields + + /// rebin the input space charge density histogram to desired binning + /// \param hOrig original histogram + TH3D rebinDensityHisto(const TH3& hOrig) const; + + static int getSign(const Side side) + { + return side == Side::C ? -1 : 1; + } + + /// get inverse spacing in z direction + DataT getInvSpacingZ(const Side side) const { return mGrid3D[side].getInvSpacingX(); } + + /// get inverse spacing in r direction + DataT getInvSpacingR(const Side side) const { return mGrid3D[side].getInvSpacingY(); } + + /// get inverse spacing in phi direction + DataT getInvSpacingPhi(const Side side) const { return mGrid3D[side].getInvSpacingZ(); } + + std::string getSideName(const Side side) const { return side == Side::A ? "A" : "C"; } + + /// calculate distortions or corrections analytical with electric fields + template > + void calcDistCorr(const DataT p1r, const DataT p1phi, const DataT p1z, const DataT p2z, DataT& ddR, DataT& ddPhi, DataT& ddZ, const Fields& formulaStruct, const bool localDistCorr) const; + + /// calculate distortions/corrections using the formulas proposed in https://edms.cern.ch/ui/file/1108138/1/ALICE-INT-2010-016.pdf page 7 + void langevinCylindrical(DataT& ddR, DataT& ddPhi, DataT& ddZ, const DataT radius, const DataT localIntErOverEz, const DataT localIntEPhiOverEz, const DataT localIntDeltaEz) const; + + /// integrate electrical fields using root integration method + template > + void integrateEFieldsRoot(const DataT p1r, const DataT p1phi, const DataT p1z, const DataT p2z, DataT& localIntErOverEz, DataT& localIntEPhiOverEz, DataT& localIntDeltaEz, const Fields& formulaStruct) const; + + /// integrate electrical fields using trapezoidal integration method + template > + void integrateEFieldsTrapezoidal(const DataT p1r, const DataT p1phi, const DataT p1z, const DataT p2z, DataT& localIntErOverEz, DataT& localIntEPhiOverEz, DataT& localIntDeltaEz, const Fields& formulaStruct) const; + + /// integrate electrical fields using simpson integration method + template > + void integrateEFieldsSimpson(const DataT p1r, const DataT p1phi, const DataT p1z, const DataT p2z, DataT& localIntErOverEz, DataT& localIntEPhiOverEz, DataT& localIntDeltaEz, const Fields& formulaStruct) const; + + /// integrate electrical fields using simpson integration method with non straight drift of electrons + template > + void integrateEFieldsSimpsonIterative(const DataT p1r, const DataT p2r, const DataT p1phi, const DataT p2phi, const DataT p1z, const DataT p2z, DataT& localIntErOverEz, DataT& localIntEPhiOverEz, DataT& localIntDeltaEz, const Fields& formulaStruct) const; + + /// calculate distortions/corrections using analytical electric fields + void processGlobalDistCorr(const DataT radius, const DataT phi, const DataT z0Tmp, const DataT z1Tmp, DataT& ddR, DataT& ddPhi, DataT& ddZ, const AnalyticalFields& formulaStruct) const + { + calcDistCorr(radius, phi, z0Tmp, z1Tmp, ddR, ddPhi, ddZ, formulaStruct, false); + } + + /// calculate distortions/corrections using electric fields from tricubic interpolator + void processGlobalDistCorr(const DataT radius, const DataT phi, const DataT z0Tmp, const DataT z1Tmp, DataT& ddR, DataT& ddPhi, DataT& ddZ, const NumericalFields& formulaStruct) const + { + calcDistCorr(radius, phi, z0Tmp, z1Tmp, ddR, ddPhi, ddZ, formulaStruct, false); + } + + /// calculate distortions/corrections by interpolation of local distortions/corrections + void processGlobalDistCorr(const DataT radius, const DataT phi, const DataT z0Tmp, [[maybe_unused]] const DataT z1Tmp, DataT& ddR, DataT& ddPhi, DataT& ddZ, const DistCorrInterpolator& localDistCorr) const + { + ddR = localDistCorr.evaldR(z0Tmp, radius, phi); + ddZ = localDistCorr.evaldZ(z0Tmp, radius, phi); + ddPhi = localDistCorr.evaldRPhi(z0Tmp, radius, phi) / radius; + } +}; + +/// +/// ======================================================================================================== +/// Inline implementations of some methods +/// ======================================================================================================== +/// + +template +template +void SpaceCharge::integrateEFieldsRoot(const DataT p1r, const DataT p1phi, const DataT p1z, const DataT p2z, DataT& localIntErOverEz, DataT& localIntEPhiOverEz, DataT& localIntDeltaEz, const Fields& formulaStruct) const +{ + const DataT ezField = getEzField(formulaStruct.getSide()); + TF1 fErOverEz( + "fErOverEz", [&](double* x, double* p) { (void)p; return static_cast(formulaStruct.evalEr(static_cast(x[0]), p1r, p1phi) / (formulaStruct.evalEz(static_cast(x[0]), p1r, p1phi) + ezField)); }, p1z, p2z, 1); + localIntErOverEz = static_cast(fErOverEz.Integral(p1z, p2z)); + + TF1 fEphiOverEz( + "fEPhiOverEz", [&](double* x, double* p) { (void)p; return static_cast(formulaStruct.evalEphi(static_cast(x[0]), p1r, p1phi) / (formulaStruct.evalEz(static_cast(x[0]), p1r, p1phi) + ezField)); }, p1z, p2z, 1); + localIntEPhiOverEz = static_cast(fEphiOverEz.Integral(p1z, p2z)); + + TF1 fEz( + "fEZOverEz", [&](double* x, double* p) { (void)p; return static_cast(formulaStruct.evalEz(static_cast(x[0]), p1r, p1phi) - ezField); }, p1z, p2z, 1); + localIntDeltaEz = getSign(formulaStruct.getSide()) * static_cast(fEz.Integral(p1z, p2z)); +} + +template +template +void SpaceCharge::integrateEFieldsTrapezoidal(const DataT p1r, const DataT p1phi, const DataT p1z, const DataT p2z, DataT& localIntErOverEz, DataT& localIntEPhiOverEz, DataT& localIntDeltaEz, const Fields& formulaStruct) const +{ + //========trapezoidal rule see: https://en.wikipedia.org/wiki/Trapezoidal_rule ============== + const DataT fielder0 = formulaStruct.evalEr(p1z, p1r, p1phi); + const DataT fieldez0 = formulaStruct.evalEz(p1z, p1r, p1phi); + const DataT fieldephi0 = formulaStruct.evalEphi(p1z, p1r, p1phi); + + const DataT fielder1 = formulaStruct.evalEr(p2z, p1r, p1phi); + const DataT fieldez1 = formulaStruct.evalEz(p2z, p1r, p1phi); + const DataT fieldephi1 = formulaStruct.evalEphi(p2z, p1r, p1phi); + + const DataT ezField = getEzField(formulaStruct.getSide()); + const DataT eZ0 = 1. / (ezField + fieldez0); + const DataT eZ1 = 1. / (ezField + fieldez1); + + const DataT deltaX = 0.5 * (p2z - p1z); + localIntErOverEz = deltaX * (fielder0 * eZ0 + fielder1 * eZ1); + localIntEPhiOverEz = deltaX * (fieldephi0 * eZ0 + fieldephi1 * eZ1); + localIntDeltaEz = getSign(formulaStruct.getSide()) * deltaX * (fieldez0 + fieldez1); +} + +template +template +void SpaceCharge::integrateEFieldsSimpson(const DataT p1r, const DataT p1phi, const DataT p1z, const DataT p2z, DataT& localIntErOverEz, DataT& localIntEPhiOverEz, DataT& localIntDeltaEz, const Fields& formulaStruct) const +{ + //==========simpsons rule see: https://en.wikipedia.org/wiki/Simpson%27s_rule ============================= + const DataT fielder0 = formulaStruct.evalEr(p1z, p1r, p1phi); + const DataT fieldez0 = formulaStruct.evalEz(p1z, p1r, p1phi); + const DataT fieldephi0 = formulaStruct.evalEphi(p1z, p1r, p1phi); + + const DataT fielder1 = formulaStruct.evalEr(p2z, p1r, p1phi); + const DataT fieldez1 = formulaStruct.evalEz(p2z, p1r, p1phi); + const DataT fieldephi1 = formulaStruct.evalEphi(p2z, p1r, p1phi); + + const DataT deltaX = p2z - p1z; + const DataT ezField = getEzField(formulaStruct.getSide()); + const DataT xk2N = (p2z - static_cast(0.5) * deltaX); + const DataT ezField2 = formulaStruct.evalEz(xk2N, p1r, p1phi); + const DataT ezField2Denominator = 1. / (ezField + ezField2); + const DataT fieldSum2ErOverEz = formulaStruct.evalEr(xk2N, p1r, p1phi) * ezField2Denominator; + const DataT fieldSum2EphiOverEz = formulaStruct.evalEphi(xk2N, p1r, p1phi) * ezField2Denominator; + + const DataT eZ0 = 1. / (ezField + fieldez0); + const DataT eZ1 = 1. / (ezField + fieldez1); + + const DataT deltaXSimpsonSixth = deltaX / 6.; + localIntErOverEz = deltaXSimpsonSixth * (4. * fieldSum2ErOverEz + fielder0 * eZ0 + fielder1 * eZ1); + localIntEPhiOverEz = deltaXSimpsonSixth * (4. * fieldSum2EphiOverEz + fieldephi0 * eZ0 + fieldephi1 * eZ1); + localIntDeltaEz = getSign(formulaStruct.getSide()) * deltaXSimpsonSixth * (4. * ezField2 + fieldez0 + fieldez1); +} + +template +template +void SpaceCharge::integrateEFieldsSimpsonIterative(const DataT p1r, const DataT p2r, const DataT p1phi, const DataT p2phi, const DataT p1z, const DataT p2z, DataT& localIntErOverEz, DataT& localIntEPhiOverEz, DataT& localIntDeltaEz, const Fields& formulaStruct) const +{ + //==========simpsons rule see: https://en.wikipedia.org/wiki/Simpson%27s_rule ============================= + const Side side = formulaStruct.getSide(); + const DataT ezField = getEzField(side); + const DataT p2phiSave = regulatePhi(p2phi, side); + + const DataT fielder0 = formulaStruct.evalEr(p1z, p1r, p1phi); + const DataT fieldez0 = formulaStruct.evalEz(p1z, p1r, p1phi); + const DataT fieldephi0 = formulaStruct.evalEphi(p1z, p1r, p1phi); + + const DataT fielder1 = formulaStruct.evalEr(p2z, p2r, p2phiSave); + const DataT fieldez1 = formulaStruct.evalEz(p2z, p2r, p2phiSave); + const DataT fieldephi1 = formulaStruct.evalEphi(p2z, p2r, p2phiSave); + + const DataT eZ0Inv = 1. / (ezField + fieldez0); + const DataT eZ1Inv = 1. / (ezField + fieldez1); + + const DataT pHalfZ = 0.5 * (p1z + p2z); // dont needs to be regulated since p1z and p2z are already regulated + const DataT pHalfPhiSave = regulatePhi(0.5 * (p1phi + p2phi), side); // needs to be regulated since p2phi is not regulated + const DataT pHalfR = 0.5 * (p1r + p2r); + + const DataT ezField2 = formulaStruct.evalEz(pHalfZ, pHalfR, pHalfPhiSave); + const DataT eZHalfInv = 1. / (ezField + ezField2); + const DataT fieldSum2ErOverEz = formulaStruct.evalEr(pHalfZ, pHalfR, pHalfPhiSave); + const DataT fieldSum2EphiOverEz = formulaStruct.evalEphi(pHalfZ, pHalfR, pHalfPhiSave); + + const DataT deltaXSimpsonSixth = (p2z - p1z) / 6; + localIntErOverEz = deltaXSimpsonSixth * (4 * fieldSum2ErOverEz * eZHalfInv + fielder0 * eZ0Inv + fielder1 * eZ1Inv); + localIntEPhiOverEz = deltaXSimpsonSixth * (4 * fieldSum2EphiOverEz * eZHalfInv + fieldephi0 * eZ0Inv + fieldephi1 * eZ1Inv); + localIntDeltaEz = getSign(side) * deltaXSimpsonSixth * (4 * ezField2 + fieldez0 + fieldez1); +} + +template +template +void SpaceCharge::calcDistCorr(const DataT p1r, const DataT p1phi, const DataT p1z, const DataT p2z, DataT& ddR, DataT& ddPhi, DataT& ddZ, const Fields& formulaStruct, const bool localDistCorr) const +{ + // see: https://edms.cern.ch/ui/file/1108138/1/ALICE-INT-2010-016.pdf + // needed for calculation of distortions/corrections + DataT localIntErOverEz = 0; // integral_p1z^p2z Er/Ez dz + DataT localIntEPhiOverEz = 0; // integral_p1z^p2z Ephi/Ez dz + DataT localIntDeltaEz = 0; // integral_p1z^p2z Ez dz + + // there are differentnumerical integration strategys implements. for details see each function. + switch (sNumericalIntegrationStrategy) { + case IntegrationStrategy::SimpsonIterative: // iterative simpson integration (should be more precise at least for the analytical E-Field case but takes alot more time than normal simpson integration) + for (int i = 0; i < sSimpsonNIteratives; ++i) { // TODO define a convergence criterion to abort the algorithm earlier for speed up. + const DataT tmpZ = localDistCorr ? (p2z + ddZ) : regulateZ(p2z + ddZ, formulaStruct.getSide()); // dont regulate for local distortions/corrections! (to get same result as using electric field at last/first bin) + integrateEFieldsSimpsonIterative(p1r, p1r + ddR, p1phi, p1phi + ddPhi, p1z, tmpZ, localIntErOverEz, localIntEPhiOverEz, localIntDeltaEz, formulaStruct); + langevinCylindrical(ddR, ddPhi, ddZ, (p1r + 0.5 * ddR), localIntErOverEz, localIntEPhiOverEz, localIntDeltaEz); // using the mean radius '(p1r + 0.5 * ddR)' for calculation of distortions/corections + } + break; + case IntegrationStrategy::Simpson: // simpson integration + integrateEFieldsSimpson(p1r, p1phi, p1z, p2z, localIntErOverEz, localIntEPhiOverEz, localIntDeltaEz, formulaStruct); + langevinCylindrical(ddR, ddPhi, ddZ, p1r, localIntErOverEz, localIntEPhiOverEz, localIntDeltaEz); + break; + case IntegrationStrategy::Trapezoidal: // trapezoidal integration (fastest) + integrateEFieldsTrapezoidal(p1r, p1phi, p1z, p2z, localIntErOverEz, localIntEPhiOverEz, localIntDeltaEz, formulaStruct); + langevinCylindrical(ddR, ddPhi, ddZ, p1r, localIntErOverEz, localIntEPhiOverEz, localIntDeltaEz); + break; + case IntegrationStrategy::Root: // using integration implemented in ROOT (slow) + integrateEFieldsRoot(p1r, p1phi, p1z, p2z, localIntErOverEz, localIntEPhiOverEz, localIntDeltaEz, formulaStruct); + langevinCylindrical(ddR, ddPhi, ddZ, p1r, localIntErOverEz, localIntEPhiOverEz, localIntDeltaEz); + break; + default: + LOGP(INFO, "no matching case: Using Simpson"); + integrateEFieldsSimpson(p1r, p1phi, p1z, p2z, localIntErOverEz, localIntEPhiOverEz, localIntDeltaEz, formulaStruct); + langevinCylindrical(ddR, ddPhi, ddZ, p1r, localIntErOverEz, localIntEPhiOverEz, localIntDeltaEz); + } +} + +template +void SpaceCharge::langevinCylindrical(DataT& ddR, DataT& ddPhi, DataT& ddZ, const DataT radius, const DataT localIntErOverEz, const DataT localIntEPhiOverEz, const DataT localIntDeltaEz) const +{ + // calculated distortions/correction with the formula described in https://edms.cern.ch/ui/file/1108138/1/ALICE-INT-2010-016.pdf page 7. + ddR = mC0 * localIntErOverEz + mC1 * localIntEPhiOverEz; + ddPhi = (mC0 * localIntEPhiOverEz - mC1 * localIntErOverEz) / radius; + ddZ = -localIntDeltaEz * TPCParameters::DVDE; +} + +} // namespace tpc +} // namespace o2 + +#endif diff --git a/Detectors/TPC/spacecharge/include/TPCSpaceCharge/SpaceChargeHelpers.h b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/SpaceChargeHelpers.h new file mode 100644 index 0000000000000..6316fd524c283 --- /dev/null +++ b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/SpaceChargeHelpers.h @@ -0,0 +1,269 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file SpaceChargeHelpers.h +/// \brief This file provides all necesseray classes which are used during the calcution of the distortions and corrections +/// +/// \author Matthias Kleiner +/// \date Aug 21, 2020 + +#ifndef ALICEO2_TPC_SPACECHARGEHELPERS_H_ +#define ALICEO2_TPC_SPACECHARGEHELPERS_H_ + +#include +#include +#include "TPCSpaceCharge/TriCubic.h" +#include "DataFormatsTPC/Defs.h" + +namespace o2 +{ +namespace tpc +{ + +/// +/// this class contains an analytical description of the space charge, potential and the electric fields. +/// The analytical functions can be used to test the poisson solver and the caluclation of distortions/corrections. +/// +template +class AnalyticalFields +{ + public: + AnalyticalFields(const o2::tpc::Side side = o2::tpc::Side::A) : mSide{side} {}; + + o2::tpc::Side getSide() const { return mSide; } + + void setSide(const o2::tpc::Side side) { mSide = side; } + + /// sets the parameters + void setParameters(const DataT parA, const DataT parB, const DataT parC) + { + mParA = parA; + mParB = parB; + mParC = parC; + } + + /// return parameter A + DataT getParA() const { return mParA; } + + /// return parameter B + DataT getParB() const { return mParB; } + + /// return parameter C + DataT getParC() const { return mParC; } + + /// \param r r coordinate + /// \param phi phi coordinate + /// \param z z coordinate + /// \return returns the function value for electric field Er for given coordinate + DataT evalEr(DataT z, DataT r, DataT phi) const { return mErFunc(z, r, phi); } + + /// \param r r coordinate + /// \param phi phi coordinate + /// \param z z coordinate + /// \return returns the function value for electric field Ez for given coordinate + DataT evalEz(DataT z, DataT r, DataT phi) const { return mEzFunc(z, r, phi); } + + /// \param r r coordinate + /// \param phi phi coordinate + /// \param z z coordinate + /// \return returns the function value for electric field Ephi for given coordinate + DataT evalEphi(DataT z, DataT r, DataT phi) const { return mEphiFunc(z, r, phi); } + + /// \param r r coordinate + /// \param phi phi coordinate + /// \param z z coordinate + /// \return returns the function value for the potential for given coordinate + DataT evalPotential(DataT z, DataT r, DataT phi) const { return mPotentialFunc(z, r, phi); } + + /// \param r r coordinate + /// \param phi phi coordinate + /// \param z z coordinate + /// \return returns the function value for the space charge density for given coordinate + DataT evalDensity(DataT z, DataT r, DataT phi) const { return mDensityFunc(z, r, phi); } + + /// analytical potential + std::function mPotentialFunc = [& mParA = mParA, &mParB = mParB, &mParC = mParC](const DataT z, const DataT r, const DataT phi) { + const DataT zz = std::abs(z); + return -mParA * (std::pow((-r + 254.5 + 83.5), 4) - 338.0 * std::pow((-r + 254.5 + 83.5), 3) + 21250.75 * std::pow((-r + 254.5 + 83.5), 2)) * std::cos(mParB * phi) * std::cos(mParB * phi) * std::exp(-1 * mParC * (zz - 125) * (zz - 125)); + }; + + /// analytical space charge - NOTE: if the space charge density is calculated analytical there would be a - sign in the formula (-mParA) - however since its an e- the sign is flipped (IS THIS CORRECT??? see for minus sign: AliTPCSpaceCharge3DCalc::SetPotentialBoundaryAndChargeFormula)- + std::function mDensityFunc = [& mParA = mParA, &mParB = mParB, &mParC = mParC](const DataT z, const DataT r, const DataT phi) { + const DataT zz = std::abs(z); + return mParA * ((1 / r * 16 * (-3311250 + 90995.5 * r - 570.375 * r * r + r * r * r)) * std::cos(mParB * phi) * std::cos(mParB * phi) * std::exp(-1 * mParC * (zz - 125) * (zz - 125)) + + (std::pow(-r + 254.5 + 83.5, 4) - 338.0 * std::pow(-r + 254.5 + 83.5, 3) + 21250.75 * std::pow(-r + 254.5 + 83.5, 2)) / (r * r) * std::exp(-1 * mParC * (zz - 125) * (zz - 125)) * -2 * mParB * mParB * std::cos(2 * mParB * phi) + + (std::pow(-r + 254.5 + 83.5, 4) - 338.0 * std::pow(-r + 254.5 + 83.5, 3) + 21250.75 * std::pow(-r + 254.5 + 83.5, 2)) * std::cos(mParB * phi) * std::cos(mParB * phi) * 2 * mParC * std::exp(-1 * mParC * (zz - 125) * (zz - 125)) * (2 * mParC * (zz - 125) * (zz - 125) - 1)); + }; + + /// analytical electric field Er + std::function mErFunc = [& mParA = mParA, &mParB = mParB, &mParC = mParC](const DataT z, const DataT r, const DataT phi) { + const DataT zz = std::abs(z); + return mParA * 4 * (r * r * r - 760.5 * r * r + 181991 * r - 1.3245 * std::pow(10, 7)) * std::cos(mParB * phi) * std::cos(mParB * phi) * std::exp(-1 * mParC * (zz - 125) * (zz - 125)); + }; + + /// analytical electric field Ephi + std::function mEphiFunc = [& mParA = mParA, &mParB = mParB, &mParC = mParC](const DataT z, const DataT r, const DataT phi) { + const DataT zz = std::abs(z); + return mParA * (std::pow(-r + 254.5 + 83.5, 4) - 338.0 * std::pow(-r + 254.5 + 83.5, 3) + 21250.75 * (-r + 254.5 + 83.5) * (-r + 254.5 + 83.5)) / r * std::exp(-1 * mParC * (zz - 125) * (zz - 125)) * -mParB * std::sin(2 * mParB * phi); + }; + + /// analytical electric field Ez + std::function mEzFunc = [& mParA = mParA, &mParB = mParB, &mParC = mParC](const DataT z, const DataT r, const DataT phi) { + const DataT zz = std::abs(z); + return mParA * (std::pow(-r + 254.5 + 83.5, 4) - 338.0 * std::pow(-r + 254.5 + 83.5, 3) + 21250.75 * (-r + 254.5 + 83.5) * (-r + 254.5 + 83.5)) * std::cos(mParB * phi) * std::cos(mParB * phi) * -2 * mParC * (zz - 125) * std::exp(-1 * mParC * (zz - 125) * (zz - 125)); + }; + + static constexpr unsigned int getID() { return ID; } + + private: + static constexpr unsigned int ID = 0; ///< needed to distinguish between the differrent classes + DataT mParA{1e-5}; ///< parameter [0] of functions + DataT mParB{0.5}; ///< parameter [1] of functions + DataT mParC{1e-4}; ///< parameter [2] of functions + o2::tpc::Side mSide{o2::tpc::Side::A}; ///< side of the TPC. Since the absolute value is taken during the calculations the choice of the side is arbitrary. +}; + +/// +/// This class gives tricubic interpolation of the electric fields and can be used to calculate the distortions/corrections. +/// The electric fields have to be calculated by the poisson solver or given by the analytical formula. +/// +template +class NumericalFields +{ + using RegularGrid = o2::tpc::RegularGrid3D; + using DataContainer = o2::tpc::DataContainer3D; + using TriCubic = o2::tpc::TriCubicInterpolator; + + public: + /// constructor + /// \param dataEr container for the data of the electrical field Er + /// \param dataEz container for the data of the electrical field Ez + /// \param dataEphi container for the data of the electrical field Ephi + /// \param gridProperties properties of the grid + /// \param side side of the tpc + NumericalFields(const DataContainer& dataEr, const DataContainer& dataEz, const DataContainer& dataEphi, const RegularGrid& gridProperties, const o2::tpc::Side side) : mDataEr{dataEr}, mDataEz{dataEz}, mDataEphi{dataEphi}, mGridProperties{gridProperties}, mSide{side} {}; + + /// \param r r coordinate + /// \param phi phi coordinate + /// \param z z coordinate + /// \return returns the function value for electric field Er for given coordinate + DataT evalEr(DataT z, DataT r, DataT phi) const { return mInterpolatorEr(z, r, phi, mInterpolType); } + + /// \param r r coordinate + /// \param phi phi coordinate + /// \param z z coordinate + /// \return returns the function value for electric field Ez for given coordinate + DataT evalEz(DataT z, DataT r, DataT phi) const { return mInterpolatorEz(z, r, phi, mInterpolType); } + + /// \param r r coordinate + /// \param phi phi coordinate + /// \param z z coordinate + /// \return returns the function value for electric field Ephi for given coordinate + DataT evalEphi(DataT z, DataT r, DataT phi) const { return mInterpolatorEphi(z, r, phi, mInterpolType); } + + o2::tpc::Side getSide() const { return mSide; } + + static constexpr unsigned int getID() { return ID; } + + /// set which kind of TriCubic interpolation algorithm is used + void setInterpolationType(const typename TriCubic::InterpolationType type) { mInterpolType = type; } + + // \return returns which kind of TriCubic interpolation algorithm is used + typename TriCubic::InterpolationType getInterpolationType() const { return mInterpolType; } + + private: + const DataContainer& mDataEr{}; ///< adress to the data container of the grid + const DataContainer& mDataEz{}; ///< adress to the data container of the grid + const DataContainer& mDataEphi{}; ///< adress to the data container of the grid + const RegularGrid& mGridProperties{}; ///< properties of the regular grid + const o2::tpc::Side mSide{}; ///< side of the TPC + + TriCubic mInterpolatorEr{mDataEr, mGridProperties}; ///< TriCubic interpolator of the electric field Er + TriCubic mInterpolatorEz{mDataEz, mGridProperties}; ///< TriCubic interpolator of the electric field Ez + TriCubic mInterpolatorEphi{mDataEphi, mGridProperties}; ///< TriCubic interpolator of the electric field Ephi + typename TriCubic::InterpolationType mInterpolType = TriCubic::InterpolationType::Sparse; ///< type of TriCubic interpolation + static constexpr unsigned int ID = 1; ///< needed to distinguish between the different classes +}; + +/// +/// This class gives tricubic interpolation of the local distortions or corrections. +/// The the local distortions or corrections can be used to calculate the global distortions/corrections. +/// +template +class DistCorrInterpolator +{ + using RegularGrid = o2::tpc::RegularGrid3D; + using DataContainer = o2::tpc::DataContainer3D; + using TriCubic = o2::tpc::TriCubicInterpolator; + + public: + /// constructor + /// \param dataDistCorrdR container for the data of the distortions dR + /// \param dataDistCorrdZ container for the data of the distortions dZ + /// \param dataDistCorrdRPhi container for the data of the distortions dPhi + /// \param gridProperties properties of the grid + /// \param side side of the tpc + DistCorrInterpolator(const DataContainer& dataDistCorrdR, const DataContainer& dataDistCorrdZ, const DataContainer& dataDistCorrdRPhi, const RegularGrid& gridProperties, const o2::tpc::Side side) : mDataDistCorrdR{dataDistCorrdR}, mDataDistCorrdZ{dataDistCorrdZ}, mDataDistCorrdRPhi{dataDistCorrdRPhi}, mGridProperties{gridProperties}, mSide{side} {}; + + /// \param r r coordinate + /// \param phi phi coordinate + /// \param z z coordinate + /// \return returns the function value for the local distortion or correction dR for given coordinate + DataT evaldR(const DataT z, const DataT r, const DataT phi) const + { + return interpolatorDistCorrdR(z, r, phi, mInterpolType); + } + + /// \param r r coordinate + /// \param phi phi coordinate + /// \param z z coordinate + /// \return returns the function value for the local distortion or correction dZ for given coordinate + DataT evaldZ(const DataT z, const DataT r, const DataT phi) const + { + return interpolatorDistCorrdZ(z, r, phi, mInterpolType); + } + + /// \param r r coordinate + /// \param phi phi coordinate + /// \param z z coordinate + /// \return returns the function value for the local distortion or correction dRPhi for given coordinate + DataT evaldRPhi(const DataT z, const DataT r, const DataT phi) const + { + return interpolatorDistCorrdRPhi(z, r, phi, mInterpolType); + } + + o2::tpc::Side getSide() const { return mSide; } + + static constexpr unsigned int getID() { return ID; } + + /// set which kind of TriCubic interpolation algorithm is used + void setInterpolationType(const typename TriCubic::InterpolationType type) { mInterpolType = type; } + + /// \return returns which kind of TriCubic interpolation algorithm is used + typename TriCubic::InterpolationType getInterpolationType() const { return mInterpolType; } + + private: + const DataContainer& mDataDistCorrdR{}; ///< adress to the data container of the grid + const DataContainer& mDataDistCorrdZ{}; ///< adress to the data container of the grid + const DataContainer& mDataDistCorrdRPhi{}; ///< adress to the data container of the grid + const RegularGrid& mGridProperties{}; ///< properties of the regular grid + const o2::tpc::Side mSide{}; ///< side of the TPC. + + TriCubic interpolatorDistCorrdR{mDataDistCorrdR, mGridProperties}; ///< TriCubic interpolator of distortion or correction dR + TriCubic interpolatorDistCorrdZ{mDataDistCorrdZ, mGridProperties}; ///< TriCubic interpolator of distortion or correction dZ + TriCubic interpolatorDistCorrdRPhi{mDataDistCorrdRPhi, mGridProperties}; ///< TriCubic interpolator of distortion or correction dRPhi + typename TriCubic::InterpolationType mInterpolType = TriCubic::InterpolationType::Sparse; ///< type of TriCubic interpolation + static constexpr unsigned int ID = 2; ///< needed to distinguish between the different classes +}; + +} // namespace tpc +} // namespace o2 + +#endif diff --git a/Detectors/TPC/spacecharge/include/TPCSpaceCharge/TriCubic.h b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/TriCubic.h new file mode 100644 index 0000000000000..6cef68f2a1b55 --- /dev/null +++ b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/TriCubic.h @@ -0,0 +1,1592 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file TriCubic.h +/// \brief Definition of TriCubic class +/// +/// \author Matthias Kleiner + +#ifndef ALICEO2_TPC_TRICUBIC_H_ +#define ALICEO2_TPC_TRICUBIC_H_ + +#include "TPCSpaceCharge/Vector.h" +#include "TPCSpaceCharge/RegularGrid3D.h" +#include "TPCSpaceCharge/DataContainer3D.h" + +#if (defined(WITH_OPENMP) || defined(_OPENMP)) && !defined(__CLING__) +#include +#else +static inline int omp_get_thread_num() { return 0; } +static inline int omp_get_max_threads() { return 1; } +#endif + +namespace o2 +{ +namespace tpc +{ + +/// \class TriCubicInterpolator +/// The TriCubic class represents tricubic interpolation on a regular 3-Dim grid. +/// The algorithm which is used is based on the method developed by F. Lekien and J. Marsden and is described +/// in 'Tricubic Interpolation in Three Dimensions (2005)' http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.89.7835 +/// In this method in a first step 64 coefficients are computed by using a predefined 64*64 matrix. +/// These coefficients have to be computed for each cell in the grid, but are only computed when querying a point in a given cell. +/// The calculated coefficient is then stored for only the last cell and will be reused if the next query point lies in the same cell. +/// +/// Additionally the classical one dimensional approach of interpolating values is implemented. This algorithm is faster when interpolating only a few values inside each cube. +/// +/// periodic boundary conditions are used in phi direction. + +/// void test() +/// { +/// // define the grid: +/// // define number of vertices per dimension +/// const int zvertices = 40; +/// const int rvertices = 40; +/// const int phivertices = 40; +/// +/// // define min range +/// float zmin = 0; +/// float rmin = 0; +/// float phimin = 0; +/// +/// // define spacing between grid vertices +/// float zSpacing = 0.25; +/// float rSpacing = 0.25; +/// float phiSpacing = 2 * M_PI / phivertices; +/// +/// // create grid and datacontainer object +/// o2::tpc::RegularGrid3D grid3D(zmin, rmin, phimin, zSpacing, rSpacing, phiSpacing); +/// o2::tpc::DataContainer3D data3D; +/// +/// // fill the DataContainer3D with some values +/// for (int iz = 0; iz < zvertices; ++iz) { +/// for (int ir = 0; ir < rvertices; ++ir) { +/// for (int iphi = 0; iphi < phivertices; ++iphi) { +/// const float izPos = zSpacing * iz + zmin; +/// const float irPos = rSpacing * ir + rmin; +/// const float iphiPos = phiSpacing * iphi + phimin; +/// data3D(iz, ir, iphi) = std::sin(irPos * izPos / 10.) + std::cos(iphiPos); // some arbitrary function is used here +/// } +/// } +/// } +/// +/// // create tricubic interpolator +/// o2::tpc::TriCubicInterpolator interpolator(data3D, grid3D); +/// +/// // query some values +/// for (float iz = grid3D.getGridMinX(); iz < grid3D.getGridMaxX(); iz += zSpacing / 3.) { +/// for (float ir = grid3D.getGridMinY(); ir < grid3D.getGridMaxY(); ir += rSpacing / 3.) { +/// for (float iphi = grid3D.getGridMinZ() - 2 * phiSpacing; iphi < grid3D.getGridMaxZ() + 2 * phiSpacing; iphi += phiSpacing / 3.) { +/// const float zQuery = iz; +/// const float rQuery = ir; +/// const float phiQuery = iphi; +/// +/// const float interpolatedSparse = interpolator(zQuery, rQuery, phiQuery, o2::tpc::TriCubicInterpolator::InterpolationType::Sparse); +/// const float interpolatedDense = interpolator(zQuery, rQuery, phiQuery, o2::tpc::TriCubicInterpolator::InterpolationType::Dense); +/// const float trueValue = std::sin(rQuery * zQuery / 10.) + std::cos(phiQuery); +/// const float interpolatedDerivative = interpolator(zQuery, rQuery, phiQuery, 1, 1, 0); +/// const float trueDerivative = 1 / 10. * std::cos(rQuery * zQuery / 10.) - rQuery / 10. * std::sin(rQuery * zQuery / 10.) * zQuery / 10.; +/// } +/// } +/// } +/// } + +/// \tparam DataT the type of data which is used during the calculations +/// \tparam Nz number of vertices in r direction +/// \tparam Nr number of vertices in phi direction +/// \tparam Nphi number of vertices in phi direction +template +class TriCubicInterpolator +{ + using Grid3D = RegularGrid3D; + using DataContainer = DataContainer3D; + using VDataT = Vc::Vector; + + public: + /// Constructor for a tricubic interpolator + /// \param gridData struct containing access to the values of the grid + /// \param gridProperties properties of the 3D grid + TriCubicInterpolator(const DataContainer& gridData, const Grid3D& gridProperties) : mGridData{gridData}, mGridProperties{gridProperties} {}; + + enum class ExtrapolationType { + Linear = 0, ///< assume linear dependency at the boundaries of the grid + Parabola = 1, ///< assume parabolic dependency at the boundaries of the grid + }; + + enum class InterpolationType { + Sparse = 0, ///< using one dimensional method of interpolation (fast when interpolating only a few values in one cube in the grid) + Dense = 1, ///< using three dimensional method of interpolation (fast when interpolating a lot values in one cube in the grid) + }; + + // interpolate value at given coordinate + /// \param z z coordinate + /// \param r r coordinate + /// \param phi phi coordinate + /// \param type interpolation algorithm + /// \return returns the interpolated value at given coordinate + DataT operator()(const DataT z, const DataT r, const DataT phi, const InterpolationType type = InterpolationType::Sparse) const + { + if (type == InterpolationType::Sparse) { + return interpolateSparse(z, r, phi); + } else { + const Vector coordinates{{z, r, phi}}; // vector holding the coordinates + const auto relPos = processInp(coordinates, false); // vector containing the relative position to + return interpolateDense(relPos); + } + } + + /// interpolate derivative at given coordinate + /// \param z z coordinate + /// \param r r coordinate + /// \param phi phi coordinate + /// \param derz order of derivative d/dz: derz=1 -> d/dz f(z,r,phi), derz=2 -> d^2/dz^2 f(z,r,phi), derz=3 -> d^3/dz^3 f(z,r,phi) + /// \param derphi order of derivative d/dr: derr=1 -> d/dr f(z,r,phi), derr=2 -> d^2/dr^2 f(z,r,phi), derr=3 -> d^3/dr^3 f(z,r,phi) + /// \param derphi order of derivative d/dphi: derphi=1 -> d/dphi f(z,r,phi), derphi=2 -> d^2/dphi^2 f(z,r,phi), derphi=3 -> d^3/dphi^3 f(z,r,phi) + /// derz=1 and derr=2 -> d/dz * d^2/dr^2 * f(z,r,phi) + /// \return returns the interpolated derivative at given coordinate + DataT operator()(const DataT z, const DataT r, const DataT phi, const size_t derz, const size_t derr, const size_t derphi) const + { + const Vector coordinates{{z, r, phi}}; // vector holding the coordinates + const auto relPos = processInp(coordinates, false); + return evalDerivative(relPos[0], relPos[1], relPos[2], derz, derr, derphi); + } + + /// set which type of extrapolation is used at the grid boundaries (linear or parabol can be used with periodic phi axis and non periodic z and r axis). + /// \param extrapolationType sets type of extrapolation. See enum ExtrapolationType for different types + void setExtrapolationType(const ExtrapolationType extrapolationType) { mExtrapolationType = extrapolationType; } + + /// \return returns the extrapolation technique for missing boundary values + ExtrapolationType getExtrapolationType() const { return mExtrapolationType; } + + /// get the number of threads used for some of the calculations + static int getNThreads() { return sNThreads; } + + /// set the number of threads used for some of the calculations + static void setNThreads(int nThreads) { sNThreads = nThreads; } + + /// \return returns the number of the thread. Each thread should have an individual thread number + int getThreadNum() const { return sThreadnum; } + + /// \return performs a check if the interpolator can be used with maximum number of threads + bool checkThreadSafety() const { return sNThreads <= omp_get_max_threads(); } + + private: + // matrix containing the 'relationship between the derivatives at the corners of the elements and the coefficients' + inline static Vc::Memory sMat[64]{ + {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {-3, 3, 0, 0, 0, 0, 0, 0, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {2, -2, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {-3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, -3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {9, -9, -9, 9, 0, 0, 0, 0, 6, 3, -6, -3, 0, 0, 0, 0, 6, -6, 3, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {-6, 6, 6, -6, 0, 0, 0, 0, -3, -3, 3, 3, 0, 0, 0, 0, -4, 4, -2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -2, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {2, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 2, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {-6, 6, 6, -6, 0, 0, 0, 0, -4, -2, 4, 2, 0, 0, 0, 0, -3, 3, -3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -1, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {4, -4, -4, 4, 0, 0, 0, 0, 2, 2, -2, -2, 0, 0, 0, 0, 2, -2, 2, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 3, 0, 0, 0, 0, 0, 0, -2, -1, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, -1, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, -9, -9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 3, -6, -3, 0, 0, 0, 0, 6, -6, 3, -3, 0, 0, 0, 0, 4, 2, 2, 1, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -6, 6, 6, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, -3, 3, 3, 0, 0, 0, 0, -4, 4, -2, 2, 0, 0, 0, 0, -2, -2, -1, -1, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -6, 6, 6, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, -2, 4, 2, 0, 0, 0, 0, -3, 3, -3, 3, 0, 0, 0, 0, -2, -1, -2, -1, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, -4, -4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, -2, -2, 0, 0, 0, 0, 2, -2, 2, -2, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0}, + {-3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, -3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {9, -9, 0, 0, -9, 9, 0, 0, 6, 3, 0, 0, -6, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, -6, 0, 0, 3, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 2, 0, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {-6, 6, 0, 0, 6, -6, 0, 0, -3, -3, 0, 0, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, 4, 0, 0, -2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -2, 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, -1, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, -9, 0, 0, -9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 3, 0, 0, -6, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, -6, 0, 0, 3, -3, 0, 0, 4, 2, 0, 0, 2, 1, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -6, 6, 0, 0, 6, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, -3, 0, 0, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, 4, 0, 0, -2, 2, 0, 0, -2, -2, 0, 0, -1, -1, 0, 0}, + {9, 0, -9, 0, -9, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 3, 0, -6, 0, -3, 0, 6, 0, -6, 0, 3, 0, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 2, 0, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 9, 0, -9, 0, -9, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 3, 0, -6, 0, -3, 0, 6, 0, -6, 0, 3, 0, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 2, 0, 2, 0, 1, 0}, + {-27, 27, 27, -27, 27, -27, -27, 27, -18, -9, 18, 9, 18, 9, -18, -9, -18, 18, -9, 9, 18, -18, 9, -9, -18, 18, 18, -18, -9, 9, 9, -9, -12, -6, -6, -3, 12, 6, 6, 3, -12, -6, 12, 6, -6, -3, 6, 3, -12, 12, -6, 6, -6, 6, -3, 3, -8, -4, -4, -2, -4, -2, -2, -1}, + {18, -18, -18, 18, -18, 18, 18, -18, 9, 9, -9, -9, -9, -9, 9, 9, 12, -12, 6, -6, -12, 12, -6, 6, 12, -12, -12, 12, 6, -6, -6, 6, 6, 6, 3, 3, -6, -6, -3, -3, 6, 6, -6, -6, 3, 3, -3, -3, 8, -8, 4, -4, 4, -4, 2, -2, 4, 4, 2, 2, 2, 2, 1, 1}, + {-6, 0, 6, 0, 6, 0, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 0, -3, 0, 3, 0, 3, 0, -4, 0, 4, 0, -2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, -2, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, -6, 0, 6, 0, 6, 0, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 0, -3, 0, 3, 0, 3, 0, -4, 0, 4, 0, -2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, -2, 0, -1, 0, -1, 0}, + {18, -18, -18, 18, -18, 18, 18, -18, 12, 6, -12, -6, -12, -6, 12, 6, 9, -9, 9, -9, -9, 9, -9, 9, 12, -12, -12, 12, 6, -6, -6, 6, 6, 3, 6, 3, -6, -3, -6, -3, 8, 4, -8, -4, 4, 2, -4, -2, 6, -6, 6, -6, 3, -3, 3, -3, 4, 2, 4, 2, 2, 1, 2, 1}, + {-12, 12, 12, -12, 12, -12, -12, 12, -6, -6, 6, 6, 6, 6, -6, -6, -6, 6, -6, 6, 6, -6, 6, -6, -8, 8, 8, -8, -4, 4, 4, -4, -3, -3, -3, -3, 3, 3, 3, 3, -4, -4, 4, 4, -2, -2, 2, 2, -4, 4, -4, 4, -2, 2, -2, 2, -2, -2, -2, -2, -1, -1, -1, -1}, + {2, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {-6, 6, 0, 0, 6, -6, 0, 0, -4, -2, 0, 0, 4, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 3, 0, 0, -3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -1, 0, 0, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {4, -4, 0, 0, -4, 4, 0, 0, 2, 2, 0, 0, -2, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 2, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -6, 6, 0, 0, 6, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, -2, 0, 0, 4, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3, 3, 0, 0, -3, 3, 0, 0, -2, -1, 0, 0, -2, -1, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, -4, 0, 0, -4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, -2, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 2, -2, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0}, + {-6, 0, 6, 0, 6, 0, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, 0, -2, 0, 4, 0, 2, 0, -3, 0, 3, 0, -3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, -1, 0, -2, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, -6, 0, 6, 0, 6, 0, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, 0, -2, 0, 4, 0, 2, 0, -3, 0, 3, 0, -3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, -1, 0, -2, 0, -1, 0}, + {18, -18, -18, 18, -18, 18, 18, -18, 12, 6, -12, -6, -12, -6, 12, 6, 12, -12, 6, -6, -12, 12, -6, 6, 9, -9, -9, 9, 9, -9, -9, 9, 8, 4, 4, 2, -8, -4, -4, -2, 6, 3, -6, -3, 6, 3, -6, -3, 6, -6, 3, -3, 6, -6, 3, -3, 4, 2, 2, 1, 4, 2, 2, 1}, + {-12, 12, 12, -12, 12, -12, -12, 12, -6, -6, 6, 6, 6, 6, -6, -6, -8, 8, -4, 4, 8, -8, 4, -4, -6, 6, 6, -6, -6, 6, 6, -6, -4, -4, -2, -2, 4, 4, 2, 2, -3, -3, 3, 3, -3, -3, 3, 3, -4, 4, -2, 2, -4, 4, -2, 2, -2, -2, -1, -1, -2, -2, -1, -1}, + {4, 0, -4, 0, -4, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, -2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 4, 0, -4, 0, -4, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, -2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0}, + {-12, 12, 12, -12, 12, -12, -12, 12, -8, -4, 8, 4, 8, 4, -8, -4, -6, 6, -6, 6, 6, -6, 6, -6, -6, 6, 6, -6, -6, 6, 6, -6, -4, -2, -4, -2, 4, 2, 4, 2, -4, -2, 4, 2, -4, -2, 4, 2, -3, 3, -3, 3, -3, 3, -3, 3, -2, -1, -2, -1, -2, -1, -2, -1}, + {8, -8, -8, 8, -8, 8, 8, -8, 4, 4, -4, -4, -4, -4, 4, 4, 4, -4, 4, -4, -4, 4, -4, 4, 4, -4, -4, 4, 4, -4, -4, 4, 2, 2, 2, 2, -2, -2, -2, -2, 2, 2, -2, -2, 2, 2, -2, -2, 2, -2, 2, -2, 2, -2, 2, -2, 1, 1, 1, 1, 1, 1, 1, 1}}; ///< matrix containing the 'relationship between the derivatives at the corners of the elements and the coefficients' + + inline static Matrix sMatrixA{sMat}; ///< this matrix is used for vectorized operations with the 64*64 matrix + static constexpr unsigned int FDim = Grid3D::getDim(); ///< dimensions of the grid + static constexpr unsigned int FZ = Grid3D::getFX(); ///< index for z coordinate + static constexpr unsigned int FR = Grid3D::getFY(); ///< index for r coordinate + static constexpr unsigned int FPHI = Grid3D::getFZ(); ///< index for phi coordinate + const DataContainer& mGridData{}; ///< adress to the data container of the grid + const Grid3D& mGridProperties{}; ///< adress to the properties of the grid + inline static thread_local const size_t sThreadnum{static_cast(omp_get_thread_num())}; ///< save for each thread the thread number to get fast access to the correct array + inline static int sNThreads{omp_get_max_threads()}; ///< number of threads the tricubic interpolator can be used with + std::unique_ptr[]> mCoefficients = std::make_unique[]>(sNThreads); ///< coefficients needed to interpolate a value + std::unique_ptr[]> mLastInd = std::make_unique[]>(sNThreads); ///< stores the index for the cell, where the coefficients are already evaluated (only the coefficients for the last cell are stored) + std::unique_ptr mInitialized = std::make_unique(sNThreads); ///< sets the flag if the coefficients are evaluated at least once + ExtrapolationType mExtrapolationType = ExtrapolationType::Parabola; ///< sets which type of extrapolation for missing points at boundary is used. Linear and Parabola is only supported for perdiodic phi axis and non periodic z and r axis + + // DEFINITION OF enum GridPos + //======================================================== + // r + // | 6------F---7 + // | / | / | + // | K G YR L H + // | / | / | + // | 2---B------3 | + // | | | | | + // | | 4---|---E--5 + // | C XL / D XR / + // | | I YL | J + // | | / | / + // | 0---A------1 + // |------------------------------- z + // / + // / + // / + // phi + //======================================================== + + enum class GridPos { + None = 27, + InnerVolume = 26, + Edge0 = 0, + Edge1 = 1, + Edge2 = 2, + Edge3 = 3, + Edge4 = 4, + Edge5 = 5, + Edge6 = 6, + Edge7 = 7, + LineA = 8, + LineB = 9, + LineC = 10, + LineD = 11, + LineE = 12, + LineF = 13, + LineG = 14, + LineH = 15, + LineI = 16, + LineJ = 17, + LineK = 18, + LineL = 19, + SideXRight = 20, + SideXLeft = 21, + SideYRight = 22, + SideYLeft = 23, + SideZRight = 24, + SideZLeft = 25 + }; + + void setValues(const int iz, const int ir, const int iphi, DataT cVals[64]) const; + + const Vector processInp(const Vector& coordinates, const bool sparse = false) const; + + // calculate the coefficients needed for the interpolation using the 64*64 matrix. + // this is the 'slow' part of the code and might be optimized + void calcCoefficients(const unsigned int iz, const unsigned int ir, const unsigned int iphi) const; + + DataT interpolateDense(const Vector& pos) const; + + // interpolate value at given coordinate - this method doesnt compute and stores the coefficients and is faster when quering only a few values per cube + /// \param z z coordinate + /// \param r r coordinate + /// \param phi phi coordinate + /// \return returns the interpolated value at given coordinate + DataT interpolateSparse(const DataT z, const DataT r, const DataT phi) const; + + DataT evalDerivative(const DataT dz, const DataT dr, const DataT dphi, const size_t derz, const size_t derr, const size_t derphi) const; + + // for periodic boundary conditions + void getDataIndexCircularArray(const int index0, const int dim, int arr[]) const; + + // for non periodic boundary conditions + void getDataIndexNonCircularArray(const int index0, const int dim, int arr[]) const; + + // this helps to get circular and non circular padding indices + int getRegulatedDelta(const int index0, const int delta, const unsigned int dim, const int offs) const + { + return mGridProperties.isIndexInGrid(index0 + delta, dim) ? delta : offs; + } + + void initInterpolator(const unsigned int iz, const unsigned int ir, const unsigned int iphi) const; + + DataT extrapolation(const DataT valk, const DataT valk1, const DataT valk2) const; + + DataT linearExtrapolation(const DataT valk, const DataT valk1) const; + + DataT parabolExtrapolation(const DataT valk, const DataT valk1, const DataT valk2) const; + + GridPos findPos(const int iz, const int ir, const int iphi) const; + + bool isInInnerVolume(const int iz, const int ir, const int iphi, GridPos& posType) const; + + bool findEdge(const int iz, const int ir, const int iphi, GridPos& posType) const; + + bool findLine(const int iz, const int ir, const int iphi, GridPos& posType) const; + + bool findSide(const int iz, const int ir, const int iphi, GridPos& posType) const; + + bool isSideRight(const int ind, const int dim) const; + + bool isSideLeft(const int ind) const; +}; + +/// +/// ======================================================================================================== +/// Inline implementations +/// ======================================================================================================== +/// + +template +void TriCubicInterpolator::initInterpolator(const unsigned int iz, const unsigned int ir, const unsigned int iphi) const +{ + calcCoefficients(iz, ir, iphi); + + // store current cell + mInitialized[sThreadnum] = true; + mLastInd[sThreadnum][FZ] = iz; + mLastInd[sThreadnum][FR] = ir; + mLastInd[sThreadnum][FPHI] = iphi; +} + +template +DataT TriCubicInterpolator::evalDerivative(const DataT dz, const DataT dr, const DataT dphi, const size_t derz, const size_t derr, const size_t derphi) const +{ + //TODO optimize this + DataT ret{}; + for (size_t i = derz; i < 4; i++) { + for (size_t j = derr; j < 4; j++) { + for (size_t k = derphi; k < 4; k++) { + + const size_t index = i + j * 4 + 16 * k; + DataT cont = mCoefficients[sThreadnum][index] * std::pow(dz, i - derz) * std::pow(dr, j - derr) * std::pow(dphi, k - derphi); + for (size_t w = 0; w < derz; w++) { + cont *= (i - w); + } + for (size_t w = 0; w < derr; w++) { + cont *= (j - w); + } + for (size_t w = 0; w < derphi; w++) { + cont *= (k - w); + } + ret += cont; + } + } + } + const DataT norm = std::pow(mGridProperties.getInvSpacingX(), derz) * std::pow(mGridProperties.getInvSpacingY(), derr) * std::pow(mGridProperties.getInvSpacingZ(), derphi); + return (ret * norm); +} + +template +DataT TriCubicInterpolator::extrapolation(const DataT valk, const DataT valk1, const DataT valk2) const +{ + switch (mExtrapolationType) { + case ExtrapolationType::Linear: + default: + return linearExtrapolation(valk, valk1); + break; + case ExtrapolationType::Parabola: + return parabolExtrapolation(valk, valk1, valk2); + break; + } +} + +template +DataT TriCubicInterpolator::linearExtrapolation(const DataT valk, const DataT valk1) const +{ + const DataT val = 2 * valk - valk1; + return val; +} + +template +DataT TriCubicInterpolator::parabolExtrapolation(const DataT valk, const DataT valk1, const DataT valk2) const +{ + const DataT val = 3 * (valk - valk1) + valk2; // legendre polynom with x0=0, x1=1, x2=2 and z=-1 + return val; +} + +template +void TriCubicInterpolator::calcCoefficients(const unsigned int iz, const unsigned int ir, const unsigned int iphi) const +{ + DataT cVals[64]{}; + setValues(iz, ir, iphi, cVals); + + // needed for first derivative + const Vector vecDeriv1A{ + {cVals[22], cVals[23], cVals[26], cVals[27], cVals[38], cVals[39], cVals[42], cVals[43], + cVals[25], cVals[26], cVals[29], cVals[30], cVals[41], cVals[42], cVals[45], cVals[46], + cVals[37], cVals[38], cVals[41], cVals[42], cVals[53], cVals[54], cVals[57], cVals[58]}}; + + const Vector vecDeriv1B{ + {cVals[20], cVals[21], cVals[24], cVals[25], cVals[36], cVals[37], cVals[40], cVals[41], + cVals[17], cVals[18], cVals[21], cVals[22], cVals[33], cVals[34], cVals[37], cVals[38], + cVals[5], cVals[6], cVals[9], cVals[10], cVals[21], cVals[22], cVals[25], cVals[26]}}; + + // needed for second derivative + const Vector vecDeriv2A{ + {cVals[26], cVals[27], cVals[30], cVals[31], cVals[42], cVals[43], cVals[46], cVals[47], + cVals[38], cVals[39], cVals[42], cVals[43], cVals[54], cVals[55], cVals[58], cVals[59], + cVals[41], cVals[42], cVals[45], cVals[46], cVals[57], cVals[58], cVals[61], cVals[62]}}; + + const Vector vecDeriv2B{ + {cVals[24], cVals[25], cVals[28], cVals[29], cVals[40], cVals[41], cVals[44], cVals[45], + cVals[36], cVals[37], cVals[40], cVals[41], cVals[52], cVals[53], cVals[56], cVals[57], + cVals[33], cVals[34], cVals[37], cVals[38], cVals[49], cVals[50], cVals[53], cVals[54]}}; + + const Vector vecDeriv2C{ + {cVals[18], cVals[19], cVals[22], cVals[23], cVals[34], cVals[35], cVals[38], cVals[39], + cVals[6], cVals[7], cVals[10], cVals[11], cVals[22], cVals[23], cVals[26], cVals[27], + cVals[9], cVals[10], cVals[13], cVals[14], cVals[25], cVals[26], cVals[29], cVals[30]}}; + + const Vector vecDeriv2D{ + {cVals[16], cVals[17], cVals[20], cVals[21], cVals[32], cVals[33], cVals[36], cVals[37], + cVals[4], cVals[5], cVals[8], cVals[9], cVals[20], cVals[21], cVals[24], cVals[25], + cVals[1], cVals[2], cVals[5], cVals[6], cVals[17], cVals[18], cVals[21], cVals[22]}}; + + // needed for third derivative + const Vector vecDeriv3A{{cVals[42], cVals[43], cVals[46], cVals[47], cVals[58], cVals[59], cVals[62], cVals[63]}}; + const Vector vecDeriv3B{{cVals[40], cVals[41], cVals[44], cVals[45], cVals[56], cVals[57], cVals[60], cVals[61]}}; + const Vector vecDeriv3C{{cVals[34], cVals[35], cVals[38], cVals[39], cVals[50], cVals[51], cVals[54], cVals[55]}}; + const Vector vecDeriv3D{{cVals[32], cVals[33], cVals[36], cVals[37], cVals[48], cVals[49], cVals[52], cVals[53]}}; + const Vector vecDeriv3E{{cVals[10], cVals[11], cVals[14], cVals[15], cVals[26], cVals[27], cVals[30], cVals[31]}}; + const Vector vecDeriv3F{{cVals[8], cVals[9], cVals[12], cVals[13], cVals[24], cVals[25], cVals[28], cVals[29]}}; + const Vector vecDeriv3G{{cVals[2], cVals[3], cVals[6], cVals[7], cVals[18], cVals[19], cVals[22], cVals[23]}}; + const Vector vecDeriv3H{{cVals[0], cVals[1], cVals[4], cVals[5], cVals[16], cVals[17], cVals[20], cVals[21]}}; + + // factor for first derivative + const DataT fac1{0.5}; + const Vector vfac1{{fac1, fac1, fac1, fac1, fac1, fac1, fac1, fac1, fac1, fac1, fac1, fac1, fac1, fac1, fac1, fac1, fac1, fac1, fac1, fac1, fac1, fac1, fac1, fac1}}; + + // factor for second derivative + const DataT fac2{0.25}; + const Vector vfac2{{fac2, fac2, fac2, fac2, fac2, fac2, fac2, fac2, fac2, fac2, fac2, fac2, fac2, fac2, fac2, fac2, fac2, fac2, fac2, fac2, fac2, fac2, fac2, fac2}}; + + // factor for third derivative + const DataT fac3{0.125}; + const Vector vfac3{{fac3, fac3, fac3, fac3, fac3, fac3, fac3, fac3}}; + + // compute the derivatives + const Vector vecDeriv1Res{vfac1 * (vecDeriv1A - vecDeriv1B)}; + const Vector vecDeriv2Res{vfac2 * (vecDeriv2A - vecDeriv2B - vecDeriv2C + vecDeriv2D)}; + const Vector vecDeriv3Res{vfac3 * (vecDeriv3A - vecDeriv3B - vecDeriv3C + vecDeriv3D - vecDeriv3E + vecDeriv3F + vecDeriv3G - vecDeriv3H)}; + + const Vector matrixPar{ + {cVals[21], cVals[22], cVals[25], cVals[26], cVals[37], cVals[38], cVals[41], cVals[42], + vecDeriv1Res[0], vecDeriv1Res[1], vecDeriv1Res[2], vecDeriv1Res[3], vecDeriv1Res[4], vecDeriv1Res[5], vecDeriv1Res[6], vecDeriv1Res[7], vecDeriv1Res[8], vecDeriv1Res[9], vecDeriv1Res[10], + vecDeriv1Res[11], vecDeriv1Res[12], vecDeriv1Res[13], vecDeriv1Res[14], vecDeriv1Res[15], vecDeriv1Res[16], vecDeriv1Res[17], vecDeriv1Res[18], vecDeriv1Res[19], vecDeriv1Res[20], vecDeriv1Res[21], + vecDeriv1Res[22], vecDeriv1Res[23], vecDeriv2Res[0], vecDeriv2Res[1], vecDeriv2Res[2], vecDeriv2Res[3], vecDeriv2Res[4], vecDeriv2Res[5], vecDeriv2Res[6], vecDeriv2Res[7], vecDeriv2Res[8], vecDeriv2Res[9], + vecDeriv2Res[10], vecDeriv2Res[11], vecDeriv2Res[12], vecDeriv2Res[13], vecDeriv2Res[14], vecDeriv2Res[15], vecDeriv2Res[16], vecDeriv2Res[17], vecDeriv2Res[18], vecDeriv2Res[19], vecDeriv2Res[20], + vecDeriv2Res[21], vecDeriv2Res[22], vecDeriv2Res[23], vecDeriv3Res[0], vecDeriv3Res[1], vecDeriv3Res[2], vecDeriv3Res[3], vecDeriv3Res[4], vecDeriv3Res[5], vecDeriv3Res[6], vecDeriv3Res[7]}}; + + // calc coeffiecients + mCoefficients[sThreadnum] = sMatrixA * matrixPar; +} + +template +DataT TriCubicInterpolator::interpolateDense(const Vector& pos) const +{ + // the formula for evaluating the interpolation is as follows: + // f(z,r,phi) = \sum_{i,j,k=0}^3 a_{ijk} * z^{i} * r^{j} * phi^{k} + // a_{ijk} is stored in mCoefficients[] and are computed in the function calcCoefficientsX() + + const Vector vals0{{1, 1, 1}}; // z^0, r^0, phi^0 + const Vector vals2{pos * pos}; // z^2, r^2, phi^2 + const Vector vals3{vals2 * pos}; // z^3, r^3, phi^3 + + const DataT valX[4]{vals0[FZ], pos[FZ], vals2[FZ], vals3[FZ]}; + const Vector vecValX{ + {valX[0], valX[1], valX[2], valX[3], valX[0], valX[1], valX[2], valX[3], valX[0], valX[1], valX[2], valX[3], valX[0], valX[1], valX[2], valX[3], + valX[0], valX[1], valX[2], valX[3], valX[0], valX[1], valX[2], valX[3], valX[0], valX[1], valX[2], valX[3], valX[0], valX[1], valX[2], valX[3], + valX[0], valX[1], valX[2], valX[3], valX[0], valX[1], valX[2], valX[3], valX[0], valX[1], valX[2], valX[3], valX[0], valX[1], valX[2], valX[3], + valX[0], valX[1], valX[2], valX[3], valX[0], valX[1], valX[2], valX[3], valX[0], valX[1], valX[2], valX[3], valX[0], valX[1], valX[2], valX[3]}}; + + const DataT valY[4]{vals0[FR], pos[FR], vals2[FR], vals3[FR]}; + const Vector vecValY{ + {valY[0], valY[0], valY[0], valY[0], valY[1], valY[1], valY[1], valY[1], valY[2], valY[2], valY[2], valY[2], valY[3], valY[3], valY[3], valY[3], + valY[0], valY[0], valY[0], valY[0], valY[1], valY[1], valY[1], valY[1], valY[2], valY[2], valY[2], valY[2], valY[3], valY[3], valY[3], valY[3], + valY[0], valY[0], valY[0], valY[0], valY[1], valY[1], valY[1], valY[1], valY[2], valY[2], valY[2], valY[2], valY[3], valY[3], valY[3], valY[3], + valY[0], valY[0], valY[0], valY[0], valY[1], valY[1], valY[1], valY[1], valY[2], valY[2], valY[2], valY[2], valY[3], valY[3], valY[3], valY[3]}}; + + const DataT valZ[4]{vals0[FPHI], pos[FPHI], vals2[FPHI], vals3[FPHI]}; + const Vector vecValZ{ + {valZ[0], valZ[0], valZ[0], valZ[0], valZ[0], valZ[0], valZ[0], valZ[0], valZ[0], valZ[0], valZ[0], valZ[0], valZ[0], valZ[0], valZ[0], valZ[0], + valZ[1], valZ[1], valZ[1], valZ[1], valZ[1], valZ[1], valZ[1], valZ[1], valZ[1], valZ[1], valZ[1], valZ[1], valZ[1], valZ[1], valZ[1], valZ[1], + valZ[2], valZ[2], valZ[2], valZ[2], valZ[2], valZ[2], valZ[2], valZ[2], valZ[2], valZ[2], valZ[2], valZ[2], valZ[2], valZ[2], valZ[2], valZ[2], + valZ[3], valZ[3], valZ[3], valZ[3], valZ[3], valZ[3], valZ[3], valZ[3], valZ[3], valZ[3], valZ[3], valZ[3], valZ[3], valZ[3], valZ[3], valZ[3]}}; + + // result = f(z,r,phi) = \sum_{i,j,k=0}^3 a_{ijk} * z^{i} * r^{j} * phi^{k} + const DataT result = sum(mCoefficients[sThreadnum] * vecValX * vecValY * vecValZ); + return result; +} + +template +DataT TriCubicInterpolator::interpolateSparse(const DataT z, const DataT r, const DataT phi) const +{ + const Vector coordinates{{z, r, phi}}; // vector holding the coordinates + const auto posRel = processInp(coordinates, true); + + DataT cVals[64]{}; + setValues(mLastInd[sThreadnum][FZ], mLastInd[sThreadnum][FR], mLastInd[sThreadnum][FPHI], cVals); + + const Vector vals0{posRel}; + const Vector vals1{vals0 * vals0}; + const Vector vals2{vals0 * vals1}; + + const int nPoints = 4; + const Vector vecValX{{1, vals0[FZ], vals1[FZ], vals2[FZ]}}; + const Vector vecValY{{1, vals0[FR], vals1[FR], vals2[FR]}}; + const Vector vecValZ{{1, vals0[FPHI], vals1[FPHI], vals2[FPHI]}}; + + const Vc::Memory matrA[nPoints]{ + {0, -0.5, 1, -0.5}, + {1, 0, -2.5, 1.5}, + {0, 0.5, 2., -1.5}, + {0, 0, -0.5, 0.5}}; + + const Matrix matrixA{matrA}; + const Vector vecValXMult{matrixA * vecValX}; + const Vector vecValYMult{matrixA * vecValY}; + const Vector vecValZMult{matrixA * vecValZ}; + + DataT result{}; + int ind = 0; + for (int slice = 0; slice < nPoints; ++slice) { + const Vector vecA{vecValZMult[slice] * vecValYMult}; + for (int row = 0; row < nPoints; ++row) { + const Vector vecD{{cVals[ind], cVals[++ind], cVals[++ind], cVals[++ind]}}; + ++ind; + result += sum(vecA[row] * vecValXMult * vecD); + } + } + return result; +} + +template +const Vector TriCubicInterpolator::processInp(const Vector& coordinates, const bool sparse) const +{ + Vector posRel{(coordinates - mGridProperties.getGridMin()) * mGridProperties.getInvSpacing()}; // needed for the grid index + posRel[FPHI] = mGridProperties.clampToGridCircularRel(posRel[FPHI], FPHI); + const Vector posRelN{posRel}; + posRel[FZ] = mGridProperties.clampToGridRel(posRel[FZ], FZ); + posRel[FR] = mGridProperties.clampToGridRel(posRel[FR], FR); + + const Vector index{floor(posRel)}; + + if (!sparse && (!mInitialized[sThreadnum] || !(mLastInd[sThreadnum] == index))) { + initInterpolator(index[FZ], index[FR], index[FPHI]); + } else if (sparse) { + mLastInd[sThreadnum][FZ] = index[FZ]; + mLastInd[sThreadnum][FR] = index[FR]; + mLastInd[sThreadnum][FPHI] = index[FPHI]; + mInitialized[sThreadnum] = false; + } + return posRelN - index; +} + +// for perdiodic boundary condition +template +void TriCubicInterpolator::getDataIndexCircularArray(const int index0, const int dim, int arr[]) const +{ + const int delta_min1 = getRegulatedDelta(index0, -1, dim, mGridProperties.getN(dim) - 1); + const int delta_plus1 = getRegulatedDelta(index0, +1, dim, 1 - mGridProperties.getN(dim)); + const int delta_plus2 = getRegulatedDelta(index0, +2, dim, 2 - mGridProperties.getN(dim)); + + arr[0] = mGridProperties.getDeltaDataIndex(delta_min1, dim); + arr[1] = mGridProperties.getDeltaDataIndex(delta_plus1, dim); + arr[2] = mGridProperties.getDeltaDataIndex(delta_plus2, dim); +} + +// for non perdiodic boundary condition +template +void TriCubicInterpolator::getDataIndexNonCircularArray(const int index0, const int dim, int arr[]) const +{ + const int delta_min1 = getRegulatedDelta(index0, -1, dim, 0); + const int delta_plus1 = getRegulatedDelta(index0, +1, dim, 0); + const int delta_plus2 = getRegulatedDelta(index0, +2, dim, delta_plus1); + + arr[0] = mGridProperties.getDeltaDataIndex(delta_min1, dim); + arr[1] = mGridProperties.getDeltaDataIndex(delta_plus1, dim); + arr[2] = mGridProperties.getDeltaDataIndex(delta_plus2, dim); +} + +template +typename TriCubicInterpolator::GridPos TriCubicInterpolator::findPos(const int iz, const int ir, const int iphi) const +{ + GridPos pos = GridPos::None; + if (isInInnerVolume(iz, ir, iphi, pos)) { + return pos; + } + + if (findEdge(iz, ir, iphi, pos)) { + return pos; + } + + if (findLine(iz, ir, iphi, pos)) { + return pos; + } + + if (findSide(iz, ir, iphi, pos)) { + return pos; + } + return GridPos::None; +} + +template +bool TriCubicInterpolator::findEdge(const int iz, const int ir, const int iphi, GridPos& posType) const +{ + const int iR = 2; + if (iz == 0 && ir == 0) { + if (iphi == 0) { + posType = GridPos::Edge0; + return true; + } else if (iphi == Nphi - iR) { + posType = GridPos::Edge4; + return true; + } + } else if (iz == Nz - iR && ir == 0) { + if (iphi == 0) { + posType = GridPos::Edge1; + return true; + } else if (iphi == Nphi - iR) { + posType = GridPos::Edge5; + return true; + } + } else if (iz == 0 && ir == Nr - iR) { + if (iphi == 0) { + posType = GridPos::Edge2; + return true; + } else if (iphi == Nphi - iR) { + posType = GridPos::Edge6; + return true; + } + } else if (iz == Nz - iR && ir == Nr - iR) { + if (iphi == 0) { + posType = GridPos::Edge3; + return true; + } else if (iphi == Nphi - iR) { + posType = GridPos::Edge7; + return true; + } + } + return false; +} + +template +bool TriCubicInterpolator::findLine(const int iz, const int ir, const int iphi, GridPos& posType) const +{ + const int iR = 2; + //check line + if (ir == 0) { + if (iphi == 0) { + posType = GridPos::LineA; + return true; + } else if (iphi == Nphi - iR) { + posType = GridPos::LineE; + return true; + } + if (iz == 0) { + posType = GridPos::LineI; + return true; + } else if (iz == Nz - iR) { + posType = GridPos::LineJ; + return true; + } + } else if (ir == Nr - iR) { + if (iphi == 0) { + posType = GridPos::LineB; + return true; + } else if (iphi == Nphi - iR) { + posType = GridPos::LineF; + return true; + } + if (iz == 0) { + posType = GridPos::LineK; + return true; + } else if (iz == Nz - iR) { + posType = GridPos::LineL; + return true; + } + } else if (iz == 0) { + if (iphi == 0) { + posType = GridPos::LineC; + return true; + } else if (iphi == Nphi - iR) { + posType = GridPos::LineG; + return true; + } + } else if (iz == Nz - iR) { + if (iphi == 0) { + posType = GridPos::LineD; + return true; + } else if (iphi == Nphi - iR) { + posType = GridPos::LineH; + return true; + } + } + return false; +} + +template +bool TriCubicInterpolator::findSide(const int iz, const int ir, const int iphi, GridPos& posType) const +{ + if (isSideRight(iz, FZ)) { + posType = GridPos::SideXRight; + return true; + } else if (isSideLeft(iz)) { + posType = GridPos::SideXLeft; + return true; + } + if (isSideRight(ir, FR)) { + posType = GridPos::SideYRight; + return true; + } else if (isSideLeft(ir)) { + posType = GridPos::SideYLeft; + return true; + } + if (isSideRight(iphi, FPHI)) { + posType = GridPos::SideZRight; + return true; + } else if (isSideLeft(iphi)) { + posType = GridPos::SideZLeft; + return true; + } + return false; +} + +template +bool TriCubicInterpolator::isInInnerVolume(const int iz, const int ir, const int iphi, GridPos& posType) const +{ + if (iz >= 1 && iz < static_cast(Nz - 2) && ir >= 1 && ir < static_cast(Nr - 2) && iphi >= 1 && iphi < static_cast(Nphi - 2)) { + posType = GridPos::InnerVolume; + return true; + } + return false; +} + +template +bool TriCubicInterpolator::isSideRight(const int ind, const int dim) const +{ + if (ind == static_cast(mGridProperties.getN(dim) - 2)) { + return true; + } + return false; +} + +template +bool TriCubicInterpolator::isSideLeft(const int ind) const +{ + if (ind == 0) { + return true; + } + return false; +} + +template +void TriCubicInterpolator::setValues(const int iz, const int ir, const int iphi, DataT cVals[64]) const +{ + const GridPos location = findPos(iz, ir, iphi); + const int ii_x_y_z = mGridData.getDataIndex(iz, ir, iphi); + cVals[21] = mGridData[ii_x_y_z]; + + int deltaZ[3]{mGridProperties.getDeltaDataIndex(-1, 0), mGridProperties.getDeltaDataIndex(1, 0), mGridProperties.getDeltaDataIndex(2, 0)}; + int deltaR[3]{mGridProperties.getDeltaDataIndex(-1, 1), mGridProperties.getDeltaDataIndex(1, 1), mGridProperties.getDeltaDataIndex(2, 1)}; + int deltaPhi[3]{}; + getDataIndexCircularArray(iphi, FPHI, deltaPhi); + + const int i0 = 0; + const int i1 = 1; + const int i2 = 2; + + switch (location) { + case GridPos::InnerVolume: + case GridPos::SideZRight: + case GridPos::SideZLeft: + default: { + const int ind[4][4][4]{ + {{ii_x_y_z + deltaPhi[i0] + deltaR[i0] + deltaZ[i0], ind[0][0][0] - deltaZ[i0], ind[0][0][1] - deltaZ[i0], ind[0][0][2] - deltaZ[i0]}, + {ind[0][0][0] - deltaR[i0], ind[0][1][0] - deltaZ[i0], ind[0][1][1] - deltaZ[i0], ind[0][1][2] - deltaZ[i0]}, + {ind[0][1][0] - deltaR[i0], ind[0][2][0] - deltaZ[i0], ind[0][2][1] - deltaZ[i0], ind[0][2][2] - deltaZ[i0]}, + {ind[0][2][0] - deltaR[i0], ind[0][3][0] - deltaZ[i0], ind[0][3][1] - deltaZ[i0], ind[0][3][2] - deltaZ[i0]}}, + {{ii_x_y_z + deltaR[i0] + deltaZ[i0], ind[1][0][0] - deltaZ[i0], ind[1][0][1] - deltaZ[i0], ind[1][0][2] - deltaZ[i0]}, + {ind[1][0][0] - deltaR[i0], ind[1][1][0] - deltaZ[i0], ind[1][1][1] - deltaZ[i0], ind[1][1][2] - deltaZ[i0]}, + {ind[1][1][0] - deltaR[i0], ind[1][2][0] - deltaZ[i0], ind[1][2][1] - deltaZ[i0], ind[1][2][2] - deltaZ[i0]}, + {ind[1][2][0] - deltaR[i0], ind[1][3][0] - deltaZ[i0], ind[1][3][1] - deltaZ[i0], ind[1][3][2] - deltaZ[i0]}}, + {{ii_x_y_z + deltaPhi[i1] + deltaR[i0] + deltaZ[i0], ind[2][0][0] - deltaZ[i0], ind[2][0][1] - deltaZ[i0], ind[2][0][2] - deltaZ[i0]}, + {ind[2][0][0] - deltaR[i0], ind[2][1][0] - deltaZ[i0], ind[2][1][1] - deltaZ[i0], ind[2][1][2] - deltaZ[i0]}, + {ind[2][1][0] - deltaR[i0], ind[2][2][0] - deltaZ[i0], ind[2][2][1] - deltaZ[i0], ind[2][2][2] - deltaZ[i0]}, + {ind[2][2][0] - deltaR[i0], ind[2][3][0] - deltaZ[i0], ind[2][3][1] - deltaZ[i0], ind[2][3][2] - deltaZ[i0]}}, + {{ii_x_y_z + deltaPhi[i2] + deltaR[i0] + deltaZ[i0], ind[3][0][0] - deltaZ[i0], ind[3][0][1] - deltaZ[i0], ind[3][0][2] - deltaZ[i0]}, + {ind[3][0][0] - deltaR[i0], ind[3][1][0] - deltaZ[i0], ind[3][1][1] - deltaZ[i0], ind[3][1][2] - deltaZ[i0]}, + {ind[3][1][0] - deltaR[i0], ind[3][2][0] - deltaZ[i0], ind[3][2][1] - deltaZ[i0], ind[3][2][2] - deltaZ[i0]}, + {ind[3][2][0] - deltaR[i0], ind[3][3][0] - deltaZ[i0], ind[3][3][1] - deltaZ[i0], ind[3][3][2] - deltaZ[i0]}}}; + + cVals[0] = mGridData[ind[0][0][0]]; + cVals[1] = mGridData[ind[0][0][1]]; + cVals[2] = mGridData[ind[0][0][2]]; + cVals[3] = mGridData[ind[0][0][3]]; + cVals[4] = mGridData[ind[0][1][0]]; + cVals[5] = mGridData[ind[0][1][1]]; + cVals[6] = mGridData[ind[0][1][2]]; + cVals[7] = mGridData[ind[0][1][3]]; + cVals[8] = mGridData[ind[0][2][0]]; + cVals[9] = mGridData[ind[0][2][1]]; + cVals[10] = mGridData[ind[0][2][2]]; + cVals[11] = mGridData[ind[0][2][3]]; + cVals[12] = mGridData[ind[0][3][0]]; + cVals[13] = mGridData[ind[0][3][1]]; + cVals[14] = mGridData[ind[0][3][2]]; + cVals[15] = mGridData[ind[0][3][3]]; + cVals[16] = mGridData[ind[1][0][0]]; + cVals[17] = mGridData[ind[1][0][1]]; + cVals[18] = mGridData[ind[1][0][2]]; + cVals[19] = mGridData[ind[1][0][3]]; + cVals[22] = mGridData[ind[1][1][2]]; + cVals[20] = mGridData[ind[1][1][0]]; + cVals[23] = mGridData[ind[1][1][3]]; + cVals[24] = mGridData[ind[1][2][0]]; + cVals[25] = mGridData[ind[1][2][1]]; + cVals[26] = mGridData[ind[1][2][2]]; + cVals[27] = mGridData[ind[1][2][3]]; + cVals[28] = mGridData[ind[1][3][0]]; + cVals[29] = mGridData[ind[1][3][1]]; + cVals[30] = mGridData[ind[1][3][2]]; + cVals[31] = mGridData[ind[1][3][3]]; + cVals[32] = mGridData[ind[2][0][0]]; + cVals[33] = mGridData[ind[2][0][1]]; + cVals[34] = mGridData[ind[2][0][2]]; + cVals[35] = mGridData[ind[2][0][3]]; + cVals[36] = mGridData[ind[2][1][0]]; + cVals[37] = mGridData[ind[2][1][1]]; + cVals[38] = mGridData[ind[2][1][2]]; + cVals[39] = mGridData[ind[2][1][3]]; + cVals[40] = mGridData[ind[2][2][0]]; + cVals[41] = mGridData[ind[2][2][1]]; + cVals[42] = mGridData[ind[2][2][2]]; + cVals[43] = mGridData[ind[2][2][3]]; + cVals[44] = mGridData[ind[2][3][0]]; + cVals[45] = mGridData[ind[2][3][1]]; + cVals[46] = mGridData[ind[2][3][2]]; + cVals[47] = mGridData[ind[2][3][3]]; + cVals[48] = mGridData[ind[3][0][0]]; + cVals[49] = mGridData[ind[3][0][1]]; + cVals[50] = mGridData[ind[3][0][2]]; + cVals[51] = mGridData[ind[3][0][3]]; + cVals[52] = mGridData[ind[3][1][0]]; + cVals[53] = mGridData[ind[3][1][1]]; + cVals[54] = mGridData[ind[3][1][2]]; + cVals[55] = mGridData[ind[3][1][3]]; + cVals[56] = mGridData[ind[3][2][0]]; + cVals[57] = mGridData[ind[3][2][1]]; + cVals[58] = mGridData[ind[3][2][2]]; + cVals[59] = mGridData[ind[3][2][3]]; + cVals[60] = mGridData[ind[3][3][0]]; + cVals[61] = mGridData[ind[3][3][1]]; + cVals[62] = mGridData[ind[3][3][2]]; + cVals[63] = mGridData[ind[3][3][3]]; + } break; + + case GridPos::SideXRight: + case GridPos::LineD: + case GridPos::LineH: { + const int ind[4][4][3]{ + {{ii_x_y_z + deltaPhi[i0] + deltaR[i0] + deltaZ[i0], ind[0][0][0] - deltaZ[i0], ind[0][0][1] - deltaZ[i0]}, + {ind[0][0][0] - deltaR[i0], ind[0][1][0] - deltaZ[i0], ind[0][1][1] - deltaZ[i0]}, + {ind[0][1][0] - deltaR[i0], ind[0][2][0] - deltaZ[i0], ind[0][2][1] - deltaZ[i0]}, + {ind[0][2][0] - deltaR[i0], ind[0][3][0] - deltaZ[i0], ind[0][3][1] - deltaZ[i0]}}, + {{ii_x_y_z + deltaR[i0] + deltaZ[i0], ind[1][0][0] - deltaZ[i0], ind[1][0][1] - deltaZ[i0]}, + {ind[1][0][0] - deltaR[i0], ind[1][1][0] - deltaZ[i0], ind[1][1][1] - deltaZ[i0]}, + {ind[1][1][0] - deltaR[i0], ind[1][2][0] - deltaZ[i0], ind[1][2][1] - deltaZ[i0]}, + {ind[1][2][0] - deltaR[i0], ind[1][3][0] - deltaZ[i0], ind[1][3][1] - deltaZ[i0]}}, + {{ii_x_y_z + deltaPhi[i1] + deltaR[i0] + deltaZ[i0], ind[2][0][0] - deltaZ[i0], ind[2][0][1] - deltaZ[i0]}, + {ind[2][0][0] - deltaR[i0], ind[2][1][0] - deltaZ[i0], ind[2][1][1] - deltaZ[i0]}, + {ind[2][1][0] - deltaR[i0], ind[2][2][0] - deltaZ[i0], ind[2][2][1] - deltaZ[i0]}, + {ind[2][2][0] - deltaR[i0], ind[2][3][0] - deltaZ[i0], ind[2][3][1] - deltaZ[i0]}}, + {{ii_x_y_z + deltaPhi[i2] + deltaR[i0] + deltaZ[i0], ind[3][0][0] - deltaZ[i0], ind[3][0][1] - deltaZ[i0]}, + {ind[3][0][0] - deltaR[i0], ind[3][1][0] - deltaZ[i0], ind[3][1][1] - deltaZ[i0]}, + {ind[3][1][0] - deltaR[i0], ind[3][2][0] - deltaZ[i0], ind[3][2][1] - deltaZ[i0]}, + {ind[3][2][0] - deltaR[i0], ind[3][3][0] - deltaZ[i0], ind[3][3][1] - deltaZ[i0]}}}; + + cVals[0] = mGridData[ind[0][0][0]]; + cVals[1] = mGridData[ind[0][0][1]]; + cVals[2] = mGridData[ind[0][0][2]]; + cVals[3] = extrapolation(mGridData[ind[0][0][2]], mGridData[ind[0][0][1]], mGridData[ind[0][0][0]]); + cVals[4] = mGridData[ind[0][1][0]]; + cVals[5] = mGridData[ind[0][1][1]]; + cVals[6] = mGridData[ind[0][1][2]]; + cVals[7] = extrapolation(mGridData[ind[0][1][2]], mGridData[ind[0][1][1]], mGridData[ind[0][1][0]]); + cVals[8] = mGridData[ind[0][2][0]]; + cVals[9] = mGridData[ind[0][2][1]]; + cVals[10] = mGridData[ind[0][2][2]]; + cVals[11] = extrapolation(mGridData[ind[0][2][2]], mGridData[ind[0][2][1]], mGridData[ind[0][2][0]]); + cVals[12] = mGridData[ind[0][3][0]]; + cVals[13] = mGridData[ind[0][3][1]]; + cVals[14] = mGridData[ind[0][3][2]]; + cVals[15] = extrapolation(mGridData[ind[0][3][2]], mGridData[ind[0][3][1]], mGridData[ind[0][3][0]]); + cVals[16] = mGridData[ind[1][0][0]]; + cVals[17] = mGridData[ind[1][0][1]]; + cVals[18] = mGridData[ind[1][0][2]]; + cVals[19] = extrapolation(mGridData[ind[1][0][2]], mGridData[ind[1][0][1]], mGridData[ind[1][0][0]]); + cVals[20] = mGridData[ind[1][1][0]]; + cVals[22] = mGridData[ind[1][1][2]]; + cVals[23] = extrapolation(mGridData[ind[1][1][2]], mGridData[ii_x_y_z], mGridData[ind[1][1][0]]); + cVals[24] = mGridData[ind[1][2][0]]; + cVals[25] = mGridData[ind[1][2][1]]; + cVals[26] = mGridData[ind[1][2][2]]; + cVals[27] = extrapolation(mGridData[ind[1][2][2]], mGridData[ind[1][2][1]], mGridData[ind[1][2][0]]); + cVals[28] = mGridData[ind[1][3][0]]; + cVals[29] = mGridData[ind[1][3][1]]; + cVals[30] = mGridData[ind[1][3][2]]; + cVals[31] = extrapolation(mGridData[ind[1][3][2]], mGridData[ind[1][3][1]], mGridData[ind[1][3][0]]); + cVals[32] = mGridData[ind[2][0][0]]; + cVals[33] = mGridData[ind[2][0][1]]; + cVals[34] = mGridData[ind[2][0][2]]; + cVals[35] = extrapolation(mGridData[ind[2][0][2]], mGridData[ind[2][0][1]], mGridData[ind[2][0][0]]); + cVals[36] = mGridData[ind[2][1][0]]; + cVals[37] = mGridData[ind[2][1][1]]; + cVals[38] = mGridData[ind[2][1][2]]; + cVals[39] = extrapolation(mGridData[ind[2][1][2]], mGridData[ind[2][1][1]], mGridData[ind[2][1][0]]); + cVals[40] = mGridData[ind[2][2][0]]; + cVals[41] = mGridData[ind[2][2][1]]; + cVals[42] = mGridData[ind[2][2][2]]; + cVals[43] = extrapolation(mGridData[ind[2][2][2]], mGridData[ind[2][2][1]], mGridData[ind[2][2][0]]); + cVals[44] = mGridData[ind[2][3][0]]; + cVals[45] = mGridData[ind[2][3][1]]; + cVals[46] = mGridData[ind[2][3][2]]; + cVals[47] = extrapolation(mGridData[ind[2][3][2]], mGridData[ind[2][3][1]], mGridData[ind[2][3][0]]); + cVals[48] = mGridData[ind[3][0][0]]; + cVals[49] = mGridData[ind[3][0][1]]; + cVals[50] = mGridData[ind[3][0][2]]; + cVals[52] = mGridData[ind[3][1][0]]; + cVals[51] = extrapolation(mGridData[ind[3][0][2]], mGridData[ind[3][0][1]], mGridData[ind[3][0][0]]); + cVals[53] = mGridData[ind[3][1][1]]; + cVals[54] = mGridData[ind[3][1][2]]; + cVals[55] = extrapolation(mGridData[ind[3][1][2]], mGridData[ind[3][1][1]], mGridData[ind[3][1][0]]); + cVals[56] = mGridData[ind[3][2][0]]; + cVals[57] = mGridData[ind[3][2][1]]; + cVals[58] = mGridData[ind[3][2][2]]; + cVals[59] = extrapolation(mGridData[ind[3][2][2]], mGridData[ind[3][2][1]], mGridData[ind[3][2][0]]); + cVals[60] = mGridData[ind[3][3][0]]; + cVals[61] = mGridData[ind[3][3][1]]; + cVals[62] = mGridData[ind[3][3][2]]; + cVals[63] = extrapolation(mGridData[ind[3][3][2]], mGridData[ind[3][3][1]], mGridData[ind[3][3][0]]); + } break; + + case GridPos::SideYRight: + case GridPos::LineB: + case GridPos::LineF: { + const int ind[4][3][4]{ + {{ii_x_y_z + deltaPhi[i0] + deltaR[i0] + deltaZ[i0], ind[0][0][0] - deltaZ[i0], ind[0][0][1] - deltaZ[i0], ind[0][0][2] - deltaZ[i0]}, + {ind[0][0][0] - deltaR[i0], ind[0][1][0] - deltaZ[i0], ind[0][1][1] - deltaZ[i0], ind[0][1][2] - deltaZ[i0]}, + {ind[0][1][0] - deltaR[i0], ind[0][2][0] - deltaZ[i0], ind[0][2][1] - deltaZ[i0], ind[0][2][2] - deltaZ[i0]}}, + {{ii_x_y_z + deltaR[i0] + deltaZ[i0], ind[1][0][0] - deltaZ[i0], ind[1][0][1] - deltaZ[i0], ind[1][0][2] - deltaZ[i0]}, + {ind[1][0][0] - deltaR[i0], ind[1][1][0] - deltaZ[i0], ind[1][1][1] - deltaZ[i0], ind[1][1][2] - deltaZ[i0]}, + {ind[1][1][0] - deltaR[i0], ind[1][2][0] - deltaZ[i0], ind[1][2][1] - deltaZ[i0], ind[1][2][2] - deltaZ[i0]}}, + {{ii_x_y_z + deltaPhi[i1] + deltaR[i0] + deltaZ[i0], ind[2][0][0] - deltaZ[i0], ind[2][0][1] - deltaZ[i0], ind[2][0][2] - deltaZ[i0]}, + {ind[2][0][0] - deltaR[i0], ind[2][1][0] - deltaZ[i0], ind[2][1][1] - deltaZ[i0], ind[2][1][2] - deltaZ[i0]}, + {ind[2][1][0] - deltaR[i0], ind[2][2][0] - deltaZ[i0], ind[2][2][1] - deltaZ[i0], ind[2][2][2] - deltaZ[i0]}}, + {{ii_x_y_z + deltaPhi[i2] + deltaR[i0] + deltaZ[i0], ind[3][0][0] - deltaZ[i0], ind[3][0][1] - deltaZ[i0], ind[3][0][2] - deltaZ[i0]}, + {ind[3][0][0] - deltaR[i0], ind[3][1][0] - deltaZ[i0], ind[3][1][1] - deltaZ[i0], ind[3][1][2] - deltaZ[i0]}, + {ind[3][1][0] - deltaR[i0], ind[3][2][0] - deltaZ[i0], ind[3][2][1] - deltaZ[i0], ind[3][2][2] - deltaZ[i0]}}}; + + cVals[0] = mGridData[ind[0][0][0]]; + cVals[1] = mGridData[ind[0][0][1]]; + cVals[2] = mGridData[ind[0][0][2]]; + cVals[3] = mGridData[ind[0][0][3]]; + cVals[4] = mGridData[ind[0][1][0]]; + cVals[5] = mGridData[ind[0][1][1]]; + cVals[6] = mGridData[ind[0][1][2]]; + cVals[7] = mGridData[ind[0][1][3]]; + cVals[8] = mGridData[ind[0][2][0]]; + cVals[9] = mGridData[ind[0][2][1]]; + cVals[10] = mGridData[ind[0][2][2]]; + cVals[11] = mGridData[ind[0][2][3]]; + cVals[12] = extrapolation(mGridData[ind[0][2][0]], mGridData[ind[0][1][0]], mGridData[ind[0][0][0]]); + cVals[13] = extrapolation(mGridData[ind[0][2][1]], mGridData[ind[0][1][1]], mGridData[ind[0][0][1]]); + cVals[14] = extrapolation(mGridData[ind[0][2][2]], mGridData[ind[0][1][2]], mGridData[ind[0][0][2]]); + cVals[15] = extrapolation(mGridData[ind[0][2][3]], mGridData[ind[0][1][3]], mGridData[ind[0][0][3]]); + cVals[16] = mGridData[ind[1][0][0]]; + cVals[17] = mGridData[ind[1][0][1]]; + cVals[18] = mGridData[ind[1][0][2]]; + cVals[19] = mGridData[ind[1][0][3]]; + cVals[20] = mGridData[ind[1][1][0]]; + cVals[22] = mGridData[ind[1][1][2]]; + cVals[23] = mGridData[ind[1][1][3]]; + cVals[24] = mGridData[ind[1][2][0]]; + cVals[25] = mGridData[ind[1][2][1]]; + cVals[26] = mGridData[ind[1][2][2]]; + cVals[27] = mGridData[ind[1][2][3]]; + cVals[28] = extrapolation(mGridData[ind[1][2][0]], mGridData[ind[1][1][0]], mGridData[ind[1][0][0]]); + cVals[29] = extrapolation(mGridData[ind[1][2][1]], mGridData[ii_x_y_z], mGridData[ind[1][0][1]]); + cVals[30] = extrapolation(mGridData[ind[1][2][2]], mGridData[ind[1][1][2]], mGridData[ind[1][0][2]]); + cVals[31] = extrapolation(mGridData[ind[1][2][3]], mGridData[ind[1][1][3]], mGridData[ind[1][0][3]]); + cVals[32] = mGridData[ind[2][0][0]]; + cVals[33] = mGridData[ind[2][0][1]]; + cVals[34] = mGridData[ind[2][0][2]]; + cVals[35] = mGridData[ind[2][0][3]]; + cVals[36] = mGridData[ind[2][1][0]]; + cVals[37] = mGridData[ind[2][1][1]]; + cVals[38] = mGridData[ind[2][1][2]]; + cVals[39] = mGridData[ind[2][1][3]]; + cVals[40] = mGridData[ind[2][2][0]]; + cVals[41] = mGridData[ind[2][2][1]]; + cVals[42] = mGridData[ind[2][2][2]]; + cVals[43] = mGridData[ind[2][2][3]]; + cVals[44] = extrapolation(mGridData[ind[2][2][0]], mGridData[ind[2][1][0]], mGridData[ind[2][0][0]]); + cVals[45] = extrapolation(mGridData[ind[2][2][1]], mGridData[ind[2][1][1]], mGridData[ind[2][0][1]]); + cVals[46] = extrapolation(mGridData[ind[2][2][2]], mGridData[ind[2][1][2]], mGridData[ind[2][0][2]]); + cVals[47] = extrapolation(mGridData[ind[2][2][3]], mGridData[ind[2][1][3]], mGridData[ind[2][0][3]]); + cVals[48] = mGridData[ind[3][0][0]]; + cVals[49] = mGridData[ind[3][0][1]]; + cVals[50] = mGridData[ind[3][0][2]]; + cVals[51] = mGridData[ind[3][0][3]]; + cVals[52] = mGridData[ind[3][1][0]]; + cVals[53] = mGridData[ind[3][1][1]]; + cVals[54] = mGridData[ind[3][1][2]]; + cVals[55] = mGridData[ind[3][1][3]]; + cVals[56] = mGridData[ind[3][2][0]]; + cVals[57] = mGridData[ind[3][2][1]]; + cVals[58] = mGridData[ind[3][2][2]]; + cVals[59] = mGridData[ind[3][2][3]]; + cVals[60] = extrapolation(mGridData[ind[3][2][0]], mGridData[ind[3][1][0]], mGridData[ind[3][0][0]]); + cVals[61] = extrapolation(mGridData[ind[3][2][1]], mGridData[ind[3][1][1]], mGridData[ind[3][0][1]]); + cVals[62] = extrapolation(mGridData[ind[3][2][2]], mGridData[ind[3][1][2]], mGridData[ind[3][0][2]]); + cVals[63] = extrapolation(mGridData[ind[3][2][3]], mGridData[ind[3][1][3]], mGridData[ind[3][0][3]]); + } break; + + case GridPos::SideYLeft: + case GridPos::LineA: + case GridPos::LineE: { + const int ind[4][3][4]{ + {{ii_x_y_z + deltaPhi[i0] + deltaZ[i0], ind[0][0][0] - deltaZ[i0], ind[0][0][1] - deltaZ[i0], ind[0][0][2] - deltaZ[i0]}, + {ind[0][0][0] - deltaR[i0], ind[0][1][0] - deltaZ[i0], ind[0][1][1] - deltaZ[i0], ind[0][1][2] - deltaZ[i0]}, + {ind[0][1][0] - deltaR[i0], ind[0][2][0] - deltaZ[i0], ind[0][2][1] - deltaZ[i0], ind[0][2][2] - deltaZ[i0]}}, + {{ii_x_y_z + deltaZ[i0], ind[1][0][0] - deltaZ[i0], ind[1][0][1] - deltaZ[i0], ind[1][0][2] - deltaZ[i0]}, + {ind[1][0][0] - deltaR[i0], ind[1][1][0] - deltaZ[i0], ind[1][1][1] - deltaZ[i0], ind[1][1][2] - deltaZ[i0]}, + {ind[1][1][0] - deltaR[i0], ind[1][2][0] - deltaZ[i0], ind[1][2][1] - deltaZ[i0], ind[1][2][2] - deltaZ[i0]}}, + {{ii_x_y_z + deltaPhi[i1] + deltaZ[i0], ind[2][0][0] - deltaZ[i0], ind[2][0][1] - deltaZ[i0], ind[2][0][2] - deltaZ[i0]}, + {ind[2][0][0] - deltaR[i0], ind[2][1][0] - deltaZ[i0], ind[2][1][1] - deltaZ[i0], ind[2][1][2] - deltaZ[i0]}, + {ind[2][1][0] - deltaR[i0], ind[2][2][0] - deltaZ[i0], ind[2][2][1] - deltaZ[i0], ind[2][2][2] - deltaZ[i0]}}, + {{ii_x_y_z + deltaPhi[i2] + deltaZ[i0], ind[3][0][0] - deltaZ[i0], ind[3][0][1] - deltaZ[i0], ind[3][0][2] - deltaZ[i0]}, + {ind[3][0][0] - deltaR[i0], ind[3][1][0] - deltaZ[i0], ind[3][1][1] - deltaZ[i0], ind[3][1][2] - deltaZ[i0]}, + {ind[3][1][0] - deltaR[i0], ind[3][2][0] - deltaZ[i0], ind[3][2][1] - deltaZ[i0], ind[3][2][2] - deltaZ[i0]}}}; + + cVals[0] = extrapolation(mGridData[ind[0][0][0]], mGridData[ind[0][1][0]], mGridData[ind[0][2][0]]); + cVals[1] = extrapolation(mGridData[ind[0][0][1]], mGridData[ind[0][1][1]], mGridData[ind[0][2][1]]); + cVals[2] = extrapolation(mGridData[ind[0][0][2]], mGridData[ind[0][1][2]], mGridData[ind[0][2][2]]); + cVals[3] = extrapolation(mGridData[ind[0][0][3]], mGridData[ind[0][1][3]], mGridData[ind[0][2][3]]); + cVals[4] = mGridData[ind[0][0][0]]; + cVals[5] = mGridData[ind[0][0][1]]; + cVals[6] = mGridData[ind[0][0][2]]; + cVals[7] = mGridData[ind[0][0][3]]; + cVals[8] = mGridData[ind[0][1][0]]; + cVals[9] = mGridData[ind[0][1][1]]; + cVals[10] = mGridData[ind[0][1][2]]; + cVals[11] = mGridData[ind[0][1][3]]; + cVals[12] = mGridData[ind[0][2][0]]; + cVals[13] = mGridData[ind[0][2][1]]; + cVals[14] = mGridData[ind[0][2][2]]; + cVals[15] = mGridData[ind[0][2][3]]; + cVals[16] = extrapolation(mGridData[ind[1][0][0]], mGridData[ind[1][1][0]], mGridData[ind[1][2][0]]); + cVals[17] = extrapolation(mGridData[ii_x_y_z], mGridData[ind[1][1][1]], mGridData[ind[1][2][1]]); + cVals[18] = extrapolation(mGridData[ind[1][0][2]], mGridData[ind[1][1][2]], mGridData[ind[1][2][2]]); + cVals[19] = extrapolation(mGridData[ind[1][0][3]], mGridData[ind[1][1][3]], mGridData[ind[1][2][3]]); + cVals[20] = mGridData[ind[1][0][0]]; + cVals[22] = mGridData[ind[1][0][2]]; + cVals[23] = mGridData[ind[1][0][3]]; + cVals[24] = mGridData[ind[1][1][0]]; + cVals[25] = mGridData[ind[1][1][1]]; + cVals[26] = mGridData[ind[1][1][2]]; + cVals[27] = mGridData[ind[1][1][3]]; + cVals[28] = mGridData[ind[1][2][0]]; + cVals[29] = mGridData[ind[1][2][1]]; + cVals[30] = mGridData[ind[1][2][2]]; + cVals[31] = mGridData[ind[1][2][3]]; + cVals[32] = extrapolation(mGridData[ind[2][0][0]], mGridData[ind[2][1][0]], mGridData[ind[2][2][0]]); + cVals[33] = extrapolation(mGridData[ind[2][0][1]], mGridData[ind[2][1][1]], mGridData[ind[2][2][1]]); + cVals[34] = extrapolation(mGridData[ind[2][0][2]], mGridData[ind[2][1][2]], mGridData[ind[2][2][2]]); + cVals[35] = extrapolation(mGridData[ind[2][0][3]], mGridData[ind[2][1][3]], mGridData[ind[2][2][3]]); + cVals[36] = mGridData[ind[2][0][0]]; + cVals[37] = mGridData[ind[2][0][1]]; + cVals[38] = mGridData[ind[2][0][2]]; + cVals[39] = mGridData[ind[2][0][3]]; + cVals[40] = mGridData[ind[2][1][0]]; + cVals[41] = mGridData[ind[2][1][1]]; + cVals[42] = mGridData[ind[2][1][2]]; + cVals[43] = mGridData[ind[2][1][3]]; + cVals[44] = mGridData[ind[2][2][0]]; + cVals[45] = mGridData[ind[2][2][1]]; + cVals[46] = mGridData[ind[2][2][2]]; + cVals[47] = mGridData[ind[2][2][3]]; + cVals[48] = extrapolation(mGridData[ind[3][0][0]], mGridData[ind[3][1][0]], mGridData[ind[3][2][0]]); + cVals[49] = extrapolation(mGridData[ind[3][0][1]], mGridData[ind[3][1][1]], mGridData[ind[3][2][1]]); + cVals[50] = extrapolation(mGridData[ind[3][0][2]], mGridData[ind[3][1][2]], mGridData[ind[3][2][2]]); + cVals[51] = extrapolation(mGridData[ind[3][0][3]], mGridData[ind[3][1][3]], mGridData[ind[3][2][3]]); + cVals[52] = mGridData[ind[3][0][0]]; + cVals[53] = mGridData[ind[3][0][1]]; + cVals[54] = mGridData[ind[3][0][2]]; + cVals[55] = mGridData[ind[3][0][3]]; + cVals[56] = mGridData[ind[3][1][0]]; + cVals[57] = mGridData[ind[3][1][1]]; + cVals[58] = mGridData[ind[3][1][2]]; + cVals[59] = mGridData[ind[3][1][3]]; + cVals[60] = mGridData[ind[3][2][0]]; + cVals[61] = mGridData[ind[3][2][1]]; + cVals[62] = mGridData[ind[3][2][2]]; + cVals[63] = mGridData[ind[3][2][3]]; + } break; + + case GridPos::SideXLeft: + case GridPos::LineC: + case GridPos::LineG: { + const int ind[4][4][3]{ + {{ii_x_y_z + deltaPhi[i0] + deltaR[i0], ind[0][0][0] - deltaZ[i0], ind[0][0][1] - deltaZ[i0]}, + {ind[0][0][0] - deltaR[i0], ind[0][1][0] - deltaZ[i0], ind[0][1][1] - deltaZ[i0]}, + {ind[0][1][0] - deltaR[i0], ind[0][2][0] - deltaZ[i0], ind[0][2][1] - deltaZ[i0]}, + {ind[0][2][0] - deltaR[i0], ind[0][3][0] - deltaZ[i0], ind[0][3][1] - deltaZ[i0]}}, + {{ii_x_y_z + deltaR[i0], ind[1][0][0] - deltaZ[i0], ind[1][0][1] - deltaZ[i0]}, + {ind[1][0][0] - deltaR[i0], ind[1][1][0] - deltaZ[i0], ind[1][1][1] - deltaZ[i0]}, + {ind[1][1][0] - deltaR[i0], ind[1][2][0] - deltaZ[i0], ind[1][2][1] - deltaZ[i0]}, + {ind[1][2][0] - deltaR[i0], ind[1][3][0] - deltaZ[i0], ind[1][3][1] - deltaZ[i0]}}, + {{ii_x_y_z + deltaPhi[i1] + deltaR[i0], ind[2][0][0] - deltaZ[i0], ind[2][0][1] - deltaZ[i0]}, + {ind[2][0][0] - deltaR[i0], ind[2][1][0] - deltaZ[i0], ind[2][1][1] - deltaZ[i0]}, + {ind[2][1][0] - deltaR[i0], ind[2][2][0] - deltaZ[i0], ind[2][2][1] - deltaZ[i0]}, + {ind[2][2][0] - deltaR[i0], ind[2][3][0] - deltaZ[i0], ind[2][3][1] - deltaZ[i0]}}, + {{ii_x_y_z + deltaPhi[i2] + deltaR[i0], ind[3][0][0] - deltaZ[i0], ind[3][0][1] - deltaZ[i0]}, + {ind[3][0][0] - deltaR[i0], ind[3][1][0] - deltaZ[i0], ind[3][1][1] - deltaZ[i0]}, + {ind[3][1][0] - deltaR[i0], ind[3][2][0] - deltaZ[i0], ind[3][2][1] - deltaZ[i0]}, + {ind[3][2][0] - deltaR[i0], ind[3][3][0] - deltaZ[i0], ind[3][3][1] - deltaZ[i0]}}}; + + cVals[0] = extrapolation(mGridData[ind[0][0][0]], mGridData[ind[0][0][1]], mGridData[ind[0][0][2]]); + cVals[1] = mGridData[ind[0][0][0]]; + cVals[2] = mGridData[ind[0][0][1]]; + cVals[3] = mGridData[ind[0][0][2]]; + cVals[4] = extrapolation(mGridData[ind[0][1][0]], mGridData[ind[0][1][1]], mGridData[ind[0][1][2]]); + cVals[5] = mGridData[ind[0][1][0]]; + cVals[6] = mGridData[ind[0][1][1]]; + cVals[7] = mGridData[ind[0][1][2]]; + cVals[8] = extrapolation(mGridData[ind[0][2][0]], mGridData[ind[0][2][1]], mGridData[ind[0][2][2]]); + cVals[9] = mGridData[ind[0][2][0]]; + cVals[10] = mGridData[ind[0][2][1]]; + cVals[11] = mGridData[ind[0][2][2]]; + cVals[12] = extrapolation(mGridData[ind[0][3][0]], mGridData[ind[0][3][1]], mGridData[ind[0][3][2]]); + cVals[13] = mGridData[ind[0][3][0]]; + cVals[14] = mGridData[ind[0][3][1]]; + cVals[15] = mGridData[ind[0][3][2]]; + cVals[16] = extrapolation(mGridData[ind[1][0][0]], mGridData[ind[1][0][1]], mGridData[ind[1][0][2]]); + cVals[17] = mGridData[ind[1][0][0]]; + cVals[18] = mGridData[ind[1][0][1]]; + cVals[19] = mGridData[ind[1][0][2]]; + cVals[20] = extrapolation(mGridData[ii_x_y_z], mGridData[ind[1][1][1]], mGridData[ind[1][1][2]]); + cVals[22] = mGridData[ind[1][1][1]]; + cVals[23] = mGridData[ind[1][1][2]]; + cVals[24] = extrapolation(mGridData[ind[1][2][0]], mGridData[ind[1][2][1]], mGridData[ind[1][2][2]]); + cVals[25] = mGridData[ind[1][2][0]]; + cVals[26] = mGridData[ind[1][2][1]]; + cVals[27] = mGridData[ind[1][2][2]]; + cVals[28] = extrapolation(mGridData[ind[1][3][0]], mGridData[ind[1][3][1]], mGridData[ind[1][3][2]]); + cVals[29] = mGridData[ind[1][3][0]]; + cVals[30] = mGridData[ind[1][3][1]]; + cVals[31] = mGridData[ind[1][3][2]]; + cVals[32] = extrapolation(mGridData[ind[2][0][0]], mGridData[ind[2][0][1]], mGridData[ind[2][0][2]]); + cVals[33] = mGridData[ind[2][0][0]]; + cVals[34] = mGridData[ind[2][0][1]]; + cVals[35] = mGridData[ind[2][0][2]]; + cVals[36] = extrapolation(mGridData[ind[2][1][0]], mGridData[ind[2][1][1]], mGridData[ind[2][1][2]]); + cVals[37] = mGridData[ind[2][1][0]]; + cVals[38] = mGridData[ind[2][1][1]]; + cVals[39] = mGridData[ind[2][1][2]]; + cVals[40] = extrapolation(mGridData[ind[2][2][0]], mGridData[ind[2][2][1]], mGridData[ind[2][2][2]]); + cVals[41] = mGridData[ind[2][2][0]]; + cVals[42] = mGridData[ind[2][2][1]]; + cVals[43] = mGridData[ind[2][2][2]]; + cVals[44] = extrapolation(mGridData[ind[2][3][0]], mGridData[ind[2][3][1]], mGridData[ind[2][3][2]]); + cVals[45] = mGridData[ind[2][3][0]]; + cVals[46] = mGridData[ind[2][3][1]]; + cVals[47] = mGridData[ind[2][3][2]]; + cVals[48] = extrapolation(mGridData[ind[3][0][0]], mGridData[ind[3][0][1]], mGridData[ind[3][0][2]]); + cVals[49] = mGridData[ind[3][0][0]]; + cVals[50] = mGridData[ind[3][0][1]]; + cVals[51] = mGridData[ind[3][0][2]]; + cVals[52] = extrapolation(mGridData[ind[3][1][0]], mGridData[ind[3][1][1]], mGridData[ind[3][1][2]]); + cVals[53] = mGridData[ind[3][1][0]]; + cVals[54] = mGridData[ind[3][1][1]]; + cVals[55] = mGridData[ind[3][1][2]]; + cVals[56] = extrapolation(mGridData[ind[3][2][0]], mGridData[ind[3][2][1]], mGridData[ind[3][2][2]]); + cVals[57] = mGridData[ind[3][2][0]]; + cVals[58] = mGridData[ind[3][2][1]]; + cVals[59] = mGridData[ind[3][2][2]]; + cVals[60] = extrapolation(mGridData[ind[3][3][0]], mGridData[ind[3][3][1]], mGridData[ind[3][3][2]]); + cVals[61] = mGridData[ind[3][3][0]]; + cVals[62] = mGridData[ind[3][3][1]]; + cVals[63] = mGridData[ind[3][3][2]]; + } break; + + case GridPos::Edge0: + case GridPos::Edge4: + case GridPos::LineI: { + const int ind[4][3][3]{ + {{ii_x_y_z + deltaPhi[i0], ind[0][0][0] - deltaZ[i0], ind[0][0][1] - deltaZ[i0]}, + {ind[0][0][0] - deltaR[i0], ind[0][1][0] - deltaZ[i0], ind[0][1][1] - deltaZ[i0]}, + {ind[0][1][0] - deltaR[i0], ind[0][2][0] - deltaZ[i0], ind[0][2][1] - deltaZ[i0]}}, + {{ii_x_y_z, ind[1][0][0] - deltaZ[i0], ind[1][0][1] - deltaZ[i0]}, + {ind[1][0][0] - deltaR[i0], ind[1][1][0] - deltaZ[i0], ind[1][1][1] - deltaZ[i0]}, + {ind[1][1][0] - deltaR[i0], ind[1][2][0] - deltaZ[i0], ind[1][2][1] - deltaZ[i0]}}, + {{ii_x_y_z + deltaPhi[i1], ind[2][0][0] - deltaZ[i0], ind[2][0][1] - deltaZ[i0]}, + {ind[2][0][0] - deltaR[i0], ind[2][1][0] - deltaZ[i0], ind[2][1][1] - deltaZ[i0]}, + {ind[2][1][0] - deltaR[i0], ind[2][2][0] - deltaZ[i0], ind[2][2][1] - deltaZ[i0]}}, + {{ii_x_y_z + deltaPhi[i2], ind[3][0][0] - deltaZ[i0], ind[3][0][1] - deltaZ[i0]}, + {ind[3][0][0] - deltaR[i0], ind[3][1][0] - deltaZ[i0], ind[3][1][1] - deltaZ[i0]}, + {ind[3][1][0] - deltaR[i0], ind[3][2][0] - deltaZ[i0], ind[3][2][1] - deltaZ[i0]}}}; + + cVals[0] = extrapolation(mGridData[ind[0][0][0]], mGridData[ind[0][1][1]], mGridData[ind[0][2][2]]); + cVals[1] = extrapolation(mGridData[ind[0][0][0]], mGridData[ind[0][1][0]], mGridData[ind[0][2][0]]); + cVals[2] = extrapolation(mGridData[ind[0][0][1]], mGridData[ind[0][1][1]], mGridData[ind[0][2][1]]); + cVals[3] = extrapolation(mGridData[ind[0][0][2]], mGridData[ind[0][1][2]], mGridData[ind[0][2][2]]); + cVals[4] = extrapolation(mGridData[ind[0][0][0]], mGridData[ind[0][0][1]], mGridData[ind[0][0][2]]); + cVals[5] = mGridData[ind[0][0][0]]; + cVals[6] = mGridData[ind[0][0][1]]; + cVals[7] = mGridData[ind[0][0][2]]; + cVals[8] = extrapolation(mGridData[ind[0][1][0]], mGridData[ind[0][1][1]], mGridData[ind[0][1][2]]); + cVals[9] = mGridData[ind[0][1][0]]; + cVals[10] = mGridData[ind[0][1][1]]; + cVals[11] = mGridData[ind[0][1][2]]; + cVals[12] = extrapolation(mGridData[ind[0][2][0]], mGridData[ind[0][2][1]], mGridData[ind[0][2][2]]); + cVals[13] = mGridData[ind[0][2][0]]; + cVals[14] = mGridData[ind[0][2][1]]; + cVals[15] = mGridData[ind[0][2][2]]; + cVals[16] = extrapolation(mGridData[ii_x_y_z], mGridData[ind[1][1][1]], mGridData[ind[1][2][2]]); + cVals[17] = extrapolation(mGridData[ii_x_y_z], mGridData[ind[1][1][0]], mGridData[ind[1][2][0]]); + cVals[18] = extrapolation(mGridData[ind[1][0][1]], mGridData[ind[1][1][1]], mGridData[ind[1][2][1]]); + cVals[19] = extrapolation(mGridData[ind[1][0][2]], mGridData[ind[1][1][2]], mGridData[ind[1][2][2]]); + cVals[20] = extrapolation(mGridData[ii_x_y_z], mGridData[ind[1][0][1]], mGridData[ind[1][0][2]]); + cVals[22] = mGridData[ind[1][0][1]]; + cVals[23] = mGridData[ind[1][0][2]]; + cVals[24] = extrapolation(mGridData[ind[1][1][0]], mGridData[ind[1][1][1]], mGridData[ind[1][1][2]]); + cVals[25] = mGridData[ind[1][1][0]]; + cVals[26] = mGridData[ind[1][1][1]]; + cVals[27] = mGridData[ind[1][1][2]]; + cVals[28] = extrapolation(mGridData[ind[1][2][0]], mGridData[ind[1][2][1]], mGridData[ind[1][2][2]]); + cVals[29] = mGridData[ind[1][2][0]]; + cVals[30] = mGridData[ind[1][2][1]]; + cVals[31] = mGridData[ind[1][2][2]]; + cVals[32] = extrapolation(mGridData[ind[2][0][0]], mGridData[ind[2][1][1]], mGridData[ind[2][2][2]]); + cVals[33] = extrapolation(mGridData[ind[2][0][0]], mGridData[ind[2][1][0]], mGridData[ind[2][2][0]]); + cVals[34] = extrapolation(mGridData[ind[2][0][1]], mGridData[ind[2][1][1]], mGridData[ind[2][2][1]]); + cVals[35] = extrapolation(mGridData[ind[2][0][2]], mGridData[ind[2][1][2]], mGridData[ind[2][2][2]]); + cVals[36] = extrapolation(mGridData[ind[2][0][0]], mGridData[ind[2][0][1]], mGridData[ind[2][0][2]]); + cVals[37] = mGridData[ind[2][0][0]]; + cVals[38] = mGridData[ind[2][0][1]]; + cVals[39] = mGridData[ind[2][0][2]]; + cVals[40] = extrapolation(mGridData[ind[2][1][0]], mGridData[ind[2][1][1]], mGridData[ind[2][1][2]]); + cVals[41] = mGridData[ind[2][1][0]]; + cVals[42] = mGridData[ind[2][1][1]]; + cVals[43] = mGridData[ind[2][1][2]]; + cVals[44] = extrapolation(mGridData[ind[2][2][0]], mGridData[ind[2][2][1]], mGridData[ind[2][2][2]]); + cVals[45] = mGridData[ind[2][2][0]]; + cVals[46] = mGridData[ind[2][2][1]]; + cVals[47] = mGridData[ind[2][2][2]]; + cVals[48] = extrapolation(mGridData[ind[3][0][0]], mGridData[ind[3][1][1]], mGridData[ind[3][2][2]]); + cVals[49] = extrapolation(mGridData[ind[3][0][0]], mGridData[ind[3][1][0]], mGridData[ind[3][2][0]]); + cVals[50] = extrapolation(mGridData[ind[3][0][1]], mGridData[ind[3][1][1]], mGridData[ind[3][2][1]]); + cVals[51] = extrapolation(mGridData[ind[3][0][2]], mGridData[ind[3][1][2]], mGridData[ind[3][2][2]]); + cVals[52] = extrapolation(mGridData[ind[3][0][0]], mGridData[ind[3][0][1]], mGridData[ind[3][0][2]]); + cVals[53] = mGridData[ind[3][0][0]]; + cVals[54] = mGridData[ind[3][0][1]]; + cVals[55] = mGridData[ind[3][0][2]]; + cVals[56] = extrapolation(mGridData[ind[3][1][0]], mGridData[ind[3][1][1]], mGridData[ind[3][1][2]]); + cVals[57] = mGridData[ind[3][1][0]]; + cVals[58] = mGridData[ind[3][1][1]]; + cVals[59] = mGridData[ind[3][1][2]]; + cVals[60] = extrapolation(mGridData[ind[3][2][0]], mGridData[ind[3][2][1]], mGridData[ind[3][2][2]]); + cVals[61] = mGridData[ind[3][2][0]]; + cVals[62] = mGridData[ind[3][2][1]]; + cVals[63] = mGridData[ind[3][2][2]]; + } break; + + case GridPos::Edge1: + case GridPos::Edge5: + case GridPos::LineJ: { + const int ind[4][3][3]{ + {{ii_x_y_z + deltaPhi[i0] + deltaZ[i0], ind[0][0][0] - deltaZ[i0], ind[0][0][1] - deltaZ[i0]}, + {ind[0][0][0] - deltaR[i0], ind[0][1][0] - deltaZ[i0], ind[0][1][1] - deltaZ[i0]}, + {ind[0][1][0] - deltaR[i0], ind[0][2][0] - deltaZ[i0], ind[0][2][1] - deltaZ[i0]}}, + {{ii_x_y_z + deltaZ[i0], ind[1][0][0] - deltaZ[i0], ind[1][0][1] - deltaZ[i0]}, + {ind[1][0][0] - deltaR[i0], ind[1][1][0] - deltaZ[i0], ind[1][1][1] - deltaZ[i0]}, + {ind[1][1][0] - deltaR[i0], ind[1][2][0] - deltaZ[i0], ind[1][2][1] - deltaZ[i0]}}, + {{ii_x_y_z + deltaPhi[i1] + deltaZ[i0], ind[2][0][0] - deltaZ[i0], ind[2][0][1] - deltaZ[i0]}, + {ind[2][0][0] - deltaR[i0], ind[2][1][0] - deltaZ[i0], ind[2][1][1] - deltaZ[i0]}, + {ind[2][1][0] - deltaR[i0], ind[2][2][0] - deltaZ[i0], ind[2][2][1] - deltaZ[i0]}}, + {{ii_x_y_z + deltaPhi[i2] + deltaZ[i0], ind[3][0][0] - deltaZ[i0], ind[3][0][1] - deltaZ[i0]}, + {ind[3][0][0] - deltaR[i0], ind[3][1][0] - deltaZ[i0], ind[3][1][1] - deltaZ[i0]}, + {ind[3][1][0] - deltaR[i0], ind[3][2][0] - deltaZ[i0], ind[3][2][1] - deltaZ[i0]}}}; + + cVals[0] = extrapolation(mGridData[ind[0][0][0]], mGridData[ind[0][1][0]], mGridData[ind[0][2][0]]); + cVals[1] = extrapolation(mGridData[ind[0][0][1]], mGridData[ind[0][1][1]], mGridData[ind[0][2][1]]); + cVals[2] = extrapolation(mGridData[ind[0][0][1]], mGridData[ind[0][1][0]], mGridData[ind[0][2][0] + deltaZ[i0]]); + cVals[3] = extrapolation(mGridData[ind[0][0][2]], mGridData[ind[0][1][1]], mGridData[ind[0][2][0]]); + cVals[4] = mGridData[ind[0][0][0]]; + cVals[5] = mGridData[ind[0][0][1]]; + cVals[6] = mGridData[ind[0][0][2]]; + cVals[7] = extrapolation(mGridData[ind[0][0][2]], mGridData[ind[0][0][1]], mGridData[ind[0][0][0]]); + cVals[8] = mGridData[ind[0][1][0]]; + cVals[9] = mGridData[ind[0][1][1]]; + cVals[10] = mGridData[ind[0][1][2]]; + cVals[11] = extrapolation(mGridData[ind[0][1][2]], mGridData[ind[0][1][1]], mGridData[ind[0][1][0]]); + cVals[12] = mGridData[ind[0][2][0]]; + cVals[13] = mGridData[ind[0][2][1]]; + cVals[14] = mGridData[ind[0][2][2]]; + cVals[15] = extrapolation(mGridData[ind[0][2][2]], mGridData[ind[0][2][1]], mGridData[ind[0][2][0]]); + cVals[16] = extrapolation(mGridData[ind[1][0][0]], mGridData[ind[1][1][0]], mGridData[ind[1][2][0]]); + cVals[17] = extrapolation(mGridData[ii_x_y_z], mGridData[ind[1][1][1]], mGridData[ind[1][2][1]]); + cVals[18] = extrapolation(mGridData[ii_x_y_z], mGridData[ind[1][1][0]], mGridData[ind[1][2][0] + deltaZ[i0]]); + cVals[19] = extrapolation(mGridData[ind[1][0][2]], mGridData[ind[1][1][1]], mGridData[ind[1][2][0]]); + cVals[20] = mGridData[ind[1][0][0]]; + cVals[22] = mGridData[ind[1][0][2]]; + cVals[23] = extrapolation(mGridData[ind[1][0][2]], mGridData[ii_x_y_z], mGridData[ind[1][0][0]]); + cVals[24] = mGridData[ind[1][1][0]]; + cVals[25] = mGridData[ind[1][1][1]]; + cVals[26] = mGridData[ind[1][1][2]]; + cVals[27] = extrapolation(mGridData[ind[1][1][2]], mGridData[ind[1][1][1]], mGridData[ind[1][1][0]]); + cVals[28] = mGridData[ind[1][2][0]]; + cVals[29] = mGridData[ind[1][2][1]]; + cVals[30] = mGridData[ind[1][2][2]]; + cVals[31] = extrapolation(mGridData[ind[1][2][2]], mGridData[ind[1][2][1]], mGridData[ind[1][2][0]]); + cVals[32] = extrapolation(mGridData[ind[2][0][0]], mGridData[ind[2][1][0]], mGridData[ind[2][2][0]]); + cVals[33] = extrapolation(mGridData[ind[2][0][1]], mGridData[ind[2][1][1]], mGridData[ind[2][2][1]]); + cVals[34] = extrapolation(mGridData[ind[2][0][1]], mGridData[ind[2][1][0]], mGridData[ind[2][2][0] + deltaZ[i0]]); + cVals[35] = extrapolation(mGridData[ind[2][0][2]], mGridData[ind[2][1][1]], mGridData[ind[2][2][0]]); + cVals[36] = mGridData[ind[2][0][0]]; + cVals[37] = mGridData[ind[2][0][1]]; + cVals[38] = mGridData[ind[2][0][2]]; + cVals[39] = extrapolation(mGridData[ind[2][0][2]], mGridData[ind[2][0][1]], mGridData[ind[2][0][0]]); + cVals[40] = mGridData[ind[2][1][0]]; + cVals[41] = mGridData[ind[2][1][1]]; + cVals[42] = mGridData[ind[2][1][2]]; + cVals[43] = extrapolation(mGridData[ind[2][1][2]], mGridData[ind[2][1][1]], mGridData[ind[2][1][0]]); + cVals[44] = mGridData[ind[2][2][0]]; + cVals[45] = mGridData[ind[2][2][1]]; + cVals[46] = mGridData[ind[2][2][2]]; + cVals[47] = extrapolation(mGridData[ind[2][2][2]], mGridData[ind[2][2][1]], mGridData[ind[2][2][0]]); + cVals[48] = extrapolation(mGridData[ind[3][0][0]], mGridData[ind[3][1][0]], mGridData[ind[3][2][0]]); + cVals[49] = extrapolation(mGridData[ind[3][0][1]], mGridData[ind[3][1][1]], mGridData[ind[3][2][1]]); + cVals[50] = extrapolation(mGridData[ind[3][0][1]], mGridData[ind[3][1][0]], mGridData[ind[3][2][0] + deltaZ[i0]]); + cVals[51] = extrapolation(mGridData[ind[3][0][2]], mGridData[ind[3][1][1]], mGridData[ind[3][2][0]]); + cVals[52] = mGridData[ind[3][0][0]]; + cVals[53] = mGridData[ind[3][0][1]]; + cVals[54] = mGridData[ind[3][0][2]]; + cVals[55] = extrapolation(mGridData[ind[3][0][2]], mGridData[ind[3][0][1]], mGridData[ind[3][0][0]]); + cVals[56] = mGridData[ind[3][1][0]]; + cVals[57] = mGridData[ind[3][1][1]]; + cVals[58] = mGridData[ind[3][1][2]]; + cVals[59] = extrapolation(mGridData[ind[3][1][2]], mGridData[ind[3][1][1]], mGridData[ind[3][1][0]]); + cVals[60] = mGridData[ind[3][2][0]]; + cVals[61] = mGridData[ind[3][2][1]]; + cVals[62] = mGridData[ind[3][2][2]]; + cVals[63] = extrapolation(mGridData[ind[3][2][2]], mGridData[ind[3][2][1]], mGridData[ind[3][2][0]]); + } break; + + case GridPos::Edge2: + case GridPos::Edge6: + case GridPos::LineK: { + const int ind[4][3][3]{ + {{ii_x_y_z + deltaPhi[i0] + deltaR[i0], ind[0][0][0] - deltaZ[i0], ind[0][0][1] - deltaZ[i0]}, + {ind[0][0][0] - deltaR[i0], ind[0][1][0] - deltaZ[i0], ind[0][1][1] - deltaZ[i0]}, + {ind[0][1][0] - deltaR[i0], ind[0][2][0] - deltaZ[i0], ind[0][2][1] - deltaZ[i0]}}, + {{ii_x_y_z + deltaR[i0], ind[1][0][0] - deltaZ[i0], ind[1][0][1] - deltaZ[i0]}, + {ind[1][0][0] - deltaR[i0], ind[1][1][0] - deltaZ[i0], ind[1][1][1] - deltaZ[i0]}, + {ind[1][1][0] - deltaR[i0], ind[1][2][0] - deltaZ[i0], ind[1][2][1] - deltaZ[i0]}}, + {{ii_x_y_z + deltaPhi[i1] + deltaR[i0], ind[2][0][0] - deltaZ[i0], ind[2][0][1] - deltaZ[i0]}, + {ind[2][0][0] - deltaR[i0], ind[2][1][0] - deltaZ[i0], ind[2][1][1] - deltaZ[i0]}, + {ind[2][1][0] - deltaR[i0], ind[2][2][0] - deltaZ[i0], ind[2][2][1] - deltaZ[i0]}}, + {{ii_x_y_z + deltaPhi[i2] + deltaR[i0], ind[3][0][0] - deltaZ[i0], ind[3][0][1] - deltaZ[i0]}, + {ind[3][0][0] - deltaR[i0], ind[3][1][0] - deltaZ[i0], ind[3][1][1] - deltaZ[i0]}, + {ind[3][1][0] - deltaR[i0], ind[3][2][0] - deltaZ[i0], ind[3][2][1] - deltaZ[i0]}}}; + + cVals[0] = extrapolation(mGridData[ind[0][0][0]], mGridData[ind[0][0][1]], mGridData[ind[0][0][2]]); + cVals[1] = mGridData[ind[0][0][0]]; + cVals[2] = mGridData[ind[0][0][1]]; + cVals[3] = mGridData[ind[0][0][2]]; + cVals[4] = extrapolation(mGridData[ind[0][1][0]], mGridData[ind[0][1][1]], mGridData[ind[0][1][2]]); + cVals[5] = mGridData[ind[0][1][0]]; + cVals[6] = mGridData[ind[0][1][1]]; + cVals[7] = mGridData[ind[0][1][2]]; + cVals[8] = extrapolation(mGridData[ind[0][1][0]], mGridData[ind[0][0][1]], mGridData[ind[0][0][2] + deltaR[i0]]); + cVals[9] = mGridData[ind[0][2][0]]; + cVals[10] = mGridData[ind[0][2][1]]; + cVals[11] = mGridData[ind[0][2][2]]; + cVals[12] = extrapolation(mGridData[ind[0][2][0]], mGridData[ind[0][1][1]], mGridData[ind[0][0][2]]); + cVals[13] = extrapolation(mGridData[ind[0][2][0]], mGridData[ind[0][1][0]], mGridData[ind[0][0][0]]); + cVals[14] = extrapolation(mGridData[ind[0][2][1]], mGridData[ind[0][1][1]], mGridData[ind[0][0][1]]); + cVals[15] = extrapolation(mGridData[ind[0][2][2]], mGridData[ind[0][1][2]], mGridData[ind[0][0][2]]); + cVals[16] = extrapolation(mGridData[ind[1][0][0]], mGridData[ind[1][0][1]], mGridData[ind[1][0][2]]); + cVals[17] = mGridData[ind[1][0][0]]; + cVals[18] = mGridData[ind[1][0][1]]; + cVals[19] = mGridData[ind[1][0][2]]; + cVals[20] = extrapolation(mGridData[ii_x_y_z], mGridData[ind[1][1][1]], mGridData[ind[1][1][2]]); + cVals[22] = mGridData[ind[1][1][1]]; + cVals[23] = mGridData[ind[1][1][2]]; + cVals[24] = extrapolation(mGridData[ii_x_y_z], mGridData[ind[1][0][1]], mGridData[ind[1][0][2] + deltaR[i0]]); + cVals[25] = mGridData[ind[1][2][0]]; + cVals[26] = mGridData[ind[1][2][1]]; + cVals[27] = mGridData[ind[1][2][2]]; + cVals[28] = extrapolation(mGridData[ind[1][2][0]], mGridData[ind[1][1][1]], mGridData[ind[1][0][2]]); + cVals[29] = extrapolation(mGridData[ind[1][2][0]], mGridData[ii_x_y_z], mGridData[ind[1][0][0]]); + cVals[30] = extrapolation(mGridData[ind[1][2][1]], mGridData[ind[1][1][1]], mGridData[ind[1][0][1]]); + cVals[32] = extrapolation(mGridData[ind[2][0][0]], mGridData[ind[2][0][1]], mGridData[ind[2][0][2]]); + cVals[31] = extrapolation(mGridData[ind[1][2][2]], mGridData[ind[1][1][2]], mGridData[ind[1][0][2]]); + cVals[33] = mGridData[ind[2][0][0]]; + cVals[34] = mGridData[ind[2][0][1]]; + cVals[35] = mGridData[ind[2][0][2]]; + cVals[36] = extrapolation(mGridData[ind[2][1][0]], mGridData[ind[2][1][1]], mGridData[ind[2][1][2]]); + cVals[37] = mGridData[ind[2][1][0]]; + cVals[38] = mGridData[ind[2][1][1]]; + cVals[39] = mGridData[ind[2][1][2]]; + cVals[40] = extrapolation(mGridData[ind[2][1][0]], mGridData[ind[2][0][1]], mGridData[ind[2][0][2] + deltaR[i0]]); + cVals[41] = mGridData[ind[2][2][0]]; + cVals[42] = mGridData[ind[2][2][1]]; + cVals[43] = mGridData[ind[2][2][2]]; + cVals[44] = extrapolation(mGridData[ind[2][2][0]], mGridData[ind[2][1][1]], mGridData[ind[2][0][2]]); + cVals[45] = extrapolation(mGridData[ind[2][2][0]], mGridData[ind[2][1][0]], mGridData[ind[2][0][0]]); + cVals[46] = extrapolation(mGridData[ind[2][2][1]], mGridData[ind[2][1][1]], mGridData[ind[2][0][1]]); + cVals[47] = extrapolation(mGridData[ind[2][2][2]], mGridData[ind[2][1][2]], mGridData[ind[2][0][2]]); + cVals[48] = extrapolation(mGridData[ind[3][0][0]], mGridData[ind[3][0][1]], mGridData[ind[3][0][2]]); + cVals[49] = mGridData[ind[3][0][0]]; + cVals[50] = mGridData[ind[3][0][1]]; + cVals[51] = mGridData[ind[3][0][2]]; + cVals[52] = extrapolation(mGridData[ind[3][1][0]], mGridData[ind[3][1][1]], mGridData[ind[3][1][2]]); + cVals[53] = mGridData[ind[3][1][0]]; + cVals[54] = mGridData[ind[3][1][1]]; + cVals[55] = mGridData[ind[3][1][2]]; + cVals[56] = extrapolation(mGridData[ind[3][1][0]], mGridData[ind[3][0][1]], mGridData[ind[3][0][2] + deltaR[i0]]); + cVals[57] = mGridData[ind[3][2][0]]; + cVals[58] = mGridData[ind[3][2][1]]; + cVals[59] = mGridData[ind[3][2][2]]; + cVals[60] = extrapolation(mGridData[ind[3][2][0]], mGridData[ind[3][1][1]], mGridData[ind[3][0][2]]); + cVals[61] = extrapolation(mGridData[ind[3][2][0]], mGridData[ind[3][1][0]], mGridData[ind[3][0][0]]); + cVals[62] = extrapolation(mGridData[ind[3][2][1]], mGridData[ind[3][1][1]], mGridData[ind[3][0][1]]); + cVals[63] = extrapolation(mGridData[ind[3][2][2]], mGridData[ind[3][1][2]], mGridData[ind[3][0][2]]); + } break; + + case GridPos::Edge3: + case GridPos::Edge7: + case GridPos::LineL: { + const int ind[4][3][3]{ + {{ii_x_y_z + deltaPhi[i0] + deltaR[i0] + deltaZ[i0], ind[0][0][0] - deltaZ[i0], ind[0][0][1] - deltaZ[i0]}, + {ind[0][0][0] - deltaR[i0], ind[0][1][0] - deltaZ[i0], ind[0][1][1] - deltaZ[i0]}, + {ind[0][1][0] - deltaR[i0], ind[0][2][0] - deltaZ[i0], ind[0][2][1] - deltaZ[i0]}}, + {{ii_x_y_z + deltaR[i0] + deltaZ[i0], ind[1][0][0] - deltaZ[i0], ind[1][0][1] - deltaZ[i0]}, + {ind[1][0][0] - deltaR[i0], ind[1][1][0] - deltaZ[i0], ind[1][1][1] - deltaZ[i0]}, + {ind[1][1][0] - deltaR[i0], ind[1][2][0] - deltaZ[i0], ind[1][2][1] - deltaZ[i0]}}, + {{ii_x_y_z + deltaPhi[i1] + deltaR[i0] + deltaZ[i0], ind[2][0][0] - deltaZ[i0], ind[2][0][1] - deltaZ[i0]}, + {ind[2][0][0] - deltaR[i0], ind[2][1][0] - deltaZ[i0], ind[2][1][1] - deltaZ[i0]}, + {ind[2][1][0] - deltaR[i0], ind[2][2][0] - deltaZ[i0], ind[2][2][1] - deltaZ[i0]}}, + {{ii_x_y_z + deltaPhi[i2] + deltaR[i0] + deltaZ[i0], ind[3][0][0] - deltaZ[i0], ind[3][0][1] - deltaZ[i0]}, + {ind[3][0][0] - deltaR[i0], ind[3][1][0] - deltaZ[i0], ind[3][1][1] - deltaZ[i0]}, + {ind[3][1][0] - deltaR[i0], ind[3][2][0] - deltaZ[i0], ind[3][2][1] - deltaZ[i0]}}}; + + cVals[0] = mGridData[ind[0][0][0]]; + cVals[1] = mGridData[ind[0][0][1]]; + cVals[2] = mGridData[ind[0][0][2]]; + cVals[3] = extrapolation(mGridData[ind[0][0][2]], mGridData[ind[0][0][1]], mGridData[ind[0][0][0]]); + cVals[4] = mGridData[ind[0][1][0]]; + cVals[5] = mGridData[ind[0][1][1]]; + cVals[6] = mGridData[ind[0][1][2]]; + cVals[7] = extrapolation(mGridData[ind[0][1][2]], mGridData[ind[0][1][1]], mGridData[ind[0][1][0]]); + cVals[8] = mGridData[ind[0][2][0]]; + cVals[9] = mGridData[ind[0][2][1]]; + cVals[10] = mGridData[ind[0][2][2]]; + cVals[11] = extrapolation(mGridData[ind[0][2][2]], mGridData[ind[0][2][1]], mGridData[ind[0][2][0]]); + cVals[12] = extrapolation(mGridData[ind[0][2][0]], mGridData[ind[0][1][0]], mGridData[ind[0][0][0]]); + cVals[13] = extrapolation(mGridData[ind[0][2][1]], mGridData[ind[0][1][1]], mGridData[ind[0][0][1]]); + cVals[14] = extrapolation(mGridData[ind[0][2][2]], mGridData[ind[0][1][2]], mGridData[ind[0][0][2]]); + cVals[15] = extrapolation(mGridData[ind[0][2][2]], mGridData[ind[0][1][1]], mGridData[ind[0][0][0]]); + cVals[16] = mGridData[ind[1][0][0]]; + cVals[17] = mGridData[ind[1][0][1]]; + cVals[18] = mGridData[ind[1][0][2]]; + cVals[19] = extrapolation(mGridData[ind[1][0][2]], mGridData[ind[1][0][1]], mGridData[ind[1][0][0]]); + cVals[20] = mGridData[ind[1][1][0]]; + cVals[22] = mGridData[ind[1][1][2]]; + cVals[23] = extrapolation(mGridData[ind[1][1][2]], mGridData[ii_x_y_z], mGridData[ind[1][1][0]]); + cVals[24] = mGridData[ind[1][2][0]]; + cVals[25] = mGridData[ind[1][2][1]]; + cVals[26] = mGridData[ind[1][2][2]]; + cVals[27] = extrapolation(mGridData[ind[1][2][2]], mGridData[ind[1][2][1]], mGridData[ind[1][2][0]]); + cVals[28] = extrapolation(mGridData[ind[1][2][0]], mGridData[ind[1][1][0]], mGridData[ind[1][0][0]]); + cVals[29] = extrapolation(mGridData[ind[1][2][1]], mGridData[ii_x_y_z], mGridData[ind[1][0][1]]); + cVals[30] = extrapolation(mGridData[ind[1][2][2]], mGridData[ind[1][1][2]], mGridData[ind[1][0][2]]); + cVals[31] = extrapolation(mGridData[ind[1][2][2]], mGridData[ind[1][1][1]], mGridData[ind[1][0][0]]); + cVals[32] = mGridData[ind[2][0][0]]; + cVals[33] = mGridData[ind[2][0][1]]; + cVals[34] = mGridData[ind[2][0][2]]; + cVals[35] = extrapolation(mGridData[ind[2][0][2]], mGridData[ind[2][0][1]], mGridData[ind[2][0][0]]); + cVals[36] = mGridData[ind[2][1][0]]; + cVals[37] = mGridData[ind[2][1][1]]; + cVals[38] = mGridData[ind[2][1][2]]; + cVals[39] = extrapolation(mGridData[ind[2][1][2]], mGridData[ind[2][1][1]], mGridData[ind[2][1][0]]); + cVals[40] = mGridData[ind[2][2][0]]; + cVals[41] = mGridData[ind[2][2][1]]; + cVals[42] = mGridData[ind[2][2][2]]; + cVals[43] = extrapolation(mGridData[ind[2][2][2]], mGridData[ind[2][2][1]], mGridData[ind[2][2][0]]); + cVals[44] = extrapolation(mGridData[ind[2][2][0]], mGridData[ind[2][1][0]], mGridData[ind[2][0][0]]); + cVals[45] = extrapolation(mGridData[ind[2][2][1]], mGridData[ind[2][1][1]], mGridData[ind[2][0][1]]); + cVals[46] = extrapolation(mGridData[ind[2][2][2]], mGridData[ind[2][1][2]], mGridData[ind[2][0][2]]); + cVals[47] = extrapolation(mGridData[ind[2][2][2]], mGridData[ind[2][1][1]], mGridData[ind[2][0][0]]); + cVals[48] = mGridData[ind[3][0][0]]; + cVals[49] = mGridData[ind[3][0][1]]; + cVals[50] = mGridData[ind[3][0][2]]; + cVals[51] = extrapolation(mGridData[ind[3][0][2]], mGridData[ind[3][0][1]], mGridData[ind[3][0][0]]); + cVals[52] = mGridData[ind[3][1][0]]; + cVals[53] = mGridData[ind[3][1][1]]; + cVals[54] = mGridData[ind[3][1][2]]; + cVals[55] = extrapolation(mGridData[ind[3][1][2]], mGridData[ind[3][1][1]], mGridData[ind[3][1][0]]); + cVals[56] = mGridData[ind[3][2][0]]; + cVals[57] = mGridData[ind[3][2][1]]; + cVals[58] = mGridData[ind[3][2][2]]; + cVals[59] = extrapolation(mGridData[ind[3][2][2]], mGridData[ind[3][2][1]], mGridData[ind[3][2][0]]); + cVals[60] = extrapolation(mGridData[ind[3][2][0]], mGridData[ind[3][1][0]], mGridData[ind[3][0][0]]); + cVals[61] = extrapolation(mGridData[ind[3][2][1]], mGridData[ind[3][1][1]], mGridData[ind[3][0][1]]); + cVals[62] = extrapolation(mGridData[ind[3][2][2]], mGridData[ind[3][1][2]], mGridData[ind[3][0][2]]); + cVals[63] = extrapolation(mGridData[ind[3][2][2]], mGridData[ind[3][1][1]], mGridData[ind[3][0][0]]); + } break; + } +} + +} // namespace tpc +} // namespace o2 + +#endif diff --git a/Detectors/TPC/spacecharge/include/TPCSpaceCharge/Vector.h b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/Vector.h new file mode 100644 index 0000000000000..a6862291d84dc --- /dev/null +++ b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/Vector.h @@ -0,0 +1,173 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file matrix.h +/// \brief Definition of Vector and Matrix class +/// +/// \author Matthias Kleiner + +#ifndef ALICEO2_TPC_VECTOR_H_ +#define ALICEO2_TPC_VECTOR_H_ + +#include + +namespace o2 +{ +namespace tpc +{ +template +class Matrix +{ + using VDataT = Vc::Vector; + + public: + /// constructor + /// \param dataMatrix pointer to the data + Matrix(const Vc::Memory* dataMatrix) : mDataMatrix(dataMatrix) {} + + const Vc::Memory& operator[](size_t i) const { return mDataMatrix[i]; } + + private: + const Vc::Memory* mDataMatrix{}; +}; + +template +class Vector +{ + using VDataT = Vc::Vector; + + public: + /// default constructor + Vector() = default; + + /// constructor + /// \param dataVector data which is assigned to the vector + Vector(const Vc::Memory& dataVector) : mDataVector(dataVector) {} + + /// operator access + const DataT operator[](size_t i) const { return mDataVector.scalar(i); } + DataT& operator[](size_t i) { return mDataVector.scalar(i); } + + /// sets the vector with index j + void setVector(const size_t j, const VDataT& vector) { mDataVector.vector(j) = vector; } + + /// \return returns the vector with index j + const VDataT getVector(const size_t j) const { return mDataVector.vector(j); } + + /// \return returns the number of Vc::Vector stored in the Vector + size_t getvectorsCount() const { return mDataVector.vectorsCount(); } + + /// \return returns the number of entries stored in the Vector + size_t getentriesCount() const { return mDataVector.entriesCount(); } + + private: + // storage for the data + Vc::Memory mDataVector{}; +}; + +template +inline Vector operator*(const Matrix& a, const Vector& b) +{ + using V = Vc::Vector; + // resulting vector c + Vector c; + for (size_t i = 0; i < N; ++i) { + V c_ij{}; + for (size_t j = 0; j < a[i].vectorsCount(); ++j) { + c_ij += a[i].vector(j) * b.getVector(j); + } + c[i] = c_ij.sum(); + } + return c; +} + +template +inline Vector floor(const Vector& a) +{ + Vector c; + for (size_t j = 0; j < a.getvectorsCount(); ++j) { + c.setVector(j, Vc::floor(a.getVector(j))); + } + return c; +} + +template +inline Vector operator-(const Vector& a, const Vector& b) +{ + // resulting matrix c + Vector c; + for (size_t j = 0; j < a.getvectorsCount(); ++j) { + c.setVector(j, a.getVector(j) - b.getVector(j)); + } + return c; +} + +template +inline Vector operator+(const Vector& a, const Vector& b) +{ + // resulting matrix c + Vector c; + for (size_t j = 0; j < a.getvectorsCount(); ++j) { + c.setVector(j, a.getVector(j) + b.getVector(j)); + } + return c; +} + +template +inline Vector operator*(const DataT a, const Vector& b) +{ + // resulting matrix c + Vector c; + for (size_t j = 0; j < b.getvectorsCount(); ++j) { + c.setVector(j, a * b.getVector(j)); + } + return c; +} + +// compute the sum of one Vector +template +inline DataT sum(const Vector& a) +{ + // resulting matrix c + Vc::Vector b = a.getVector(0); + for (size_t j = 1; j < a.getvectorsCount(); ++j) { + b += a.getVector(j); + } + return b.sum(); +} + +// multiply each row from a vector with the row from a second vector +template +inline Vector operator*(const Vector& a, const Vector& b) +{ + // resulting matrix c + Vector c{}; + for (size_t j = 0; j < a.getvectorsCount(); ++j) { + c.setVector(j, a.getVector(j) * b.getVector(j)); + } + return c; +} + +// check if all elements are equal +template +inline bool operator==(const Vector& a, const Vector& b) +{ + for (size_t j = 0; j < a.getvectorsCount(); ++j) { + if (any_of(a.getVector(j) != b.getVector(j))) { + return false; + } + } + return true; +} + +} // namespace tpc +} // namespace o2 + +#endif diff --git a/Detectors/TPC/spacecharge/include/TPCSpaceCharge/Vector3D.h b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/Vector3D.h new file mode 100644 index 0000000000000..bd00405f1c9a7 --- /dev/null +++ b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/Vector3D.h @@ -0,0 +1,109 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file Vector3D.h +/// \brief this is a simple 3D-matrix class with the possibility to resize the dimensions +/// +/// \author Matthias Kleiner +/// \date Oct 23, 2020 + +#ifndef ALICEO2_TPC_VECTOR3D_H_ +#define ALICEO2_TPC_VECTOR3D_H_ + +namespace o2 +{ +namespace tpc +{ + +/// this is a simple vector class which is used in the poisson solver class + +/// \tparam DataT the data type of the mStorage which is used during the calculations +template +class Vector3D +{ + public: + /// Constructor for a tricubic interpolator + /// \param nr number of data points in r directions + /// \param nz number of data points in r directions + /// \param nphi number of data points in r directions + Vector3D(const unsigned int nr, const unsigned int nz, const unsigned int nphi) : mNr{nr}, mNz{nz}, mNphi{nphi}, mStorage{nr * nz * nphi} {}; + + /// default constructor + Vector3D() = default; + + /// operator to set the values + DataT& operator()(const unsigned int iR, const unsigned int iZ, const unsigned int iPhi) + { + return mStorage[getIndex(iR, iZ, iPhi)]; + } + + /// operator to read the values + const DataT& operator()(const unsigned int iR, const unsigned int iZ, const unsigned int iPhi) const + { + return mStorage[getIndex(iR, iZ, iPhi)]; + } + + /// operator to directly access the values + DataT& operator[](const unsigned int index) + { + return mStorage[index]; + } + + const DataT& operator[](const unsigned int index) const + { + return mStorage[index]; + } + + /// \param iR index in r direction + /// \param iZ index in z direction + /// \param iPhi index in phi direction + /// \return returns the index for given indices + int getIndex(const unsigned int iR, const unsigned int iZ, const unsigned int iPhi) const + { + return iR + mNr * (iZ + mNz * iPhi); + } + + /// resize the vector + /// \param nr number of data points in r directions + /// \param nz number of data points in r directions + /// \param nphi number of data points in r directions + void resize(const unsigned int nr, const unsigned int nz, const unsigned int nphi) + { + mNr = nr; + mNz = nz; + mNphi = nphi; + mStorage.resize(nr * nz * nphi); + } + + const auto& data() const { return mStorage; } + auto& data() { return mStorage; } + + unsigned int getNr() const { return mNr; } ///< get number of data points in r direction + unsigned int getNz() const { return mNz; } ///< get number of data points in z direction + unsigned int getNphi() const { return mNphi; } ///< get number of data points in phi direction + unsigned int size() const { return mStorage.size; } ///< get number of data points + + auto begin() const { return mStorage.begin(); } + auto begin() { return mStorage.begin(); } + + auto end() const { return mStorage.end(); } + auto end() { return mStorage.end(); } + + private: + unsigned int mNr{}; ///< number of data points in r direction + unsigned int mNz{}; ///< number of data points in z direction + unsigned int mNphi{}; ///< number of data points in phi direction + std::vector mStorage{}; ///< vector containing the data +}; + +} // namespace tpc +} // namespace o2 + +#endif diff --git a/Detectors/TPC/spacecharge/macro/calculateDistortionsCorrections.C b/Detectors/TPC/spacecharge/macro/calculateDistortionsCorrections.C new file mode 100644 index 0000000000000..05a6050a79dda --- /dev/null +++ b/Detectors/TPC/spacecharge/macro/calculateDistortionsCorrections.C @@ -0,0 +1,365 @@ +// g++ -o spacecharge ~/alice/O2/Detectors/TPC/spacecharge/macro/calculateDistortionsCorrections.C -I ~/alice/sw/osx_x86-64/FairLogger/latest/include -L ~/alice/sw/osx_x86-64/FairLogger/latest/lib -I$O2_ROOT/include -L$O2_ROOT/lib -lO2TPCSpacecharge -lO2CommonUtils -std=c++17 -I$ROOTSYS/include -L$ROOTSYS/lib -lCore -L$VC_ROOT/lib -lVc -I$VC_ROOT/include -Xpreprocessor -fopenmp -I/usr/local/include -L/usr/local/lib -lomp -O3 -ffast-math -lFairLogger -lRIO +#include "TPCSpaceCharge/SpaceCharge.h" +#include +#include +#include +#include "CommonUtils/TreeStreamRedirector.h" + +template +void calculateDistortionsAnalytical(const int globalEFieldTypeAna = 1, const int globalDistTypeAna = 1, const int eFieldTypeAna = 1, const int usePoissonSolverAna = 1, const int nSteps = 1, const int simpsonIterations = 3, const int nThreads = -1); + +template +void calculateDistortionsFromHist(const char* path, const char* histoName, const int sides, const int globalEFieldType = 1, const int globalDistType = 1, const int nSteps = 1, const int simpsonIterations = 3, const int nThreads = -1); + +/// \param gridType granularity of the grid +/// \param globalEFieldTypeAna setting for global distortions/corrections: 0: using electric field, 1: using local dis/corr interpolator +/// \param globalDistTypeAna setting for global distortions: 0: standard method, 1: interpolation of global corrections +/// \param eFieldTypeAna setting for electrc field: 0: analytical formula, 1: tricubic interpolator +/// \param usePoissonSolverAna setting for use poisson solver or analytical formula for potential: 0: analytical formula, 1: poisson solver +/// \param nSteps number of which are used for calculation of distortions/corrections per z-bin +/// \param simpsonIterations number of iterations used in the simpson intergration +/// \param nThreads number of threads which are used (if the value is -1 all threads should be used) +void calcDistAna(const int gridType = 0, const int globalEFieldTypeAna = 1, const int globalDistTypeAna = 1, const int eFieldTypeAna = 1, const int usePoissonSolverAna = 1, const int nSteps = 1, const int simpsonIterations = 3, const int nThreads = -1) +{ + if (gridType == 0) { + calculateDistortionsAnalytical(globalEFieldTypeAna, globalDistTypeAna, eFieldTypeAna, usePoissonSolverAna, nSteps, simpsonIterations, nThreads); + } else if (gridType == 1) { + calculateDistortionsAnalytical(globalEFieldTypeAna, globalDistTypeAna, eFieldTypeAna, usePoissonSolverAna, nSteps, simpsonIterations, nThreads); + } else if (gridType == 2) { + calculateDistortionsAnalytical(globalEFieldTypeAna, globalDistTypeAna, eFieldTypeAna, usePoissonSolverAna, nSteps, simpsonIterations, nThreads); + } else if (gridType == 3) { + calculateDistortionsAnalytical(globalEFieldTypeAna, globalDistTypeAna, eFieldTypeAna, usePoissonSolverAna, nSteps, simpsonIterations, nThreads); + } else if (gridType == 4) { + calculateDistortionsAnalytical(globalEFieldTypeAna, globalDistTypeAna, eFieldTypeAna, usePoissonSolverAna, nSteps, simpsonIterations, nThreads); + } +} + +/// \param path path to the root file containing the 3D density histogram +/// \param histoName name of the histogram in the root file +/// \param gridType granularity of the grid +/// \param sides setting which sides will be processed: 0: A- and C-Side, 1: A-Side, 2: C-Side +/// \param globalEFieldType setting for global distortions/corrections: 0: using electric field, 1: using local dis/corr interpolator +/// \param globalDistType setting for global distortions: 0: standard method, 1: interpolation of global corrections +/// \param nSteps number of which are used for calculation of distortions/corrections per z-bin +/// \param simpsonIterations number of iterations used in the simpson intergration +/// \param nThreads number of threads which are used (if the value is -1 all threads should be used) +void calcDistFromHist(const char* path, const char* histoName, const int gridType = 0, const int sides = 0, const int globalEFieldType = 1, const int globalDistType = 1, const int nSteps = 1, const int simpsonIterations = 3, const int nThreads = -1) +{ + if (gridType == 0) { + calculateDistortionsFromHist(path, histoName, sides, globalEFieldType, globalDistType, nSteps, simpsonIterations, nThreads); + } else if (gridType == 1) { + calculateDistortionsFromHist(path, histoName, sides, globalEFieldType, globalDistType, nSteps, simpsonIterations, nThreads); + } else if (gridType == 2) { + calculateDistortionsFromHist(path, histoName, sides, globalEFieldType, globalDistType, nSteps, simpsonIterations, nThreads); + } else if (gridType == 3) { + calculateDistortionsFromHist(path, histoName, sides, globalEFieldType, globalDistType, nSteps, simpsonIterations, nThreads); + } else if (gridType == 4) { + calculateDistortionsFromHist(path, histoName, sides, globalEFieldType, globalDistType, nSteps, simpsonIterations, nThreads); + } else if (gridType == 5) { + calculateDistortionsFromHist(path, histoName, sides, globalEFieldType, globalDistType, nSteps, simpsonIterations, nThreads); + } +} + +/// \param spaceChargeCalc SpaceCharge object in which the calculations are performed +/// \param anaFields struct containing the analytical electric fields potential, space charge +/// \param fileOut output file where the calculated values are stored +/// \param side side of the TPC +/// \param globalEFieldType settings for global distortions/corrections: 0: using electric field for calculation of global distortions/corrections, 1: using local dis/corr interpolator for calculation of global distortions/corrections +/// \param globalDistType settings for global distortions: 0: standard method (start calculation of global distortion at each voxel in the tpc and follow electron drift to readout -slow-), 1: interpolation of global corrections (use the global corrections to apply an iterative approach to obtain the global distortions -fast-) +/// \param eFieldType setting for the electric field: 0: use analytical formula for the eletrical field for all calculations, 1: use the tricubic interpolator for the electric field +/// \param usePoissonSolver use poisson solver to calculate the potential or get the potential from the analytical formula 0: use analytical formula, 1: use poisson solver to calculate the potential (also calculates Efields using the obtained potential) +template +void calculateDistortionsCorrectionsAnalytical(o2::tpc::SpaceCharge& spaceChargeCalc, o2::tpc::AnalyticalFields anaFields, const int globalEFieldType, const int eFieldType, const int globalDistType, const int usePoissonSolver) +{ + using timer = std::chrono::high_resolution_clock; + + std::cout << "====== STARTING CALCULATIION OF DISTORTIONS AND CORRECTIONS BY USING A ANALYTICAL FORMULA AS INPUT ======" << std::endl; + std::cout << "bins in z: " << Nz << std::endl; + std::cout << "bins in r: " << Nr << std::endl; + std::cout << "bins in phi: " << Nphi << std::endl; + + const std::array sGlobalType{"Electric fields", "local distortion/correction interpolator"}; + std::cout << "calculation of global distortions and corrections are performed by using: " << sGlobalType[globalEFieldType] << std::endl; + + const std::array sGlobalDistType{"Standard method", "interpolation of global corrections"}; + std::cout << "calculation of global distortions performed by following method: " << sGlobalDistType[globalDistType] << std::endl; + + const std::array sEfieldType{"analytical formula", "tricubic interpolator"}; + std::cout << "Using the E-fields from: " << sEfieldType[eFieldType] << std::endl; + + const std::array sUsePoissonSolver{"analytical formula", "poisson solver"}; + std::cout << "Using the Potential from: " << sUsePoissonSolver[usePoissonSolver] << std::endl; + std::cout << std::endl; + + const o2::tpc::Side side = anaFields.getSide(); + spaceChargeCalc.setChargeDensityFromFormula(anaFields); + + auto startTotal = timer::now(); + + if (usePoissonSolver == 1) { + spaceChargeCalc.setPotentialBoundaryFromFormula(anaFields); + auto start = timer::now(); + spaceChargeCalc.poissonSolver(side); + auto stop = timer::now(); + std::chrono::duration time = stop - start; + std::cout << "poissonSolver: " << time.count() << std::endl; + } else { + spaceChargeCalc.setPotentialFromFormula(anaFields); + } + + if (usePoissonSolver == 1) { + auto start = timer::now(); + spaceChargeCalc.calcEField(side); + auto stop = timer::now(); + std::chrono::duration time = stop - start; + std::cout << "electric field calculation: " << time.count() << std::endl; + } else { + spaceChargeCalc.setEFieldFromFormula(anaFields); + } + + const auto numEFields = spaceChargeCalc.getElectricFieldsInterpolator(side); + auto start = timer::now(); + const auto dist = o2::tpc::SpaceCharge::Type::Distortions; + (eFieldType == 1) ? spaceChargeCalc.calcLocalDistortionsCorrections(dist, numEFields) : spaceChargeCalc.calcLocalDistortionsCorrections(dist, anaFields); // local distortion calculation + auto stop = timer::now(); + std::chrono::duration time = stop - start; + std::cout << "local distortions analytical: " << time.count() << std::endl; + + start = timer::now(); + const auto corr = o2::tpc::SpaceCharge::Type::Corrections; + (eFieldType == 1) ? spaceChargeCalc.calcLocalDistortionsCorrections(corr, numEFields) : spaceChargeCalc.calcLocalDistortionsCorrections(corr, anaFields); // local correction calculation + stop = timer::now(); + time = stop - start; + std::cout << "local corrections analytical: " << time.count() << std::endl; + + start = timer::now(); + const auto lCorrInterpolator = spaceChargeCalc.getLocalCorrInterpolator(side); + if (globalEFieldType == 1) { + spaceChargeCalc.calcGlobalCorrections(lCorrInterpolator); + } else if (eFieldType == 1) { + spaceChargeCalc.calcGlobalCorrections(numEFields); + } else { + spaceChargeCalc.calcGlobalCorrections(anaFields); + } + + stop = timer::now(); + time = stop - start; + std::cout << "global corrections analytical: " << time.count() << std::endl; + + start = timer::now(); + const auto lDistInterpolator = spaceChargeCalc.getLocalDistInterpolator(side); + if (globalDistType == 0) { + if (globalEFieldType == 1) { + spaceChargeCalc.calcGlobalDistortions(lDistInterpolator); + } else if (eFieldType == 1) { + spaceChargeCalc.calcGlobalDistortions(numEFields); + } else { + spaceChargeCalc.calcGlobalDistortions(anaFields); + } + } else { + const auto globalCorrInterpolator = spaceChargeCalc.getGlobalCorrInterpolator(side); + spaceChargeCalc.calcGlobalDistWithGlobalCorrIterative(globalCorrInterpolator); + } + stop = timer::now(); + time = stop - start; + std::cout << "global distortions analytical: " << time.count() << std::endl; + + auto stopTotal = timer::now(); + time = stopTotal - startTotal; + std::cout << "====== everything is done. Total Time: " << time.count() << std::endl; + std::cout << std::endl; +} + +template +void writeToTree(o2::tpc::SpaceCharge& spaceCharge3D, o2::utils::TreeStreamRedirector& pcstream, const o2::tpc::Side side) +{ + for (size_t iPhi = 0; iPhi < Nphi; ++iPhi) { + for (size_t iR = 0; iR < Nr; ++iR) { + for (size_t iZ = 0; iZ < Nz; ++iZ) { + auto ldistR = spaceCharge3D.getLocalDistR(iZ, iR, iPhi, side); + auto ldistZ = spaceCharge3D.getLocalDistZ(iZ, iR, iPhi, side); + auto ldistRPhi = spaceCharge3D.getLocalDistRPhi(iZ, iR, iPhi, side); + auto lcorrR = spaceCharge3D.getLocalCorrR(iZ, iR, iPhi, side); + auto lcorrZ = spaceCharge3D.getLocalCorrZ(iZ, iR, iPhi, side); + auto lcorrRPhi = spaceCharge3D.getLocalCorrRPhi(iZ, iR, iPhi, side); + + auto distR = spaceCharge3D.getGlobalDistR(iZ, iR, iPhi, side); + auto distZ = spaceCharge3D.getGlobalDistZ(iZ, iR, iPhi, side); + auto distRPhi = spaceCharge3D.getGlobalDistRPhi(iZ, iR, iPhi, side); + auto corrR = spaceCharge3D.getGlobalCorrR(iZ, iR, iPhi, side); + auto corrZ = spaceCharge3D.getGlobalCorrZ(iZ, iR, iPhi, side); + auto corrRPhi = spaceCharge3D.getGlobalCorrRPhi(iZ, iR, iPhi, side); + + // distort then correct + auto radius = spaceCharge3D.getRVertex(iR, side); + auto z = spaceCharge3D.getZVertex(iZ, side); + auto phi = spaceCharge3D.getPhiVertex(iPhi, side); + DataT corrRDistPoint{}; + DataT corrZDistPoint{}; + DataT corrRPhiDistPoint{}; + + const DataT zDistorted = z + distZ; + const DataT radiusDistorted = radius + distR; + const DataT phiDistorted = spaceCharge3D.regulatePhi(phi + distRPhi / radius, side); + spaceCharge3D.getCorrectionsCyl(zDistorted, radiusDistorted, phiDistorted, side, corrZDistPoint, corrRDistPoint, corrRPhiDistPoint); + corrRPhiDistPoint *= radius / radiusDistorted; + + auto eZ = spaceCharge3D.getEz(iZ, iR, iPhi, side); + auto eR = spaceCharge3D.getEr(iZ, iR, iPhi, side); + auto ePhi = spaceCharge3D.getEphi(iZ, iR, iPhi, side); + auto pot = spaceCharge3D.getPotential(iZ, iR, iPhi, side); + auto charge = spaceCharge3D.getDensity(iZ, iR, iPhi, side); + + auto xPos = spaceCharge3D.getXFromPolar(radius, phi); + auto yPos = spaceCharge3D.getYFromPolar(radius, phi); + + int nr = Nr; + int nz = Nz; + int nphi = Nphi; + int iSide = side; + + pcstream << "distortions" + /// numer of bins + << "nR=" << nr + << "nPhi=" << nphi + << "nZ=" << nz + // bin indices + << "ir=" << iR + << "iz=" << iZ + << "iphi=" << iPhi + // coordinates + << "r=" << radius + << "z=" << z + << "x=" << xPos + << "y=" << yPos + << "phi=" << phi + // local distortions + << "ldistR=" << ldistR + << "ldistZ=" << ldistZ + << "ldistRPhi=" << ldistRPhi + // local corrections + << "lcorrR=" << lcorrR + << "lcorrZ=" << lcorrZ + << "lcorrRPhi=" << lcorrRPhi + // global distortions + << "distR=" << distR + << "distZ=" << distZ + << "distRPhi=" << distRPhi + // global corrections + << "corrR=" << corrR + << "corrZ=" << corrZ + << "corrRPhi=" << corrRPhi + // correction after distortion applied (test for consistency) + << "corrRDistortedPoint=" << corrRDistPoint + << "corrRPhiDistortedPoint=" << corrRPhiDistPoint + << "corrZDistortedPoint=" << corrZDistPoint + // electric fields etc. + << "Er=" << eR + << "Ez=" << eZ + << "Ephi=" << ePhi + << "potential=" << pot + << "charge=" << charge + << "side=" << iSide + << "\n"; + } + } + } +} + +/// \param globalEFieldTypeAna setting for global distortions/corrections: 0: using electric field, 1: using local dis/corr interpolator +/// \param globalDistTypeAna setting for global distortions: 0: standard method, 1: interpolation of global corrections +/// \param eFieldTypeAna setting for electrc field: 0: analytical formula, 1: tricubic interpolator +/// \param usePoissonSolverAna setting for use poisson solver or analytical formula for potential: 0: analytical formula, 1: poisson solver +/// \param nSteps number of which are used for calculation of distortions/corrections per z-bin +/// \param simpsonIterations number of iterations used in the simpson intergration +template +void calculateDistortionsAnalytical(const int globalEFieldTypeAna, const int globalDistTypeAna, const int eFieldTypeAna, const int usePoissonSolverAna, const int nSteps, const int simpsonIterations, const int nThreads) +{ + const auto integrationStrategy = o2::tpc::SpaceCharge::IntegrationStrategy::SimpsonIterative; + o2::tpc::SpaceCharge spaceCharge3D; + spaceCharge3D.setOmegaTauT1T2(0.32f, 1, 1); + spaceCharge3D.setNStep(nSteps); + spaceCharge3D.setSimpsonNIteratives(simpsonIterations); + spaceCharge3D.setNumericalIntegrationStrategy(integrationStrategy); + if (nThreads != -1) { + spaceCharge3D.setNThreads(nThreads); + } + + // write to root file + o2::utils::TreeStreamRedirector pcstream(TString::Format("distortions_ana_nR%lu_nZ%lu_nPhi%lu_SimpsonsIter%i.root", Nr, Nz, Nphi, simpsonIterations).Data(), "RECREATE"); + for (int iside = 0; iside < 2; ++iside) { + std::cout << "side: " << iside << std::endl; + o2::tpc::Side side = (iside == 0) ? o2::tpc::Side::A : o2::tpc::Side::C; + o2::tpc::AnalyticalFields anaFields(side); + calculateDistortionsCorrectionsAnalytical(spaceCharge3D, anaFields, globalEFieldTypeAna, eFieldTypeAna, globalDistTypeAna, usePoissonSolverAna); + pcstream.GetFile()->cd(); + writeToTree(spaceCharge3D, pcstream, side); + } + + pcstream.GetFile()->cd(); + pcstream.Close(); +} + +/// \param path path to the root file containing the 3D density histogram +/// \param histoName name of the histogram in the root file +/// \param sides setting which sides will be processed: 0: A- and C-Side, 1: A-Side, 2: C-Side +/// \param globalEFieldType setting for global distortions/corrections: 0: using electric field, 1: using local dis/corr interpolator +/// \param globalDistType setting for global distortions: 0: standard method, 1: interpolation of global corrections +/// \param nSteps number of which are used for calculation of distortions/corrections per z-bin +/// \param simpsonIterations number of iterations used in the simpson intergration +template +void calculateDistortionsFromHist(const char* path, const char* histoName, const int sides, const int globalEFieldType, const int globalDistType, const int nSteps, const int simpsonIterations, const int nThreads) +{ + using SC = o2::tpc::SpaceCharge; + const auto integrationStrategy = o2::tpc::SpaceCharge::IntegrationStrategy::SimpsonIterative; + SC spaceCharge3D; + spaceCharge3D.setOmegaTauT1T2(0.32f, 1, 1); + spaceCharge3D.setNStep(nSteps); + spaceCharge3D.setSimpsonNIteratives(simpsonIterations); + spaceCharge3D.setNumericalIntegrationStrategy(integrationStrategy); + if (nThreads != -1) { + spaceCharge3D.setNThreads(nThreads); + } + + // set density from input file + TFile fileOutSCDensity(path, "READ"); + spaceCharge3D.fillChargeDensityFromFile(fileOutSCDensity, histoName); + fileOutSCDensity.Close(); + + o2::utils::TreeStreamRedirector pcstream(TString::Format("distortions_real_nR%lu_nZ%lu_nPhi%lu_SimpsonsIter%i.root", Nr, Nz, Nphi, simpsonIterations).Data(), "RECREATE"); + int iSideStart = 0; + int iSideEnd = 2; + if (sides == 1) { + // a side only + iSideEnd = 1; + } else if (sides == 2) { + // c side only + iSideStart = 1; + } + for (int iside = iSideStart; iside < iSideEnd; ++iside) { + std::cout << "side: " << iside << std::endl; + o2::tpc::Side side = (iside == 0) ? o2::tpc::Side::A : o2::tpc::Side::C; + const auto distType = globalDistType == 0 ? SC::GlobalDistType::Standard : SC ::GlobalDistType::Fast; + spaceCharge3D.setGlobalDistType(distType); + const auto eType = globalEFieldType == 1 ? SC::GlobalDistCorrMethod::LocalDistCorr : SC::GlobalDistCorrMethod::ElectricalField; + spaceCharge3D.setGlobalDistCorrMethod(eType); + spaceCharge3D.calculateDistortionsCorrections(side); + // write to root file + pcstream.GetFile()->cd(); + writeToTree(spaceCharge3D, pcstream, side); + } + pcstream.GetFile()->cd(); + pcstream.Close(); + + // write global corrections and distortions to file + TFile fOut("spacecharge.root", "RECREATE"); + if (sides != 2) { + spaceCharge3D.dumpGlobalDistortions(fOut, o2::tpc::Side::A); + spaceCharge3D.dumpGlobalCorrections(fOut, o2::tpc::Side::A); + } + if (sides != 1) { + spaceCharge3D.dumpGlobalDistortions(fOut, o2::tpc::Side::C); + spaceCharge3D.dumpGlobalCorrections(fOut, o2::tpc::Side::C); + } + fOut.Close(); +} diff --git a/Detectors/TPC/spacecharge/macro/createResidualDistortionObject.C b/Detectors/TPC/spacecharge/macro/createResidualDistortionObject.C new file mode 100644 index 0000000000000..f3b7d02f0c750 --- /dev/null +++ b/Detectors/TPC/spacecharge/macro/createResidualDistortionObject.C @@ -0,0 +1,215 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file createResidualDistortionObject.C +/// \brief This macro creates a SpaceCharge object with residual distortions from a fluctuating and an average space-charge density histogram and stores it in a file. +/// \author Ernst Hellbär, Goethe-Universität Frankfurt, ernst.hellbar@cern.ch + +#include + +#include "TFile.h" +#include "CommonUtils/TreeStreamRedirector.h" +#include "TPCSpaceCharge/SpaceCharge.h" +#include "MathUtils/Cartesian.h" + +using namespace o2::tpc; +using DataT = double; +constexpr int NZ = 129; +constexpr int NR = 129; +constexpr int NPHI = 180; +using DataContainer = DataContainer3D; + +// function declarations +void createSpaceCharge(o2::tpc::SpaceCharge& spaceCharge, const char* path, const char* histoName, const int bSign, const int nThreads = -1); +void fillDistortionLookupMatrices(o2::tpc::SpaceCharge& spaceChargeCalcFluc, o2::tpc::SpaceCharge* spaceChargeCalcAvg, DataContainer matrixDistDr[2], DataContainer matrixDistDrphi[2], DataContainer matrixDistDz[2]); +void makeDebugTreeResiduals(o2::tpc::SpaceCharge& calcFluc, o2::tpc::SpaceCharge& calcAvg, o2::tpc::SpaceCharge& spaceChargeRes); +/// Create a SpaceCharge object with residual distortions from a fluctuating and an average space-charge density histogram and write it to a file. +/// \param bSign sign of the B-field: -1 = negative, 0 = no B-field, 1 = positive +/// \param pathToHistoFile path to a file with the fluctuating and average histograms +/// \param histoFlucName name of the fluctuating histogram +/// \param histoAvgName name of the average histogram +void createResidualDistortionObject(int bSign, const char* pathToHistoFile = "InputSCDensityHistograms_8000events.root", const char* histoFlucName = "inputSCDensity3D_8000_0", const char* histoAvgName = "inputSCDensity3D_8000_avg", const int nThreads = -1, bool debug = false) +{ + /* + Usage: + root -l -b -q createResidualDistortionObject.C+\(-1,\"InputSCDensityHistograms_8000events.root\",\"inputSCDensity3D_8000_0\",\"inputSCDensity3D_8000_avg\",true\) + */ + + // Calculate fluctuating and average distortion and correction lookup tables + o2::tpc::SpaceCharge scCalcFluc; + o2::tpc::SpaceCharge scCalcAvg; + createSpaceCharge(scCalcFluc, pathToHistoFile, histoFlucName, bSign, nThreads); + createSpaceCharge(scCalcAvg, pathToHistoFile, histoAvgName, bSign, nThreads); + + // Create matrices and fill them with residual distortions. Create SpaceCharge object, assign residual distortion matrices to it and store it in the output file. + DataContainer matrixResDistDr[2]; + DataContainer matrixResDistDrphi[2]; + DataContainer matrixResDistDz[2]; + + fillDistortionLookupMatrices(scCalcFluc, &scCalcAvg, matrixResDistDr, matrixResDistDrphi, matrixResDistDz); + o2::tpc::SpaceCharge spaceChargeRes; + spaceChargeRes.setDistortionLookupTables(matrixResDistDz[0], matrixResDistDr[0], matrixResDistDrphi[0], o2::tpc::Side::A); + spaceChargeRes.setDistortionLookupTables(matrixResDistDz[1], matrixResDistDr[1], matrixResDistDrphi[1], o2::tpc::Side::C); + + TFile fileOutput("ResidualDistortions.root", "recreate"); + spaceChargeRes.dumpGlobalDistortions(fileOutput, o2::tpc::Side::A); + spaceChargeRes.dumpGlobalDistortions(fileOutput, o2::tpc::Side::C); + + if (debug) { + makeDebugTreeResiduals(scCalcFluc, scCalcAvg, spaceChargeRes); + } +} + +/// calculate the distortions and corrections from a space-charge density histogram +/// \param spaceCharge input space-charge density object which will be filled +/// \param path path to a file with the histograms +/// \param histoName name of the histogram +/// \param bSign sign of the B-field: -1 = negative, 0 = no B-field, 1 = positive +/// \param nThreads number of threads which are used +void createSpaceCharge(o2::tpc::SpaceCharge& spaceCharge, const char* path, const char* histoName, const int bSign, const int nThreads) +{ + const float omegaTau = -0.32 * bSign; + spaceCharge.setOmegaTauT1T2(omegaTau, 1, 1); + if (nThreads != -1) { + spaceCharge.setNThreads(nThreads); + } + // set density from input file + TFile fileOutSCDensity(path, "READ"); + spaceCharge.fillChargeDensityFromFile(fileOutSCDensity, histoName); + fileOutSCDensity.Close(); + + // calculate the distortions + spaceCharge.calculateDistortionsCorrections(o2::tpc::Side::A); + spaceCharge.calculateDistortionsCorrections(o2::tpc::Side::C); +} + +/// Store distortions from spaceChargeCalcFluc in the matrices provided. If providing an object with average space-charge distortions spaceChargeCalcAvg, the residual distortions (dist_fluctuation(xyzTrue) + corr_average(xyzDistorted)) will be stored. +/// \param spaceChargeCalcFluc fluctuating distortions object +/// \param spaceChargeCalcAvg average distortions object +/// \param matrixDistDr matrix to store radial distortions on the A and C side +/// \param matrixDistDrphi matrix to store rphi distortions on the A and C side +/// \param matrixDistDz matrix to store z distortions on the A and C side +void fillDistortionLookupMatrices(o2::tpc::SpaceCharge& spaceChargeCalcFluc, o2::tpc::SpaceCharge* spaceChargeCalcAvg, DataContainer matrixDistDr[2], DataContainer matrixDistDrphi[2], DataContainer matrixDistDz[2]) +{ + for (int iside = 0; iside < 2; ++iside) { + o2::tpc::Side side = (iside == 0) ? o2::tpc::Side::A : o2::tpc::Side::C; + for (int iphi = 0; iphi < NPHI; ++iphi) { + const auto phi = spaceChargeCalcFluc.getPhiVertex(iphi, side); + + for (int ir = 0; ir < NR; ++ir) { + const auto r = spaceChargeCalcFluc.getRVertex(ir, side); + + for (int iz = 0; iz < NZ; ++iz) { + const auto z = spaceChargeCalcFluc.getZVertex(iz, side); + + // get fluctuating distortions + DataT distFlucdZ = 0; + DataT distFlucdR = 0; + DataT distFlucdRPhi = 0; + spaceChargeCalcFluc.getDistortionsCyl(z, r, phi, side, distFlucdZ, distFlucdR, distFlucdRPhi); + + // get average corrections if provided and add them to the fluctuating distortions + if (spaceChargeCalcAvg) { + const float zDistorted = z + distFlucdZ; + const float rDistorted = r + distFlucdR; + const float phiDistorted = phi + distFlucdRPhi / r; + DataT corrZDistPoint = 0; + DataT corrRDistPoint = 0; + DataT corrRPhiDistPoint = 0; + spaceChargeCalcAvg->getCorrectionsCyl(zDistorted, rDistorted, phiDistorted, side, corrZDistPoint, corrRDistPoint, corrRPhiDistPoint); + distFlucdZ += corrZDistPoint; + distFlucdR += corrRDistPoint; + distFlucdRPhi += corrRPhiDistPoint * r / rDistorted; + } + + // store (residual) distortions in the matrices + matrixDistDr[iside](ir, iz, iphi) = distFlucdR; + matrixDistDrphi[iside](ir, iz, iphi) = distFlucdRPhi; + matrixDistDz[iside](ir, iz, iphi) = distFlucdZ; + } + } + } + } +} + +/// Calculate and stream residual distortions from spaceChargeRes and from calcFluc and calcAvg for comparison. +/// \param calcFluc space charge object with fluctuation distortions +/// \param calcAvg space charge object with average distortions +/// \param spaceChargeRes space charge object with residual distortions +void makeDebugTreeResiduals(o2::tpc::SpaceCharge& calcFluc, o2::tpc::SpaceCharge& calcAvg, o2::tpc::SpaceCharge& spaceChargeRes) +{ + o2::utils::TreeStreamRedirector pcstream("debugResidualDistortions.root", "recreate"); + for (int iside = 0; iside < 2; ++iside) { + o2::tpc::Side side = (iside == 0) ? o2::tpc::Side::A : o2::tpc::Side::C; + for (int iphi = 0; iphi < NPHI; ++iphi) { + auto phi = calcFluc.getPhiVertex(iphi, side); + for (int ir = 0; ir < NR; ++ir) { + auto r = calcFluc.getRVertex(ir, side); + DataT x = r * std::cos(phi); + DataT y = r * std::sin(phi); + for (int iz = 1; iz < NZ; ++iz) { + DataT z = calcFluc.getZVertex(iz, side); + + GlobalPosition3D posDistRes(x, y, z); + spaceChargeRes.distortElectron(posDistRes); + DataT xyzDistRes[3] = {posDistRes.x(), posDistRes.y(), posDistRes.z()}; + DataT distRes[3] = {posDistRes.x() - x, posDistRes.y() - y, posDistRes.z() - z}; + + DataT distFlucX = 0; + DataT distFlucY = 0; + DataT distFlucZ = 0; + calcFluc.getDistortions(x, y, z, side, distFlucX, distFlucY, distFlucZ); + + DataT xDistorted = x + distFlucX; + DataT yDistorted = y + distFlucY; + DataT zDistorted = z + distFlucZ; + + DataT corrAvgX = 0; + DataT corrAvgY = 0; + DataT corrAvgZ = 0; + calcAvg.getCorrections(xDistorted, yDistorted, zDistorted, side, corrAvgX, corrAvgY, corrAvgZ); + DataT xyzDistResTrue[3] = {xDistorted + corrAvgX, yDistorted + corrAvgY, zDistorted + corrAvgZ}; + DataT distResTrue[3] = {distFlucX + corrAvgX, distFlucY + corrAvgY, distFlucZ + corrAvgZ}; + + pcstream << "debug" + << "iside=" << iside + << "iphi=" << iphi + << "ir=" << ir + << "iz=" << iz + // original position + << "phi=" << phi + << "r=" << r + << "x=" << x + << "y=" << y + << "z=" << z + // position of distorted points + << "xRes=" << xyzDistRes[0] + << "yRes=" << xyzDistRes[1] + << "zRes=" << xyzDistRes[2] + // true position of distorted points + << "xResTrue=" << xyzDistResTrue[0] + << "yResTrue=" << xyzDistResTrue[1] + << "zResTrue=" << xyzDistResTrue[2] + // residual distortions + << "distX=" << distRes[0] + << "distY=" << distRes[1] + << "distZ=" << distRes[2] + // true residual distortions + << "distXTrue=" << distResTrue[0] + << "distYTrue=" << distResTrue[1] + << "distZTrue=" << distResTrue[2] + // + << "\n"; + } + } + } + } + pcstream.Close(); +} diff --git a/Detectors/TPC/spacecharge/src/PoissonSolver.cxx b/Detectors/TPC/spacecharge/src/PoissonSolver.cxx new file mode 100644 index 0000000000000..1a52719225b88 --- /dev/null +++ b/Detectors/TPC/spacecharge/src/PoissonSolver.cxx @@ -0,0 +1,1497 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file PoissonSolver.cxx +/// \brief This class provides implementation of Poisson Eq +/// solver by MultiGrid Method +/// +/// +/// +/// \author Matthias Kleiner +/// \date Aug 21, 2020 + +#include "TPCSpaceCharge/PoissonSolver.h" +#include "Framework/Logger.h" +#include +#include + +#ifdef WITH_OPENMP +#include +#endif + +using namespace o2::tpc; + +template +void PoissonSolver::poissonSolver3D(DataContainer& matricesV, const DataContainer& matricesCharge, const int symmetry) +{ + if (MGParameters::isFull3D) { + poissonMultiGrid3D(matricesV, matricesCharge, symmetry); + } else { + poissonMultiGrid3D2D(matricesV, matricesCharge, symmetry); + } +} + +template +void PoissonSolver::poissonSolver2D(DataContainer& matricesV, const DataContainer& matricesCharge) +{ + poissonMultiGrid2D(matricesV, matricesCharge); +} + +template +void PoissonSolver::poissonMultiGrid2D(DataContainer& matricesV, const DataContainer& matricesCharge, const int iPhi) +{ + /// Geometry of TPC -- should be use AliTPCParams instead + const DataT gridSpacingR = getSpacingR(); + const DataT gridSpacingZ = getSpacingZ(); + const DataT ratioZ = gridSpacingR * gridSpacingR / (gridSpacingZ * gridSpacingZ); // ratio_{Z} = gridSize_{r} / gridSize_{z} + + int nGridRow = 0; // number grid + int nGridCol = 0; // number grid + + int nnRow = Nr; + while (nnRow >>= 1) { + ++nGridRow; + } + + int nnCol = Nz; + while (nnCol >>= 1) { + ++nGridCol; + } + + //Check that number of Nr and Nz is suitable for multi grid + if (!isPowerOfTwo(Nr - 1)) { + LOGP(ERROR, "PoissonMultiGrid2D: PoissonMultiGrid - Error in the number of Nr. Must be 2**M + 1"); + return; + } + if (!isPowerOfTwo(Nz - 1)) { + LOGP(ERROR, "PoissonMultiGrid2D: PoissonMultiGrid - Error in the number of Nz. Must be 2**N - 1"); + return; + } + + const int nLoop = std::max(nGridRow, nGridCol); // Calculate the number of nLoop for the binary expansion + + LOGP(info, "{}", fmt::format("PoissonMultiGrid2D: nGridRow={}, nGridCol={}, nLoop={}, nMGCycle={}", nGridRow, nGridCol, nLoop, MGParameters::nMGCycle)); + + int iOne = 1; // in/dex + int jOne = 1; // index + int tnRRow = Nr; + int tnZColumn = Nz; + + // Vector for storing multi grid array + std::vector tvArrayV(nLoop); // potential <--> error + std::vector tvChargeFMG(nLoop); // charge is restricted in full multiGrid + std::vector tvCharge(nLoop); // charge <--> residue + std::vector tvResidue(nLoop); // residue calculation + + // Allocate memory for temporary grid + for (int count = 1; count <= nLoop; ++count) { + tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; + tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; + // if one just address to matrixV + tvResidue[count - 1].resize(tnRRow, tnZColumn, 1); + tvChargeFMG[count - 1].resize(tnRRow, tnZColumn, 1); + tvArrayV[count - 1].resize(tnRRow, tnZColumn, 1); + tvCharge[count - 1].resize(tnRRow, tnZColumn, 1); + + if (count == 1) { + for (int iphi = iPhi; iphi <= iPhi; ++iphi) { + for (int ir = 0; ir < Nr; ++ir) { + for (int iz = 0; iz < Nz; ++iz) { + tvChargeFMG[count - 1](ir, iz, iphi) = matricesCharge(iz, ir, iphi); + tvCharge[count - 1](ir, iz, iphi) = matricesCharge(iz, ir, iphi); + tvArrayV[count - 1](ir, iz, iphi) = matricesV(iz, ir, iphi); + } + } + } + } else { + restrict2D(tvChargeFMG[count - 1], tvChargeFMG[count - 2], tnRRow, tnZColumn, 0); + } + iOne = 2 * iOne; + jOne = 2 * jOne; + } + + /// full multi grid + if (MGParameters::cycleType == CycleType::FCycle) { + + LOGP(info, "PoissonMultiGrid2D: Do full cycle"); + // FMG + // 1) Relax on the coarsest grid + iOne = iOne * 0.5; + jOne = jOne * 0.5; + tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; + tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; + const DataT h = gridSpacingR * nLoop; + const DataT h2 = h * h; + const DataT tempRatio = ratioZ * iOne * iOne / (jOne * jOne); + const DataT tempFourth = 1.0 / (2.0 + 2.0 * tempRatio); + + std::vector coefficient1(tnRRow); + std::vector coefficient2(tnRRow); + calcCoefficients2D(1, tnRRow - 1, h, coefficient1, coefficient2); + + relax2D(tvArrayV[nLoop - 1], tvChargeFMG[nLoop - 1], tnRRow, tnZColumn, h2, tempFourth, tempRatio, coefficient1, coefficient2); + + // Do VCycle from nLoop H to h + for (int count = nLoop - 2; count >= 0; --count) { + + iOne = iOne * 0.5; + jOne = jOne * 0.5; + + tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; + tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; + + interp2D(tvArrayV[count], tvArrayV[count + 1], tnRRow, tnZColumn, iPhi); + + // Copy the relax charge to the tvCharge + tvCharge[count] = tvChargeFMG[count]; //copy + + // Do V cycle + for (int mgCycle = 0; mgCycle < MGParameters::nMGCycle; ++mgCycle) { + vCycle2D(count + 1, nLoop, MGParameters::nPre, MGParameters::nPost, gridSpacingR, ratioZ, tvArrayV, tvCharge, tvResidue); + } + } + } else if (MGParameters::cycleType == CycleType::VCycle) { + // 2. VCycle + LOGP(info, "PoissonMultiGrid2D: Do V cycle"); + + int gridFrom = 1; + int gridTo = nLoop; + + // Do MGCycle + for (int mgCycle = 0; mgCycle < MGParameters::nMGCycle; ++mgCycle) { + vCycle2D(gridFrom, gridTo, MGParameters::nPre, MGParameters::nPost, gridSpacingR, ratioZ, tvArrayV, tvCharge, tvResidue); + } + } else if (MGParameters::cycleType == CycleType::WCycle) { + // 3. W Cycle (TODO:) + int gridFrom = 1; + int gridTo = nLoop; + // Do MGCycle + for (Int_t mgCycle = 0; mgCycle < MGParameters::nMGCycle; mgCycle++) { + wCycle2D(gridFrom, gridTo, MGParameters::gamma, MGParameters::nPre, MGParameters::nPost, gridSpacingR, ratioZ, tvArrayV, tvCharge, tvResidue); + } + } + + // fill output + for (int iphi = iPhi; iphi <= iPhi; ++iphi) { + for (int ir = 0; ir < Nr; ++ir) { + for (int iz = 0; iz < Nz; ++iz) { + matricesV(iz, ir, iphi) = tvArrayV[0](ir, iz, iphi); + } + } + } +} + +template +void PoissonSolver::poissonMultiGrid3D2D(DataContainer& matricesV, const DataContainer& matricesCharge, const int symmetry) +{ + LOGP(info, "{}", fmt::format("PoissonMultiGrid3D2D: in Poisson Solver 3D multiGrid semi coarsening Nr={}, cols={}, Nphi={}", Nz, Nr, Nphi)); + + // Check that the number of Nr and Nz is suitable for a binary expansion + if (!isPowerOfTwo((Nr - 1))) { + LOGP(ERROR, "PoissonMultiGrid3D2D: Poisson3DMultiGrid - Error in the number of Nr. Must be 2**M + 1"); + return; + } + if (!isPowerOfTwo((Nz - 1))) { + LOGP(ERROR, "PoissonMultiGrid3D2D: Poisson3DMultiGrid - Error in the number of Nz. Must be 2**N - 1"); + return; + } + if (Nphi <= 3) { + LOGP(ERROR, "PoissonMultiGrid3D2D: Poisson3DMultiGrid - Error in the number of Nphi. Must be larger than 3"); + return; + } + if (Nphi > 1000) { + LOGP(ERROR, "PoissonMultiGrid3D2D: Poisson3D Nphi > 1000 is not allowed (nor wise)"); + return; + } + + const DataT gridSpacingR = getSpacingR(); + const DataT gridSpacingZ = getSpacingZ(); + const DataT gridSpacingPhi = getSpacingPhi(); + const DataT ratioPhi = gridSpacingR * gridSpacingR / (gridSpacingPhi * gridSpacingPhi); // ratio_{phi} = gridSize_{r} / gridSize_{phi} + const DataT ratioZ = gridSpacingR * gridSpacingR / (gridSpacingZ * gridSpacingZ); // ratio_{Z} = gridSize_{r} / gridSize_{z} + + // Solve Poisson's equation in cylindrical coordinates by multiGrid technique + // Allow for different size grid spacing in R and Z directions + int nGridRow = 0; // number grid + int nGridCol = 0; // number grid + int nnRow = Nr; + int nnCol = Nz; + + while (nnRow >>= 1) { + ++nGridRow; + } + while (nnCol >>= 1) { + ++nGridCol; + } + + const int maxVal = std::max(nGridRow, nGridCol); // Calculate the number of nLoop for the binary expansion + const size_t nLoop = (maxVal > MGParameters::maxLoop) ? MGParameters::maxLoop : maxVal; + unsigned int iOne = 1; // index i in gridSize r (original) + unsigned int jOne = 1; // index j in gridSize z (original) + + std::vector tvArrayV(nLoop); // potential <--> error + std::vector tvChargeFMG(nLoop); // charge is restricted in full multiGrid + std::vector tvCharge(nLoop); // charge <--> residue + std::vector tvPrevArrayV(nLoop); // error calculation + std::vector tvResidue(nLoop); // residue calculation + + for (unsigned int count = 1; count <= nLoop; count++) { + const unsigned int tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; + const unsigned int tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; + tvResidue[count - 1].resize(tnRRow, tnZColumn, Nphi); + tvPrevArrayV[count - 1].resize(tnRRow, tnZColumn, Nphi); + + // memory for the finest grid is from parameters + tvChargeFMG[count - 1].resize(tnRRow, tnZColumn, Nphi); + tvArrayV[count - 1].resize(tnRRow, tnZColumn, Nphi); + tvCharge[count - 1].resize(tnRRow, tnZColumn, Nphi); + + if (count == 1) { + for (int iphi = 0; iphi < Nphi; ++iphi) { + for (int ir = 0; ir < Nr; ++ir) { + for (int iz = 0; iz < Nz; ++iz) { + tvChargeFMG[count - 1](ir, iz, iphi) = matricesCharge(iz, ir, iphi); + tvArrayV[count - 1](ir, iz, iphi) = matricesV(iz, ir, iphi); + } + } + } + } else { + restrict3D(tvChargeFMG[count - 1], tvChargeFMG[count - 2], tnRRow, tnZColumn, Nphi, Nphi); + restrictBoundary3D(tvArrayV[count - 1], tvArrayV[count - 2], tnRRow, tnZColumn, Nphi, Nphi); + } + iOne = 2 * iOne; // doubling + jOne = 2 * jOne; // doubling + } + + std::array coefficient1{}; // coefficient1(Nr) for storing (1 + h_{r}/2r_{i}) from central differences in r direction + std::array coefficient2{}; // coefficient2(Nr) for storing (1 + h_{r}/2r_{i}) from central differences in r direction + std::array coefficient3{}; // coefficient3(Nr) for storing (1/r_{i}^2) from central differences in phi direction + std::array coefficient4{}; // coefficient4(Nr) for storing 1/2 + std::array inverseCoefficient4{}; // inverse of coefficient4(Nr) + + // Case full multi grid (FMG) + if (MGParameters::cycleType == CycleType::FCycle) { + // 1) Relax on the coarsest grid + iOne = iOne * 0.5; + jOne = jOne * 0.5; + int tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; + int tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; + + const DataT h = getSpacingR() * iOne; + const DataT h2 = h * h; + const DataT iOne2 = iOne * iOne; + const DataT tempRatioPhi = ratioPhi * iOne2; // Used tobe divided by ( m_one * m_one ) when m_one was != 1 + const DataT tempRatioZ = ratioZ * iOne2 / (jOne * jOne); + + calcCoefficients(1, tnRRow - 1, h, tempRatioZ, tempRatioPhi, coefficient1, coefficient2, coefficient3, coefficient4); + + // relax on the coarsest level + relax3D(tvArrayV[nLoop - 1], tvChargeFMG[nLoop - 1], tnRRow, tnZColumn, Nphi, symmetry, h2, tempRatioZ, coefficient1, coefficient2, coefficient3, coefficient4); + + // 2) Do multiGrid v-cycle from coarsest to finest + for (int count = nLoop - 2; count >= 0; --count) { + // move to finer grid + iOne = iOne * 0.5; + jOne = jOne * 0.5; + tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; + tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; + + // 2) a) Interpolate potential for h -> 2h (coarse -> fine) + interp3D(tvArrayV[count], tvArrayV[count + 1], tnRRow, tnZColumn, Nphi, Nphi); + + // 2) c) Copy the restricted charge to charge for calculation + tvCharge[count] = tvChargeFMG[count]; //copy + + // 2) c) Do V cycle MGParameters::nMGCycle times at most + for (int mgCycle = 0; mgCycle < MGParameters::nMGCycle; ++mgCycle) { + // Copy the potential to temp array for convergence calculation + tvPrevArrayV[count] = tvArrayV[count]; + + // 2) c) i) Call V cycle from grid count+1 (current fine level) to nLoop (coarsest) + vCycle3D2D(symmetry, count + 1, nLoop, MGParameters::nPre, MGParameters::nPost, ratioZ, ratioPhi, tvArrayV, tvCharge, tvResidue, coefficient1, coefficient2, coefficient3, coefficient4, inverseCoefficient4); + + const DataT convergenceError = getConvergenceError(tvArrayV[count], tvPrevArrayV[count]); + + /// if already converge just break move to finer grid + if (convergenceError <= sConvergenceError) { + break; + } + } + } + } + + // fill output + for (int iphi = 0; iphi < Nphi; ++iphi) { + for (int ir = 0; ir < Nr; ++ir) { + for (int iz = 0; iz < Nz; ++iz) { + matricesV(iz, ir, iphi) = tvArrayV[0](ir, iz, iphi); + } + } + } +} + +template +void PoissonSolver::poissonMultiGrid3D(DataContainer& matricesV, const DataContainer& matricesCharge, const int symmetry) +{ + const DataT gridSpacingR = getSpacingR(); + const DataT gridSpacingZ = getSpacingZ(); + const DataT ratioZ = gridSpacingR * gridSpacingR / (gridSpacingZ * gridSpacingZ); // ratio_{Z} = gridSize_{r} / gridSize_{z} + + LOGP(info, "{}", fmt::format("PoissonMultiGrid3D: in Poisson Solver 3D multi grid full coarsening Nr={}, cols={}, Nphi={}", Nr, Nz, Nphi)); + + // Check that the number of Nr and Nz is suitable for a binary expansion + if (!isPowerOfTwo((Nr - 1))) { + LOGP(ERROR, "PoissonMultiGrid3D: Poisson3DMultiGrid - Error in the number of Nr. Must be 2**M + 1"); + return; + } + if (!isPowerOfTwo((Nz - 1))) { + LOGP(ERROR, "PoissonMultiGrid3D: Poisson3DMultiGrid - Error in the number of Nz. Must be 2**N - 1"); + return; + } + if (Nphi <= 3) { + LOGP(ERROR, "PoissonMultiGrid3D: Poisson3DMultiGrid - Error in the number of Nphi. Must be larger than 3"); + return; + } + if (Nphi > 1000) { + LOGP(ERROR, "PoissonMultiGrid3D: Poisson3D Nphi > 1000 is not allowed (nor wise)"); + return; + } + + // Solve Poisson's equation in cylindrical coordinates by multi grid technique + // Allow for different size grid spacing in R and Z directions + int nGridRow = 0; // number grid + int nGridCol = 0; // number grid + int nGridPhi = 0; + + int nnRow = Nr; + while (nnRow >>= 1) { + ++nGridRow; + } + + int nnCol = Nz; + while (nnCol >>= 1) { + ++nGridCol; + } + + int nnPhi = Nphi; + while (nnPhi % 2 == 0) { + ++nGridPhi; + nnPhi *= 0.5; + } + + LOGP(info, "{}", fmt::format("PoissonMultiGrid3D: nGridRow={}, nGridCol={}, nGridPhi={}", nGridRow, nGridCol, nGridPhi)); + const int nLoop = std::max({nGridRow, nGridCol, nGridPhi}); // Calculate the number of nLoop for the binary expansion + + // Vector for storing multi grid array + int iOne = 1; // index i in gridSize r (original) + int jOne = 1; // index j in gridSize z (original) + int kOne = 1; // index k in gridSize phi + int tnRRow = Nr; + int tnZColumn = Nz; + int tPhiSlice = Nphi; + + // 1) Memory allocation for multi grid + std::vector tvArrayV(nLoop); // potential <--> error + std::vector tvChargeFMG(nLoop); // charge is restricted in full multiGrid + std::vector tvCharge(nLoop); // charge <--> residue + std::vector tvPrevArrayV(nLoop); // error calculation + std::vector tvResidue(nLoop); // residue calculation + + std::array coefficient1{}; // coefficient1(Nr) for storing (1 + h_{r}/2r_{i}) from central differences in r direction + std::array coefficient2{}; // coefficient2(Nr) for storing (1 + h_{r}/2r_{i}) from central differences in r direction + std::array coefficient3{}; // coefficient3(Nr) for storing (1/r_{i}^2) from central differences in phi direction + std::array coefficient4{}; // coefficient4(Nr) for storing 1/2 + std::array inverseCoefficient4{}; // inverse of coefficient4(Nr) + + for (int count = 1; count <= nLoop; ++count) { + // tnRRow,tnZColumn in new grid + tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; + tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; + tPhiSlice = kOne == 1 ? Nphi : Nphi / kOne; + tPhiSlice = tPhiSlice < nnPhi ? nnPhi : tPhiSlice; + + // allocate memory for residue + tvResidue[count - 1].resize(tnRRow, tnZColumn, tPhiSlice); + tvPrevArrayV[count - 1].resize(tnRRow, tnZColumn, tPhiSlice); + tvChargeFMG[count - 1].resize(tnRRow, tnZColumn, tPhiSlice); + tvArrayV[count - 1].resize(tnRRow, tnZColumn, tPhiSlice); + tvCharge[count - 1].resize(tnRRow, tnZColumn, tPhiSlice); + + // memory for the finest grid is from parameters + if (count == 1) { + for (int iphi = 0; iphi < Nphi; ++iphi) { + for (int ir = 0; ir < Nr; ++ir) { + for (int iz = 0; iz < Nz; ++iz) { + tvChargeFMG[count - 1](ir, iz, iphi) = matricesCharge(iz, ir, iphi); + tvArrayV[count - 1](ir, iz, iphi) = matricesV(iz, ir, iphi); + } + } + } + tvCharge[count - 1] = tvChargeFMG[count - 1]; + } + iOne = 2 * iOne; // doubling + jOne = 2 * jOne; // doubling + kOne = 2 * kOne; + } + + // Case full multi grid (FMG) + if (MGParameters::cycleType == CycleType::FCycle) { + // Restrict the charge to coarser grid + iOne = 2; + jOne = 2; + kOne = 2; + int otPhiSlice = Nphi; + + // 1) Restrict Charge and Boundary to coarser grid + for (int count = 2; count <= nLoop; ++count) { + // tnRRow,tnZColumn in new grid + tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; + tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; + tPhiSlice = kOne == 1 ? Nphi : Nphi / kOne; + tPhiSlice = tPhiSlice < nnPhi ? nnPhi : tPhiSlice; + + LOGP(info, "{}", fmt::format("PoissonMultiGrid3D: Restrict3D, tnRRow={}, tnZColumn={}, newPhiSlice={}, oldPhiSlice={}", tnRRow, tnZColumn, tPhiSlice, otPhiSlice)); + restrict3D(tvChargeFMG[count - 1], tvChargeFMG[count - 2], tnRRow, tnZColumn, tPhiSlice, otPhiSlice); + // copy boundary values of V + restrictBoundary3D(tvArrayV[count - 1], tvArrayV[count - 2], tnRRow, tnZColumn, tPhiSlice, otPhiSlice); + otPhiSlice = tPhiSlice; + + iOne = 2 * iOne; // doubling + jOne = 2 * jOne; // doubling + kOne = 2 * kOne; + } + + // Relax on the coarsest grid + // FMG + // 2) Relax on the coarsest grid + // move to the coarsest + 1 + iOne = iOne * 0.5; + jOne = jOne * 0.5; + kOne = kOne * 0.5; + tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; + tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; + tPhiSlice = kOne == 1 ? Nphi : Nphi / kOne; + tPhiSlice = tPhiSlice < nnPhi ? nnPhi : tPhiSlice; + otPhiSlice = tPhiSlice; + + const DataT h = gridSpacingR * iOne; + const DataT h2 = h * h; + const DataT gridSizePhiInv = tPhiSlice * INVTWOPI; // h_{phi} + const DataT tempRatioPhi = h2 * gridSizePhiInv * gridSizePhiInv; // ratio_{phi} = gridSize_{r} / gridSize_{phi} + const DataT tempRatioZ = ratioZ * iOne * iOne / (jOne * jOne); + + calcCoefficients(1, tnRRow - 1, h, tempRatioZ, tempRatioPhi, coefficient1, coefficient2, coefficient3, coefficient4); + + // 3) Relax on the coarsest grid + relax3D(tvArrayV[nLoop - 1], tvChargeFMG[nLoop - 1], tnRRow, tnZColumn, tPhiSlice, symmetry, h2, tempRatioZ, coefficient1, coefficient2, coefficient3, coefficient4); + + // 4) V Cycle from coarsest to finest + for (int count = nLoop - 2; count >= 0; --count) { + // move to finer grid + std::fill(std::begin(coefficient1), std::end(coefficient1), 0); + std::fill(std::begin(coefficient2), std::end(coefficient2), 0); + std::fill(std::begin(coefficient3), std::end(coefficient3), 0); + std::fill(std::begin(coefficient4), std::end(coefficient4), 0); + std::fill(std::begin(inverseCoefficient4), std::end(inverseCoefficient4), 0); + + iOne = iOne * 0.5; + jOne = jOne * 0.5; + kOne = kOne * 0.5; + + tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; + tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; + tPhiSlice = kOne == 1 ? Nphi : Nphi / kOne; + tPhiSlice = tPhiSlice < nnPhi ? nnPhi : tPhiSlice; + + // 4) a) interpolate from 2h --> h grid + interp3D(tvArrayV[count], tvArrayV[count + 1], tnRRow, tnZColumn, tPhiSlice, otPhiSlice); + + // Copy the relax charge to the tvCharge + if (count > 0) { + tvCharge[count] = tvChargeFMG[count]; + } + for (int mgCycle = 0; mgCycle < MGParameters::nMGCycle; ++mgCycle) { + // copy to store previous potential + tvPrevArrayV[count] = tvArrayV[count]; + + vCycle3D(symmetry, count + 1, nLoop, MGParameters::nPre, MGParameters::nPost, ratioZ, tvArrayV, tvCharge, tvResidue, coefficient1, coefficient2, coefficient3, coefficient4, inverseCoefficient4); + + // converge error + const DataT convergenceError = getConvergenceError(tvArrayV[count], tvPrevArrayV[count]); + // if already converge just break move to finer grid + if (convergenceError <= sConvergenceError) { + break; + } + } + // keep old slice information + otPhiSlice = tPhiSlice; + } + } else if (MGParameters::cycleType == CycleType::VCycle) { + // V-cycle + int gridFrom = 1; + int gridTo = nLoop; + + for (int mgCycle = 0; mgCycle < MGParameters::nMGCycle; ++mgCycle) { + // copy to store previous potential + tvPrevArrayV[0] = tvArrayV[0]; + + // Do V Cycle from the coarsest to finest grid + vCycle3D(symmetry, gridFrom, gridTo, MGParameters::nPre, MGParameters::nPost, ratioZ, tvArrayV, tvCharge, tvResidue, coefficient1, coefficient2, coefficient3, coefficient4, inverseCoefficient4); + + // convergence error + const DataT convergenceError = getConvergenceError(tvArrayV[0], tvPrevArrayV[0]); + + // if error already achieved then stop mg iteration + if (convergenceError <= sConvergenceError) { + break; + } + } + } + + // fill output + for (int iphi = 0; iphi < Nphi; ++iphi) { + for (int ir = 0; ir < Nr; ++ir) { + for (int iz = 0; iz < Nz; ++iz) { + matricesV(iz, ir, iphi) = tvArrayV[0](ir, iz, iphi); + } + } + } +} + +template +void PoissonSolver::wCycle2D(const int gridFrom, const int gridTo, const int gamma, const int nPre, const int nPost, const DataT gridSizeR, const DataT ratio, + std::vector& tvArrayV, std::vector& tvCharge, std::vector& tvResidue) +{ + int iOne = 1 << (gridFrom - 1); + int jOne = 1 << (gridFrom - 1); + + int tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; + int tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; + + std::vector coefficient1(Nr); + std::vector coefficient2(Nz); + + // 1) Go to coarsest level + for (int count = gridFrom; count <= gridTo - 2; ++count) { + const DataT h = gridSizeR * iOne; + const DataT h2 = h * h; + const DataT ih2 = 1.0 / h2; + const DataT tempRatio = ratio * iOne * iOne / (jOne * jOne); + const DataT tempFourth = 1.0 / (2.0 + 2.0 * tempRatio); + const DataT inverseTempFourth = 1.0 / tempFourth; + calcCoefficients2D(1, tnRRow - 1, h, coefficient1, coefficient2); + Vector matricesCurrentV = tvArrayV[count - 1]; + Vector matricesCurrentCharge = tvCharge[count - 1]; + Vector residue = tvResidue[count - 1]; + + // 1) Pre-Smoothing: Gauss-Seidel Relaxation or Jacobi + for (int jPre = 1; jPre <= nPre; ++jPre) { + relax2D(matricesCurrentV, matricesCurrentCharge, tnRRow, tnZColumn, h2, tempFourth, tempRatio, coefficient1, coefficient2); + } + + // 2) Residue calculation + residue2D(residue, matricesCurrentV, matricesCurrentCharge, tnRRow, tnZColumn, ih2, inverseTempFourth, tempRatio, coefficient1, coefficient2); + + iOne = 2 * iOne; + jOne = 2 * jOne; + tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; + tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; + + matricesCurrentCharge = tvCharge[count]; + matricesCurrentV = tvArrayV[count]; + + //3) Restriction + restrict2D(matricesCurrentCharge, residue, tnRRow, tnZColumn, 0); + } + + // Do V cycle from: gridTo-1 to gridTo gamma times + for (int iGamma = 0; iGamma < gamma; ++iGamma) { + vCycle2D(gridTo - 1, gridTo, nPre, nPost, gridSizeR, ratio, tvArrayV, tvCharge, tvResidue); + } + + // Go to finest grid + for (int count = gridTo - 2; count >= gridFrom; --count) { + iOne = iOne * 0.5; + jOne = jOne * 0.5; + + const DataT h = gridSizeR * iOne; + const DataT h2 = h * h; + const DataT tempRatio = ratio * iOne * iOne / (jOne * jOne); + const DataT tempFourth = 1.0 / (2.0 + 2.0 * tempRatio); + + tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; + tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; + const Vector matricesCurrentCharge = tvCharge[count - 1]; + Vector matricesCurrentV = tvArrayV[count - 1]; + const Vector matricesCurrentVC = tvArrayV[count]; + + // 6) Interpolation/Prolongation + addInterp2D(matricesCurrentV, matricesCurrentVC, tnRRow, tnZColumn, 0); + + calcCoefficients2D(1, tnRRow - 1, h, coefficient1, coefficient2); + + // 7) Post-Smoothing: Gauss-Seidel Relaxation + for (Int_t jPost = 1; jPost <= nPost; ++jPost) { + relax2D(matricesCurrentV, matricesCurrentCharge, tnRRow, tnZColumn, h2, tempFourth, tempRatio, coefficient1, coefficient2); + } // end post smoothing + } +} + +template +void PoissonSolver::vCycle2D(const int gridFrom, const int gridTo, const int nPre, const int nPost, const DataT gridSizeR, const DataT ratio, std::vector& tvArrayV, + std::vector& tvCharge, std::vector& tvResidue) +{ + int iOne = 1 << (gridFrom - 1); + int jOne = 1 << (gridFrom - 1); + + int tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; + int tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; + + std::vector coefficient1(Nr); + std::vector coefficient2(Nz); + + // 1) Go to coarsest level + for (int count = gridFrom; count <= gridTo - 1; ++count) { + const DataT h = gridSizeR * iOne; + const DataT h2 = h * h; + const DataT ih2 = 1.0 / h2; + const DataT tempRatio = ratio * iOne * iOne / (jOne * jOne); + const DataT tempFourth = 1.0 / (2.0 + 2.0 * tempRatio); + const DataT inverseTempFourth = 1.0 / tempFourth; + calcCoefficients2D(1, tnRRow - 1, h, coefficient1, coefficient2); + + for (int jPre = 1; jPre <= nPre; ++jPre) { + relax2D(tvArrayV[count - 1], tvCharge[count - 1], tnRRow, tnZColumn, h2, tempFourth, tempRatio, coefficient1, coefficient2); + } + + // 2) Residue calculation + residue2D(tvResidue[count - 1], tvArrayV[count - 1], tvCharge[count - 1], tnRRow, tnZColumn, ih2, inverseTempFourth, tempRatio, coefficient1, coefficient2); + + iOne = 2 * iOne; + jOne = 2 * jOne; + tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; + tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; + + //3) Restriction + restrict2D(tvCharge[count], tvResidue[count - 1], tnRRow, tnZColumn, 0); + + //4) Zeroing coarser V + std::fill(tvArrayV[count].begin(), tvArrayV[count].end(), 0); // is this necessary??? + } + + // 5) coarsest grid + const DataT h = gridSizeR * iOne; + const DataT h2 = h * h; + const DataT tempRatio = ratio * iOne * iOne / (jOne * jOne); + const DataT tempFourth = 1.0 / (2.0 + 2.0 * tempRatio); + calcCoefficients2D(1, tnRRow - 1, h, coefficient1, coefficient2); + + relax2D(tvArrayV[gridTo - 1], tvCharge[gridTo - 1], tnRRow, tnZColumn, h2, tempFourth, tempRatio, coefficient1, coefficient2); + + // Go to finest grid + for (int count = gridTo - 1; count >= gridFrom; count--) { + + iOne = iOne * 0.5; + jOne = jOne * 0.5; + + const DataT h = gridSizeR * iOne; + const DataT h2 = h * h; + const DataT tempRatio = ratio * iOne * iOne / (jOne * jOne); + const DataT tempFourth = 1.0 / (2.0 + 2.0 * tempRatio); + + tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; + tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; + + // 6) Interpolation/Prolongation + addInterp2D(tvArrayV[count - 1], tvArrayV[count], tnRRow, tnZColumn, 0); + + calcCoefficients2D(1, tnRRow - 1, h, coefficient1, coefficient2); + + // 7) Post-Smoothing: Gauss-Seidel Relaxation + for (int jPost = 1; jPost <= nPost; ++jPost) { + relax2D(tvArrayV[count - 1], tvCharge[count - 1], tnRRow, tnZColumn, h2, tempFourth, tempRatio, coefficient1, coefficient2); + } // end post smoothing + } +} + +template +void PoissonSolver::vCycle3D2D(const int symmetry, const int gridFrom, const int gridTo, const int nPre, const int nPost, const DataT ratioZ, const DataT ratioPhi, + std::vector& tvArrayV, std::vector& tvCharge, std::vector& tvResidue, std::array& coefficient1, + std::array& coefficient2, std::array& coefficient3, std::array& coefficient4, std::array& inverseCoefficient4) const +{ + int iOne = 1 << (gridFrom - 1); + int jOne = 1 << (gridFrom - 1); + int tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; + int tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; + + for (int count = gridFrom; count <= gridTo - 1; ++count) { + const DataT h = getSpacingR() * iOne; + const DataT h2 = h * h; + const DataT ih2 = 1.0 / h2; + const int iOne2 = iOne * iOne; + const DataT tempRatioPhi = ratioPhi * iOne2; // Used tobe divided by ( m_one * m_one ) when m_one was != 1 + const DataT tempRatioZ = ratioZ * iOne2 / (jOne * jOne); + + calcCoefficients(1, tnRRow - 1, h, tempRatioZ, tempRatioPhi, coefficient1, coefficient2, coefficient3, coefficient4); + for (unsigned int i = 1; i < tnRRow - 1; ++i) { + inverseCoefficient4[i] = 1.0 / coefficient4[i]; + } + + //Info("VCycle3D2D","Before Pre-smoothing"); + // 1) Pre-Smoothing: Gauss-Seidel Relaxation or Jacobi + for (int jPre = 1; jPre <= nPre; ++jPre) { + relax3D(tvArrayV[count - 1], tvCharge[count - 1], tnRRow, tnZColumn, Nphi, symmetry, h2, tempRatioZ, coefficient1, coefficient2, coefficient3, coefficient4); + } // end pre smoothing + + // 2) Residue calculation + residue3D(tvResidue[count - 1], tvArrayV[count - 1], tvCharge[count - 1], tnRRow, tnZColumn, Nphi, symmetry, ih2, tempRatioZ, coefficient1, coefficient2, coefficient3, inverseCoefficient4); + + iOne = 2 * iOne; + jOne = 2 * jOne; + tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; + tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; + + //3) Restriction + restrict3D(tvCharge[count], tvResidue[count - 1], tnRRow, tnZColumn, Nphi, Nphi); + + //4) Zeroing coarser V + std::fill(tvArrayV[count].begin(), tvArrayV[count].end(), 0); + } + + // coarsest grid + const DataT h = getSpacingR() * iOne; + const DataT h2 = h * h; + + const int iOne2 = iOne * iOne; + const DataT tempRatioPhi = ratioPhi * iOne2; + const DataT tempRatioZ = ratioZ * iOne2 / (jOne * jOne); + + calcCoefficients(1, tnRRow - 1, h, tempRatioZ, tempRatioPhi, coefficient1, coefficient2, coefficient3, coefficient4); + + // 3) Relax on the coarsest grid + relax3D(tvArrayV[gridTo - 1], tvCharge[gridTo - 1], tnRRow, tnZColumn, Nphi, symmetry, h2, tempRatioZ, coefficient1, coefficient2, coefficient3, coefficient4); + + // back to fine + for (int count = gridTo - 1; count >= gridFrom; --count) { + iOne = iOne * 0.5; + jOne = jOne * 0.5; + + tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; + tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; + + const DataT h = getSpacingR() * iOne; + const DataT h2 = h * h; + const int iOne2 = iOne * iOne; + const DataT tempRatioPhi = ratioPhi * iOne2; // Used tobe divided by ( m_one * m_one ) when m_one was != 1 + const DataT tempRatioZ = ratioZ * iOne2 / (jOne * jOne); + + // 4) Interpolation/Prolongation + addInterp3D(tvArrayV[count - 1], tvArrayV[count], tnRRow, tnZColumn, Nphi, Nphi); + + calcCoefficients(1, tnRRow - 1, h, tempRatioZ, tempRatioPhi, coefficient1, coefficient2, coefficient3, coefficient4); + + // 5) Post-Smoothing: Gauss-Seidel Relaxation + for (int jPost = 1; jPost <= nPost; ++jPost) { + relax3D(tvArrayV[count - 1], tvCharge[count - 1], tnRRow, tnZColumn, Nphi, symmetry, h2, tempRatioZ, coefficient1, coefficient2, coefficient3, coefficient4); + } // end post smoothing + } +} + +template +void PoissonSolver::vCycle3D(const int symmetry, const int gridFrom, const int gridTo, const int nPre, const int nPost, const DataT ratioZ, std::vector& tvArrayV, + std::vector& tvCharge, std::vector& tvResidue, std::array& coefficient1, std::array& coefficient2, std::array& coefficient3, + std::array& coefficient4, std::array& inverseCoefficient4) const +{ + const DataT gridSpacingR = getSpacingR(); + + int iOne = 1 << (gridFrom - 1); + int jOne = 1 << (gridFrom - 1); + int kOne = 1 << (gridFrom - 1); + + int nnPhi = Nphi; + while (nnPhi % 2 == 0) { + nnPhi *= 0.5; + } + + int tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; + int tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; + int tPhiSlice = kOne == 1 ? Nphi : Nphi / kOne; + tPhiSlice = tPhiSlice < nnPhi ? nnPhi : tPhiSlice; + + for (int count = gridFrom; count <= gridTo - 1; ++count) { + const int otPhiSlice = tPhiSlice; + const DataT h = gridSpacingR * iOne; + const DataT h2 = h * h; + const DataT ih2 = 1.0 / h2; + const DataT tempGridSizePhiInv = tPhiSlice * INVTWOPI; // phi now is multiGrid + const DataT tempRatioPhi = h2 * tempGridSizePhiInv * tempGridSizePhiInv; // ratio_{phi} = gridSize_{r} / gridSize_{phi} + const DataT tempRatioZ = ratioZ * iOne * iOne / (jOne * jOne); + + calcCoefficients(1, tnRRow - 1, h, tempRatioZ, tempRatioPhi, coefficient1, coefficient2, coefficient3, coefficient4); + + for (int i = 1; i < tnRRow - 1; ++i) { + inverseCoefficient4[i] = 1.0 / coefficient4[i]; + } + + // 1) Pre-Smoothing: Gauss-Seidel Relaxation or Jacobi + for (int jPre = 1; jPre <= nPre; ++jPre) { + relax3D(tvArrayV[count - 1], tvCharge[count - 1], tnRRow, tnZColumn, tPhiSlice, symmetry, h2, tempRatioZ, coefficient1, coefficient2, coefficient3, coefficient4); + } // end pre smoothing + + // 2) Residue calculation + residue3D(tvResidue[count - 1], tvArrayV[count - 1], tvCharge[count - 1], tnRRow, tnZColumn, tPhiSlice, symmetry, ih2, tempRatioZ, coefficient1, coefficient2, coefficient3, inverseCoefficient4); + + iOne = 2 * iOne; + jOne = 2 * jOne; + kOne = 2 * kOne; + tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; + tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; + tPhiSlice = Nphi / kOne; + tPhiSlice = tPhiSlice < nnPhi ? nnPhi : tPhiSlice; + + //3) Restriction + restrict3D(tvCharge[count], tvResidue[count - 1], tnRRow, tnZColumn, tPhiSlice, otPhiSlice); + + //4) Zeroing coarser V + std::fill(tvArrayV[count].begin(), tvArrayV[count].end(), 0); + } + + // coarsest grid + const DataT h = gridSpacingR * iOne; + const DataT h2 = h * h; + const DataT tempGridSizePhiInv = tPhiSlice * INVTWOPI; // phi now is multiGrid + const DataT tempRatioPhi = h2 * tempGridSizePhiInv * tempGridSizePhiInv; // ratio_{phi} = gridSize_{r} / gridSize_{phi} + const DataT tempRatioZ = ratioZ * iOne * iOne / (jOne * jOne); + + calcCoefficients(1, tnRRow - 1, h, tempRatioZ, tempRatioPhi, coefficient1, coefficient2, coefficient3, coefficient4); + + // 3) Relax on the coarsest grid + relax3D(tvArrayV[gridTo - 1], tvCharge[gridTo - 1], tnRRow, tnZColumn, tPhiSlice, symmetry, h2, tempRatioZ, coefficient1, coefficient2, coefficient3, coefficient4); + // back to fine + for (int count = gridTo - 1; count >= gridFrom; --count) { + const int otPhiSlice = tPhiSlice; + iOne = iOne * 0.5; + jOne = jOne * 0.5; + kOne = kOne * 0.5; + + tnRRow = iOne == 1 ? Nr : Nr / iOne + 1; + tnZColumn = jOne == 1 ? Nz : Nz / jOne + 1; + tPhiSlice = kOne == 1 ? Nphi : Nphi / kOne; + tPhiSlice = tPhiSlice < nnPhi ? nnPhi : tPhiSlice; + + const DataT h = gridSpacingR * iOne; + const DataT h2 = h * h; + const DataT tempGridSizePhiInv = tPhiSlice * INVTWOPI; + const DataT tempRatioPhi = h2 * tempGridSizePhiInv * tempGridSizePhiInv; // ratio_{phi} = gridSize_{r} / gridSize_{phi} + const DataT tempRatioZ = ratioZ * iOne * iOne / (jOne * jOne); + + // 4) Interpolation/Prolongation + addInterp3D(tvArrayV[count - 1], tvArrayV[count], tnRRow, tnZColumn, tPhiSlice, otPhiSlice); + + calcCoefficients(1, tnRRow - 1, h, tempRatioZ, tempRatioPhi, coefficient1, coefficient2, coefficient3, coefficient4); + + // 5) Post-Smoothing: Gauss-Seidel Relaxation + for (int jPost = 1; jPost <= nPost; ++jPost) { + relax3D(tvArrayV[count - 1], tvCharge[count - 1], tnRRow, tnZColumn, tPhiSlice, symmetry, h2, tempRatioZ, coefficient1, coefficient2, coefficient3, coefficient4); + } + } +} + +template +void PoissonSolver::residue2D(Vector& residue, const Vector& matricesCurrentV, const Vector& matricesCurrentCharge, const int tnRRow, const int tnZColumn, const DataT ih2, const DataT inverseTempFourth, + const DataT tempRatio, std::vector& coefficient1, std::vector& coefficient2) +{ + const int iPhi = 0; +#pragma omp parallel for num_threads(sNThreads) + for (int i = 1; i < tnRRow - 1; ++i) { + 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 + + //Boundary points. + for (int i = 0; i < tnRRow; ++i) { + residue(i, 0, iPhi) = residue(i, tnZColumn - 1, iPhi) = 0.0; + } + + for (int j = 0; j < tnZColumn; ++j) { + residue(0, j, iPhi) = residue(tnRRow - 1, j, iPhi) = 0.0; + } +} + +template +void PoissonSolver::residue3D(Vector& residue, const Vector& matricesCurrentV, const Vector& matricesCurrentCharge, const int tnRRow, const int tnZColumn, const int tnPhi, const int symmetry, + const DataT ih2, const DataT tempRatioZ, const std::array& coefficient1, const std::array& coefficient2, const std::array& coefficient3, const std::array& inverseCoefficient4) const +{ +#pragma omp parallel for num_threads(sNThreads) // parallising this loop is possible - but using more than 2 cores makes it slower - + for (int m = 0; m < tnPhi; ++m) { + int mp1 = m + 1; + int signPlus = 1; + int mm1 = m - 1; + int signMinus = 1; + + // Reflection symmetry in phi (e.g. symmetry at sector boundaries, or half sectors, etc.) + if (symmetry == 1) { + if (mp1 > tnPhi - 1) { + mp1 = tnPhi - 2; + } + if (mm1 < 0) { + mm1 = 1; + } + } + // Anti-symmetry in phi + else if (symmetry == -1) { + if (mp1 > tnPhi - 1) { + mp1 = tnPhi - 2; + signPlus = -1; + } + if (mm1 < 0) { + mm1 = 1; + signMinus = -1; + } + } else { // No Symmetries in phi, no boundaries, the calculation is continuous across all phi + if (mp1 > tnPhi - 1) { + mp1 = m + 1 - tnPhi; + } + if (mm1 < 0) { + mm1 = m - 1 + tnPhi; + } + } + + for (int j = 1; j < tnZColumn - 1; ++j) { + for (int i = 1; i < tnRRow - 1; ++i) { + residue(i, j, m) = ih2 * (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)) - inverseCoefficient4[i] * matricesCurrentV(i, j, m)) + + matricesCurrentCharge(i, j, m); + } // end cols + } // end Nr + } +} + +template +void PoissonSolver::interp3D(Vector& matricesCurrentV, const Vector& matricesCurrentVC, const int tnRRow, const int tnZColumn, const int newPhiSlice, const int oldPhiSlice) const +{ + // Do restrict 2 D for each slice + if (newPhiSlice == 2 * oldPhiSlice) { + for (int m = 0; m < newPhiSlice; m += 2) { + // assuming no symmetry + int mm = m * 0.5; + int mmPlus = mm + 1; + int mp1 = m + 1; + + // round + if (mmPlus > oldPhiSlice - 1) { + mmPlus = mm + 1 - oldPhiSlice; + } + if (mp1 > newPhiSlice - 1) { + mp1 = m + 1 - newPhiSlice; + } + + for (int j = 2; j < tnZColumn - 1; j += 2) { + for (int i = 2; i < tnRRow - 1; i += 2) { + const int iHalf = i * 0.5; + const int jHalf = j * 0.5; + matricesCurrentV(i, j, m) = matricesCurrentVC(iHalf, jHalf, mm); + // point on corner lines at phi direction + matricesCurrentV(i, j, mp1) = 0.5 * (matricesCurrentVC(iHalf, jHalf, mm) + matricesCurrentVC(iHalf, jHalf, mmPlus)); + } + } + + for (int j = 1; j < tnZColumn - 1; j += 2) { + for (int i = 2; i < tnRRow - 1; i += 2) { + const int iHalf = i * 0.5; + const int jHalf = j * 0.5; + matricesCurrentV(i, j, m) = 0.5 * (matricesCurrentVC(iHalf, jHalf, mm) + matricesCurrentVC(iHalf, jHalf + 1, mm)); + // point on corner lines at phi direction + matricesCurrentV(i, j, mp1) = 0.25 * (matricesCurrentVC(iHalf, jHalf, mm) + matricesCurrentVC(iHalf, jHalf + 1, mm) + matricesCurrentVC(iHalf, jHalf, mmPlus) + matricesCurrentVC(iHalf, jHalf + 1, mmPlus)); + } + } + + for (int j = 2; j < tnZColumn - 1; j += 2) { + for (int i = 1; i < tnRRow - 1; i += 2) { + const int iHalf = i * 0.5; + const int jHalf = j * 0.5; + matricesCurrentV(i, j, m) = 0.5 * (matricesCurrentVC(iHalf, jHalf, mm) + matricesCurrentVC(iHalf + 1, jHalf, mm)); + // point on line at phi direction + matricesCurrentV(i, j, mp1) = 0.25 * ((matricesCurrentVC(iHalf, jHalf, mm) + matricesCurrentVC(iHalf, jHalf, mmPlus)) + (matricesCurrentVC(iHalf + 1, jHalf, mmPlus) + matricesCurrentVC(iHalf + 1, jHalf, mm))); + } + } + + for (int j = 1; j < tnZColumn - 1; j += 2) { + for (int i = 1; i < tnRRow - 1; i += 2) { + const int iHalf = i * 0.5; + const int jHalf = j * 0.5; + matricesCurrentV(i, j, m) = 0.25 * ((matricesCurrentVC(iHalf, jHalf, mm) + matricesCurrentVC(iHalf, jHalf + 1, mm)) + (matricesCurrentVC(iHalf + 1, jHalf, mm) + matricesCurrentVC(iHalf + 1, jHalf + 1, mm))); + // point at the center at phi direction + matricesCurrentV(i, j, mp1) = 0.125 * ((matricesCurrentVC(iHalf, jHalf, mm) + matricesCurrentVC(iHalf, jHalf + 1, mm) + matricesCurrentVC(iHalf, jHalf, mmPlus) + matricesCurrentVC(iHalf, jHalf + 1, mmPlus)) + + (matricesCurrentVC(iHalf + 1, jHalf, mm) + matricesCurrentVC(iHalf + 1, jHalf + 1, mm) + matricesCurrentVC(iHalf + 1, jHalf, mmPlus) + matricesCurrentVC(iHalf + 1, jHalf + 1, mmPlus))); + } + } + } + + } else { +#pragma omp parallel for num_threads(sNThreads) // no change + for (int m = 0; m < newPhiSlice; ++m) { + interp2D(matricesCurrentV, matricesCurrentVC, tnRRow, tnZColumn, m); + } + } +} + +template +void PoissonSolver::interp2D(Vector& matricesCurrentV, const Vector& matricesCurrentVC, const int tnRRow, const int tnZColumn, const int iphi) const +{ + for (int j = 2; j < tnZColumn - 1; j += 2) { + for (int i = 2; i < tnRRow - 1; i += 2) { + matricesCurrentV(i, j, iphi) = matricesCurrentVC(i * 0.5, j * 0.5, iphi); + } + } + + for (int j = 1; j < tnZColumn - 1; j += 2) { + for (int i = 2; i < tnRRow - 1; i += 2) { + const int iHalf = i * 0.5; + const int jHalf = j * 0.5; + matricesCurrentV(i, j, iphi) = 0.5 * (matricesCurrentVC(iHalf, jHalf, iphi) + matricesCurrentVC(iHalf, jHalf + 1, iphi)); + } + } + + for (int j = 2; j < tnZColumn - 1; j += 2) { + for (int i = 1; i < tnRRow - 1; i += 2) { + const int iHalf = i * 0.5; + const int jHalf = j * 0.5; + matricesCurrentV(i, j, iphi) = 0.5 * (matricesCurrentVC(iHalf, jHalf, iphi) + matricesCurrentVC(iHalf + 1, jHalf, iphi)); + } + } + + // only if full + if (MGParameters::gtType == GridTransferType::Full) { + for (int j = 1; j < tnZColumn - 1; j += 2) { + for (int i = 1; i < tnRRow - 1; i += 2) { + const int iHalf = i * 0.5; + const int jHalf = j * 0.5; + matricesCurrentV(i, j, iphi) = 0.25 * (matricesCurrentVC(iHalf, jHalf, iphi) + matricesCurrentVC(iHalf, jHalf + 1, iphi) + matricesCurrentVC(iHalf + 1, jHalf, iphi) + matricesCurrentVC(iHalf + 1, jHalf + 1, iphi)); + } + } + } +} + +template +void PoissonSolver::addInterp3D(Vector& matricesCurrentV, const Vector& matricesCurrentVC, const int tnRRow, const int tnZColumn, const int newPhiSlice, const int oldPhiSlice) const +{ + // Do restrict 2 D for each slice + if (newPhiSlice == 2 * oldPhiSlice) { + for (int m = 0; m < newPhiSlice; m += 2) { + // assuming no symmetry + int mm = m * 0.5; + int mmPlus = mm + 1; + int mp1 = m + 1; + + // round + if (mmPlus > (oldPhiSlice)-1) { + mmPlus = mm + 1 - (oldPhiSlice); + } + if (mp1 > (newPhiSlice)-1) { + mp1 = m + 1 - (newPhiSlice); + } + + for (int j = 2; j < tnZColumn - 1; j += 2) { + for (int i = 2; i < tnRRow - 1; i += 2) { + const int iHalf = i * 0.5; + const int jHalf = j * 0.5; + matricesCurrentV(i, j, m) += matricesCurrentVC(iHalf, jHalf, mm); + // point on corner lines at phi direction + matricesCurrentV(i, j, mp1) += 0.5 * (matricesCurrentVC(iHalf, jHalf, mm) + matricesCurrentVC(iHalf, jHalf, mmPlus)); + } + } + + for (int j = 1; j < tnZColumn - 1; j += 2) { + for (int i = 2; i < tnRRow - 1; i += 2) { + const int iHalf = i * 0.5; + const int jHalf = j * 0.5; + matricesCurrentV(i, j, m) += 0.5 * (matricesCurrentVC(iHalf, jHalf, mm) + matricesCurrentVC(iHalf, jHalf + 1, mm)); + // point on corner lines at phi direction + matricesCurrentV(i, j, mp1) += 0.25 * (matricesCurrentVC(iHalf, jHalf, mm) + matricesCurrentVC(iHalf, jHalf + 1, mm) + matricesCurrentVC(iHalf, jHalf, mmPlus) + matricesCurrentVC(iHalf, jHalf + 1, mmPlus)); + } + } + + for (int j = 2; j < tnZColumn - 1; j += 2) { + for (int i = 1; i < tnRRow - 1; i += 2) { + const int iHalf = i * 0.5; + const int jHalf = j * 0.5; + matricesCurrentV(i, j, m) += 0.5 * (matricesCurrentVC(iHalf, jHalf, mm) + matricesCurrentVC(iHalf + 1, jHalf, mm)); + // point on line at phi direction + matricesCurrentV(i, j, mp1) += 0.25 * ((matricesCurrentVC(iHalf, jHalf, mm) + matricesCurrentVC(iHalf, jHalf, mmPlus)) + (matricesCurrentVC(iHalf + 1, jHalf, mmPlus) + matricesCurrentVC(iHalf + 1, jHalf, mm))); + } + } + + for (int j = 1; j < tnZColumn - 1; j += 2) { + for (int i = 1; i < tnRRow - 1; i += 2) { + const int iHalf = i * 0.5; + const int jHalf = j * 0.5; + matricesCurrentV(i, j, m) += 0.25 * ((matricesCurrentVC(iHalf, jHalf, mm) + matricesCurrentVC(iHalf, jHalf + 1, mm)) + (matricesCurrentVC(iHalf + 1, jHalf, mm) + matricesCurrentVC(iHalf + 1, jHalf + 1, mm))); + // point at the center at phi direction + matricesCurrentV(i, j, mp1) += 0.125 * ((matricesCurrentVC(iHalf, jHalf, mm) + matricesCurrentVC(iHalf, jHalf + 1, mm) + matricesCurrentVC(iHalf, jHalf, mmPlus) + matricesCurrentVC(iHalf, jHalf + 1, mmPlus)) + + (matricesCurrentVC(iHalf + 1, jHalf, mm) + matricesCurrentVC(iHalf + 1, jHalf + 1, mm) + matricesCurrentVC(iHalf + 1, jHalf, mmPlus) + matricesCurrentVC(iHalf + 1, jHalf + 1, mmPlus))); + } + } + } + + } else { +#pragma omp parallel for num_threads(sNThreads) // no change + for (int m = 0; m < newPhiSlice; m++) { + addInterp2D(matricesCurrentV, matricesCurrentVC, tnRRow, tnZColumn, m); + } + } +} + +template +void PoissonSolver::addInterp2D(Vector& matricesCurrentV, const Vector& matricesCurrentVC, const int tnRRow, const int tnZColumn, const int tnPhi) const +{ + for (int j = 2; j < tnZColumn - 1; j += 2) { + for (int i = 2; i < tnRRow - 1; i += 2) { + matricesCurrentV(i, j, tnPhi) = matricesCurrentV(i, j, tnPhi) + matricesCurrentVC(i * 0.5, j * 0.5, tnPhi); + } + } + + for (int j = 1; j < tnZColumn - 1; j += 2) { + for (int i = 2; i < tnRRow - 1; i += 2) { + const int iHalf = 0.5 * i; + const int jHalf = 0.5 * j; + matricesCurrentV(i, j, tnPhi) = matricesCurrentV(i, j, tnPhi) + 0.5 * (matricesCurrentVC(iHalf, jHalf, tnPhi) + matricesCurrentVC(iHalf, jHalf + 1, tnPhi)); + } + } + + for (int j = 2; j < tnZColumn - 1; j += 2) { + for (int i = 1; i < tnRRow - 1; i += 2) { + const int iHalf = 0.5 * i; + const int jHalf = 0.5 * j; + matricesCurrentV(i, j, tnPhi) = matricesCurrentV(i, j, tnPhi) + 0.5 * (matricesCurrentVC(iHalf, jHalf, tnPhi) + matricesCurrentVC(iHalf + 1, jHalf, tnPhi)); + } + } + + // only if full + if (MGParameters::gtType == GridTransferType::Full) { + for (int j = 1; j < tnZColumn - 1; j += 2) { + for (int i = 1; i < tnRRow - 1; i += 2) { + const int iHalf = 0.5 * i; + const int jHalf = 0.5 * j; + matricesCurrentV(i, j, tnPhi) = matricesCurrentV(i, j, tnPhi) + 0.25 * (matricesCurrentVC(iHalf, jHalf, tnPhi) + matricesCurrentVC(iHalf, jHalf + 1, tnPhi) + matricesCurrentVC(iHalf + 1, jHalf, tnPhi) + matricesCurrentVC(iHalf + 1, jHalf + 1, tnPhi)); + } + } + } +} + +template +void PoissonSolver::relax3D(Vector& matricesCurrentV, const Vector& matricesCurrentCharge, const int tnRRow, const int tnZColumn, const int iPhi, const int symmetry, const DataT h2, + const DataT tempRatioZ, const std::array& coefficient1, const std::array& coefficient2, const std::array& coefficient3, const std::array& coefficient4) const +{ + // Gauss-Seidel (Read Black} + if (MGParameters::relaxType == RelaxType::GaussSeidel) { + // for each slice + for (int iPass = 1; iPass <= 2; ++iPass) { + const int msw = (iPass % 2) ? 1 : 2; + for (int m = 0; m < iPhi; ++m) { + const int jsw = ((msw + m) % 2) ? 1 : 2; + int mp1 = m + 1; + int signPlus = 1; + int mm1 = m - 1; + int signMinus = 1; + // Reflection symmetry in phi (e.g. symmetry at sector boundaries, or half sectors, etc.) + if (symmetry == 1) { + if (mp1 > iPhi - 1) { + mp1 = iPhi - 2; + } + if (mm1 < 0) { + mm1 = 1; + } + } + // Anti-symmetry in phi + else if (symmetry == -1) { + if (mp1 > iPhi - 1) { + mp1 = iPhi - 2; + signPlus = -1; + } + if (mm1 < 0) { + mm1 = 1; + signMinus = -1; + } + } else { // No Symmetries in phi, no boundaries, the calculation is continuous across all phi + if (mp1 > iPhi - 1) { + mp1 = m + 1 - iPhi; + } + if (mm1 < 0) { + mm1 = m - 1 + iPhi; + } + } + int isw = jsw; + for (int j = 1; j < tnZColumn - 1; ++j, isw = 3 - isw) { + 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 Nr + } // end phi + } // end sweep + } else if (MGParameters::relaxType == RelaxType::Jacobi) { + // for each slice + for (int m = 0; m < iPhi; ++m) { + int mp1 = m + 1; + int signPlus = 1; + int mm1 = m - 1; + int signMinus = 1; + + // Reflection symmetry in phi (e.g. symmetry at sector boundaries, or half sectors, etc.) + if (symmetry == 1) { + if (mp1 > iPhi - 1) { + mp1 = iPhi - 2; + } + if (mm1 < 0) { + mm1 = 1; + } + } + // Anti-symmetry in phi + else if (symmetry == -1) { + if (mp1 > iPhi - 1) { + mp1 = iPhi - 2; + signPlus = -1; + } + if (mm1 < 0) { + mm1 = 1; + signMinus = -1; + } + } else { // No Symmetries in phi, no boundaries, the calculation is continuous across all phi + if (mp1 > iPhi - 1) { + mp1 = m + 1 - iPhi; + } + if (mm1 < 0) { + mm1 = m - 1 + iPhi; + } + } + // Jacobian + for (int j = 1; j < tnZColumn - 1; ++j) { + 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 Nr + } // end phi + } else { + // Case weighted Jacobi + // TODO + } +} + +template +void PoissonSolver::relax2D(Vector& matricesCurrentV, const Vector& matricesCurrentCharge, const int tnRRow, const int tnZColumn, const DataT h2, const DataT tempFourth, const DataT tempRatio, + std::vector& coefficient1, std::vector& coefficient2) +{ + // Gauss-Seidel + const int iPhi = 0; + if (MGParameters::relaxType == RelaxType::GaussSeidel) { + int jsw = 1; + for (int iPass = 1; iPass <= 2; ++iPass, jsw = 3 - jsw) { + int isw = jsw; + for (int j = 1; j < tnZColumn - 1; ++j, isw = 3 - isw) { + for (int i = isw; i < tnRRow - 1; i += 2) { + 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 Nr + } // 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 Nr + } else if (MGParameters::relaxType == RelaxType::WeightedJacobi) { + // Weighted Jacobi + // TODO + } +} + +template +void PoissonSolver::restrictBoundary3D(Vector& matricesCurrentCharge, const Vector& residue, const int tnRRow, const int tnZColumn, const int newPhiSlice, const int oldPhiSlice) const +{ + // in case of full 3d and the Nphi is also coarsening + if (2 * newPhiSlice == oldPhiSlice) { + for (int m = 0, mm = 0; m < newPhiSlice; ++m, mm += 2) { + // for boundary + for (int j = 0, jj = 0; j < tnZColumn; ++j, jj += 2) { + matricesCurrentCharge(0, j, m) = residue(0, jj, mm); + matricesCurrentCharge(tnRRow - 1, j, m) = residue((tnRRow - 1) * 2, jj, mm); + } + + // for boundary + for (int i = 0, ii = 0; i < tnRRow; ++i, ii += 2) { + matricesCurrentCharge(i, 0, m) = residue(ii, 0, mm); + matricesCurrentCharge(i, tnZColumn - 1, m) = residue(ii, (tnZColumn - 1) * 2, mm); + } + } // end phis + } else { + for (int m = 0; m < newPhiSlice; ++m) { + restrictBoundary2D(matricesCurrentCharge, residue, tnRRow, tnZColumn, m); + } + } +} + +template +void PoissonSolver::restrictBoundary2D(Vector& matricesCurrentCharge, const Vector& residue, const int tnRRow, const int tnZColumn, const int tnPhi) const +{ + // for boundary + for (int j = 0, jj = 0; j < tnZColumn; ++j, jj += 2) { + matricesCurrentCharge(0, j, tnPhi) = residue(0, jj, tnPhi); + matricesCurrentCharge(tnRRow - 1, j, tnPhi) = residue((tnRRow - 1) * 2, jj, tnPhi); + } + + // for boundary + for (int i = 0, ii = 0; i < tnRRow; ++i, ii += 2) { + matricesCurrentCharge(i, 0, tnPhi) = residue(ii, 0, tnPhi); + matricesCurrentCharge(i, tnZColumn - 1, tnPhi) = residue(ii, (tnZColumn - 1) * 2, tnPhi); + } +} + +template +void PoissonSolver::restrict3D(Vector& matricesCurrentCharge, const Vector& residue, const int tnRRow, const int tnZColumn, const int newPhiSlice, const int oldPhiSlice) const +{ + if (2 * newPhiSlice == oldPhiSlice) { + int mm = 0; + for (int m = 0; m < newPhiSlice; m++, mm += 2) { + // assuming no symmetry + int mp1 = mm + 1; + int mm1 = mm - 1; + + if (mp1 > (oldPhiSlice)-1) { + mp1 = mm + 1 - (oldPhiSlice); + } + if (mm1 < 0) { + mm1 = mm - 1 + (oldPhiSlice); + } + + for (int i = 1, ii = 2; i < tnRRow - 1; ++i, ii += 2) { + for (int j = 1, jj = 2; j < tnZColumn - 1; ++j, jj += 2) { + + // at the same plane + const int iip1 = ii + 1; + const int iim1 = ii - 1; + const int jjp1 = jj + 1; + const int jjm1 = jj - 1; + const DataT s1 = residue(iip1, jj, mm) + residue(iim1, jj, mm) + residue(ii, jjp1, mm) + residue(ii, jjm1, mm) + residue(ii, jj, mp1) + residue(ii, jj, mm1); + + const DataT s2 = (residue(iip1, jjp1, mm) + residue(iip1, jjm1, mm) + residue(iip1, jj, mp1) + residue(iip1, jj, mm1)) + + (residue(iim1, jjm1, mm) + residue(iim1, jjp1, mm) + residue(iim1, jj, mp1) + residue(iim1, jj, mm1)) + + residue(ii, jjm1, mp1) + residue(ii, jjp1, mm1) + residue(ii, jjm1, mm1) + residue(ii, jjp1, mp1); + + const DataT s3 = (residue(iip1, jjp1, mp1) + residue(iip1, jjm1, mp1) + residue(iip1, jjp1, mm1) + residue(iip1, jjm1, mm1)) + + (residue(iim1, jjm1, mm1) + residue(iim1, jjp1, mm1) + residue(iim1, jjm1, mp1) + residue(iim1, jjp1, mp1)); + + matricesCurrentCharge(i, j, m) = 0.125 * residue(ii, jj, mm) + 0.0625 * s1 + 0.03125 * s2 + 0.015625 * s3; + } // end cols + } // end Nr + + // for boundary + for (int j = 0, jj = 0; j < tnZColumn; ++j, jj += 2) { + matricesCurrentCharge(0, j, m) = residue(0, jj, mm); + matricesCurrentCharge(tnRRow - 1, j, m) = residue((tnRRow - 1) * 2, jj, mm); + } + + // for boundary + for (int i = 0, ii = 0; i < tnRRow; ++i, ii += 2) { + matricesCurrentCharge(i, 0, m) = residue(ii, 0, mm); + matricesCurrentCharge(i, tnZColumn - 1, m) = residue(ii, (tnZColumn - 1) * 2, mm); + } + } // end phis + + } else { + for (int m = 0; m < newPhiSlice; ++m) { + restrict2D(matricesCurrentCharge, residue, tnRRow, tnZColumn, m); + } + } +} + +template +void PoissonSolver::restrict2D(Vector& matricesCurrentCharge, const Vector& residue, const int tnRRow, const int tnZColumn, const int iphi) const +{ + for (int i = 1, ii = 2; i < tnRRow - 1; ++i, ii += 2) { + for (int j = 1, jj = 2; j < tnZColumn - 1; ++j, jj += 2) { + const int iip1 = ii + 1; + const int iim1 = ii - 1; + const int jjp1 = jj + 1; + const int jjm1 = jj - 1; + if (MGParameters::gtType == GridTransferType::Half) { + // half + matricesCurrentCharge(i, j, iphi) = 0.5 * residue(ii, jj, iphi) + 0.125 * (residue(iip1, jj, iphi) + residue(iim1, jj, iphi) + residue(ii, jjp1, iphi) + residue(ii, jjm1, iphi)); + } else if (MGParameters::gtType == GridTransferType::Full) { + matricesCurrentCharge(i, j, iphi) = 0.25 * residue(ii, jj, iphi) + 0.125 * (residue(iip1, jj, iphi) + residue(iim1, jj, iphi) + residue(ii, jjp1, iphi) + residue(ii, jjm1, iphi)) + + 0.0625 * (residue(iip1, jjp1, iphi) + residue(iim1, jjp1, iphi) + residue(iip1, jjm1, iphi) + residue(iim1, jjm1, iphi)); + } + } // end cols + } // end Nr + // boundary + // for boundary + for (int j = 0, jj = 0; j < tnZColumn; ++j, jj += 2) { + matricesCurrentCharge(0, j, iphi) = residue(0, jj, iphi); + matricesCurrentCharge(tnRRow - 1, j, iphi) = residue((tnRRow - 1) * 2, jj, iphi); + } + // for boundary + for (int i = 0, ii = 0; i < tnRRow; ++i, ii += 2) { + matricesCurrentCharge(i, 0, iphi) = residue(ii, 0, iphi); + matricesCurrentCharge(i, tnZColumn - 1, iphi) = residue(ii, (tnZColumn - 1) * 2, iphi); + } +} + +template +DataT PoissonSolver::getConvergenceError(const Vector& matricesCurrentV, Vector& prevArrayV) const +{ + std::vector errorArr(prevArrayV.getNphi()); + + // subtract the two matrices + std::transform(prevArrayV.begin(), prevArrayV.end(), matricesCurrentV.begin(), prevArrayV.begin(), std::minus()); + +#pragma omp parallel for num_threads(sNThreads) // parallising this loop is possible - but using more than 2 cores makes it slower - + for (unsigned int m = 0; m < prevArrayV.getNphi(); ++m) { + // square each entry in the vector and sum them up + const auto phiStep = prevArrayV.getNr() * prevArrayV.getNz(); // number of points in one phi slice + const auto start = prevArrayV.begin() + m * phiStep; + const auto end = start + phiStep; + errorArr[m] = std::inner_product(start, end, start, 0.); // inner product "Sum (matrix[a]*matrix[a])" + } + // return largest error + return *std::max_element(std::begin(errorArr), std::end(errorArr)); +} + +template +void PoissonSolver::calcCoefficients(unsigned int from, unsigned int to, const DataT h, const DataT tempRatioZ, const DataT tempRatioPhi, std::array& coefficient1, std::array& coefficient2, std::array& coefficient3, std::array& coefficient4) const +{ + for (unsigned int i = from; i < to; ++i) { + const DataT radiusInv = 1. / (TPCParameters::IFCRADIUS + i * h); + const DataT hRadiusTmp = h * 0.5 * radiusInv; + coefficient1[i] = 1.0 + hRadiusTmp; + coefficient2[i] = 1.0 - hRadiusTmp; + coefficient3[i] = tempRatioPhi * radiusInv * radiusInv; + coefficient4[i] = 0.5 / (1.0 + tempRatioZ + coefficient3[i]); + } +} + +template +void PoissonSolver::calcCoefficients2D(unsigned int from, unsigned int to, const DataT h, std::vector& coefficient1, std::vector& coefficient2) const +{ + for (int i = from; i < to; ++i) { + DataT radiusInvHalf = h * 0.5 / (TPCParameters::IFCRADIUS + i * h); + coefficient1[i] = 1.0 + radiusInvHalf; + coefficient2[i] = 1.0 - radiusInvHalf; + } +} + +template class o2::tpc::PoissonSolver; +template class o2::tpc::PoissonSolver; +template class o2::tpc::PoissonSolver; +template class o2::tpc::PoissonSolver; +template class o2::tpc::PoissonSolver; +template class o2::tpc::PoissonSolver; +template class o2::tpc::PoissonSolver; // for 2D Poisson Solver diff --git a/Detectors/TPC/spacecharge/src/SpaceCharge.cxx b/Detectors/TPC/spacecharge/src/SpaceCharge.cxx new file mode 100644 index 0000000000000..dbe6f8af67120 --- /dev/null +++ b/Detectors/TPC/spacecharge/src/SpaceCharge.cxx @@ -0,0 +1,1251 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file SpaceCharge.cxx +/// \brief Definition of SpaceCharge class +/// +/// \author Matthias Kleiner +/// \date Aug 21, 2020 + +#include "TPCSpaceCharge/SpaceCharge.h" +#include "fmt/core.h" +#include "Framework/Logger.h" +#include + +#ifdef WITH_OPENMP +#include +#endif + +templateClassImp(o2::tpc::SpaceCharge); + +using namespace o2::tpc; + +template +void SpaceCharge::calculateDistortionsCorrections(const o2::tpc::Side side) +{ + using timer = std::chrono::high_resolution_clock; + using SC = o2::tpc::SpaceCharge; + if (!mIsChargeSet[side]) { + LOGP(ERROR, "the charge is not set!"); + } + + const std::array sglobalType{"local distortion/correction interpolator", "Electric fields"}; + const std::array sglobalDistType{"Standard method", "interpolation of global corrections"}; + const std::array sideName{"A", "C"}; + + LOGP(info, "====== starting calculation of distortions and corrections for Side {} ======", sideName[side]); + LOGP(info, "Using {} threads", getNThreads()); + + if (getGlobalDistCorrMethod() == SC::GlobalDistCorrMethod::LocalDistCorr) { + LOGP(info, "calculation of global distortions and corrections are performed by using: {}", sglobalType[0]); + } else { + LOGP(info, "calculation of global distortions and corrections are performed by using: {}", sglobalType[1]); + } + + if (getGlobalDistType() == SC::GlobalDistType::Fast) { + LOGP(info, "calculation of global distortions performed by following method: {}", sglobalDistType[1]); + } else if (getGlobalDistType() == SC::GlobalDistType::Standard) { + LOGP(info, "calculation of global distortions performed by following method: {}", sglobalDistType[0]); + } else { + LOGP(info, "skipping calculation of global distortions"); + } + + auto startTotal = timer::now(); + + auto start = timer::now(); + poissonSolver(side); + auto stop = timer::now(); + std::chrono::duration time = stop - start; + LOGP(info, "Poisson Solver time: {}", time.count()); + + start = timer::now(); + calcEField(side); + stop = timer::now(); + time = stop - start; + LOGP(info, "electric field calculation time: {}", time.count()); + + const auto numEFields = getElectricFieldsInterpolator(side); + if (getGlobalDistType() == SC::GlobalDistType::Standard) { + start = timer::now(); + const auto dist = o2::tpc::SpaceCharge::Type::Distortions; + calcLocalDistortionsCorrections(dist, numEFields); // local distortion calculation + stop = timer::now(); + time = stop - start; + LOGP(info, "local distortions time: {}", time.count()); + } else { + LOGP(info, "skipping local distortions (not needed)"); + } + + start = timer::now(); + const auto corr = o2::tpc::SpaceCharge::Type::Corrections; + calcLocalDistortionsCorrections(corr, numEFields); // local correction calculation + stop = timer::now(); + time = stop - start; + LOGP(info, "local corrections time: {}", time.count()); + + start = timer::now(); + const auto lCorrInterpolator = getLocalCorrInterpolator(side); + (getGlobalDistCorrMethod() == SC::GlobalDistCorrMethod::LocalDistCorr) ? calcGlobalCorrections(lCorrInterpolator) : calcGlobalCorrections(numEFields); + stop = timer::now(); + time = stop - start; + LOGP(info, "global corrections time: {}", time.count()); + start = timer::now(); + if (getGlobalDistType() == SC::GlobalDistType::Fast) { + const auto globalCorrInterpolator = getGlobalCorrInterpolator(side); + calcGlobalDistWithGlobalCorrIterative(globalCorrInterpolator); + } else if (getGlobalDistType() == SC::GlobalDistType::Standard) { + const auto lDistInterpolator = getLocalDistInterpolator(side); + (getGlobalDistCorrMethod() == SC::GlobalDistCorrMethod::LocalDistCorr) ? calcGlobalDistortions(lDistInterpolator) : calcGlobalDistortions(numEFields); + } else { + } + + stop = timer::now(); + time = stop - start; + LOGP(info, "global distortions time: {}", time.count()); + + stop = timer::now(); + time = stop - startTotal; + LOGP(info, "everything is done. Total Time: {}", time.count()); +} + +template +DataT SpaceCharge::regulateR(const DataT posR, const Side side) const +{ + const DataT minR = getRMin(side) - 4 * getGridSpacingR(side); + if (posR < minR) { + return minR; + } + const DataT maxR = getRMax(side) + 2 * getGridSpacingR(side); + if (posR > maxR) { + return maxR; + } + return posR; +} + +template +void SpaceCharge::setFromFile(TFile& file, const Side side) +{ + setDensityFromFile(file, side); + setPotentialFromFile(file, side); + setElectricFieldsFromFile(file, side); + setLocalDistortionsFromFile(file, side); + setLocalCorrectionsFromFile(file, side); + setGlobalDistortionsFromFile(file, side); + setGlobalCorrectionsFromFile(file, side); +} + +template +void SpaceCharge::setChargeDensityFromFormula(const AnalyticalFields& formulaStruct) +{ + const Side side = formulaStruct.getSide(); + for (size_t iPhi = 0; iPhi < Nphi; ++iPhi) { + const DataT phi = getPhiVertex(iPhi, side); + for (size_t iR = 0; iR < Nr; ++iR) { + const DataT radius = getRVertex(iR, side); + for (size_t iZ = 0; iZ < Nz; ++iZ) { + const DataT z = getZVertex(iZ, side); + mDensity[side](iZ, iR, iPhi) = formulaStruct.evalDensity(z, radius, phi); + } + } + } + mIsChargeSet[side] = true; +} + +template +void SpaceCharge::setPotentialFromFormula(const AnalyticalFields& formulaStruct) +{ + const Side side = formulaStruct.getSide(); + for (size_t iPhi = 0; iPhi < Nphi; ++iPhi) { + const DataT phi = getPhiVertex(iPhi, side); + for (size_t iR = 0; iR < Nr; ++iR) { + const DataT radius = getRVertex(iR, side); + for (size_t iZ = 0; iZ < Nz; ++iZ) { + const DataT z = getZVertex(iZ, side); + mPotential[side](iZ, iR, iPhi) = formulaStruct.evalPotential(z, radius, phi); + } + } + } +} + +template +void SpaceCharge::setPotentialBoundaryFromFormula(const AnalyticalFields& formulaStruct) +{ + const Side side = formulaStruct.getSide(); + for (size_t iPhi = 0; iPhi < Nphi; ++iPhi) { + const DataT phi = getPhiVertex(iPhi, side); + for (size_t iZ = 0; iZ < Nz; ++iZ) { + const DataT z = getZVertex(iZ, side); + const size_t iR = 0; + const DataT radius = getRVertex(iR, side); + mPotential[side](iZ, iR, iPhi) = formulaStruct.evalPotential(z, radius, phi); + } + } + + for (size_t iPhi = 0; iPhi < Nphi; ++iPhi) { + const DataT phi = getPhiVertex(iPhi, side); + for (size_t iZ = 0; iZ < Nz; ++iZ) { + const DataT z = getZVertex(iZ, side); + const size_t iR = Nr - 1; + const DataT radius = getRVertex(iR, side); + mPotential[side](iZ, iR, iPhi) = formulaStruct.evalPotential(z, radius, phi); + } + } + + for (size_t iPhi = 0; iPhi < Nphi; ++iPhi) { + const DataT phi = getPhiVertex(iPhi, side); + for (size_t iR = 0; iR < Nr; ++iR) { + const DataT radius = getRVertex(iR, side); + const size_t iZ = 0; + const DataT z = getZVertex(iZ, side); + mPotential[side](iZ, iR, iPhi) = formulaStruct.evalPotential(z, radius, phi); + } + } + + for (size_t iPhi = 0; iPhi < Nphi; ++iPhi) { + const DataT phi = getPhiVertex(iPhi, side); + for (size_t iR = 0; iR < Nr; ++iR) { + const DataT radius = getRVertex(iR, side); + const size_t iZ = Nz - 1; + const DataT z = getZVertex(iZ, side); + mPotential[side](iZ, iR, iPhi) = formulaStruct.evalPotential(z, radius, phi); + } + } +} + +template +void SpaceCharge::poissonSolver(const Side side, const int maxIteration, const DataT stoppingConvergence, const int symmetry) +{ + ASolv::setConvergenceError(stoppingConvergence); + ASolv poissonSolver(mGrid3D[0]); + poissonSolver.poissonSolver3D(mPotential[side], mDensity[side], symmetry); +} + +template +void SpaceCharge::setEFieldFromFormula(const AnalyticalFields& formulaStruct) +{ + const Side side = formulaStruct.getSide(); + for (size_t iPhi = 0; iPhi < Nphi; ++iPhi) { + for (size_t iR = 0; iR < Nr; ++iR) { + for (size_t iZ = 0; iZ < Nz; ++iZ) { + const DataT radius = getRVertex(iR, side); + const DataT z = getZVertex(iZ, side); + const DataT phi = getPhiVertex(iPhi, side); + mElectricFieldEr[side](iZ, iR, iPhi) = formulaStruct.evalEr(z, radius, phi); + mElectricFieldEz[side](iZ, iR, iPhi) = formulaStruct.evalEz(z, radius, phi); + mElectricFieldEphi[side](iZ, iR, iPhi) = formulaStruct.evalEphi(z, radius, phi); + } + } + } + mIsEfieldSet[side] = true; +} + +template +void SpaceCharge::calcEField(const Side side) +{ +#pragma omp parallel for num_threads(sNThreads) + for (size_t iPhi = 0; iPhi < Nphi; ++iPhi) { + const int symmetry = 0; + size_t tmpPlus = iPhi + 1; + int signPlus = 1; + int tmpMinus = static_cast(iPhi - 1); + int signMinus = 1; + if (symmetry == 1 || symmetry == -1) { // Reflection symmetry in phi (e.g. symmetry at sector boundaries, or half sectors, etc.) + if (tmpPlus > Nphi - 1) { + if (symmetry == -1) { + signPlus = -1; + } + tmpPlus = Nphi - 2; + } + if (tmpMinus < 0) { + tmpMinus = 1; // SHOULD IT BE =0? + if (symmetry == -1) { + signMinus = -1; + } + } + } else { // No Symmetries in phi, no boundaries, the calculations is continuous across all phi + if (tmpPlus > Nphi - 1) { + tmpPlus = iPhi + 1 - Nphi; + } + if (tmpMinus < 0) { + tmpMinus = static_cast(iPhi - 1 + Nphi); + } + } + + // for non-boundary V + for (size_t iR = 1; iR < Nr - 1; iR++) { + const DataT radius = getRVertex(iR, side); + for (size_t iZ = 1; iZ < Nz - 1; iZ++) { + mElectricFieldEr[side](iZ, iR, iPhi) = -1 * (mPotential[side](iZ, iR + 1, iPhi) - mPotential[side](iZ, iR - 1, iPhi)) * static_cast(0.5) * getInvSpacingR(side); // r direction + mElectricFieldEz[side](iZ, iR, iPhi) = -1 * (mPotential[side](iZ + 1, iR, iPhi) - mPotential[side](iZ - 1, iR, iPhi)) * static_cast(0.5) * getInvSpacingZ(side); // z direction + mElectricFieldEphi[side](iZ, iR, iPhi) = -1 * (signPlus * mPotential[side](iZ, iR, tmpPlus) - signMinus * mPotential[side](iZ, iR, tmpMinus)) * static_cast(0.5) * getInvSpacingPhi(side) / radius; // phi direction + } + } + + // for boundary-r + for (size_t iZ = 0; iZ < Nz; iZ++) { + mElectricFieldEr[side](iZ, 0, iPhi) = -1 * (-static_cast(0.5) * mPotential[side](iZ, 2, iPhi) + 2 * mPotential[side](iZ, 1, iPhi) - static_cast(1.5) * mPotential[side](iZ, 0, iPhi)) * getInvSpacingR(side); // forward difference + mElectricFieldEr[side](iZ, Nr - 1, iPhi) = -1 * (static_cast(1.5) * mPotential[side](iZ, Nr - 1, iPhi) - 2 * mPotential[side](iZ, Nr - 2, iPhi) + static_cast(0.5) * mPotential[side](iZ, Nr - 3, iPhi)) * getInvSpacingR(side); // backward difference + } + + for (size_t iR = 0; iR < Nr; iR += Nr - 1) { + const DataT radius = getRVertex(iR, side); + for (size_t iZ = 1; iZ < Nz - 1; iZ++) { + mElectricFieldEz[side](iZ, iR, iPhi) = -1 * (mPotential[side](iZ + 1, iR, iPhi) - mPotential[side](iZ - 1, iR, iPhi)) * static_cast(0.5) * getInvSpacingZ(side); // z direction + mElectricFieldEphi[side](iZ, iR, iPhi) = -1 * (signPlus * mPotential[side](iZ, iR, tmpPlus) - signMinus * mPotential[side](iZ, iR, tmpMinus)) * static_cast(0.5) * getInvSpacingPhi(side) / radius; // phi direction + } + } + + // for boundary-z + for (size_t iR = 0; iR < Nr; ++iR) { + mElectricFieldEz[side](0, iR, iPhi) = -1 * (-static_cast(0.5) * mPotential[side](2, iR, iPhi) + 2 * mPotential[side](1, iR, iPhi) - static_cast(1.5) * mPotential[side](0, iR, iPhi)) * getInvSpacingZ(side); + mElectricFieldEz[side](Nz - 1, iR, iPhi) = -1 * (static_cast(1.5) * mPotential[side](Nz - 1, iR, iPhi) - 2 * mPotential[side](Nz - 2, iR, iPhi) + static_cast(0.5) * mPotential[side](Nz - 3, iR, iPhi)) * getInvSpacingZ(side); + } + + for (size_t iR = 1; iR < Nr - 1; ++iR) { + const DataT radius = getRVertex(iR, side); + for (size_t iZ = 0; iZ < Nz; iZ += Nz - 1) { + mElectricFieldEr[side](iZ, iR, iPhi) = -1 * (mPotential[side](iZ, iR + 1, iPhi) - mPotential[side](iZ, iR - 1, iPhi)) * static_cast(0.5) * getInvSpacingR(side); // r direction + mElectricFieldEphi[side](iZ, iR, iPhi) = -1 * (signPlus * mPotential[side](iZ, iR, tmpPlus) - signMinus * mPotential[side](iZ, iR, tmpMinus)) * static_cast(0.5) * getInvSpacingPhi(side) / radius; // phi direction + } + } + + // corner points for EPhi + for (size_t iR = 0; iR < Nr; iR += Nr - 1) { + const DataT radius = getRVertex(iR, side); + for (size_t iZ = 0; iZ < Nz; iZ += Nz - 1) { + mElectricFieldEphi[side](iZ, iR, iPhi) = -1 * (signPlus * mPotential[side](iZ, iR, tmpPlus) - signMinus * mPotential[side](iZ, iR, tmpMinus)) * static_cast(0.5) * getInvSpacingPhi(side) / radius; // phi direction + } + } + } + mIsEfieldSet[side] = true; +} + +template +void SpaceCharge::calcGlobalDistWithGlobalCorrIterative(const DistCorrInterpolator& globCorr, const int maxIter, const DataT approachZ, const DataT approachR, const DataT approachPhi, const DataT diffCorr) +{ + const Side side = globCorr.getSide(); + +#pragma omp parallel for num_threads(sNThreads) + for (unsigned int iPhi = 0; iPhi < Nphi; ++iPhi) { + const DataT phi = getPhiVertex(iPhi, side); + for (unsigned int iR = 0; iR < Nr; ++iR) { + const DataT radius = getRVertex(iR, side); + for (unsigned int iZ = 0; iZ < Nz; ++iZ) { + const DataT z = getZVertex(iZ, side); + + unsigned int nearestiZ = iZ; + unsigned int nearestiR = iR; + unsigned int nearestiPhi = iPhi; + + DataT nearestZ = getZVertex(nearestiZ, side); + DataT nearestR = getRVertex(nearestiR, side); + DataT nearestPhi = getPhiVertex(nearestiPhi, side); + + // + //========================================================================================== + //==== start algorithm: use tricubic upsampling to numerically approach the query point ==== + //========================================================================================== + // + // 1. calculate difference from nearest point to query point with stepwidth factor x + // and approach the new point + // + DataT stepR = (radius - nearestR) * approachR; + DataT stepZ = (z - nearestZ) * approachZ; + DataT stepPhi = (phi - nearestPhi) * approachPhi; + + // needed to check for convergence + DataT lastCorrdR = std::numeric_limits::max(); + DataT lastCorrdZ = std::numeric_limits::max(); + DataT lastCorrdRPhi = std::numeric_limits::max(); + + // interpolated global correction + DataT corrdR = 0; + DataT corrdRPhi = 0; + DataT corrdZ = 0; + + for (int iter = 0; iter < maxIter; ++iter) { + // 2. get new point coordinates + const DataT rCurrPos = getRVertex(nearestiR, side) + stepR; + const DataT zCurrPos = getZVertex(nearestiZ, side) + stepZ; + const DataT phiCurrPos = getPhiVertex(nearestiPhi, side) + stepPhi; + + // interpolate global correction at new point and calculate position of global correction + // corrdR = globCorr.evalSparsedR(zCurrPos, rCurrPos, phiCurrPos); + corrdR = globCorr.evaldR(zCurrPos, rCurrPos, phiCurrPos); + const DataT rNewPos = rCurrPos + corrdR; + + // const DataT corrPhi = globCorr.evalSparsedRPhi(zCurrPos, rCurrPos, phiCurrPos) / rCurrPos; + const DataT corrPhi = globCorr.evaldRPhi(zCurrPos, rCurrPos, phiCurrPos) / rCurrPos; + corrdRPhi = corrPhi * rNewPos; // normalize to new r coordinate + const DataT phiNewPos = phiCurrPos + corrPhi; + + // corrdZ = globCorr.evalSparsedZ(zCurrPos, rCurrPos, phiCurrPos); + corrdZ = globCorr.evaldZ(zCurrPos, rCurrPos, phiCurrPos); + const DataT zNewPos = zCurrPos + corrdZ; + + // approach desired coordinate + stepR += (radius - rNewPos) * approachR; + stepZ += (z - zNewPos) * approachZ; + stepPhi += (phi - phiNewPos) * approachPhi; + + // check for convergence + const DataT diffCorrdR = std::abs(corrdR - lastCorrdR); + const DataT diffCorrdRZ = std::abs(corrdZ - lastCorrdZ); + const DataT diffCorrdRPhi = std::abs(corrdRPhi - lastCorrdRPhi); + + // stop algorithm if converged + if (diffCorrdR < diffCorr && diffCorrdRZ < diffCorr && diffCorrdRPhi < diffCorr) { + break; + } + + lastCorrdR = corrdR; + lastCorrdZ = corrdZ; + lastCorrdRPhi = corrdRPhi; + } + // set global distortions if algorithm converged or iterations exceed max numbers of iterations + mGlobalDistdR[side](iZ, iR, iPhi) = -corrdR; + mGlobalDistdRPhi[side](iZ, iR, iPhi) = -corrdRPhi; + mGlobalDistdZ[side](iZ, iR, iPhi) = -corrdZ; + } + } + } + // set flag that global distortions are set to true + mIsGlobalDistSet[side] = true; +} + +template +NumericalFields SpaceCharge::getElectricFieldsInterpolator(const Side side) const +{ + if (!mIsEfieldSet[side]) { + LOGP(warning, "============== E-Fields are not set! ==============\n"); + } + NumericalFields numFields(mElectricFieldEr[side], mElectricFieldEz[side], mElectricFieldEphi[side], mGrid3D[side], side); + return numFields; +} + +template +DistCorrInterpolator SpaceCharge::getLocalDistInterpolator(const Side side) const +{ + if (!mIsLocalDistSet[side]) { + LOGP(warning, "============== local distortions not set! ==============\n"); + } + DistCorrInterpolator numFields(mLocalDistdR[side], mLocalDistdZ[side], mLocalDistdRPhi[side], mGrid3D[side], side); + return numFields; +} + +template +DistCorrInterpolator SpaceCharge::getLocalCorrInterpolator(const Side side) const +{ + if (!mIsLocalCorrSet[side]) { + LOGP(warning, "============== local corrections not set! ==============\n"); + } + DistCorrInterpolator numFields(mLocalCorrdR[side], mLocalCorrdZ[side], mLocalCorrdRPhi[side], mGrid3D[side], side); + return numFields; +} + +template +DistCorrInterpolator SpaceCharge::getGlobalDistInterpolator(const Side side) const +{ + if (!mIsGlobalDistSet[side]) { + LOGP(warning, "============== global distortions not set ==============\n"); + } + DistCorrInterpolator numFields(mGlobalDistdR[side], mGlobalDistdZ[side], mGlobalDistdRPhi[side], mGrid3D[side], side); + return numFields; +} + +template +DistCorrInterpolator SpaceCharge::getGlobalCorrInterpolator(const Side side) const +{ + if (!mIsGlobalCorrSet[side]) { + LOGP(warning, "============== global corrections not set ==============\n"); + } + DistCorrInterpolator numFields(mGlobalCorrdR[side], mGlobalCorrdZ[side], mGlobalCorrdRPhi[side], mGrid3D[side], side); + return numFields; +} + +template +int SpaceCharge::dumpElectricFields(TFile& outf, const Side side) const +{ + if (!mIsEfieldSet[side]) { + LOGP(warning, "============== E-Fields are not set! returning ==============\n"); + return 0; + } + const std::string sideName = getSideName(side); + const int er = mElectricFieldEr[side].writeToFile(outf, fmt::format("fieldEr_side{}", sideName).data()); + const int ez = mElectricFieldEz[side].writeToFile(outf, fmt::format("fieldEz_side{}", sideName).data()); + const int ephi = mElectricFieldEphi[side].writeToFile(outf, fmt::format("fieldEphi_side{}", sideName).data()); + return er + ez + ephi; +} + +template +void SpaceCharge::setElectricFieldsFromFile(TFile& inpf, const Side side) +{ + const std::string sideName = getSideName(side); + mElectricFieldEr[side].initFromFile(inpf, fmt::format("fieldEr_side{}", sideName).data()); + mElectricFieldEz[side].initFromFile(inpf, fmt::format("fieldEz_side{}", sideName).data()); + mElectricFieldEphi[side].initFromFile(inpf, fmt::format("fieldEphi_side{}", sideName).data()); + mIsEfieldSet[side] = true; +} + +template +int SpaceCharge::dumpGlobalDistortions(TFile& outf, const Side side) const +{ + if (!mIsGlobalDistSet[side]) { + LOGP(warning, "============== global distortions are not set! returning ==============\n"); + return 0; + } + const std::string sideName = getSideName(side); + const int er = mGlobalDistdR[side].writeToFile(outf, fmt::format("distR_side{}", sideName).data()); + const int ez = mGlobalDistdZ[side].writeToFile(outf, fmt::format("distZ_side{}", sideName).data()); + const int ephi = mGlobalDistdRPhi[side].writeToFile(outf, fmt::format("distRphi_side{}", sideName).data()); + return er + ez + ephi; +} + +template +void SpaceCharge::setGlobalDistortionsFromFile(TFile& inpf, const Side side) +{ + mIsGlobalDistSet[side] = true; + const std::string sideName = getSideName(side); + mGlobalDistdR[side].initFromFile(inpf, fmt::format("distR_side{}", sideName).data()); + mGlobalDistdZ[side].initFromFile(inpf, fmt::format("distZ_side{}", sideName).data()); + mGlobalDistdRPhi[side].initFromFile(inpf, fmt::format("distRphi_side{}", sideName).data()); +} + +template +int SpaceCharge::dumpGlobalCorrections(TFile& outf, const Side side) const +{ + if (!mIsGlobalCorrSet[side]) { + LOGP(warning, "============== global corrections are not set! returning ==============\n"); + return 0; + } + const std::string sideName = getSideName(side); + const int er = mGlobalCorrdR[side].writeToFile(outf, fmt::format("corrR_side{}", sideName).data()); + const int ez = mGlobalCorrdZ[side].writeToFile(outf, fmt::format("corrZ_side{}", sideName).data()); + const int ephi = mGlobalCorrdRPhi[side].writeToFile(outf, fmt::format("corrRPhi_side{}", sideName).data()); + return er + ez + ephi; +} + +template +void SpaceCharge::setGlobalCorrectionsFromFile(TFile& inpf, const Side side) +{ + mIsGlobalCorrSet[side] = true; + const std::string sideName = getSideName(side); + mGlobalCorrdR[side].initFromFile(inpf, fmt::format("corrR_side{}", sideName).data()); + mGlobalCorrdZ[side].initFromFile(inpf, fmt::format("corrZ_side{}", sideName).data()); + mGlobalCorrdRPhi[side].initFromFile(inpf, fmt::format("corrRPhi_side{}", sideName).data()); +} + +template +int SpaceCharge::dumpLocalCorrections(TFile& outf, const Side side) const +{ + if (!mIsLocalCorrSet[side]) { + LOGP(warning, "============== local corrections are not set! returning ==============\n"); + return 0; + } + const std::string sideName = getSideName(side); + const int lCorrdR = mLocalCorrdR[side].writeToFile(outf, fmt::format("lcorrR_side{}", sideName).data()); + const int lCorrdZ = mLocalCorrdZ[side].writeToFile(outf, fmt::format("lcorrZ_side{}", sideName).data()); + const int lCorrdRPhi = mLocalCorrdRPhi[side].writeToFile(outf, fmt::format("lcorrRPhi_side{}", sideName).data()); + return lCorrdR + lCorrdZ + lCorrdRPhi; +} + +template +void SpaceCharge::setLocalCorrectionsFromFile(TFile& inpf, const Side side) +{ + const std::string sideName = getSideName(side); + const bool lCorrdR = mLocalCorrdR[side].initFromFile(inpf, fmt::format("lcorrR_side{}", sideName).data()); + const bool lCorrdZ = mLocalCorrdZ[side].initFromFile(inpf, fmt::format("lcorrZ_side{}", sideName).data()); + const bool lCorrdRPhi = mLocalCorrdRPhi[side].initFromFile(inpf, fmt::format("lcorrRPhi_side{}", sideName).data()); + if (lCorrdR && lCorrdZ && lCorrdRPhi) { + mIsLocalCorrSet[side] = true; + } else { + mIsLocalCorrSet[side] = false; + } +} + +template +int SpaceCharge::dumpLocalDistortions(TFile& outf, const Side side) const +{ + if (!mIsLocalDistSet[side]) { + LOGP(warning, "============== local distortions are not set! returning ==============\n"); + return 0; + } + const std::string sideName = getSideName(side); + const int lDistdR = mLocalDistdR[side].writeToFile(outf, fmt::format("ldistR_side{}", sideName).data()); + const int lDistdZ = mLocalDistdZ[side].writeToFile(outf, fmt::format("ldistZ_side{}", sideName).data()); + const int lDistdRPhi = mLocalDistdRPhi[side].writeToFile(outf, fmt::format("ldistRPhi_side{}", sideName).data()); + return lDistdR + lDistdZ + lDistdRPhi; +} + +template +void SpaceCharge::setLocalDistortionsFromFile(TFile& inpf, const Side side) +{ + const std::string sideName = getSideName(side); + const bool lDistdR = mLocalDistdR[side].initFromFile(inpf, fmt::format("ldistR_side{}", sideName).data()); + const bool lDistdZ = mLocalDistdZ[side].initFromFile(inpf, fmt::format("ldistZ_side{}", sideName).data()); + const bool lDistdRPhi = mLocalDistdRPhi[side].initFromFile(inpf, fmt::format("ldistRPhi_side{}", sideName).data()); + + if (lDistdR && lDistdZ && lDistdRPhi) { + mIsLocalDistSet[side] = true; + } else { + mIsLocalDistSet[side] = false; + } +} +template +void SpaceCharge::fillChargeDensityFromFile(TFile& fInp, const char* name) +{ + const TH3* hisSCDensity3D = (TH3*)fInp.Get(name); + fillChargeDensityFromHisto(*hisSCDensity3D); +} + +template +void SpaceCharge::fillChargeDensityFromHisto(const TH3& hisSCDensity3D) +{ + TH3D hRebin = rebinDensityHisto(hisSCDensity3D); + for (int side = Side::A; side < SIDES; ++side) { + for (size_t iPhi = 0; iPhi < Nphi; ++iPhi) { + for (size_t iR = 0; iR < Nr; ++iR) { + for (size_t iZ = 0; iZ < Nz; ++iZ) { + const size_t zBin = side == Side::A ? Nz + iZ + 1 : Nz - iZ; // TODO CHECK THIS! + mDensity[side](iZ, iR, iPhi) = hRebin.GetBinContent(iPhi, iR, zBin); + } + } + } + mIsChargeSet[side] = true; + } +} + +template +TH3D SpaceCharge::rebinDensityHisto(const TH3& hOrig) const +{ + TH3D hRebin{}; + + const int nBinsPhiNew = Nphi; + const int nBinsRNew = Nr; + const int nBinsZNew = 2 * Nz; + + const auto phiLow = hOrig.GetXaxis()->GetBinLowEdge(1); + const auto phiUp = hOrig.GetXaxis()->GetBinUpEdge(hOrig.GetNbinsX()); + const auto rLow = hOrig.GetYaxis()->GetBinLowEdge(1); + const auto rUp = hOrig.GetYaxis()->GetBinUpEdge(hOrig.GetNbinsY()); + const auto zLow = hOrig.GetZaxis()->GetBinLowEdge(1); + const auto zUp = hOrig.GetZaxis()->GetBinUpEdge(hOrig.GetNbinsZ()); + hRebin.SetBins(nBinsPhiNew, phiLow, phiUp, nBinsRNew, rLow, rUp, nBinsZNew, zLow, zUp); + + for (int iBinPhi = 1; iBinPhi <= nBinsPhiNew; ++iBinPhi) { + const auto phiLowEdge = hRebin.GetXaxis()->GetBinLowEdge(iBinPhi); + const auto phiUpEdge = hRebin.GetXaxis()->GetBinUpEdge(iBinPhi); + + const int phiLowBinOrig = hOrig.GetXaxis()->FindBin(phiLowEdge); + const int phiUpBinOrig = hOrig.GetXaxis()->FindBin(phiUpEdge); + + // calculate the weights (area of original bin lies in the new bin / binwidthOrig) of the first and last bins + const auto binWidthPhiOrig = hOrig.GetXaxis()->GetBinWidth(phiLowBinOrig); + const auto lowerBinWeightPhi = std::abs(phiLowEdge - hOrig.GetXaxis()->GetBinUpEdge(phiLowBinOrig)) / binWidthPhiOrig; + const auto upperBinWeightPhi = std::abs(phiUpEdge - hOrig.GetXaxis()->GetBinLowEdge(phiUpBinOrig)) / binWidthPhiOrig; + + for (int iBinR = 1; iBinR <= nBinsRNew; ++iBinR) { + const auto rLowEdge = hRebin.GetYaxis()->GetBinLowEdge(iBinR); + const auto rUpEdge = hRebin.GetYaxis()->GetBinUpEdge(iBinR); + + const int rLowBinOrig = hOrig.GetYaxis()->FindBin(rLowEdge); + const int rUpBinOrig = hOrig.GetYaxis()->FindBin(rUpEdge); + + // calculate the weights (area of original bin lies in the new bin / binwidthOrig) of the first and last bins + const auto binWidthROrig = hOrig.GetYaxis()->GetBinWidth(rLowBinOrig); + const auto lowerBinWeightR = std::abs(rLowEdge - hOrig.GetYaxis()->GetBinUpEdge(rLowBinOrig)) / binWidthROrig; + const auto upperBinWeightR = std::abs(rUpEdge - hOrig.GetYaxis()->GetBinLowEdge(rUpBinOrig)) / binWidthROrig; + + for (int iBinZ = 1; iBinZ <= nBinsZNew; ++iBinZ) { + const auto zLowEdge = hRebin.GetZaxis()->GetBinLowEdge(iBinZ); + const auto zUpEdge = hRebin.GetZaxis()->GetBinUpEdge(iBinZ); + const auto zCenter = hRebin.GetZaxis()->GetBinCenter(iBinZ); + + int zLowBinOrig = hOrig.GetZaxis()->FindBin(zLowEdge); + int zUpBinOrig = hOrig.GetZaxis()->FindBin(zUpEdge); + const int currside = getSide(zCenter); // set the side of the current z-bin + // get the side of the lowest and uppest bin from the orig histo + const int sideLowOrig = getSide(hOrig.GetZaxis()->GetBinCenter(zLowBinOrig)); + const int sideUpOrig = getSide(hOrig.GetZaxis()->GetBinCenter(zUpBinOrig)); + + // make bounds/side check of the zLowBinOrig and zUpBinOrig bins. They must be on the same side as the currside!!! + if (currside != sideLowOrig && zLowBinOrig != zUpBinOrig) { + // if the lower bins from the orig histo are not on the same side as the rebinned increase the binnumber until they are on the same side + bool notequal = true; + do { + zLowBinOrig += 1; + if (zLowBinOrig > zUpBinOrig) { + LOGP(WARNING, "SOMETHING WENT WRONG: SETTING BINS TO: {}", zUpBinOrig); + zLowBinOrig = zUpBinOrig; + notequal = false; + } + const int sideTmp = getSide(hOrig.GetZaxis()->GetBinCenter(zLowBinOrig)); + if (sideTmp == currside) { + notequal = false; + } + } while (notequal); + } + + if (currside != sideUpOrig && zLowBinOrig != zUpBinOrig) { + // if the upper bins from the orig histo are not on the same side as the rebinned increase the binnumber until they are on the same side + bool notequal = true; + do { + zUpBinOrig -= 1; + if (zUpBinOrig < zLowBinOrig) { + LOGP(WARNING, "SOMETHING WENT WRONG: SETTING BINS TO: {}", zLowBinOrig); + zUpBinOrig = zLowBinOrig; + notequal = false; + } + const int sideTmp = getSide(hOrig.GetZaxis()->GetBinCenter(zUpBinOrig)); + if (sideTmp == currside) { + notequal = false; + } + } while (notequal); + } + + const auto binWidthZOrig = hOrig.GetZaxis()->GetBinWidth(zLowBinOrig); + const auto lowerBinWeightZ = std::abs(zLowEdge - hOrig.GetZaxis()->GetBinUpEdge(zLowBinOrig)) / binWidthZOrig; + const auto upperBinWeightZ = std::abs(zUpEdge - hOrig.GetZaxis()->GetBinLowEdge(zUpBinOrig)) / binWidthZOrig; + + // get the mean value of the original histogram of the found bin range + DataT sum = 0; + DataT sumW = 0; + for (int iPhi = phiLowBinOrig; iPhi <= phiUpBinOrig; ++iPhi) { + DataT weightPhi = 1; + if (iPhi == phiLowBinOrig) { + weightPhi = lowerBinWeightPhi; + } else if (iPhi == phiUpBinOrig) { + weightPhi = upperBinWeightPhi; + } + + for (int iR = rLowBinOrig; iR <= rUpBinOrig; ++iR) { + DataT weightR = 1; + if (iR == rLowBinOrig) { + weightR = lowerBinWeightR; + } else if (iR == rUpBinOrig) { + weightR = upperBinWeightR; + } + + for (int iZ = zLowBinOrig; iZ <= zUpBinOrig; ++iZ) { + DataT weightZ = 1; + if (iZ == zLowBinOrig) { + weightZ = lowerBinWeightZ; + } else if (iZ == zUpBinOrig) { + weightZ = upperBinWeightZ; + } + const auto val = hOrig.GetBinContent(iPhi, iR, iZ); + // if(val==0){ + // what to do now??? + // } + const auto totalWeight = weightPhi * weightR * weightZ; + sum += val * totalWeight; + sumW += totalWeight; + } + } + } + sum /= sumW; + hRebin.SetBinContent(iBinPhi, iBinR, iBinZ, sum); + } + } + } + return hRebin; +} + +template +template +void SpaceCharge::calcLocalDistortionsCorrections(const SpaceCharge::Type type, const ElectricFields& formulaStruct) +{ + const Side side = formulaStruct.getSide(); + // calculate local distortions/corrections for each vertex in the tpc +#pragma omp parallel for num_threads(sNThreads) + for (size_t iPhi = 0; iPhi < Nphi; ++iPhi) { + const DataT phi = getPhiVertex(iPhi, side); + for (size_t iR = 0; iR < Nr; ++iR) { + const DataT radius = getRVertex(iR, side); + for (size_t iZ = 0; iZ < Nz - 1; ++iZ) { + // set z coordinate depending on distortions or correction calculation + const DataT z0 = type == Type::Corrections ? getZVertex(iZ + 1, side) : getZVertex(iZ, side); + const DataT z1 = type == Type::Corrections ? getZVertex(iZ, side) : getZVertex(iZ + 1, side); + + DataT drTmp = 0; // local distortion dR + DataT dPhiTmp = 0; // local distortion dPhi (multiplication with R has to be done at the end) + DataT dzTmp = 0; // local distortion dZ + + const DataT stepSize = (z1 - z0) / sSteps; // the distortions are calculated by leting the elctron drift this distance in z direction + for (int iter = 0; iter < sSteps; ++iter) { + const DataT z0Tmp = (z0 + iter * stepSize + dzTmp); // starting z position + const DataT z1Tmp = (z0Tmp + stepSize); // electron drifts from z0Tmp to z1Tmp + + DataT ddR = 0; // distortion dR for drift from z0Tmp to z1Tmp + DataT ddPhi = 0; // distortion dPhi for drift from z0Tmp to z1Tmp + DataT ddZ = 0; // distortion dZ for drift from z0Tmp to z1Tmp + + const DataT radiusTmp = regulateR(radius + drTmp, side); // current radial position + const DataT phiTmp = regulatePhi(phi + dPhiTmp, side); // current phi position + + // calculate distortions/corrections + calcDistCorr(radiusTmp, phiTmp, z0Tmp, z1Tmp, ddR, ddPhi, ddZ, formulaStruct, true); + + // add temp distortions to local distortions + drTmp += ddR; + dPhiTmp += ddPhi; + dzTmp += ddZ; + } + + // store local distortions/corrections + switch (type) { + case Type::Corrections: + mLocalCorrdR[side](iZ + 1, iR, iPhi) = drTmp; + mLocalCorrdRPhi[side](iZ + 1, iR, iPhi) = dPhiTmp * radius; + mLocalCorrdZ[side](iZ + 1, iR, iPhi) = dzTmp; + break; + + case Type::Distortions: + mLocalDistdR[side](iZ, iR, iPhi) = drTmp; + mLocalDistdRPhi[side](iZ, iR, iPhi) = dPhiTmp * radius; + mLocalDistdZ[side](iZ, iR, iPhi) = dzTmp; + break; + } + } + //extrapolate local distortion/correction to last/first bin using legendre polynoms with x0=0, x1=1, x2=2 and x=-1. This has to be done to ensure correct interpolation in the last,second last/first,second bin! + switch (type) { + case Type::Corrections: + mLocalCorrdR[side](0, iR, iPhi) = 3 * (mLocalCorrdR[side](1, iR, iPhi) - mLocalCorrdR[side](2, iR, iPhi)) + mLocalCorrdR[side](3, iR, iPhi); + mLocalCorrdRPhi[side](0, iR, iPhi) = 3 * (mLocalCorrdRPhi[side](1, iR, iPhi) - mLocalCorrdRPhi[side](2, iR, iPhi)) + mLocalCorrdRPhi[side](3, iR, iPhi); + mLocalCorrdZ[side](0, iR, iPhi) = 3 * (mLocalCorrdZ[side](1, iR, iPhi) - mLocalCorrdZ[side](2, iR, iPhi)) + mLocalCorrdZ[side](3, iR, iPhi); + break; + + case Type::Distortions: + mLocalDistdR[side](Nz - 1, iR, iPhi) = 3 * (mLocalDistdR[side](Nz - 2, iR, iPhi) - mLocalDistdR[side](Nz - 3, iR, iPhi)) + mLocalDistdR[side](Nz - 4, iR, iPhi); + mLocalDistdRPhi[side](Nz - 1, iR, iPhi) = 3 * (mLocalDistdRPhi[side](Nz - 2, iR, iPhi) - mLocalDistdRPhi[side](Nz - 3, iR, iPhi)) + mLocalDistdRPhi[side](Nz - 4, iR, iPhi); + mLocalDistdZ[side](Nz - 1, iR, iPhi) = 3 * (mLocalDistdZ[side](Nz - 2, iR, iPhi) - mLocalDistdZ[side](Nz - 3, iR, iPhi)) + mLocalDistdZ[side](Nz - 4, iR, iPhi); + break; + } + } + } + switch (type) { + case Type::Corrections: + mIsLocalCorrSet[side] = true; + break; + case Type::Distortions: + mIsLocalDistSet[side] = true; + break; + } +} + +template +template +void SpaceCharge::calcGlobalDistortions(const Fields& formulaStruct) +{ + const Side side = formulaStruct.getSide(); + const DataT stepSize = formulaStruct.getID() == 2 ? getGridSpacingZ(side) : getGridSpacingZ(side) / sSteps; // if one used local distortions then no smaller stepsize is needed. if electric fields are used then smaller stepsize can be used + // loop over tpc volume and let the electron drift from each vertex to the readout of the tpc +#pragma omp parallel for num_threads(sNThreads) + for (size_t iPhi = 0; iPhi < Nphi; ++iPhi) { + const DataT phi0 = getPhiVertex(iPhi, side); + for (size_t iR = 0; iR < Nr; ++iR) { + const DataT r0 = getRVertex(iR, side); + for (size_t iZ = 0; iZ < Nz - 1; ++iZ) { + const DataT z0 = getZVertex(iZ, side); // the electron starts at z0, r0, phi0 + DataT drDist = 0.0; // global distortion dR + DataT dPhiDist = 0.0; // global distortion dPhi (multiplication with R has to be done at the end) + DataT dzDist = 0.0; // global distortion dZ + int iter = 0; + + for (;;) { + const DataT z0Tmp = z0 + dzDist + iter * stepSize; // starting z position + const DataT z1Tmp = regulateZ(z0Tmp + stepSize, side); // electron drifts from z0Tmp to z1Tmp + const DataT radius = regulateR(r0 + drDist, side); // current radial position of the electron + const DataT phi = regulatePhi(phi0 + dPhiDist, side); // current phi position of the electron + + DataT ddR = 0; // distortion dR for drift from z0Tmp to z1Tmp + DataT ddPhi = 0; // distortion dPhi for drift from z0Tmp to z1Tmp + DataT ddZ = 0; // distortion dZ for drift from z0Tmp to z1Tmp + + // get the distortion from interpolation of local distortions or calculate distortions with the electric field + processGlobalDistCorr(radius, phi, z0Tmp, z1Tmp, ddR, ddPhi, ddZ, formulaStruct); + + // if one uses local distortions the interpolated value for the last bin has to be scaled. + // This has to be done because of the interpolated value is defined for a drift length of one z bin, but in the last bin the distance to the readout can be smaller than one z bin. + const bool checkReached = side == Side::A ? z1Tmp >= getZMax(side) : z1Tmp <= getZMax(side); + if (formulaStruct.getID() == 2 && checkReached) { + const DataT fac = std::abs((getZMax(side) - z0Tmp) * getInvSpacingZ(side)); + ddR *= fac; + ddZ *= fac; + ddPhi *= fac; + } + + // add local distortions to global distortions + drDist += ddR; + dPhiDist += ddPhi; + dzDist += ddZ; + + // set loop to exit if the readout is reached and approximate distortion of 'missing' (one never ends exactly on the readout: z1Tmp + ddZ != ZMAX) drift distance. + // approximation is done by the current calculated values of the distortions and scaled linear to the 'missing' distance. + if (checkReached) { + const DataT endPoint = z1Tmp + ddZ; + const DataT deltaZ = getZMax(side) - endPoint; // distance from last point to read out + const DataT diff = endPoint - z0Tmp; + const DataT fac = diff != 0 ? std::abs(deltaZ / diff) : 0; // approximate the distortions for the 'missing' distance deltaZ + drDist += ddR * fac; + dPhiDist += ddPhi * fac; + dzDist += ddZ * fac; + break; + } + ++iter; + } + // store global distortions + mGlobalDistdR[side](iZ, iR, iPhi) = drDist; + mGlobalDistdRPhi[side](iZ, iR, iPhi) = dPhiDist * r0; + mGlobalDistdZ[side](iZ, iR, iPhi) = dzDist; + } + } + } + // set flag that global distortions are set to true + mIsGlobalDistSet[side] = true; +} + +template +template +void SpaceCharge::calcGlobalCorrections(const Formulas& formulaStruct) +{ + const Side side = formulaStruct.getSide(); + const int iSteps = formulaStruct.getID() == 2 ? 1 : sSteps; // if one used local corrections no step width is needed. since it is already used for calculation of the local corrections + const DataT stepSize = -getGridSpacingZ(side) / iSteps; + // loop over tpc volume and let the electron drift from each vertex to the readout of the tpc +#pragma omp parallel for num_threads(sNThreads) + for (size_t iPhi = 0; iPhi < Nphi; ++iPhi) { + const DataT phi0 = getPhiVertex(iPhi, side); + for (size_t iR = 0; iR < Nr; ++iR) { + + const DataT r0 = getRVertex(iR, side); + DataT drCorr = 0; + DataT dPhiCorr = 0; + DataT dzCorr = 0; + + // start at the readout and follow electron towards central electrode + for (size_t iZ = Nz - 1; iZ >= 1; --iZ) { + const DataT z0 = getZVertex(iZ, side); // the electron starts at z0, r0, phi0 + // flag which is set when the central electrode is reached. if the central electrode is reached the calculation of the global corrections is aborted and the value set is the last calculated value. + bool centralElectrodeReached = false; + for (int iter = 0; iter < iSteps; ++iter) { + if (centralElectrodeReached) { + break; + } + const DataT radius = regulateR(r0 + drCorr, side); // current radial position of the electron + const DataT phi = regulatePhi(phi0 + dPhiCorr, side); // current phi position of the electron + const DataT z0Tmp = z0 + dzCorr + iter * stepSize; // starting z position + const DataT z1Tmp = regulateZ(z0Tmp + stepSize, side); // follow electron from z0Tmp to z1Tmp + DataT ddR = 0; // distortion dR for z0Tmp to z1Tmp + DataT ddPhi = 0; // distortion dPhi for z0Tmp to z1Tmp + DataT ddZ = 0; // distortion dZ for z0Tmp to z1Tmp + + // get the distortion from interpolation of local distortions or calculate distortions with the electric field + processGlobalDistCorr(radius, phi, z0Tmp, z1Tmp, ddR, ddPhi, ddZ, formulaStruct); + + // if one uses local corrections the interpolated value for the first bin has to be scaled. + // This has to be done because of the interpolated value is defined for a drift length of one z bin, but in the first bin the distance to the readout can be smaller than one z bin. + centralElectrodeReached = getSign(side) * z1Tmp <= getZMin(side); + if (formulaStruct.getID() == 2 && centralElectrodeReached) { + const DataT fac = (z0Tmp - getZMin(side)) * getInvSpacingZ(side); + ddR *= fac; + ddZ *= fac; + ddPhi *= fac; + } + + // add local corrections to global corrections + drCorr += ddR; + dPhiCorr += ddPhi; + dzCorr += ddZ; + + // set loop to exit if the central electrode is reached and approximate correction of 'missing' (one never ends exactly on the central electrode: z1Tmp + ddZ != ZMIN) distance. + // approximation is done by the current calculated values of the corrections and scaled linear to the 'missing' distance deltaZ. (NOT TESTED) + if (centralElectrodeReached) { + const DataT endPoint = z1Tmp + ddZ; + const DataT deltaZ = endPoint - getZMin(side); + const DataT diff = z0Tmp - endPoint; + const DataT fac = diff != 0 ? deltaZ / diff : 0; // approximate the distortions for the 'missing' distance deltaZ + drCorr += ddR * fac; + dPhiCorr += ddPhi * fac; + dzCorr += ddZ * fac; + break; + } + } + // store global corrections + mGlobalCorrdR[side](iZ - 1, iR, iPhi) = drCorr; + mGlobalCorrdRPhi[side](iZ - 1, iR, iPhi) = dPhiCorr * r0; + mGlobalCorrdZ[side](iZ - 1, iR, iPhi) = dzCorr; + } + } + } + // set flag that global corrections are set to true + mIsGlobalCorrSet[side] = true; +} + +template +void SpaceCharge::correctElectron(GlobalPosition3D& point) +{ + DataT corrX{}; + DataT corrY{}; + DataT corrZ{}; + const Side side = getSide(point.Z()); + + // get the distortions for input coordinate + getCorrections(point.X(), point.Y(), point.Z(), side, corrX, corrY, corrZ); + + // set distorted coordinates + point.SetXYZ(point.X() + corrX, point.Y() + corrY, point.Y() + corrY); +} + +template +void SpaceCharge::distortElectron(GlobalPosition3D& point) const +{ + DataT distX{}; + DataT distY{}; + DataT distZ{}; + const Side side = getSide(point.Z()); + // get the distortions for input coordinate + getDistortions(point.X(), point.Y(), point.Z(), side, distX, distY, distZ); + + // set distorted coordinates + point.SetXYZ(point.X() + distX, point.Y() + distY, point.Z() + distZ); +} + +template +DataT SpaceCharge::getChargeCyl(const DataT z, const DataT r, const DataT phi, const Side side) const +{ + return mInterpolatorDensity[side](z, r, phi); +} + +template +DataT SpaceCharge::getPotentialCyl(const DataT z, const DataT r, const DataT phi, const Side side) const +{ + return mInterpolatorPotential[side](z, r, phi); +} + +template +void SpaceCharge::getElectricFieldsCyl(const DataT z, const DataT r, const DataT phi, const Side side, DataT& eZ, DataT& eR, DataT& ePhi) const +{ + eZ = mInterpolatorEField[side].evalEz(z, r, phi); + eR = mInterpolatorEField[side].evalEr(z, r, phi); + ePhi = mInterpolatorEField[side].evalEphi(z, r, phi); +} + +template +void SpaceCharge::getLocalCorrectionsCyl(const DataT z, const DataT r, const DataT phi, const Side side, DataT& lcorrZ, DataT& lcorrR, DataT& lcorrRPhi) const +{ + lcorrZ = mInterpolatorLocalCorr[side].evaldZ(z, r, phi); + lcorrR = mInterpolatorLocalCorr[side].evaldR(z, r, phi); + lcorrRPhi = mInterpolatorLocalCorr[side].evaldRPhi(z, r, phi); +} + +template +void SpaceCharge::getCorrectionsCyl(const DataT z, const DataT r, const DataT phi, const Side side, DataT& corrZ, DataT& corrR, DataT& corrRPhi) const +{ + corrZ = mInterpolatorGlobalCorr[side].evaldZ(z, r, phi); + corrR = mInterpolatorGlobalCorr[side].evaldR(z, r, phi); + corrRPhi = mInterpolatorGlobalCorr[side].evaldRPhi(z, r, phi); +} + +template +void SpaceCharge::getCorrections(const DataT x, const DataT y, const DataT z, const Side side, DataT& corrX, DataT& corrY, DataT& corrZ) const +{ + // convert cartesian to polar + const DataT radius = getRadiusFromCartesian(x, y); + const DataT phi = getPhiFromCartesian(x, y); + + DataT corrR{}; + DataT corrRPhi{}; + getCorrectionsCyl(z, radius, phi, side, corrZ, corrR, corrRPhi); + + // Calculate corrected position + const DataT radiusCorr = radius + corrR; + const DataT phiCorr = phi + corrRPhi / radius; + + corrX = getXFromPolar(radiusCorr, phiCorr) - x; // difference between corrected and original x coordinate + corrY = getYFromPolar(radiusCorr, phiCorr) - y; // difference between corrected and original y coordinate +} + +template +void SpaceCharge::getLocalDistortionsCyl(const DataT z, const DataT r, const DataT phi, const Side side, DataT& ldistZ, DataT& ldistR, DataT& ldistRPhi) const +{ + ldistZ = mInterpolatorLocalDist[side].evaldZ(z, r, phi); + ldistR = mInterpolatorLocalDist[side].evaldR(z, r, phi); + ldistRPhi = mInterpolatorLocalDist[side].evaldRPhi(z, r, phi); +} + +template +void SpaceCharge::getDistortionsCyl(const DataT z, const DataT r, const DataT phi, const Side side, DataT& distZ, DataT& distR, DataT& distRPhi) const +{ + distZ = mInterpolatorGlobalDist[side].evaldZ(z, r, phi); + distR = mInterpolatorGlobalDist[side].evaldR(z, r, phi); + distRPhi = mInterpolatorGlobalDist[side].evaldRPhi(z, r, phi); +} + +template +void SpaceCharge::getDistortions(const DataT x, const DataT y, const DataT z, const Side side, DataT& distX, DataT& distY, DataT& distZ) const +{ + // convert cartesian to polar + const DataT radius = getRadiusFromCartesian(x, y); + const DataT phi = getPhiFromCartesian(x, y); + + DataT distR{}; + DataT distRPhi{}; + getDistortionsCyl(z, radius, phi, side, distZ, distR, distRPhi); + + // Calculate distorted position + const DataT radiusDist = radius + distR; + const DataT phiDist = phi + distRPhi / radius; + + distX = getXFromPolar(radiusDist, phiDist) - x; // difference between distorted and original x coordinate + distY = getYFromPolar(radiusDist, phiDist) - y; // difference between distorted and original y coordinate +} + +template +void SpaceCharge::init() +{ + using timer = std::chrono::high_resolution_clock; + if (!mInitLookUpTables) { + auto start = timer::now(); + auto o2field = static_cast(TGeoGlobalMagField::Instance()->GetField()); + const float bzField = o2field->solenoidField(); // magnetic field in kGauss + /// TODO is there a faster way to get the drift velocity + auto& gasParam = ParameterGas::Instance(); + float vDrift = gasParam.DriftV; // drift velocity in cm/us + /// TODO fix hard coded values (ezField, t1, t2): export to Constants.h or get from somewhere? + const float t1 = 1.; + const float t2 = 1.; + /// TODO use this parameterization or fixed value(s) from Magboltz calculations? + const float omegaTau = -10. * bzField * vDrift / std::abs(getEzField(Side::A)); + setOmegaTauT1T2(omegaTau, t1, t2); + if (mUseInitialSCDensity) { + LOG(WARNING) << "mUseInitialSCDensity" << mUseInitialSCDensity; + calculateDistortionsCorrections(Side::A); + calculateDistortionsCorrections(Side::C); + mInitLookUpTables = true; + } + auto stop = timer::now(); + std::chrono::duration time = stop - start; + LOGP(info, "Total Time Distortions and Corrections for A and C Side: {}", time.count()); + } +} + +template +void SpaceCharge::setDistortionLookupTables(const DataContainer& distdZ, const DataContainer& distdR, const DataContainer& distdRPhi, const Side side) +{ + mGlobalDistdR[side] = distdR; + mGlobalDistdZ[side] = distdZ; + mGlobalDistdRPhi[side] = distdRPhi; + mIsGlobalDistSet[side] = true; +} + +using DataTD = double; +template class o2::tpc::SpaceCharge; +template class o2::tpc::SpaceCharge; +template class o2::tpc::SpaceCharge; +template class o2::tpc::SpaceCharge; +template class o2::tpc::SpaceCharge; +template class o2::tpc::SpaceCharge; + +// 129*129*180 +using NumFields129D = NumericalFields; +using AnaFields129D = AnalyticalFields; +using DistCorrInterp129D = DistCorrInterpolator; +using O2TPCSpaceCharge3DCalc129D = SpaceCharge; + +template void O2TPCSpaceCharge3DCalc129D::calcLocalDistortionsCorrections(const O2TPCSpaceCharge3DCalc129D::Type, const NumFields129D&); +template void O2TPCSpaceCharge3DCalc129D::calcLocalDistortionsCorrections(const O2TPCSpaceCharge3DCalc129D::Type, const AnaFields129D&); +template void O2TPCSpaceCharge3DCalc129D::calcGlobalCorrections(const NumFields129D&); +template void O2TPCSpaceCharge3DCalc129D::calcGlobalCorrections(const AnaFields129D&); +template void O2TPCSpaceCharge3DCalc129D::calcGlobalCorrections(const DistCorrInterp129D&); + +template void O2TPCSpaceCharge3DCalc129D::calcGlobalDistortions(const NumFields129D&); +template void O2TPCSpaceCharge3DCalc129D::calcGlobalDistortions(const AnaFields129D&); +template void O2TPCSpaceCharge3DCalc129D::calcGlobalDistortions(const DistCorrInterp129D&); + +// 33*33*180 +using NumFields33D = NumericalFields; +using AnaFields33D = AnalyticalFields; +using DistCorrInterp33D = DistCorrInterpolator; +using O2TPCSpaceCharge3DCalc33D = SpaceCharge; + +template void O2TPCSpaceCharge3DCalc33D::calcLocalDistortionsCorrections(const O2TPCSpaceCharge3DCalc33D::Type, const NumFields33D&); +template void O2TPCSpaceCharge3DCalc33D::calcLocalDistortionsCorrections(const O2TPCSpaceCharge3DCalc33D::Type, const AnaFields33D&); +template void O2TPCSpaceCharge3DCalc33D::calcGlobalCorrections(const NumFields33D&); +template void O2TPCSpaceCharge3DCalc33D::calcGlobalCorrections(const AnaFields33D&); +template void O2TPCSpaceCharge3DCalc33D::calcGlobalCorrections(const DistCorrInterp33D&); + +template void O2TPCSpaceCharge3DCalc33D::calcGlobalDistortions(const NumFields33D&); +template void O2TPCSpaceCharge3DCalc33D::calcGlobalDistortions(const AnaFields33D&); +template void O2TPCSpaceCharge3DCalc33D::calcGlobalDistortions(const DistCorrInterp33D&); + +// 65*65*180 +using NumFields65D = NumericalFields; +using AnaFields65D = AnalyticalFields; +using DistCorrInterp65D = DistCorrInterpolator; +using O2TPCSpaceCharge3DCalc65D = SpaceCharge; + +template void O2TPCSpaceCharge3DCalc65D::calcLocalDistortionsCorrections(const O2TPCSpaceCharge3DCalc65D::Type, const NumFields65D&); +template void O2TPCSpaceCharge3DCalc65D::calcLocalDistortionsCorrections(const O2TPCSpaceCharge3DCalc65D::Type, const AnaFields65D&); +template void O2TPCSpaceCharge3DCalc65D::calcGlobalCorrections(const NumFields65D&); +template void O2TPCSpaceCharge3DCalc65D::calcGlobalCorrections(const AnaFields65D&); +template void O2TPCSpaceCharge3DCalc65D::calcGlobalCorrections(const DistCorrInterp65D&); + +template void O2TPCSpaceCharge3DCalc65D::calcGlobalDistortions(const NumFields65D&); +template void O2TPCSpaceCharge3DCalc65D::calcGlobalDistortions(const AnaFields65D&); +template void O2TPCSpaceCharge3DCalc65D::calcGlobalDistortions(const DistCorrInterp65D&); + +// 17*17*90 +using NumFields17D = NumericalFields; +using AnaFields17D = AnalyticalFields; +using DistCorrInterp17D = DistCorrInterpolator; +using O2TPCSpaceCharge3DCalc17D = SpaceCharge; + +template void O2TPCSpaceCharge3DCalc17D::calcLocalDistortionsCorrections(const O2TPCSpaceCharge3DCalc17D::Type, const NumFields17D&); +template void O2TPCSpaceCharge3DCalc17D::calcLocalDistortionsCorrections(const O2TPCSpaceCharge3DCalc17D::Type, const AnaFields17D&); +template void O2TPCSpaceCharge3DCalc17D::calcGlobalCorrections(const NumFields17D&); +template void O2TPCSpaceCharge3DCalc17D::calcGlobalCorrections(const AnaFields17D&); +template void O2TPCSpaceCharge3DCalc17D::calcGlobalCorrections(const DistCorrInterp17D&); + +template void O2TPCSpaceCharge3DCalc17D::calcGlobalDistortions(const NumFields17D&); +template void O2TPCSpaceCharge3DCalc17D::calcGlobalDistortions(const AnaFields17D&); +template void O2TPCSpaceCharge3DCalc17D::calcGlobalDistortions(const DistCorrInterp17D&); + +// 257*257*180 +using NumFields257D = NumericalFields; +using AnaFields257D = AnalyticalFields; +using DistCorrInterp257D = DistCorrInterpolator; +using O2TPCSpaceCharge3DCalc257D = SpaceCharge; + +template void O2TPCSpaceCharge3DCalc257D::calcLocalDistortionsCorrections(const O2TPCSpaceCharge3DCalc257D::Type, const NumFields257D&); +template void O2TPCSpaceCharge3DCalc257D::calcLocalDistortionsCorrections(const O2TPCSpaceCharge3DCalc257D::Type, const AnaFields257D&); +template void O2TPCSpaceCharge3DCalc257D::calcGlobalCorrections(const NumFields257D&); +template void O2TPCSpaceCharge3DCalc257D::calcGlobalCorrections(const AnaFields257D&); +template void O2TPCSpaceCharge3DCalc257D::calcGlobalCorrections(const DistCorrInterp257D&); + +template void O2TPCSpaceCharge3DCalc257D::calcGlobalDistortions(const NumFields257D&); +template void O2TPCSpaceCharge3DCalc257D::calcGlobalDistortions(const AnaFields257D&); +template void O2TPCSpaceCharge3DCalc257D::calcGlobalDistortions(const DistCorrInterp257D&); + +// 257*257*360 +using NumFields257360D = NumericalFields; +using AnaFields257360D = AnalyticalFields; +using DistCorrInterp257360D = DistCorrInterpolator; +using O2TPCSpaceCharge3DCalc257360D = SpaceCharge; + +template void O2TPCSpaceCharge3DCalc257360D::calcLocalDistortionsCorrections(const O2TPCSpaceCharge3DCalc257360D::Type, const NumFields257360D&); +template void O2TPCSpaceCharge3DCalc257360D::calcLocalDistortionsCorrections(const O2TPCSpaceCharge3DCalc257360D::Type, const AnaFields257360D&); +template void O2TPCSpaceCharge3DCalc257360D::calcGlobalCorrections(const NumFields257360D&); +template void O2TPCSpaceCharge3DCalc257360D::calcGlobalCorrections(const AnaFields257360D&); +template void O2TPCSpaceCharge3DCalc257360D::calcGlobalCorrections(const DistCorrInterp257360D&); + +template void O2TPCSpaceCharge3DCalc257360D::calcGlobalDistortions(const NumFields257360D&); +template void O2TPCSpaceCharge3DCalc257360D::calcGlobalDistortions(const AnaFields257360D&); +template void O2TPCSpaceCharge3DCalc257360D::calcGlobalDistortions(const DistCorrInterp257360D&); diff --git a/Detectors/TPC/spacecharge/src/TPCSpacechargeLinkDef.h b/Detectors/TPC/spacecharge/src/TPCSpacechargeLinkDef.h new file mode 100644 index 0000000000000..1dd77a5bcafd9 --- /dev/null +++ b/Detectors/TPC/spacecharge/src/TPCSpacechargeLinkDef.h @@ -0,0 +1,81 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file TPCSpacechargeLinkDef.h +/// \author Matthias Kleiner + +#ifdef __CLING__ + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ nestedclasses; +#pragma link C++ nestedtypedef; + +#pragma link C++ class o2::tpc::MGParameters + ; +#pragma link C++ class o2::tpc::TPCParameters < double> + ; +#pragma link C++ class o2::tpc::AnalyticalFields < double> + ; + +// 257*257*360 +#pragma link C++ class o2::tpc::RegularGrid3D < double, 257, 257, 360> + ; +#pragma link C++ class o2::tpc::DataContainer3D < double, 257, 257, 360> + ; +#pragma link C++ class o2::tpc::PoissonSolver < double, 257, 257, 360> + ; +#pragma link C++ class o2::tpc::SpaceCharge < double, 257, 257, 360> + ; +#pragma link C++ class o2::tpc::NumericalFields < double, 257, 257, 360> + ; +#pragma link C++ class o2::tpc::DistCorrInterpolator < double, 257, 257, 360> + ; +#pragma link C++ class o2::tpc::GridProperties < double, 257, 257, 360> + ; + +// 257*257*180 +#pragma link C++ class o2::tpc::RegularGrid3D < double, 257, 257, 180> + ; +#pragma link C++ class o2::tpc::DataContainer3D < double, 257, 257, 180> + ; +#pragma link C++ class o2::tpc::PoissonSolver < double, 257, 257, 180> + ; +#pragma link C++ class o2::tpc::SpaceCharge < double, 257, 257, 180> + ; +#pragma link C++ class o2::tpc::NumericalFields < double, 257, 257, 180> + ; +#pragma link C++ class o2::tpc::DistCorrInterpolator < double, 257, 257, 180> + ; +#pragma link C++ class o2::tpc::GridProperties < double, 257, 257, 180> + ; + +// 129*129*180 +#pragma link C++ class o2::tpc::RegularGrid3D < double, 129, 129, 180> + ; +#pragma link C++ class o2::tpc::DataContainer3D < double, 129, 129, 180> + ; +#pragma link C++ class o2::tpc::PoissonSolver < double, 129, 129, 180> + ; +#pragma link C++ class o2::tpc::SpaceCharge < double, 129, 129, 180> + ; +#pragma link C++ class o2::tpc::NumericalFields < double, 129, 129, 180> + ; +#pragma link C++ class o2::tpc::DistCorrInterpolator < double, 129, 129, 180> + ; +#pragma link C++ class o2::tpc::GridProperties < double, 129, 129, 180> + ; + +// 65*65*180 +#pragma link C++ class o2::tpc::RegularGrid3D < double, 65, 65, 180> + ; +#pragma link C++ class o2::tpc::DataContainer3D < double, 65, 65, 180> + ; +#pragma link C++ class o2::tpc::PoissonSolver < double, 65, 65, 180> + ; +#pragma link C++ class o2::tpc::SpaceCharge < double, 65, 65, 180> + ; +#pragma link C++ class o2::tpc::NumericalFields < double, 65, 65, 180> + ; +#pragma link C++ class o2::tpc::DistCorrInterpolator < double, 65, 65, 180> + ; +#pragma link C++ class o2::tpc::GridProperties < double, 65, 65, 180> + ; + +// 33*33*180 +#pragma link C++ class o2::tpc::RegularGrid3D < double, 33, 33, 180> + ; +#pragma link C++ class o2::tpc::DataContainer3D < double, 33, 33, 180> + ; +#pragma link C++ class o2::tpc::PoissonSolver < double, 33, 33, 180> + ; +#pragma link C++ class o2::tpc::SpaceCharge < double, 33, 33, 180> + ; +#pragma link C++ class o2::tpc::NumericalFields < double, 33, 33, 180> + ; +#pragma link C++ class o2::tpc::DistCorrInterpolator < double, 33, 33, 180> + ; +#pragma link C++ class o2::tpc::GridProperties < double, 33, 33, 180> + ; + +// 17*17*90 +#pragma link C++ class o2::tpc::RegularGrid3D < double, 17, 17, 90> + ; +#pragma link C++ class o2::tpc::DataContainer3D < double, 17, 17, 90> + ; +#pragma link C++ class o2::tpc::PoissonSolver < double, 17, 17, 90> + ; +#pragma link C++ class o2::tpc::SpaceCharge < double, 17, 17, 90> + ; +#pragma link C++ class o2::tpc::NumericalFields < double, 17, 17, 90> + ; +#pragma link C++ class o2::tpc::DistCorrInterpolator < double, 17, 17, 90> + ; +#pragma link C++ class o2::tpc::GridProperties < double, 17, 17, 90> + ; + +#endif diff --git a/Detectors/TPC/spacecharge/test/testO2TPCPoissonSolver.cxx b/Detectors/TPC/spacecharge/test/testO2TPCPoissonSolver.cxx new file mode 100644 index 0000000000000..61d615b252d99 --- /dev/null +++ b/Detectors/TPC/spacecharge/test/testO2TPCPoissonSolver.cxx @@ -0,0 +1,240 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file testPoissonSolver.cxx +/// \brief this task tests the poisson solver +/// +/// \author Matthias Kleiner + +#define BOOST_TEST_MODULE Test TPC O2TPCSpaceCharge3DCalc class +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK +#include +#include "TPCSpaceCharge/PoissonSolver.h" +#include "TPCSpaceCharge/SpaceChargeHelpers.h" + +namespace o2 +{ +namespace tpc +{ + +using DataT = double; // using float actually takes alot longer than double (doesnt converge when using float) +static constexpr DataT TOLERANCE = 3; // relative tolerance for 3D (maximum large error is at phi=90 since there the potential is 0!) +static constexpr DataT TOLERANCE2D = 8.5; // relative tolerance for 2D TODO check why the difference between numerical and analyticial is larger than for 3D! +static constexpr DataT ABSTOLERANCE = 0.01; // absolute tolerance is taken at small values near 0 +static constexpr int NR = 129; // grid in r +static constexpr int NZ = 129; // grid in z +static constexpr int NPHI = 180; // grid in phi + +/// Get phi vertex position for index in phi direction +/// \param indexPhi index in phi direction +template +DataT getPhiVertex(const size_t indexPhi, const o2::tpc::RegularGrid3D& grid) +{ + return grid.getZVertex(indexPhi); +} + +/// Get r vertex position for index in r direction +/// \param indexR index in r direction +template +DataT getRVertex(const size_t indexR, const o2::tpc::RegularGrid3D& grid) +{ + return grid.getYVertex(indexR); +} + +/// Get z vertex position for index in z direction +/// \param indexZ index in z direction +template +DataT getZVertex(const size_t indexZ, const o2::tpc::RegularGrid3D& grid) +{ + return grid.getXVertex(indexZ); +} + +template +void setChargeDensityFromFormula(const AnalyticalFields& formulas, const o2::tpc::RegularGrid3D& grid, o2::tpc::DataContainer3D& density) +{ + for (size_t iPhi = 0; iPhi < Nphi; ++iPhi) { + const DataT phi = getPhiVertex(iPhi, grid); + for (size_t iR = 0; iR < Nr; ++iR) { + const DataT radius = getRVertex(iR, grid); + for (size_t iZ = 0; iZ < Nz; ++iZ) { + const DataT z = getZVertex(iZ, grid); + density(iZ, iR, iPhi) = formulas.evalDensity(z, radius, phi); + } + } + } +} + +template +void setPotentialFromFormula(const AnalyticalFields& formulas, const o2::tpc::RegularGrid3D& grid, o2::tpc::DataContainer3D& potential) +{ + for (size_t iPhi = 0; iPhi < Nphi; ++iPhi) { + const DataT phi = getPhiVertex(iPhi, grid); + for (size_t iR = 0; iR < Nr; ++iR) { + const DataT radius = getRVertex(iR, grid); + for (size_t iZ = 0; iZ < Nz; ++iZ) { + const DataT z = getZVertex(iZ, grid); + potential(iZ, iR, iPhi) = formulas.evalPotential(z, radius, phi); + } + } + } +} + +template +void setPotentialBoundaryFromFormula(const AnalyticalFields& formulas, const o2::tpc::RegularGrid3D& grid, o2::tpc::DataContainer3D& potential) +{ + for (size_t iPhi = 0; iPhi < Nphi; ++iPhi) { + const DataT phi = getPhiVertex(iPhi, grid); + for (size_t iZ = 0; iZ < Nz; ++iZ) { + const DataT z = getZVertex(iZ, grid); + const size_t iR = 0; + const DataT radius = getRVertex(iR, grid); + potential(iZ, iR, iPhi) = formulas.evalPotential(z, radius, phi); + } + } + + for (size_t iPhi = 0; iPhi < Nphi; ++iPhi) { + const DataT phi = getPhiVertex(iPhi, grid); + for (size_t iZ = 0; iZ < Nz; ++iZ) { + const DataT z = getZVertex(iZ, grid); + const size_t iR = Nr - 1; + const DataT radius = getRVertex(iR, grid); + potential(iZ, iR, iPhi) = formulas.evalPotential(z, radius, phi); + } + } + + for (size_t iPhi = 0; iPhi < Nphi; ++iPhi) { + const DataT phi = getPhiVertex(iPhi, grid); + for (size_t iR = 0; iR < Nr; ++iR) { + const DataT radius = getRVertex(iR, grid); + const size_t iZ = 0; + const DataT z = getZVertex(iZ, grid); + potential(iZ, iR, iPhi) = formulas.evalPotential(z, radius, phi); + } + } + + for (size_t iPhi = 0; iPhi < Nphi; ++iPhi) { + const DataT phi = getPhiVertex(iPhi, grid); + for (size_t iR = 0; iR < Nr; ++iR) { + const DataT radius = getRVertex(iR, grid); + const size_t iZ = Nz - 1; + const DataT z = getZVertex(iZ, grid); + potential(iZ, iR, iPhi) = formulas.evalPotential(z, radius, phi); + } + } +} + +template +void testAlmostEqualArray(o2::tpc::DataContainer3D& analytical, o2::tpc::DataContainer3D& numerical) +{ + for (size_t iPhi = 0; iPhi < Nphi; ++iPhi) { + for (size_t iR = 0; iR < Nr; ++iR) { + for (size_t iZ = 0; iZ < Nz; ++iZ) { + if (std::fabs(analytical(iZ, iR, iPhi)) < ABSTOLERANCE) { + BOOST_CHECK_SMALL(numerical(iZ, iR, iPhi) - analytical(iZ, iR, iPhi), ABSTOLERANCE); + } else { + BOOST_CHECK_CLOSE(numerical(iZ, iR, iPhi), analytical(iZ, iR, iPhi), TOLERANCE); + } + } + } + } +} + +template +void testAlmostEqualArray2D(o2::tpc::DataContainer3D& analytical, o2::tpc::DataContainer3D& numerical) +{ + for (size_t iPhi = 0; iPhi < Nphi; ++iPhi) { + for (size_t iR = 0; iR < Nr; ++iR) { + for (size_t iZ = 0; iZ < Nz; ++iZ) { + if (std::fabs(analytical(iZ, iR, iPhi)) < ABSTOLERANCE) { + BOOST_CHECK_SMALL(numerical(iZ, iR, iPhi) - analytical(iZ, iR, iPhi), ABSTOLERANCE); + } else { + BOOST_CHECK_CLOSE(numerical(iZ, iR, iPhi), analytical(iZ, iR, iPhi), TOLERANCE2D); + } + } + } + } +} + +template +void poissonSolver3D() +{ + using GridProp = GridProperties; + const o2::tpc::RegularGrid3D grid3D{GridProp::ZMIN, GridProp::RMIN, GridProp::PHIMIN, GridProp::GRIDSPACINGZ, GridProp::GRIDSPACINGR, GridProp::GRIDSPACINGPHI}; + + using DataContainer = o2::tpc::DataContainer3D; + DataContainer potentialNumerical{}; + DataContainer potentialAnalytical{}; + DataContainer charge{}; + + const o2::tpc::AnalyticalFields analyticalFields; + // set the boudnary and charge for numerical poisson solver + setChargeDensityFromFormula(analyticalFields, grid3D, charge); + setPotentialBoundaryFromFormula(analyticalFields, grid3D, potentialNumerical); + + // set analytical potential + setPotentialFromFormula(analyticalFields, grid3D, potentialAnalytical); + + //calculate numerical potential + PoissonSolver poissonSolver(grid3D); + const int symmetry = 0; + poissonSolver.poissonSolver3D(potentialNumerical, charge, symmetry); + + // compare numerical with analytical solution of the potential + testAlmostEqualArray(potentialAnalytical, potentialNumerical); +} + +template +void poissonSolver2D() +{ + using GridProp = GridProperties; + const o2::tpc::RegularGrid3D grid3D{GridProp::ZMIN, GridProp::RMIN, GridProp::PHIMIN, GridProp::GRIDSPACINGZ, GridProp::GRIDSPACINGR, GridProp::GRIDSPACINGPHI}; + + using DataContainer = o2::tpc::DataContainer3D; + DataContainer potentialNumerical{}; + DataContainer potentialAnalytical{}; + DataContainer charge{}; + + // set the boudnary and charge for numerical poisson solver + const o2::tpc::AnalyticalFields analyticalFields; + setChargeDensityFromFormula(analyticalFields, grid3D, charge); + setPotentialBoundaryFromFormula(analyticalFields, grid3D, potentialNumerical); + + // set analytical potential + setPotentialFromFormula(analyticalFields, grid3D, potentialAnalytical); + + //calculate numerical potential + PoissonSolver poissonSolver(grid3D); + poissonSolver.poissonSolver2D(potentialNumerical, charge); + + // compare numerical with analytical solution of the potential + testAlmostEqualArray2D(potentialAnalytical, potentialNumerical); +} + +BOOST_AUTO_TEST_CASE(PoissonSolver3D_test) +{ + o2::tpc::MGParameters::isFull3D = true; //3D + poissonSolver3D(); +} + +BOOST_AUTO_TEST_CASE(PoissonSolver3D2D_test) +{ + o2::tpc::MGParameters::isFull3D = false; // 3D2D + poissonSolver3D(); +} + +BOOST_AUTO_TEST_CASE(PoissonSolver2D_test) +{ + const int Nphi = 1; + poissonSolver2D(); +} + +} // namespace tpc +} // namespace o2 diff --git a/GPU/CMakeLists.txt b/GPU/CMakeLists.txt index e2ba9741f3508..03b9b7d00427f 100644 --- a/GPU/CMakeLists.txt +++ b/GPU/CMakeLists.txt @@ -21,6 +21,3 @@ add_subdirectory(Common) add_subdirectory(Utils) add_subdirectory(TPCFastTransformation) add_subdirectory(GPUTracking) -if(NOT ALIGPU_BUILD_TYPE STREQUAL "Standalone") - add_subdirectory(TPCSpaceChargeBase) -endif() diff --git a/GPU/TPCFastTransformation/macro/generateTPCCorrectionNTuple.C b/GPU/TPCFastTransformation/macro/generateTPCCorrectionNTuple.C index 9dfd2a4d271fe..d052e03efe2e7 100644 --- a/GPU/TPCFastTransformation/macro/generateTPCCorrectionNTuple.C +++ b/GPU/TPCFastTransformation/macro/generateTPCCorrectionNTuple.C @@ -24,17 +24,22 @@ #include "TH3.h" #include "DataFormatsTPC/Defs.h" -#include "TPCSimulation/SpaceCharge.h" +#include "TPCSpaceCharge/SpaceCharge.h" #include "GPU/TPCFastTransform.h" #include "TPCReconstruction/TPCFastTransformHelperO2.h" #endif -o2::tpc::SpaceCharge* sc = 0; +// define number of bins in r, rphi, z for the lookup tables +// o2::tpc::SpaceCharge +// for valid values see the definitions in TPCSpacechargeLinkDef.h +using SC = o2::tpc::SpaceCharge; +SC* sc = nullptr; + using namespace o2::tpc; using namespace o2::gpu; -void generateTPCCorrectionNTuple() +void generateTPCCorrectionNTuple(const char* path = "InputSCDensityHistograms.root", const char* histoName = "inputSCDensity3D8") { // open file with space-charge density histograms // location: alien:///alice/cern.ch/user/e/ehellbar/TPCSpaceCharge/RUN3/ SCDensityHistograms/InputSCDensityHistograms.root @@ -43,35 +48,30 @@ void generateTPCCorrectionNTuple() // - nPileUpEvents = 0,2,4,6,8,10,12 // - number of events (in units of 1000) contributing to the ion density. 0 = 130000 events, 2 = 2000 events, 4 = 4000 events, etc. // - expected default map with 8000 pileup events: inputSCDensity3D8 - auto mFileSCDensity = std::unique_ptr(TFile::Open("InputSCDensityHistograms.root")); + auto mFileSCDensity = std::unique_ptr(TFile::Open(path)); if (!mFileSCDensity || !mFileSCDensity->IsOpen()) { std::cout << " input file does not exist!" << std::endl; return; } // get the histogram with the sc density - std::unique_ptr mHisSCDensity3D = std::unique_ptr((TH3*)mFileSCDensity->Get("inputSCDensity3D8")); + std::unique_ptr mHisSCDensity3D = std::unique_ptr((TH3*)mFileSCDensity->Get(histoName)); if (!mHisSCDensity3D) { - std::cout << "inputSCDensity3D8 histogramm does not exist!" << std::endl; + std::cout << Form("%s histogramm does not exist!", histoName) << std::endl; return; } - // define number of bins in r, rphi, z for the lookup tables - // nR annd nZ have to be 2^n + 1, nPhi is arbitrary - // expected default for desired precision: 129, 129, 144 (takes a lot of time, you can also us 65 in r and z) - int mNRRows = 129; - int mNZCols = 129; - int mNPhiSlices = 180; - // create space-charge object - sc = new o2::tpc::SpaceCharge(mNRRows, mNPhiSlices, mNZCols); - sc->setInitialSpaceChargeDensity((TH3*)mHisSCDensity3D.get()); + sc = new SC; + sc->setGlobalDistType(SC::GlobalDistType::None); + sc->fillChargeDensityFromHisto(*mHisSCDensity3D.get()); // select constant distortions (over time), realistic distortions changing in time not yet pushed to official code - sc->setSCDistortionType(o2::tpc::SpaceCharge::SCDistortionType::SCDistortionsConstant); + sc->setSCDistortionType(SC::SCDistortionType::SCDistortionsConstant); // gas parameters nor Ne-CO2-N2 90-10-5 sc->setOmegaTauT1T2(0.32, 1, 1); // start calculation of lookup tables (takes some time) - sc->calculateLookupTables(); + sc->calculateDistortionsCorrections(Side::A); + sc->calculateDistortionsCorrections(Side::C); // create TPC transformation to get the TPC geometry diff --git a/GPU/TPCSpaceChargeBase/AliTPC3DCylindricalInterpolator.cxx b/GPU/TPCSpaceChargeBase/AliTPC3DCylindricalInterpolator.cxx deleted file mode 100644 index 9fbfa42db6491..0000000000000 --- a/GPU/TPCSpaceChargeBase/AliTPC3DCylindricalInterpolator.cxx +++ /dev/null @@ -1,533 +0,0 @@ -// Copyright CERN and copyright holders of ALICE O2. This software is -// distributed under the terms of the GNU General Public License v3 (GPL -// Version 3), copied verbatim in the file "COPYING". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file AliTPC3DCylindricalInterpolator.cxx -/// \brief Interpolator for cylindrical coordinate -/// this class provides: cubic spline, quadratic and linear interpolation -/// -/// \author Rifki Sadikin , Indonesian Institute of Sciences -/// \date Jan 5, 2016 - -#include "TMath.h" -#include "AliTPC3DCylindricalInterpolator.h" - -/// \cond CLASSIMP -ClassImp(AliTPC3DCylindricalInterpolator); -/// \endcond - -/// constructor -/// -AliTPC3DCylindricalInterpolator::AliTPC3DCylindricalInterpolator() -{ - fOrder = 1; - fIsAllocatingLookUp = kFALSE; - fIsInitCubic = kFALSE; -} - -/// destructor -/// -AliTPC3DCylindricalInterpolator::~AliTPC3DCylindricalInterpolator() -{ - delete fValue; - delete fRList; - delete fPhiList; - delete fZList; - if (fIsInitCubic) { - delete fSecondDerZ; - } -} - -/// Get interpolation value on a point in a cylindrical volume -/// -/// \param r position r -/// \param phi position $\phi$ -/// \param z position z -/// -/// \return interpolation value -Double_t AliTPC3DCylindricalInterpolator::GetValue(Double_t r, Double_t phi, Double_t z) const -{ - return InterpolateCylindrical(r, z, phi); -} - -/// Get interpolation value on a point in a cylindrical volume -/// -/// \param r Double_t position r -/// \param phi Double_t position $\phi$ -/// \param z Double_t position z -/// -/// \return interpolation value -Double_t AliTPC3DCylindricalInterpolator::InterpolateCylindrical(Double_t r, Double_t z, Double_t phi) const -{ - Int_t iLow = 0, jLow = 0, m = 0; - Int_t kLow = 0; - Int_t index; - - // tri cubic points - Double_t saveArray[fOrder + 1]; - Double_t savedArray[fOrder + 1]; - Double_t zListM1[fOrder + 1]; - Double_t valueM1[fOrder + 1]; - - Bool_t neg = kFALSE; - - // check phi - while (phi < 0.0) { - phi = TMath::TwoPi() + phi; - } - while (phi > TMath::TwoPi()) { - phi = phi - TMath::TwoPi(); - } - - // search lowest index related to r,z and phi - Search(fNR, fRList, r, iLow); - Search(fNZ, fZList, z, jLow); - Search(fNPhi, fPhiList, phi, kLow); - - // order >= 3 - kLow -= (fOrder / 2); - iLow -= (fOrder / 2); - jLow -= (fOrder / 2); - - // check if out of range - if (iLow < 0) { - iLow = 0; - } - if (jLow < 0) { - jLow = 0; - } - if (kLow < 0) { - kLow = fNPhi + kLow; - } - // check if out of range - if (iLow + fOrder >= fNR - 1) { - iLow = fNR - 1 - fOrder; - } - if (jLow + fOrder >= fNZ - 1) { - jLow = fNZ - 1 - fOrder; - } - - // do for each - for (Int_t k = 0; k < fOrder + 1; k++) { - m = (kLow + k) % fNPhi; - // interpolate - for (Int_t i = iLow; i < iLow + fOrder + 1; i++) { - if (fOrder < 3) { - if (jLow >= 0) { - index = m * (fNZ * fNR) + i * (fNZ) + jLow; - saveArray[i - iLow] = Interpolate(&fZList[jLow], &fValue[index], z); - } else { - index = m * (fNZ * fNR) + i * (fNZ); - zListM1[0] = fZList[0] - (fZList[1] - fZList[0]); - zListM1[1] = fZList[0]; - zListM1[2] = fZList[1]; - valueM1[0] = fValue[index] - (fValue[index + 1] - fValue[index]); - valueM1[1] = fValue[index]; - valueM1[2] = fValue[index + 1]; - saveArray[i - iLow] = Interpolate(&zListM1[0], &valueM1[0], z); - } - - } else { - index = m * (fNZ * fNR) + i * (fNZ); - saveArray[i - iLow] = InterpolateCubicSpline(fZList, &fValue[index], &fSecondDerZ[index], fNZ, fNZ, fNZ, - z, 1); - } - } - savedArray[k] = Interpolate(&fRList[iLow], saveArray, r); - } - return (InterpolatePhi(&fPhiList[0], kLow, fNPhi, savedArray, phi)); -} - -/// Get interpolation for 1 dimension non cyclic -/// -/// \param xArray Double_t[] known position x -/// \param yArray Double_t[] known y = f(x) -/// \param x unknown position -/// -/// \return interpolation value f(x) -Double_t AliTPC3DCylindricalInterpolator::Interpolate(Double_t xArray[], Double_t yArray[], Double_t x) const -{ - Double_t y; - - // if cubic spline - if (fOrder > 2) { - Double_t y2Array[fOrder + 1]; - InitCubicSpline(xArray, yArray, fOrder + 1, y2Array, 1); - y = InterpolateCubicSpline(xArray, yArray, y2Array, fOrder + 1, fOrder + 1, fOrder + 1, x, 1); - } else if (fOrder == 2) { - // Quadratic Interpolation = 2 - y = (x - xArray[1]) * (x - xArray[2]) * yArray[0] / ((xArray[0] - xArray[1]) * (xArray[0] - xArray[2])); - y += (x - xArray[2]) * (x - xArray[0]) * yArray[1] / ((xArray[1] - xArray[2]) * (xArray[1] - xArray[0])); - y += (x - xArray[0]) * (x - xArray[1]) * yArray[2] / ((xArray[2] - xArray[0]) * (xArray[2] - xArray[1])); - } else { - // Linear Interpolation = 1 - y = yArray[0] + (yArray[1] - yArray[0]) * (x - xArray[0]) / (xArray[1] - xArray[0]); - } - return (y); -} - -/// Get interpolation for 1 dimension cyclic -/// -/// \param xArray Double_t[] known position x -/// \param yArray Double_t[] known y = f(x) -/// \param x unknown position -/// -/// \return interpolation value f(x) -Double_t AliTPC3DCylindricalInterpolator::InterpolatePhi( - Double_t xArray[], const Int_t iLow, const Int_t lenX, Double_t yArray[], Double_t x) const -{ - Int_t i0 = iLow; - Double_t xi0 = xArray[iLow]; - Int_t i1 = (iLow + 1) % lenX; - Double_t xi1 = xArray[i1]; - Int_t i2 = (iLow + 2) % lenX; - Double_t xi2 = xArray[i2]; - - if (fOrder <= 2) { - if (xi1 < xi0) { - xi1 = TMath::TwoPi() + xi1; - } - if (xi2 < xi1) { - xi2 = TMath::TwoPi() + xi2; - } - if (x < xi0) { - x = TMath::TwoPi() + x; - } - } - - Double_t y; - if (fOrder > 2) { - Double_t y2Array[fOrder + 1]; - Double_t xArrayTemp[fOrder + 1]; - Double_t dPhi = xArray[1] - xArray[0]; - // make list phi ascending order - for (Int_t i = 0; i < fOrder + 1; i++) { - xArrayTemp[i] = xArray[iLow] + (dPhi * i); - } - if (x < xArrayTemp[0]) { - x = TMath::TwoPi() + x; - } - if (x < xArrayTemp[0] || x > xArrayTemp[fOrder]) { - printf("x (%f) is outside of interpolation box (%f,%f)\n", x, xArrayTemp[0], xArrayTemp[fOrder]); - } - - InitCubicSpline(xArrayTemp, yArray, fOrder + 1, y2Array, 1); - y = InterpolateCubicSpline(xArrayTemp, yArray, y2Array, fOrder + 1, fOrder + 1, fOrder + 1, x, 1); - } else if (fOrder == 2) { // Quadratic Interpolation = 2 - y = (x - xi1) * (x - xi2) * yArray[0] / ((xi0 - xi1) * (xi0 - xi2)); - y += (x - xi2) * (x - xi0) * yArray[1] / ((xi1 - xi2) * (xi1 - xi0)); - y += (x - xi0) * (x - xi1) * yArray[2] / ((xi2 - xi0) * (xi2 - xi1)); - } else { // Li2near Interpolation = 1 - y = yArray[0] + (yArray[1] - yArray[0]) * (x - xi0) / (xi1 - xi0); - } - return (y); -} - -/// Solving cubic splines for system of splines -/// -/// \param xArray Double_t[] known position x -/// \param yArray Double_t[] known y = f(x) -/// \param n Int_t length of splines -/// \param y2Array Double_t[] calculated $d^2Y$ spline (output) -/// \param skip memory offset for xArray -/// -void AliTPC3DCylindricalInterpolator::InitCubicSpline(Double_t* xArray, Double_t* yArray, const Int_t n, Double_t* y2Array, - const Int_t skip) const -{ - Double_t u[n]; - Double_t sig, p, qn, un; - - y2Array[0] = 0.0; - u[0] = 0.0; //natural condition - - for (Int_t i = 1; i <= n - 2; i++) { - sig = (xArray[i] - xArray[i - 1]) / (xArray[i + 1] - xArray[i - 1]); - p = sig * y2Array[(i - 1) * skip] + 2.0; - y2Array[i * skip] = (sig - 1.0) / p; - u[i] = (yArray[(i + 1) * skip] - yArray[i * skip]) / (xArray[i + 1] - xArray[i]) - - (yArray[i * skip] - yArray[(i - 1) * skip]) / (xArray[i] - xArray[i - 1]); - u[i] = (6.0 * u[i] / (xArray[i + 1] - xArray[i - 1]) - sig * u[i - 1]) / p; - } - - qn = un = 0.0; - - y2Array[(n - 1) * skip] = (un - qn * u[n - 2]) / (qn * y2Array[(n - 2) * skip] + 1.0); - for (Int_t k = n - 2; k >= 0; k--) { - y2Array[k * skip] = y2Array[k * skip] * y2Array[(k + 1) * skip] + u[k]; - } -} - -/// Solving cubic splines for system of splines -/// -/// \param xArray Double_t[] known position x -/// \param yArray Double_t[] known y = f(x) -/// \param n Int_t length of splines -/// \param y2Array Double_t[] calculated $d^2Y$ spline (output) -/// \param skip memory offset for xArray -/// -void AliTPC3DCylindricalInterpolator::InitCubicSpline(Double_t* xArray, Double_t* yArray, const Int_t n, Double_t* y2Array, - const Int_t skip, Double_t yp0, Double_t ypn1) const -{ - Double_t u[n]; - Double_t sig, p, qn, un; - - y2Array[0] = 0.0; - u[0] = 0.0; //natural condition - - for (Int_t i = 1; i <= n - 2; i++) { - sig = (xArray[i] - xArray[i - 1]) / (xArray[i + 1] - xArray[i - 1]); - p = sig * y2Array[(i - 1) * skip] + 2.0; - y2Array[i * skip] = (sig - 1.0) / p; - u[i] = (yArray[(i + 1) * skip] - yArray[i * skip]) / (xArray[i + 1] - xArray[i]) - - (yArray[i * skip] - yArray[(i - 1) * skip]) / (xArray[i] - xArray[i - 1]); - u[i] = (6.0 * u[i] / (xArray[i + 1] - xArray[i - 1]) - sig * u[i - 1]) / p; - } - - qn = un = 0.0; - y2Array[(n - 1) * skip] = (un - qn * u[n - 2]) / (qn * y2Array[(n - 2) * skip] + 1.0); - for (Int_t k = n - 2; k >= 0; k--) { - y2Array[k * skip] = y2Array[k * skip] * y2Array[(k + 1) * skip] + u[k]; - } -} - -/// Interpolate initialized cubic spline -/// -/// \param xArray -/// \param yArray -/// \param y2Array -/// \param nxArray -/// \param nyArray -/// \param ny2Array -/// \param x -/// \param skip -/// \return -Double_t AliTPC3DCylindricalInterpolator::InterpolateCubicSpline(Double_t* xArray, Double_t* yArray, Double_t* y2Array, - const Int_t nxArray, const Int_t nyArray, - const Int_t ny2Array, Double_t x, Int_t skip) const -{ - Int_t klo, khi, k; - Float_t h, b, a; - klo = 0; - khi = nxArray - 1; - - while (khi - klo > 1) { - k = (khi + klo) >> 1; - if (xArray[k] > x) { - khi = k; - } else { - klo = k; - } - } - - h = xArray[khi] - xArray[klo]; - - if (TMath::Abs(h) < 1e-10) { - return 0.0; - } - - a = (xArray[khi] - x) / h; - b = (x - xArray[klo]) / h; - - Double_t y = a * yArray[klo] + b * yArray[khi] + - ((a * a * a - a) * y2Array[klo * skip] + (b * b * b - b) * y2Array[khi * skip]) * (h * h) / 6.0; - - return y; -} - -/// init cubic spline for all -/// -void AliTPC3DCylindricalInterpolator::InitCubicSpline() -{ - - Double_t yp0, ypn1; - if (fIsInitCubic != kTRUE) { - fSecondDerZ = new Double_t[fNR * fNZ * fNPhi]; - - // Init at Z direction - for (Int_t m = 0; m < fNPhi; m++) { - for (Int_t i = 0; i < fNR; i++) { - yp0 = (-(11.0 / 6.0) * fValue[(m * (fNZ * fNR) + i * fNZ)] + - (3.0 * fValue[(m * (fNZ * fNR) + i * fNZ) + 1]) - - (1.5 * fValue[(m * (fNZ * fNR) + i * fNZ) + 2]) + - ((1.0 / 3.0) * fValue[(m * (fNZ * fNR) + i * fNZ) + 4])) / - (fZList[1] - fZList[0]); - ypn1 = (-(11.0 / 6.0) * fValue[(m * (fNZ * fNR) + i * fNZ) + (fNZ - 1)] + - (3.0 * fValue[(m * (fNZ * fNR) + i * fNZ) + (fNZ - 2)]) - - (1.5 * fValue[(m * (fNZ * fNR) + i * fNZ) + (fNZ - 3)]) + - ((1.0 / 3.0) * fValue[(m * (fNZ * fNR) + i * fNZ) + (fNZ - 4)])) / - (fZList[0] - fZList[1]); - InitCubicSpline(fZList, &fValue[m * (fNZ * fNR) + i * fNZ], fNZ, - &fSecondDerZ[m * (fNZ * fNR) + i * fNZ], 1); - } - } - - fIsInitCubic = kTRUE; - } -} - -/// Search the nearest grid index position to a Point -/// -/// \param n -/// \param xArray -/// \param x -/// \param low -void AliTPC3DCylindricalInterpolator::Search(Int_t n, const Double_t xArray[], Double_t x, Int_t& low) const -{ - /// Search an ordered table by starting at the most recently used point - - Long_t middle, high; - Int_t ascend = 0, increment = 1; - - if (xArray[n - 1] > xArray[0]) { - ascend = 1; // Ascending ordered table if true - } - if (low < 0 || low > n - 1) { - low = -1; - high = n; - } else { // Ordered Search phase - if ((Int_t)(x > xArray[low]) == ascend) { - if (low == n - 1) { - return; - } - high = low + 1; - while ((Int_t)(x > xArray[high]) == ascend) { - low = high; - increment *= 2; - high = low + increment; - if (high > n - 1) { - high = n; - break; - } - } - } else { - if (low == 0) { - low = -1; - return; - } - high = low - 1; - while ((Int_t)(x < xArray[low]) == ascend) { - high = low; - increment *= 2; - if (increment >= high) { - low = -1; - break; - } else { - low = high - increment; - } - } - } - } - - while ((high - low) != 1) { // Binary Search Phase - middle = (high + low) / 2; - if ((Int_t)(x > xArray[middle]) == ascend) { - low = middle; - } else { - high = middle; - } - } - - if (x > xArray[n - 1]) { - low = n; - } - if (x < xArray[0]) { - low = -1; - } -} - -/// Set the value as interpolation point -/// -/// \param matricesVal TMatrixD** reference value for each point -void AliTPC3DCylindricalInterpolator::SetValue(TMatrixD** matricesVal) -{ - Int_t indexVal1D; - Int_t index1D; - if (!fIsAllocatingLookUp) { - fValue = new Double_t[fNPhi * fNR * fNZ]; - fIsAllocatingLookUp = kTRUE; - } - for (Int_t m = 0; m < fNPhi; m++) { - indexVal1D = m * fNR * fNZ; - TMatrixD* mat = matricesVal[m]; - for (Int_t i = 0; i < fNR; i++) { - index1D = indexVal1D + i * fNZ; - for (Int_t j = 0; j < fNZ; j++) { - fValue[index1D + j] = (*mat)(i, j); - } - } - } -} - -/// Set the value as interpolation point -/// -/// \param matricesVal TMatrixD** reference value for each point -void AliTPC3DCylindricalInterpolator::SetValue(TMatrixD** matricesVal, Int_t iZ) -{ - Int_t indexVal1D; - Int_t index1D; - if (!fIsAllocatingLookUp) { - fValue = new Double_t[fNPhi * fNR * fNZ]; - fIsAllocatingLookUp = kTRUE; - } - for (Int_t m = 0; m < fNPhi; m++) { - indexVal1D = m * fNR * fNZ; - TMatrixD* mat = matricesVal[m]; - for (Int_t i = 0; i < fNR; i++) { - index1D = indexVal1D + i * fNZ; - fValue[index1D + iZ] = (*mat)(i, iZ); - } - } -} - -/// set the position of R -/// -/// \param rList -void AliTPC3DCylindricalInterpolator::SetRList(Double_t* rList) -{ - fRList = new Double_t[fNR]; - for (Int_t i = 0; i < fNR; i++) { - fRList[i] = rList[i]; - } -} - -/// set the position of phi -/// -/// \param phiList -void AliTPC3DCylindricalInterpolator::SetPhiList(Double_t* phiList) -{ - fPhiList = new Double_t[fNPhi]; - for (Int_t i = 0; i < fNPhi; i++) { - fPhiList[i] = phiList[i]; - } -} - -/// Setting z position -/// -/// \param zList -void AliTPC3DCylindricalInterpolator::SetZList(Double_t* zList) -{ - fZList = new Double_t[fNZ]; - for (Int_t i = 0; i < fNZ; i++) { - fZList[i] = zList[i]; - } -} - -/// Setting values from 1D -/// -/// \param valueList -void AliTPC3DCylindricalInterpolator::SetValue(Double_t* valueList) { fValue = valueList; } - -/// Set number of total grid points -void AliTPC3DCylindricalInterpolator::SetNGridPoints() -{ - if (fNR == 0 || fNPhi == 0 || fNZ == 0) { - Error("AliTPC3DCylindricalInterpolator::SetNGridPoints", "Error in calculating total number of grid points! Either nR, nPhi or nZ are zero!"); - } - fNGridPoints = fNR * fNPhi * fNZ; -} \ No newline at end of file diff --git a/GPU/TPCSpaceChargeBase/AliTPC3DCylindricalInterpolator.h b/GPU/TPCSpaceChargeBase/AliTPC3DCylindricalInterpolator.h deleted file mode 100644 index 67e01e640dbd4..0000000000000 --- a/GPU/TPCSpaceChargeBase/AliTPC3DCylindricalInterpolator.h +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright CERN and copyright holders of ALICE O2. This software is -// distributed under the terms of the GNU General Public License v3 (GPL -// Version 3), copied verbatim in the file "COPYING". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file AliTPC3DCylindricalInterpolator.h -/// \brief Interpolator for cylindrical coordinate -/// this class provides: cubic spline, quadratic and linear interpolation -/// -/// \author Rifki Sadikin , Indonesian Institute of Sciences -/// \date Jan 5, 2016 - -#ifndef AliTPC3DCylindricalInterpolator_H -#define AliTPC3DCylindricalInterpolator_H - -#include - -class AliTPC3DCylindricalInterpolator -{ - public: - AliTPC3DCylindricalInterpolator(); - ~AliTPC3DCylindricalInterpolator(); - Double_t GetValue(Double_t r, Double_t phi, Double_t z) const; - void InitCubicSpline(); - void SetOrder(Int_t order) { fOrder = order; } - void SetNR(Int_t nR) { fNR = nR; } - void SetNPhi(Int_t nPhi) { fNPhi = nPhi; } - void SetNZ(Int_t nZ) { fNZ = nZ; } - void SetNGridPoints(); - void SetRList(Double_t* rList); - void SetPhiList(Double_t* phiList); - void SetZList(Double_t* zList); - void SetValue(Double_t* vList); - void SetValue(TMatrixD** vList); - void SetValue(TMatrixD** vList, Int_t iZ); - - Int_t GetNR() { return fNR; } - Int_t GetNPhi() { return fNPhi; } - Int_t GetNZ() { return fNZ; } - Int_t GetOrder() { return fOrder; } - - Double_t* GetSecondDerZ() { return fSecondDerZ; } - - private: - Int_t fOrder; ///< Order of interpolation, 1 - linear, 2 - quadratic, 3 >= - cubic, - Int_t fNR; ///< Grid size in direction of R - Int_t fNPhi; ///< Grid size in direction of Phi - Int_t fNZ; ///< Grid size in direction of Z - Int_t fNGridPoints; ///< Total number of grid points (needed for streamer) - - Double_t* fValue = nullptr; //[fNGridPoints] Description 3D for storing known values interpolation should be in size fNR*fNPhi*fNZ - Double_t* fRList = nullptr; //[fNR] coordinate in R (cm) (should be increasing) - Double_t* fPhiList = nullptr; //[fNPhi] coordinate in phiList (rad) (should be increasing) 0 <= < 2 pi (cyclic) - Double_t* fZList = nullptr; //[fNZ] coordinate in z list (cm) (should be increasing) - Double_t* fSecondDerZ = nullptr; //[fNGridPoints] store second derivative of cubic interpolation in z direction - - Bool_t fIsAllocatingLookUp; ///< is allocating memory - Bool_t fIsInitCubic; ///< is cubic second derivative already been initialized - - Double_t InterpolatePhi(Double_t xArray[], const Int_t iLow, const Int_t lenX, Double_t yArray[], Double_t x) const; - Double_t InterpolateCylindrical(Double_t r, Double_t z, Double_t phi) const; - Double_t Interpolate(Double_t xArray[], Double_t yArray[], Double_t x) const; - Double_t InterpolateCubicSpline(Double_t* xArray, Double_t* yArray, Double_t* y2Array, const Int_t nxArray, - const Int_t nyArray, const Int_t ny2Array, Double_t x, const Int_t skip) const; - void Search(Int_t n, const Double_t xArray[], Double_t x, Int_t& low) const; - void InitCubicSpline(Double_t* xArray, Double_t* yArray, const Int_t n, Double_t* y2Array, const Int_t skip) const; - void InitCubicSpline(Double_t* xArray, Double_t* yArray, const Int_t n, Double_t* y2Array, const Int_t skip, - Double_t yp0, Double_t ypn1) const; - - /// \cond CLASSIMP - ClassDefNV(AliTPC3DCylindricalInterpolator, 1); - /// \endcond -}; - -#endif diff --git a/GPU/TPCSpaceChargeBase/AliTPC3DCylindricalInterpolatorIrregular.cxx b/GPU/TPCSpaceChargeBase/AliTPC3DCylindricalInterpolatorIrregular.cxx deleted file mode 100644 index b4d395ceadbaa..0000000000000 --- a/GPU/TPCSpaceChargeBase/AliTPC3DCylindricalInterpolatorIrregular.cxx +++ /dev/null @@ -1,1514 +0,0 @@ -// Copyright CERN and copyright holders of ALICE O2. This software is -// distributed under the terms of the GNU General Public License v3 (GPL -// Version 3), copied verbatim in the file "COPYING". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file AliTPC3DCylindricalInterpolatorIrregular.cxx -/// \brief Irregular grid interpolator for cylindrical coordinate with r,phi,z different coordinates -/// RBF-based interpolation -/// -/// \author Rifki Sadikin , Indonesian Institute of Sciences -/// \date Jan 5, 2016 - -#include "TMath.h" -#include "TVector.h" -#include "TVectorD.h" -#include "TMatrix.h" -#include "TMatrixD.h" -#include "TDecompSVD.h" -#include "AliTPCPoissonSolver.h" -#include "AliTPC3DCylindricalInterpolatorIrregular.h" -#include - -/// \cond CLASSIMP3 -ClassImp(AliTPC3DCylindricalInterpolatorIrregular); -/// \endcond - -/// constructor -/// -/// \param nRRow -/// \param nZColumn -/// \param nPhiSlice -/// \param rStep -/// \param zStep -/// \param phiStep -/// \param type -AliTPC3DCylindricalInterpolatorIrregular::AliTPC3DCylindricalInterpolatorIrregular( - Int_t nRRow, Int_t nZColumn, Int_t nPhiSlice, Int_t rStep, Int_t zStep, Int_t phiStep, Int_t type) -{ - fOrder = 1; - fIsAllocatingLookUp = kFALSE; - fMinZIndex = 0; - fNR = nRRow; - fNZ = nZColumn; - fNPhi = nPhiSlice; - fNGridPoints = nRRow * nZColumn * nPhiSlice; - - fRBFWeightLookUp = new Int_t[nRRow * nZColumn * nPhiSlice]; - - Int_t nd = rStep * zStep * phiStep; - fStepR = rStep; - fStepZ = zStep; - fStepPhi = phiStep; - fNRBFpoints = nRRow * nZColumn * nPhiSlice * nd; - - fType = type; - fRBFWeight = new Double_t[nRRow * nZColumn * nPhiSlice * nd]; - for (Int_t i = 0; i < nRRow * nZColumn * nPhiSlice; i++) { - fRBFWeightLookUp[i] = 0; - } - - SetKernelType(kRBFInverseMultiQuadratic); -} - -/// constructor -/// -AliTPC3DCylindricalInterpolatorIrregular::AliTPC3DCylindricalInterpolatorIrregular() -{ - fOrder = 1; - fIsAllocatingLookUp = kFALSE; - - fMinZIndex = 0; -} - -/// destructor -/// -AliTPC3DCylindricalInterpolatorIrregular::~AliTPC3DCylindricalInterpolatorIrregular() -{ - - delete fValue; - delete fRList; - delete fPhiList; - delete fZList; - - if (fKDTreeIrregularPoints) { - delete[] fKDTreeIrregularPoints; - delete fKDTreeIrregularRoot; - } - delete[] fRBFWeightLookUp; - delete[] fRBFWeight; -} - -/// irregular grid interpolation with IDW (inverse distance weight) -/// -/// \param r -/// \param z -/// \param phi -/// \param rIndex -/// \param zIndex -/// \param phiIndex -/// \param rStep -/// \param phiStep -/// \param zStep -/// \return -Double_t - AliTPC3DCylindricalInterpolatorIrregular::Interpolate3DTableCylIDW( - Double_t r, Double_t z, Double_t phi, Int_t rIndex, Int_t zIndex, Int_t phiIndex, Int_t rStep, Int_t phiStep, - Int_t zStep) -{ - Double_t r0, z0, phi0, d; - Double_t MIN_DIST = 1e-3; - Double_t val = 0.0; - Int_t startPhi = phiIndex - phiStep / 2; - Int_t indexPhi; - Int_t startR = rIndex - rStep / 2; - Int_t startZ = zIndex - zStep / 2; - - if (startPhi < 0) { - startPhi = fNPhi + startPhi; - } - if (startR < 0) { - startR = 0; - } - if (startR + rStep >= fNR) { - startR = fNR - rStep; - } - - if (startZ < fMinZIndex) { - startZ = fMinZIndex; - } - if (startZ + zStep >= fNZ) { - startZ = fNZ - zStep; - } - - Int_t index; - Double_t sum_w = 0.0; - Double_t sum_d = 0.0; - Double_t shortest_d = 10000.0; - Int_t new_rIndex = 0; - Int_t new_zIndex = 0; - Int_t new_phiIndex = 0; - - for (Int_t iPhi = startPhi; iPhi < startPhi + phiStep; iPhi++) { - indexPhi = iPhi % fNPhi; - for (Int_t index_r = startR; index_r < startR + rStep; index_r++) { - for (Int_t index_z = startZ; index_z < startZ + zStep; index_z++) { - // check for the closest poInt_t - index = indexPhi * (fNZ * fNR) + index_r * fNZ + index_z; - - r0 = fRList[index]; - z0 = fZList[index]; - phi0 = fPhiList[index]; - - d = Distance(r0, phi0, z0, r, phi, z); - if (d < shortest_d) { - shortest_d = d; - new_rIndex = index_r; - new_phiIndex = indexPhi; - new_zIndex = index_z; - } - } - } - } - - phiStep = 3; - rStep = 3; - startPhi = new_phiIndex - phiStep / 2; - startR = new_rIndex - rStep / 2; - startZ = new_zIndex - zStep / 2; - - if (startPhi < 0) { - startPhi = fNPhi + startPhi; - } - if (startR < 0) { - startR = 0; - } - if (startR + rStep >= fNR) { - startR = fNR - rStep; - } - - if (startZ < fMinZIndex) { - startZ = fMinZIndex; - } - if (startZ + zStep >= fNZ) { - startZ = fNZ - zStep; - } - - for (Int_t iPhi = startPhi; iPhi < startPhi + phiStep; iPhi++) { - indexPhi = iPhi % fNPhi; - for (Int_t index_r = startR; index_r < startR + rStep; index_r++) { - for (Int_t index_z = startZ; index_z < startZ + zStep; index_z++) { - // check for the closest poInt_t - index = indexPhi * (fNZ * fNR) + index_r * fNZ + index_z; - - r0 = fRList[indexPhi * (fNR * fNZ) + index_r * fNZ + index_z]; - z0 = fZList[indexPhi * (fNR * fNZ) + index_r * fNZ + index_z]; - phi0 = fPhiList[indexPhi * (fNR * fNZ) + index_r * fNZ + index_z]; - d = Distance(r0, phi0, z0, r, phi, z); - if (d < MIN_DIST) { - return fValue[index]; - } - d = 1.0 / d; - sum_w += (fValue[index] * d * d * d * d); - sum_d += d * d * d * d; - } - } - } - return (sum_w / sum_d); -} - -/// distance in Cyl coordinate -/// -/// \param r0 -/// \param phi0 -/// \param z0 -/// \param r -/// \param phi -/// \param z -/// \return -Double_t - AliTPC3DCylindricalInterpolatorIrregular::Distance(Double_t r0, Double_t phi0, Double_t z0, Double_t r, Double_t phi, - Double_t z) -{ - if (phi < 0) { - phi = TMath::TwoPi() + phi; - } - if (phi > TMath::TwoPi()) { - phi = phi - TMath::TwoPi(); - } - - if (phi0 < 0) { - phi0 = TMath::TwoPi() + phi0; - } - if (phi0 > TMath::TwoPi()) { - phi0 = phi0 - TMath::TwoPi(); - } - - Double_t dPhi = phi - phi0; - if (dPhi > TMath::Pi()) { - dPhi = TMath::TwoPi() - dPhi; - } - if (dPhi < -TMath::Pi()) { - dPhi = TMath::TwoPi() + dPhi; - } - - Double_t ret = r * r + r0 * r0 - 2 * r0 * r * TMath::Cos(dPhi) + (z - z0) * (z - z0); - - return TMath::Sqrt(ret); -} - -/// main operation -/// interpolation by RBF -/// -/// \param r -/// \param z -/// \param phi -/// \param rIndex -/// \param zIndex -/// \param phiIndex -/// \param rStep -/// \param phiStep -/// \param zStep -/// \param radiusRBF0 -/// \return -Double_t - AliTPC3DCylindricalInterpolatorIrregular::Interpolate3DTableCylRBF( - Double_t r, Double_t z, Double_t phi, Int_t rIndex, Int_t zIndex, Int_t phiIndex, Int_t rStep, Int_t phiStep, - Int_t zStep, Double_t radiusRBF0) -{ - const Float_t gridSizeR = (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (fNR - 1); - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (fNZ - 1); - const Float_t gridSizePhi = TMath::TwoPi() / fNPhi; - Double_t r0, z0, phi0, d; - Double_t MIN_DIST = 1e-3; - Double_t val = 0.0; - Int_t startPhi = phiIndex - phiStep / 2; - Int_t indexPhi; - Int_t startR = rIndex - rStep / 2; - Int_t startZ = zIndex - zStep / 2; - - if (startPhi < 0) { - startPhi = fNPhi + startPhi; - } - if (startR < 0) { - startR = 0; - } - if (startR + rStep >= fNR) { - startR = fNR - rStep; - } - - if (startZ < fMinZIndex) { - startZ = fMinZIndex; - } - if (startZ + zStep >= fNZ) { - startZ = fNZ - zStep; - } - - Int_t index; - Double_t sum_w = 0.0; - Double_t sum_d = 0.0; - Double_t shortest_d = 10000.0; - Int_t new_rIndex = 0; - Int_t new_zIndex = 0; - Int_t new_phiIndex = 0; - - for (Int_t iPhi = startPhi; iPhi < startPhi + phiStep; iPhi++) { - indexPhi = iPhi % fNPhi; - for (Int_t index_r = startR; index_r < startR + rStep; index_r++) { - for (Int_t index_z = startZ; index_z < startZ + zStep; index_z++) { - // check for the closest poInt_t - index = indexPhi * (fNZ * fNR) + index_r * fNZ + index_z; - - r0 = fRList[index]; - z0 = fZList[index]; - phi0 = fPhiList[index]; - - d = Distance(r0, phi0, z0, r, phi, z); - if (d < shortest_d) { - shortest_d = d; - new_rIndex = index_r; - new_phiIndex = indexPhi; - new_zIndex = index_z; - } - } - } - } - - index = new_phiIndex * (fNZ * fNR) + new_rIndex * fNZ + new_zIndex; - phiStep = fStepPhi; - rStep = fStepR; - zStep = fStepZ; - startPhi = new_phiIndex - phiStep / 2; - - startR = new_rIndex - rStep / 2; - startZ = new_zIndex - zStep / 2; - - if (startPhi < 0) { - startPhi = fNPhi + startPhi; - } - - if (startR < 0) { - startR = 0; - } - if (startR + rStep >= fNR) { - startR = fNR - rStep; - } - - if (startZ < fMinZIndex) { - startZ = fMinZIndex; - } - if (startZ + zStep >= fNZ) { - startZ = fNZ - zStep; - } - - Double_t* w; - - //Int_t nd = (phiStep-1) + (rStep-1) + (zStep-1) + 1; - Int_t nd = phiStep * rStep * zStep; - - w = new Double_t[nd]; - - Float_t minTemp, minTemp2; - - radiusRBF0 = GetRadius0RBF(new_rIndex, new_phiIndex, new_zIndex); - - if (fType == 1) { - - for (Int_t i = 0; i < nd; i++) { - w[i] = 0.0; - } - GetRBFWeight(new_rIndex, new_zIndex, new_phiIndex, rStep, phiStep, zStep, radiusRBF0, 0, w); - val = InterpRBF(r, phi, z, startR, startPhi, startZ, rStep, phiStep, zStep, radiusRBF0, 0, w); - } else { - GetRBFWeightHalf(new_rIndex, new_zIndex, new_phiIndex, rStep, phiStep, zStep, radiusRBF0, 0, w); - val = InterpRBFHalf(r, phi, z, startR, startPhi, startZ, rStep, phiStep, zStep, radiusRBF0, 0, w); - } - delete[] w; - return val; -} - -/// Search nearest point at grid -/// \param n -/// \param xArray -/// \param offset -/// \param x -/// \param low -void AliTPC3DCylindricalInterpolatorIrregular::Search(Int_t n, Double_t* xArray, Int_t offset, Double_t x, Int_t& low) -{ - /// Search an ordered table by starting at the most recently used poInt_t - - Long_t middle, high; - Int_t ascend = 0, increment = 1; - - if (xArray[(n - 1) * offset] >= xArray[0 * offset]) { - ascend = 1; // Ascending ordered table if true - } - if (low < 0 || low > n - 1) { - low = -1; - high = n; - } else { // Ordered Search phase - if ((Int_t)(x >= xArray[low * offset]) == ascend) { - if (low == n - 1) { - return; - } - high = low + 1; - while ((Int_t)(x >= xArray[high * offset]) == ascend) { - low = high; - increment *= 2; - high = low + increment; - if (high > n - 1) { - high = n; - break; - } - } - } else { - if (low == 0) { - low = -1; - return; - } - high = low - 1; - while ((Int_t)(x < xArray[low * offset]) == ascend) { - high = low; - increment *= 2; - if (increment >= high) { - low = -1; - break; - } else { - low = high - increment; - } - } - } - } - - while ((high - low) != 1) { // Binary Search Phase - middle = (high + low) / 2; - if ((Int_t)(x >= xArray[middle * offset]) == ascend) { - low = middle; - } else { - high = middle; - } - } - - if (x > xArray[n - 1]) { - low = n; - } - if (x < xArray[0]) { - low = -1; - } -} - -/// get value, interpolation with RBF -/// -/// \param r -/// \param phi -/// \param z -/// \param rIndex -/// \param phiIndex -/// \param zIndex -/// \param rStep -/// \param phiStep -/// \param zStep -/// \return -Double_t AliTPC3DCylindricalInterpolatorIrregular::GetValue( - Double_t r, Double_t phi, Double_t z, Int_t rIndex, Int_t phiIndex, Int_t zIndex, Int_t rStep, Int_t phiStep, - Int_t zStep) -{ - - fMinZIndex = 0; - return Interpolate3DTableCylRBF(r, z, phi, rIndex, zIndex, phiIndex, rStep, phiStep, zStep, 0.0); -} - -/// get value -/// -/// \param r -/// \param phi -/// \param z -/// \param rIndex -/// \param phiIndex -/// \param zIndex -/// \param rStep -/// \param phiStep -/// \param zStep -/// \param minZColumnIndex -/// \return -Double_t AliTPC3DCylindricalInterpolatorIrregular::GetValue( - Double_t r, Double_t phi, Double_t z, Int_t rIndex, Int_t phiIndex, Int_t zIndex, Int_t rStep, Int_t phiStep, - Int_t zStep, Int_t minZColumnIndex) -{ - fMinZIndex = minZColumnIndex; - return Interpolate3DTableCylRBF(r, z, phi, rIndex, zIndex, phiIndex, rStep, phiStep, zStep, 0.0); -} - -// GetValue using searching at KDTree -Double_t AliTPC3DCylindricalInterpolatorIrregular::GetValue( - Double_t r, Double_t phi, Double_t z) -{ - - KDTreeNode n; - n.pR = &r; - n.pPhi = φ - n.pZ = &z; - KDTreeNode* nearest; - Double_t dist; - dist = 100000000.0; - Int_t startIndex = 0; // Z - Int_t dim = 3; // dimenstion - KDTreeNearest(fKDTreeIrregularRoot, &n, startIndex, dim, &nearest, &dist); - return Interpolate3DTableCylRBF(r, z, phi, nearest); -} -/// Set value and distorted point for irregular grid interpolation -/// -/// \param matrixRicesValue -/// \param matrixRicesRPoint -/// \param matrixRicesPhiPoint -/// \param matrixRicesZPoint -void AliTPC3DCylindricalInterpolatorIrregular::SetValue( - TMatrixD** matrixRicesValue, TMatrixD** matrixRicesRPoint, TMatrixD** matrixRicesPhiPoint, - TMatrixD** matrixRicesZPoint) -{ - Int_t indexInner; - Int_t index; - - if (!fIsAllocatingLookUp) { - fValue = new Double_t[fNPhi * fNR * fNZ]; - fRList = new Double_t[fNPhi * fNR * fNZ]; - fPhiList = new Double_t[fNPhi * fNR * fNZ]; - fZList = new Double_t[fNPhi * fNR * fNZ]; - fIsAllocatingLookUp = kTRUE; - } - - for (Int_t m = 0; m < fNPhi; m++) { - indexInner = m * fNR * fNZ; - TMatrixD* mat = matrixRicesValue[m]; - TMatrixD* matrixR = matrixRicesRPoint[m]; - TMatrixD* matrixPhi = matrixRicesPhiPoint[m]; - TMatrixD* matrixZ = matrixRicesZPoint[m]; - - for (Int_t i = 0; i < fNR; i++) { - index = indexInner + i * fNZ; - for (Int_t j = 0; j < fNZ; j++) { - fValue[index + j] = (*mat)(i, j); - - fRList[index + j] = (*matrixR)(i, j); - fPhiList[index + j] = (*matrixPhi)(i, j); - fZList[index + j] = (*matrixZ)(i, j); - } - } - } - // KD Tree is used for look-up a point to irregular grid to find - // closest neughboor point - InitKDTree(); - InitRBFWeight(); -} - -/// init RBF Weights assume value already been set -/// -void AliTPC3DCylindricalInterpolatorIrregular::InitRBFWeight() -{ - - Int_t indexInner; - Int_t rIndex; - Int_t index; - Int_t startR; - Int_t nd; - - const Double_t gridSizeR = (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (fNR - 1); - const Double_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (fNZ - 1); - const Double_t gridSizePhi = TMath::TwoPi() / fNPhi; - - Float_t r0; - Double_t radiusRBF0, minTemp, minTemp2; - - nd = fStepR * fStepPhi * fStepZ; - for (Int_t m = 0; m < fNPhi; m++) { - indexInner = m * fNR * fNZ; - for (Int_t i = 0; i < fNR; i++) { - rIndex = indexInner + i * fNZ; - - startR = i - fStepR / 2; - - if (startR < 0) { - startR = 0; - } - if (startR + fStepR >= fNR) { - startR = fNR - fStepR; - } - - for (Int_t j = 0; j < fNZ; j++) { - index = rIndex + j; - - radiusRBF0 = GetRadius0RBF(i, j, m); - - RBFWeight( - i, - j, - m, - fStepR, - fStepPhi, - fStepZ, - radiusRBF0, - fKernelType, - &fRBFWeight[index * nd]); - fRBFWeightLookUp[index] = 1; - } - } - } -} - -/// Set value and distorted Point -/// -/// \param matrixRicesValue -/// \param matrixRicesRPoint -/// \param matrixRicesPhiPoint -/// \param matrixRicesZPoint -/// \param jy -void AliTPC3DCylindricalInterpolatorIrregular::SetValue( - TMatrixD** matrixRicesValue, TMatrixD** matrixRicesRPoint, TMatrixD** matrixRicesPhiPoint, - TMatrixD** matrixRicesZPoint, - Int_t jy) -{ - Int_t indexInner; - Int_t index; - - if (!fIsAllocatingLookUp) { - fValue = new Double_t[fNPhi * fNR * fNZ]; - fRList = new Double_t[fNPhi * fNR * fNZ]; - fPhiList = new Double_t[fNPhi * fNR * fNZ]; - fZList = new Double_t[fNPhi * fNR * fNZ]; - - fIsAllocatingLookUp = kTRUE; - } - - for (Int_t m = 0; m < fNPhi; m++) { - indexInner = m * fNR * fNZ; - TMatrixD* mat = matrixRicesValue[m]; - TMatrixD* matrixR = matrixRicesRPoint[m]; - TMatrixD* matrixPhi = matrixRicesPhiPoint[m]; - TMatrixD* matrixZ = matrixRicesZPoint[m]; - - for (Int_t i = 0; i < fNR; i++) { - index = indexInner + i * fNZ; - fValue[index + jy] = (*mat)(i, jy); - fRList[index + jy] = (*matrixR)(i, jy); - fPhiList[index + jy] = (*matrixPhi)(i, jy); - fZList[index + jy] = (*matrixZ)(i, jy); - } - } -} - -/// calculate -/// RBFWeight for all points in the interpolation -/// -/// \param rIndex -/// \param zIndex -/// \param phiIndex -/// \param rStep -/// \param phiStep -/// \param zStep -/// \param radius0 -/// \param kernelType -/// \param w -void AliTPC3DCylindricalInterpolatorIrregular::RBFWeight( - Int_t rIndex, Int_t zIndex, Int_t phiIndex, Int_t rStep, Int_t phiStep, Int_t zStep, Double_t radius0, - Int_t kernelType, Double_t* w) -{ - - Double_t* a; - Int_t i; - Int_t j; - Int_t k; - Int_t ii; - Int_t jj; - Int_t kk; - - Int_t index0, index1; - Int_t indexCyl0, indexCyl1; - Double_t* r; - Double_t* v; - - Double_t phi0; - Double_t z0; - Double_t r0; - - Double_t phi1; - Double_t z1; - Double_t r1; - - Int_t nd = rStep * phiStep * zStep; - - a = new Double_t[nd * nd]; - r = new Double_t[nd]; - v = new Double_t[nd]; - - Int_t startPhi = phiIndex - phiStep / 2; - Int_t indexPhi; - Int_t indexPhi1; - - Int_t startR = rIndex - rStep / 2; - Int_t startZ = zIndex - zStep / 2; - - if (startPhi < 0) { - startPhi = fNPhi + startPhi; - } - - if (startR < 0) { - startR = 0; - } - if (startR + rStep >= fNR) { - startR = fNR - rStep; - } - - if (startZ < fMinZIndex) { - startZ = fMinZIndex; - } - if (startZ + zStep >= fNZ) { - startZ = fNZ - zStep; - } - - index0 = 0; - - for (i = startPhi; i < startPhi + phiStep; i++) { - indexPhi = i % fNPhi; - - for (j = startR; j < startR + rStep; j++) { - for (k = startZ; k < startZ + zStep; k++) { - indexCyl0 = indexPhi * fNR * fNZ + j * fNZ + k; - - r0 = fRList[indexCyl0]; - z0 = fZList[indexCyl0]; - phi0 = fPhiList[indexCyl0]; - - index1 = 0; - for (ii = startPhi; ii < startPhi + phiStep; ii++) { - indexPhi1 = ii % fNPhi; - for (jj = startR; jj < startR + rStep; jj++) { - for (kk = startZ; kk < startZ + zStep; kk++) { - indexCyl1 = indexPhi1 * fNR * fNZ + jj * fNZ + kk; - r1 = fRList[indexCyl1]; - z1 = fZList[indexCyl1]; - phi1 = fPhiList[indexCyl1]; - r[index1] = Distance(r0, phi0, z0, r1, phi1, z1); - - index1++; - } - } - } - - Phi(nd, r, radius0, v); - - index1 = 0; - for (ii = startPhi; ii < startPhi + phiStep; ii++) { - indexPhi1 = ii % fNPhi; - for (jj = startR; jj < startR + rStep; jj++) { - for (kk = startZ; kk < startZ + zStep; kk++) { - a[index0 * nd + index1] = v[index1]; - index1++; - } - } - } - w[index0] = fValue[indexCyl0]; - index0++; - } - } - } - - TMatrixD mat_a; - mat_a.Use(nd, nd, a); - TVectorD vec_w; - vec_w.Use(nd, w); - TDecompSVD svd(mat_a); - - svd.Solve(vec_w); - - delete[] a; - delete[] r; - delete[] v; -} - -/// rbf1 -/// \param n -/// \param r -/// \param r0 -/// \param v -void AliTPC3DCylindricalInterpolatorIrregular::rbf1(Int_t n, Double_t r[], Double_t r0, Double_t v[]) -{ - Int_t i; - - for (i = 0; i < n; i++) { - v[i] = sqrt(1 + (r[i] * r[i] + r0 * r0)); - } - return; -} - -/// rbf2 -/// \param n -/// \param r -/// \param r0 -/// \param v - -void AliTPC3DCylindricalInterpolatorIrregular::rbf2(Int_t n, Double_t r[], Double_t r0, Double_t v[]) -{ - Int_t i; - - for (i = 0; i < n; i++) { - v[i] = 1.0 / sqrt(1 + (r[i] * r[i] + r0 * r0)); - } - return; -} - -/// rbf3 -/// \param n -/// \param r -/// \param r0 -/// \param v -void AliTPC3DCylindricalInterpolatorIrregular::rbf3(Int_t n, Double_t r[], Double_t r0, Double_t v[]) -{ - Int_t i; - - for (i = 0; i < n; i++) { - if (r[i] <= 0.0) { - v[i] = 0.0; - } else { - v[i] = r[i] * r[i] * log(r[i] / r0); - } - } - return; -} - -/// rbf4 -/// \param n -/// \param r -/// \param r0 -/// \param v -void AliTPC3DCylindricalInterpolatorIrregular::rbf4(Int_t n, Double_t r[], Double_t r0, Double_t v[]) -{ - Int_t i; - - for (i = 0; i < n; i++) { - v[i] = TMath::Exp(-0.5 * r[i] * r[i] / (r0 * r0)); - } - return; -} - -// RBF based interpolation -// return interpolated value -/// -/// \param r -/// \param phi -/// \param z -/// \param startR -/// \param startPhi -/// \param startZ -/// \param rStep -/// \param phiStep -/// \param zStep -/// \param radius0 -/// \param kernelType -/// \param weight -/// \return -Double_t AliTPC3DCylindricalInterpolatorIrregular::InterpRBF( - Double_t r, Double_t phi, Double_t z, Int_t startR, Int_t startPhi, Int_t startZ, Int_t rStep, Int_t phiStep, - Int_t zStep, Double_t radius0, Int_t kernelType, Double_t* weight) -{ - Double_t interpVal = 0.0; - Double_t r0, z0, phi0; - Double_t* dList; - Double_t* v; - - Int_t indexCyl0, index0, indexPhi; - - Int_t nd = rStep * phiStep * zStep; - - dList = new Double_t[nd]; - v = new Double_t[nd]; - - index0 = 0; - for (Int_t i = startPhi; i < startPhi + phiStep; i++) { - indexPhi = i % fNPhi; - - for (Int_t j = startR; j < startR + rStep; j++) { - for (Int_t k = startZ; k < startZ + zStep; k++) { - - indexCyl0 = indexPhi * fNR * fNZ + j * fNZ + k; - - r0 = fRList[indexCyl0]; - z0 = fZList[indexCyl0]; - phi0 = fPhiList[indexCyl0]; - - dList[index0] = Distance(r, phi, z, r0, phi0, z0); - index0++; - } - } - } - - Phi(nd, dList, radius0, v); - - TVectorD vec_v; - vec_v.Use(nd, v); - - TVectorD vec_w; - vec_w.Use(nd, weight); - - interpVal = vec_v * vec_w; - delete[] v; - delete[] dList; - return interpVal; -} - -// calculate -// RBFWeight for all points in the interpolation -/// -/// \param rIndex -/// \param zIndex -/// \param phiIndex -/// \param rStep -/// \param phiStep -/// \param zStep -/// \param radius0 -/// \param kernelType -/// \param w -void AliTPC3DCylindricalInterpolatorIrregular::GetRBFWeight( - Int_t rIndex, Int_t zIndex, Int_t phiIndex, Int_t rStep, Int_t phiStep, Int_t zStep, Double_t radius0, - Int_t kernelType, Double_t* w) -{ - - Int_t index = phiIndex * fNR * fNZ + rIndex * fNZ + zIndex; - if (fRBFWeightLookUp[index] == 0) { - RBFWeight(rIndex, zIndex, phiIndex, rStep, phiStep, zStep, radius0, kernelType, w); - - fRBFWeightLookUp[index] = 1; - Int_t nd = rStep * zStep * phiStep; - - for (Int_t i = 0; i < nd; i++) { - fRBFWeight[index * nd + i] = w[i]; - } - } else { - - Int_t ndw = rStep * zStep * phiStep; - - Int_t nd = fStepR * fStepZ * fStepPhi; - Int_t indexWeight = phiIndex * fNR * fNZ * nd + rIndex * fNZ * nd + zIndex * nd; - - for (Int_t i = 0; i < nd; i++) { - w[i] = fRBFWeight[indexWeight + i]; - } - } -} - -// calculate -// RBFWeight for all points in the interpolation -/// -/// \param rIndex -/// \param zIndex -/// \param phiIndex -/// \param rStep -/// \param phiStep -/// \param zStep -/// \param radius0 -/// \param kernelType -/// \param w -void AliTPC3DCylindricalInterpolatorIrregular::GetRBFWeightHalf( - Int_t rIndex, Int_t zIndex, Int_t phiIndex, Int_t rStep, Int_t phiStep, Int_t zStep, Double_t radius0, - Int_t kernelType, Double_t* w) -{ - - Int_t index = phiIndex * fNR * fNZ + rIndex * fNZ + zIndex; - - if (fRBFWeightLookUp[index] == 0) { - RBFWeightHalf(rIndex, zIndex, phiIndex, rStep, phiStep, zStep, radius0, kernelType, w); - - if ((rStep == fStepR) && (zStep == fStepZ) && (phiStep == fStepPhi) && (zIndex > fMinZIndex + fStepZ)) { - fRBFWeightLookUp[index] = 1; - // copy to lookup - Int_t nd = rStep + zStep + phiStep - 2; - - for (Int_t i = 0; i < nd; i++) { - fRBFWeight[index * nd + i] = w[i]; - } - } - } else { - - //Int_t ndw = rStep*zStep*phiStep; - Int_t nd = rStep + zStep + phiStep - 2; - Int_t indexWeight = phiIndex * fNR * fNZ * nd + rIndex * fNZ * nd + zIndex * nd; - - for (Int_t i = 0; i < nd; i++) { - w[i] = fRBFWeight[indexWeight + i]; - } - } -} - -// calculate -// RBFWeight for all points in the interpolation -// half cubes (not included -/// -/// \param rIndex -/// \param zIndex -/// \param phiIndex -/// \param rStep -/// \param phiStep -/// \param zStep -/// \param radius0 -/// \param kernelType -/// \param w -void AliTPC3DCylindricalInterpolatorIrregular::RBFWeightHalf( - Int_t rIndex, Int_t zIndex, Int_t phiIndex, Int_t rStep, Int_t phiStep, Int_t zStep, Double_t radius0, - Int_t kernelType, Double_t* w) -{ - Double_t* a; - Int_t i; - Int_t j; - Int_t k; - Int_t ii; - Int_t jj; - Int_t kk; - - Int_t index0, index1; - Int_t indexCyl0, indexCyl1; - Double_t* r; - Double_t* v; - - Double_t phi0; - Double_t z0; - Double_t r0; - - Double_t phi1; - Double_t z1; - Double_t r1; - - Int_t nd = (rStep - 1) + (phiStep - 1) + (zStep - 1) + 1; - - a = new Double_t[nd * nd]; - r = new Double_t[nd]; - v = new Double_t[nd]; - - Int_t startPhi = phiIndex - phiStep / 2; - Int_t indexPhi; - Int_t indexPhi1; - - Int_t startR = rIndex - rStep / 2; - Int_t startZ = zIndex - zStep / 2; - - if (startPhi < 0) { - startPhi = fNPhi + startPhi; - } - - if (startR < 0) { - startR = 0; - } - if (startR + rStep >= fNR) { - startR = fNR - rStep; - } - - if (startZ < fMinZIndex) { - startZ = fMinZIndex; - } - if (startZ + zStep >= fNZ) { - startZ = fNZ - zStep; - } - - index0 = 0; - - for (i = startPhi; i < startPhi + phiStep; i++) { - indexPhi = i % fNPhi; - - for (j = startR; j < startR + rStep; j++) { - for (k = startZ; k < startZ + zStep; k++) { - - if ( - (i == (startPhi + phiStep / 2) && j == (startR + rStep / 2)) || - (i == (startPhi + phiStep / 2) && k == (startZ + zStep / 2)) || - (j == (startR + rStep / 2) && k == (startZ + zStep / 2))) { - indexCyl0 = indexPhi * fNR * fNZ + j * fNZ + k; - - r0 = fRList[indexCyl0]; - z0 = fZList[indexCyl0]; - phi0 = fPhiList[indexCyl0]; - - index1 = 0; - for (ii = startPhi; ii < startPhi + phiStep; ii++) { - indexPhi1 = ii % fNPhi; - for (jj = startR; jj < startR + rStep; jj++) { - for (kk = startZ; kk < startZ + zStep; kk++) { - if ( - (ii == (startPhi + phiStep / 2) && jj == (startR + rStep / 2)) || - (ii == (startPhi + phiStep / 2) && kk == (startZ + zStep / 2)) || - (jj == (startR + rStep / 2) && kk == (startZ + zStep / 2))) { - - indexCyl1 = indexPhi1 * fNR * fNZ + jj * fNZ + kk; - r1 = fRList[indexCyl1]; - z1 = fZList[indexCyl1]; - phi1 = fPhiList[indexCyl1]; - - r[index1] = Distance(r0, phi0, z0, r1, phi1, z1); - index1++; - } - } - } - } - - Phi(nd, r, radius0, v); - - index1 = 0; - for (ii = startPhi; ii < startPhi + phiStep; ii++) { - indexPhi1 = ii % fNPhi; - for (jj = startR; jj < startR + rStep; jj++) { - for (kk = startZ; kk < startZ + zStep; kk++) { - if ( - (ii == (startPhi + phiStep / 2) && jj == (startR + rStep / 2)) || - (ii == (startPhi + phiStep / 2) && kk == (startZ + zStep / 2)) || - (jj == (startR + rStep / 2) && kk == (startZ + zStep / 2))) { - a[index0 * nd + index1] = v[index1]; - index1++; - } - } - } - } - - w[index0] = fValue[indexCyl0]; - index0++; - } - } - } - } - - TMatrixD mat_a; - mat_a.Use(nd, nd, a); - TVectorD vec_w; - - vec_w.Use(nd, w); - TDecompSVD svd(mat_a); - - svd.Solve(vec_w); - - delete[] a; - delete[] r; - delete[] v; -} - -// RBF based interpolation -// return interpolated value -// half points -/// -/// \param r -/// \param phi -/// \param z -/// \param startR -/// \param startPhi -/// \param startZ -/// \param rStep -/// \param phiStep -/// \param zStep -/// \param radius0 -/// \param kernelType -/// \param weight -/// \return -Double_t AliTPC3DCylindricalInterpolatorIrregular::InterpRBFHalf( - Double_t r, Double_t phi, Double_t z, Int_t startR, Int_t startPhi, Int_t startZ, Int_t rStep, Int_t phiStep, - Int_t zStep, Double_t radius0, Int_t kernelType, Double_t* weight) -{ - Double_t interpVal = 0.0; - Double_t r0, z0, phi0; - Double_t* dList; - Double_t* v; - - Int_t indexCyl0, index0, indexPhi; - - // Int_t nd = rStep * phiStep * zStep; - Int_t nd = (rStep - 1) + (phiStep - 1) + (zStep - 1) + 1; - - dList = new Double_t[nd]; - v = new Double_t[nd]; - - index0 = 0; - for (Int_t i = startPhi; i < startPhi + phiStep; i++) { - indexPhi = i % fNPhi; - - for (Int_t j = startR; j < startR + rStep; j++) { - for (Int_t k = startZ; k < startZ + zStep; k++) { - if ( - (i == (startPhi + phiStep / 2) && j == (startR + rStep / 2)) || - (i == (startPhi + phiStep / 2) && k == (startZ + zStep / 2)) || - (j == (startR + rStep / 2) && k == (startZ + zStep / 2))) { - - indexCyl0 = indexPhi * fNR * fNZ + j * fNZ + k; - - r0 = fRList[indexCyl0]; - z0 = fZList[indexCyl0]; - phi0 = fPhiList[indexCyl0]; - - dList[index0] = Distance(r, phi, z, r0, phi0, z0); - index0++; - } - } - } - } - - Phi(nd, dList, radius0, v); - - TVectorD vec_v; - vec_v.Use(nd, v); - - TVectorD vec_w; - vec_w.Use(nd, weight); - - interpVal = vec_v * vec_w; - delete[] v; - delete[] dList; - return interpVal; -} - -// set Radius0 -/// -/// \param n -/// \param r -/// \param r0 -/// \param v -void AliTPC3DCylindricalInterpolatorIrregular::Phi(Int_t n, Double_t r[], Double_t r0, Double_t v[]) -{ - - switch (fKernelType) { - case kRBFMultiQuadratic: - rbf1(n, r, r0, v); - break; - case kRBFInverseMultiQuadratic: - rbf2(n, r, r0, v); - break; - case kRBFThinPlateSpline: - rbf3(n, r, r0, v); - break; - case kRBFGaussian: - rbf4(n, r, r0, v); - break; - - default: - rbf1(n, r, r0, v); - break; - } -} - -// -Double_t - AliTPC3DCylindricalInterpolatorIrregular::GetRadius0RBF(const Int_t rIndex, const Int_t phiIndex, const Int_t zIndex) -{ - const Float_t gridSizeR = (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (fNR - 1); - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (fNZ - 1); - const Float_t gridSizePhi = TMath::TwoPi() / fNPhi; - Int_t startPhi = phiIndex - fStepPhi / 2; - - Int_t startR = rIndex - fStepR / 2; - Int_t startZ = zIndex - fStepZ / 2; - - if (startPhi < 0) { - startPhi = fNPhi + startPhi; - } - - if (startR < 0) { - startR = 0; - } - if (startR + fStepR >= fNR) { - startR = fNR - fStepR; - } - - if (startZ < 0) { - startZ = 0; - } - if (startZ + fStepZ >= fNZ) { - startZ = fNZ - fStepZ; - } - - Double_t r0 = AliTPCPoissonSolver::fgkIFCRadius + (startR * gridSizeR); - Double_t phi0 = startPhi * gridSizePhi; - Double_t z0 = startZ * gridSizeZ; - - Double_t r1 = AliTPCPoissonSolver::fgkIFCRadius + (startR * gridSizeR); - Double_t phi1 = (startPhi + 1) * gridSizePhi; - Double_t z1 = (startZ + 1) * gridSizeZ; - - if (fKernelType == kRBFThinPlateSpline) { - r0 = AliTPCPoissonSolver::fgkIFCRadius + ((startR - 1) * gridSizeR); - } else { - r0 = AliTPCPoissonSolver::fgkIFCRadius + (startR * gridSizeR); - } - - return Distance(r0, 0.0, 0.0, r0 + gridSizeR, gridSizePhi, gridSizeR); -} - -// make kdtree for irregular look-up -void AliTPC3DCylindricalInterpolatorIrregular::InitKDTree() -{ - Int_t count = fNR * fNZ * fNPhi; - - fKDTreeIrregularPoints = new KDTreeNode[count]; - - for (Int_t i = 0; i < count; i++) { - fKDTreeIrregularPoints[i].pR = &fRList[i]; - fKDTreeIrregularPoints[i].pZ = &fZList[i]; - fKDTreeIrregularPoints[i].pPhi = &fPhiList[i]; - fKDTreeIrregularPoints[i].index = i; - } - - fKDTreeIrregularRoot = MakeKDTree(fKDTreeIrregularPoints, count, 0, 3); -} - -// create KDTree -AliTPC3DCylindricalInterpolatorIrregular::KDTreeNode* AliTPC3DCylindricalInterpolatorIrregular::MakeKDTree(KDTreeNode* t, Int_t count, Int_t index, Int_t dim) -{ - KDTreeNode* n; - - if (!count) { - return nullptr; - } - if ((n = FindMedian(t, t + count, index))) { - index = (index + 1) % dim; - n->left = MakeKDTree(t, (n - t), index, dim); - n->right = MakeKDTree(n + 1, (t + count) - (n + 1), index, dim); - } - return n; -} - -// find median -AliTPC3DCylindricalInterpolatorIrregular::KDTreeNode* AliTPC3DCylindricalInterpolatorIrregular::FindMedian(KDTreeNode* start, KDTreeNode* end, Int_t index) -{ - if (end <= start) { - return nullptr; - } - if (end == start + 1) { - return start; - } - - KDTreeNode *p, *store, *md = start + (end - start) / 2; - Double_t pivot; - - while (1) { - if (index == 0) { - pivot = *(md->pZ); - } else if (index == 1) { - pivot = *(md->pR); - } else { - pivot = *(md->pPhi); - } - - Swap(md, end - 1); - - for (store = p = start; p < end; p++) { - - if (((index == 0) && (*(p->pZ) < pivot)) || - ((index == 1) && (*(p->pR) < pivot)) || - ((index == 2) && (*(p->pPhi) < pivot))) - - { - if (p != store) { - Swap(p, store); - } - store++; - } - } - Swap(store, end - 1); - - if ((index == 0) && (*(store->pZ) == *(md->pZ))) { - return md; - } - if ((index == 1) && (*(store->pR) == *(md->pR))) { - return md; - } - if ((index == 2) && (*(store->pPhi) == *(md->pPhi))) { - return md; - } - - // if (md->index == store->index) return md; - - if (store > md) { - end = store; - } else { - start = store; - } - } -} - -//swap -void AliTPC3DCylindricalInterpolatorIrregular::Swap(KDTreeNode* x, KDTreeNode* y) -{ - KDTreeNode* tmp = new KDTreeNode; - tmp->pR = x->pR; - tmp->pZ = x->pZ; - tmp->pPhi = x->pPhi; - tmp->index = x->index; - - x->pR = y->pR; - x->pZ = y->pZ; - x->pPhi = y->pPhi; - x->index = y->index; - - y->pR = tmp->pR; - y->pZ = tmp->pZ; - y->pPhi = tmp->pPhi; - y->index = tmp->index; - - delete tmp; -} - -// look for nearest point -void AliTPC3DCylindricalInterpolatorIrregular::KDTreeNearest(KDTreeNode* root, KDTreeNode* nd, Int_t index, Int_t dim, - KDTreeNode** best, Double_t* best_dist) -{ - Double_t d, dx2, dx; - - if (!root) { - return; - } - d = Distance(*(root->pR), *(root->pPhi), *(root->pZ), *(nd->pR), *(nd->pPhi), *(nd->pZ)); - if (index == 0) { - dx = *(root->pZ) - *(nd->pZ); - dx2 = Distance(*(nd->pR), *(nd->pPhi), *(root->pZ), *(nd->pR), *(nd->pPhi), *(nd->pZ)); - } else if (index == 1) { - dx = *(root->pR) - *(nd->pR); - dx2 = Distance(*(root->pR), *(nd->pPhi), *(nd->pZ), *(nd->pR), *(nd->pPhi), *(nd->pZ)); - } else { - dx = *(root->pPhi) - *(nd->pPhi); - dx2 = Distance(*(nd->pR), *(root->pPhi), *(nd->pZ), *(nd->pR), *(nd->pPhi), *(nd->pZ)); - } - - if (!*best || (d < *best_dist)) { - *best_dist = d; - *best = root; - } - - if (!*best_dist) { - return; - } - - if (++index >= dim) { - index = 0; - } - - KDTreeNearest(dx > 0 ? root->left : root->right, nd, index, dim, best, best_dist); - if (dx2 >= *best_dist) { - return; - } - KDTreeNearest(dx > 0 ? root->right : root->left, nd, index, dim, best, best_dist); -} - -// interpolate on the nearest neighbor of irregular grid -Double_t - AliTPC3DCylindricalInterpolatorIrregular::Interpolate3DTableCylRBF( - Double_t r, Double_t z, Double_t phi, KDTreeNode* nearestNode) -{ - Double_t val = 0.0; - Int_t startPhi, startR, startZ; - Int_t phiIndex, rIndex, zIndex; - - phiIndex = nearestNode->index / (fNR * fNZ); - rIndex = (nearestNode->index - (phiIndex * (fNR * fNZ))) / fNZ; - zIndex = nearestNode->index - (phiIndex * (fNR * fNZ) + rIndex * fNZ); - - startPhi = phiIndex - fStepPhi / 2; - startR = rIndex - fStepR / 2; - startZ = zIndex - fStepZ / 2; - - if (startPhi < 0) { - startPhi = fNPhi + startPhi; - } - - if (startR < 0) { - startR = 0; - } - if (startR + fStepR >= fNR) { - startR = fNR - fStepR; - } - - if (startZ < 0) { - startZ = 0; - } - if (startZ + fStepZ >= fNZ) { - startZ = fNZ - fStepZ; - } - Int_t indexPhi; - - Int_t index; - Double_t r0, z0, phi0; - - Int_t rStep = fStepR; - Int_t zStep = fStepZ; - Int_t phiStep = fStepPhi; - - Double_t* w; - - //Int_t nd = (phiStep-1) + (rStep-1) + (zStep-1) + 1; - Int_t nd = fStepPhi * fStepR * fStepZ; - - w = new Double_t[nd]; - - Float_t minTemp, minTemp2; - - Double_t radiusRBF0 = GetRadius0RBF(rIndex, phiIndex, zIndex); - - if (fType == 1) { - - for (Int_t i = 0; i < nd; i++) { - w[i] = 0.0; - } - GetRBFWeight(rIndex, zIndex, phiIndex, rStep, phiStep, zStep, radiusRBF0, 0, w); - val = InterpRBF(r, phi, z, startR, startPhi, startZ, rStep, phiStep, zStep, radiusRBF0, 0, w); - } else { - GetRBFWeightHalf(rIndex, zIndex, phiIndex, rStep, phiStep, zStep, radiusRBF0, 0, w); - val = InterpRBFHalf(r, phi, z, startR, startPhi, startZ, rStep, phiStep, zStep, radiusRBF0, 0, w); - } - delete[] w; - return val; -} diff --git a/GPU/TPCSpaceChargeBase/AliTPC3DCylindricalInterpolatorIrregular.h b/GPU/TPCSpaceChargeBase/AliTPC3DCylindricalInterpolatorIrregular.h deleted file mode 100644 index 952114b174cd2..0000000000000 --- a/GPU/TPCSpaceChargeBase/AliTPC3DCylindricalInterpolatorIrregular.h +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright CERN and copyright holders of ALICE O2. This software is -// distributed under the terms of the GNU General Public License v3 (GPL -// Version 3), copied verbatim in the file "COPYING". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file AliTPC3DCylindricalInterpolatorIrregular.h -/// \brief Irregular grid interpolator for cylindrical coordinate with r,phi,z different coordinates -/// RBF-based interpolation -/// -/// \author Rifki Sadikin , Indonesian Institute of Sciences -/// \date Jan 5, 2016 - -#ifndef AliTPC3DCylindricalInterpolatorIrregular_H -#define AliTPC3DCylindricalInterpolatorIrregular_H - -#include "TMatrixD.h" - -class AliTPC3DCylindricalInterpolatorIrregular -{ - public: - AliTPC3DCylindricalInterpolatorIrregular(Int_t nRRow, Int_t nZColumn, Int_t nPhiSlice, Int_t rStep, Int_t zStep, - Int_t phiStep, Int_t intType); - AliTPC3DCylindricalInterpolatorIrregular(); - ~AliTPC3DCylindricalInterpolatorIrregular(); - - Double_t - GetValue(Double_t r, Double_t phi, Double_t z, Int_t rIndex, Int_t phiIndex, Int_t zIndex, Int_t stepR, Int_t stepPhi, - Int_t stepZ); - Double_t - GetValue(Double_t r, Double_t phi, Double_t z, Int_t rIndex, Int_t phiIndex, Int_t zIndex, Int_t stepR, Int_t stepPhi, - Int_t stepZ, Int_t minZColumnIndex); - Double_t GetValue(Double_t r, Double_t phi, Double_t z); - void SetOrder(Int_t order) { fOrder = order; } - - void InitRBFWeight(); - void SetIrregularGridSize(Int_t size) { fIrregularGridSize = size; } - Int_t GetIrregularGridSize() { return fIrregularGridSize; } - void SetKernelType(Int_t kernelType) { fKernelType = kernelType; } - Int_t GetKernelType() { return fKernelType; } - - ///< Enumeration of Poisson Solver Strategy Type - enum RBFKernelType { - kRBFMultiQuadratic = 0, - kRBFInverseMultiQuadratic = 1, - kRBFThinPlateSpline = 2, - kRBFGaussian = 3 - }; - - void SetNR(Int_t nRRow) { fNR = nRRow; } - void SetNPhi(Int_t nPhiSlice) { fNPhi = nPhiSlice; } - void SetNZ(Int_t nZColumn) { fNZ = nZColumn; } - - Int_t GetNR() { return fNR; } - Int_t GetNPhi() { return fNPhi; } - Int_t GetNZ() { return fNZ; } - - void SetRList(Double_t* rList) { fRList = rList; } - void SetPhiList(Double_t* phiList) { fPhiList = phiList; } - void SetZList(Double_t* zList) { fZList = zList; } - - void SetValue(Double_t* value) { fValue = value; } - void - SetValue(TMatrixD** matricesValue, TMatrixD** matricesRPoint, TMatrixD** matricesPhiPoint, TMatrixD** matricesZPoint); - void - SetValue(TMatrixD** matricesValue, TMatrixD** matricesRPoint, TMatrixD** matricesPhiPoint, TMatrixD** matricesZPoint, - Int_t jy); - - struct KDTreeNode { - Double_t* pR; //!, Indonesian Institute of Sciences -/// \date Mar 4, 2015 - -#include "AliTPCLookUpTable3DInterpolatorD.h" - -/// \cond CLASSIMP3 -ClassImp(AliTPCLookUpTable3DInterpolatorD); -/// \endcond - -/// constructor -AliTPCLookUpTable3DInterpolatorD::AliTPCLookUpTable3DInterpolatorD() -{ - fOrder = 1; - fIsAllocatingLookUp = kFALSE; -} - -/// constructor -/// -/// \param nRRow Int_t size of grid in R direction -/// \param rMin Double_t minimal value of R -/// \param rMax Double_t maximal value of R -/// \param nPhiSlice Int_t size of grid Phi direction -/// \param phiMin Double_t minimal value of Phi -/// \param phiMax Double_t maximal value of Phi -/// \param nZColumn Int_t size of grid Z direction -/// \param zMin Double_t minimal value of Z -/// \param zMax Double_t maximal value of Z - -/** - AliTPCLookUpTable3DInterpolatorD::AliTPCLookUpTable3DInterpolatorD(Int_t nRRow, Double_t rMin, Double_t rMax, - Int_t nPhiSlice, - Double_t phiMin, Double_t phiMax, Int_t nZColumn, - Double_t zMin, Double_t zMax) { - fOrder = 1; - fIsAllocatingLookUp = kTRUE; - - fNR = nRRow; - fNPhi = nPhiSlice; - fNZ = nZColumn; - - fLookUpR = new TMatrixD *[fNPhi]; - fLookUpPhi = new TMatrixD *[fNPhi]; - fLookUpZ = new TMatrixD *[fNPhi]; - - for (Int_t m = 0; m < fNPhi; m++) { - fLookUpR[m] = new TMatrixD(fNR, fNZ); - fLookUpPhi[m] = new TMatrixD(fNR, fNZ); - fLookUpZ[m] = new TMatrixD(fNR, fNZ); - } - - fRList = new Double_t[fNR]; - fPhiList = new Double_t[fNPhi]; - fZList = new Double_t[fNZ]; - - Double_t dR = (rMax - rMin) / fNR; - Double_t dPhi = (phiMax - phiMin) / fNPhi; - Double_t dZ = (zMax - zMin) / fNPhi; - - for (Int_t m = 0; m < fNPhi; m++) fPhiList[m] = phiMin + dPhi * m; - for (Int_t m = 0; m < fNR; m++) fRList[m] = rMin + dR * m; - for (Int_t m = 0; m < fNZ; m++) fZList[m] = zMin + dZ * m; - } - **/ - -/// Constructor -/// -/// \param nRRow Int_t size of grid in R direction -/// \param matricesRValue TMatrixD** values of component R -/// \param rList Double_t* list of position R -/// \param nPhiSlice Int_t size of grid in Phi direction -/// \param matricesPhiValue TMatrixD** values of component Phi -/// \param phiList Double_t* list of position Phi -/// \param nZColumn Int_t size of grid in Z direction -/// \param matricesZValue TMatrixD** values of component Z -/// \param zList Double_t* list of position Z -/// \param order Int_t order of interpolation -AliTPCLookUpTable3DInterpolatorD::AliTPCLookUpTable3DInterpolatorD( - Int_t nRRow, TMatrixD** matricesRValue, Double_t* rList, - Int_t nPhiSlice, TMatrixD** matricesPhiValue, Double_t* phiList, - Int_t nZColumn, TMatrixD** matricesZValue, Double_t* zList, Int_t order) -{ - fIsAllocatingLookUp = kFALSE; - - SetNR(nRRow); - SetLookUpR(matricesRValue); - SetRList(rList); - SetNPhi(nPhiSlice); - SetLookUpPhi(matricesPhiValue); - SetPhiList(phiList); - SetNZ(nZColumn); - SetLookUpZ(matricesZValue); - SetZList(zList); - - fInterpolatorR = new AliTPC3DCylindricalInterpolator(); - fInterpolatorZ = new AliTPC3DCylindricalInterpolator(); - fInterpolatorPhi = new AliTPC3DCylindricalInterpolator(); - - SetOrder(order); - fInterpolatorR->SetNR(nRRow); - fInterpolatorR->SetNZ(nZColumn); - fInterpolatorR->SetNPhi(nPhiSlice); - fInterpolatorR->SetNGridPoints(); - fInterpolatorR->SetRList(rList); - fInterpolatorR->SetZList(zList); - fInterpolatorR->SetPhiList(phiList); - fInterpolatorR->SetOrder(order); - - fInterpolatorZ->SetNR(nRRow); - fInterpolatorZ->SetNZ(nZColumn); - fInterpolatorZ->SetNPhi(nPhiSlice); - fInterpolatorZ->SetNGridPoints(); - fInterpolatorZ->SetRList(rList); - fInterpolatorZ->SetZList(zList); - fInterpolatorZ->SetPhiList(phiList); - fInterpolatorZ->SetOrder(order); - - fInterpolatorPhi->SetNR(nRRow); - fInterpolatorPhi->SetNZ(nZColumn); - fInterpolatorPhi->SetNPhi(nPhiSlice); - fInterpolatorPhi->SetNGridPoints(); - fInterpolatorPhi->SetRList(rList); - fInterpolatorPhi->SetZList(zList); - fInterpolatorPhi->SetPhiList(phiList); - fInterpolatorPhi->SetOrder(order); -} - -/// destructor -AliTPCLookUpTable3DInterpolatorD::~AliTPCLookUpTable3DInterpolatorD() -{ - delete fInterpolatorR; - delete fInterpolatorZ; - delete fInterpolatorPhi; -} - -/// copy from matrices to 1D array for interpolation algorithm -void AliTPCLookUpTable3DInterpolatorD::CopyFromMatricesToInterpolator() -{ - fInterpolatorR->SetValue(fLookUpR); - fInterpolatorZ->SetValue(fLookUpZ); - fInterpolatorPhi->SetValue(fLookUpPhi); - - if (fOrder > 2) { - fInterpolatorR->InitCubicSpline(); - fInterpolatorZ->InitCubicSpline(); - fInterpolatorPhi->InitCubicSpline(); - } -} - -/// copy from matrices to 1D array for interpolation algorithm -void AliTPCLookUpTable3DInterpolatorD::CopyFromMatricesToInterpolator(Int_t iZ) -{ - fInterpolatorR->SetValue(fLookUpR, iZ); - fInterpolatorZ->SetValue(fLookUpZ, iZ); - fInterpolatorPhi->SetValue(fLookUpPhi, iZ); - - // no implementation for cubic spline interpolation -} - -/// get value of 3-components at a P(r,phi,z) -/// -/// \param r Double_t r position -/// \param phi Double_t phi position -/// \param z Double_t z position -/// \param rValue Double_t value of r-component -/// \param phiValue Double_t value of phi-component -/// \param zValue Double_t value of z-component -void AliTPCLookUpTable3DInterpolatorD::GetValue( - Double_t r, Double_t phi, Double_t z, - Double_t& rValue, Double_t& phiValue, Double_t& zValue) const -{ - rValue = fInterpolatorR->GetValue(r, phi, z); - phiValue = fInterpolatorPhi->GetValue(r, phi, z); - zValue = fInterpolatorZ->GetValue(r, phi, z); -} - -/// get value for return value is a Float_t -/// -/// \param r Double_t r position -/// \param phi Double_t phi position -/// \param z Double_t z position -/// \param rValue Float_t value of r-component -/// \param phiValue Float_t value of phi-component -/// \param zValue Float_t value of z-component -void AliTPCLookUpTable3DInterpolatorD::GetValue( - Double_t r, Double_t phi, Double_t z, - Float_t& rValue, Float_t& phiValue, Float_t& zValue) const -{ - rValue = fInterpolatorR->GetValue(r, phi, z); - phiValue = fInterpolatorPhi->GetValue(r, phi, z); - zValue = fInterpolatorZ->GetValue(r, phi, z); -} - -// Set Order of interpolation -// -void AliTPCLookUpTable3DInterpolatorD::SetOrder(Int_t order) -{ - fOrder = order; - fInterpolatorR->SetOrder(order); - fInterpolatorZ->SetOrder(order); - fInterpolatorPhi->SetOrder(order); -} diff --git a/GPU/TPCSpaceChargeBase/AliTPCLookUpTable3DInterpolatorD.h b/GPU/TPCSpaceChargeBase/AliTPCLookUpTable3DInterpolatorD.h deleted file mode 100644 index 470a84577a69e..0000000000000 --- a/GPU/TPCSpaceChargeBase/AliTPCLookUpTable3DInterpolatorD.h +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright CERN and copyright holders of ALICE O2. This software is -// distributed under the terms of the GNU General Public License v3 (GPL -// Version 3), copied verbatim in the file "COPYING". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file AliTPCLookUpTable3DInterpolatorD.h -/// \brief Wrap up look-up table for correction/distortion integral or derivative (electric field) -/// assume 3 components: r-component, phi-component and z-component -/// -/// \author Rifki Sadikin , Indonesian Institute of Sciences -/// \date Mar 4, 2015 - -#ifndef AliTPCLookUpTable3DInterpolatorD_H -#define AliTPCLookUpTable3DInterpolatorD_H - -#include "TMatrixD.h" -#include "AliTPC3DCylindricalInterpolator.h" - -class AliTPCLookUpTable3DInterpolatorD -{ - public: - AliTPCLookUpTable3DInterpolatorD(); - //AliTPCLookUpTable3DInterpolatorD(Int_t nRRow, Double_t rMin, Double_t rMax, Int_t nPhiSlice, Double_t phiMin, Double_t phiMax, Int_t nZColumn , Double_t zMin, Double_t zMax ); - AliTPCLookUpTable3DInterpolatorD(Int_t nRRow, TMatrixD** matricesRValue, Double_t* rList, Int_t nPhiSlice, TMatrixD** matricesPhiValue, Double_t* phiList, Int_t nZColumn, TMatrixD** matricesZValue, Double_t* zList, Int_t order); - ~AliTPCLookUpTable3DInterpolatorD(); - - void SetNR(Int_t nRRow) { fNR = nRRow; } - void SetNPhi(Int_t nPhiSlice) { fNPhi = nPhiSlice; } - void SetNZ(Int_t nZColumn) { fNZ = nZColumn; } - Int_t GetNR() { return fNR; } - Int_t GetNPhi() { return fNPhi; } - Int_t GetNZ() { return fNZ; } - - void SetRList(Double_t* rList) { fRList = rList; } - void SetPhiList(Double_t* phiList) { fPhiList = phiList; } - void SetZList(Double_t* zList) { fZList = zList; } - void SetLookUpR(TMatrixD** matricesRValue) { fLookUpR = matricesRValue; } - void SetLookUpPhi(TMatrixD** matricesPhiValue) { fLookUpPhi = matricesPhiValue; } - void SetLookUpZ(TMatrixD** matricesZValue) { fLookUpZ = matricesZValue; } - void SetOrder(Int_t order); - void GetValue(Double_t r, Double_t phi, Double_t z, Double_t& rValue, Double_t& phiValue, Double_t& zValue) const; - void GetValue(Double_t r, Double_t phi, Double_t z, Float_t& rValue, Float_t& phiValue, Float_t& zValue) const; - void CopyFromMatricesToInterpolator(); - void CopyFromMatricesToInterpolator(Int_t iZ); // copy only iZ - - TMatrixD** GetLookUpR() { return fLookUpR; } - TMatrixD** GetLookUpPhi() { return fLookUpPhi; } - TMatrixD** GetLookUpZ() { return fLookUpZ; } - Double_t* GetRList() { return fRList; } - Double_t* GetPhiList() { return fPhiList; } - Double_t* GetZList() { return fZList; } - - AliTPC3DCylindricalInterpolator* GetInterpolatorR() { return fInterpolatorR; } - AliTPC3DCylindricalInterpolator* GetInterpolatorPhi() { return fInterpolatorPhi; } - AliTPC3DCylindricalInterpolator* GetInterpolatorZ() { return fInterpolatorZ; } - - private: - Int_t fOrder; ///< order of interpolation - Int_t fNR; ///< number of grid in R - Int_t fNPhi; ///< number of grid in Phi - Int_t fNZ; ///< number of grid in Z - - TMatrixD** fLookUpR = nullptr; //! Interpolator for R component - AliTPC3DCylindricalInterpolator* fInterpolatorPhi = nullptr; //-> Interpolator for Phi component - AliTPC3DCylindricalInterpolator* fInterpolatorZ = nullptr; //-> Interpolator for Z component - - Double_t* fRList = nullptr; //!, Indonesian Institute of Sciences -/// \date Mar 4, 2015 - -#include "AliTPCLookUpTable3DInterpolatorIrregularD.h" - -/// \cond CLASSIMP3 -ClassImp(AliTPCLookUpTable3DInterpolatorIrregularD); -/// \endcond - -/// constructor -AliTPCLookUpTable3DInterpolatorIrregularD::AliTPCLookUpTable3DInterpolatorIrregularD() -{ - fOrder = 1; - fIsAllocatingLookUp = kFALSE; -} - -/// constructor -/// -/// \param nRRow -/// \param matricesRValue -/// \param matricesRPoint -/// \param nPhiSlice -/// \param matricesPhiValue -/// \param matricesPhiPoint -/// \param nZColumn -/// \param matricesZValue -/// \param matricesZPoint -/// \param order -/// \param stepR -/// \param stepZ -/// \param stepPhi -/// \param type -AliTPCLookUpTable3DInterpolatorIrregularD::AliTPCLookUpTable3DInterpolatorIrregularD( - Int_t nRRow, TMatrixD** matricesRValue, TMatrixD** matricesRPoint, Int_t nPhiSlice, TMatrixD** matricesPhiValue, - TMatrixD** matricesPhiPoint, Int_t nZColumn, - TMatrixD** matricesZValue, TMatrixD** matricesZPoint, Int_t order, Int_t stepR, Int_t stepZ, Int_t stepPhi, - Int_t type) -{ - fIsAllocatingLookUp = kFALSE; - - SetNR(nRRow); - SetLookUpR(matricesRValue); - SetRList(matricesRPoint); - SetNPhi(nPhiSlice); - SetLookUpPhi(matricesPhiValue); - SetPhiList(matricesPhiPoint); - SetNZ(nZColumn); - SetLookUpZ(matricesZValue); - SetZList(matricesZPoint); - SetOrder(order); - - fInterpolatorR = new AliTPC3DCylindricalInterpolatorIrregular( - nRRow, nZColumn, nPhiSlice, stepR, stepZ, stepPhi, type); - fInterpolatorZ = new AliTPC3DCylindricalInterpolatorIrregular( - nRRow, nZColumn, nPhiSlice, stepR, stepZ, stepPhi, type); - fInterpolatorPhi = new AliTPC3DCylindricalInterpolatorIrregular( - nRRow, nZColumn, nPhiSlice, stepR, stepZ, stepPhi, type); - - fInterpolatorR->SetNR(nRRow); - fInterpolatorR->SetNZ(nZColumn); - fInterpolatorR->SetNPhi(nPhiSlice); - - fInterpolatorR->SetOrder(order); - fInterpolatorZ->SetNR(nRRow); - fInterpolatorZ->SetNZ(nZColumn); - fInterpolatorZ->SetNPhi(nPhiSlice); - fInterpolatorZ->SetOrder(order); - fInterpolatorPhi->SetNR(nRRow); - fInterpolatorPhi->SetNZ(nZColumn); - fInterpolatorPhi->SetNPhi(nPhiSlice); - fInterpolatorPhi->SetOrder(order); -} - -/// destructor -AliTPCLookUpTable3DInterpolatorIrregularD::~AliTPCLookUpTable3DInterpolatorIrregularD() -{ - delete fInterpolatorR; - delete fInterpolatorZ; - delete fInterpolatorPhi; -} - -/// copy from matrices to the interpolator -void AliTPCLookUpTable3DInterpolatorIrregularD::CopyFromMatricesToInterpolator() -{ - - fInterpolatorR->SetValue(fMatricesRValue, fMatricesRPoint, fMatricesPhiPoint, fMatricesZPoint); - fInterpolatorZ->SetValue(fMatricesZValue, fMatricesRPoint, fMatricesPhiPoint, fMatricesZPoint); - fInterpolatorPhi->SetValue(fMatricesPhiValue, fMatricesRPoint, fMatricesPhiPoint, fMatricesZPoint); -} - -/// -/// \param j -void AliTPCLookUpTable3DInterpolatorIrregularD::CopyFromMatricesToInterpolator(Int_t j) -{ - fInterpolatorR->SetValue(fMatricesRValue, fMatricesRPoint, fMatricesPhiPoint, fMatricesZPoint, j); - fInterpolatorZ->SetValue(fMatricesZValue, fMatricesRPoint, fMatricesPhiPoint, fMatricesZPoint, j); - fInterpolatorPhi->SetValue(fMatricesPhiValue, fMatricesRPoint, fMatricesPhiPoint, fMatricesZPoint, j); -} - -/// Get interpolation -/// \param r -/// \param phi -/// \param z -/// \param rValue -/// \param phiValue -/// \param zValue -/// \param rIndex -/// \param phiIndex -/// \param zIndex -/// \param stepR -/// \param stepPhi -/// \param stepZ -void AliTPCLookUpTable3DInterpolatorIrregularD::GetValue( - Double_t r, Double_t phi, Double_t z, Double_t& rValue, Double_t& phiValue, Double_t& zValue, - Int_t rIndex, Int_t phiIndex, Int_t zIndex, Int_t stepR, Int_t stepPhi, Int_t stepZ) -{ - rValue = fInterpolatorR->GetValue(r, phi, z, rIndex, phiIndex, zIndex, stepR, stepPhi, stepZ); - phiValue = fInterpolatorPhi->GetValue(r, phi, z, rIndex, phiIndex, zIndex, stepR, stepPhi, stepZ); - zValue = fInterpolatorZ->GetValue(r, phi, z, rIndex, phiIndex, zIndex, stepR, stepPhi, stepZ); -} - -/// Interpolation for a point (r,phi,z) -/// -/// \param r -/// \param phi -/// \param z -/// \param rValue -/// \param phiValue -/// \param zValue -/// \param rIndex -/// \param phiIndex -/// \param zIndex -/// \param stepR -/// \param stepPhi -/// \param stepZ -/// \param minZColumnIndex -void AliTPCLookUpTable3DInterpolatorIrregularD::GetValue( - Double_t r, Double_t phi, Double_t z, Double_t& rValue, Double_t& phiValue, Double_t& zValue, Int_t rIndex, - Int_t phiIndex, Int_t zIndex, Int_t stepR, Int_t stepPhi, Int_t stepZ, Int_t minZColumnIndex) -{ - rValue = fInterpolatorR->GetValue(r, phi, z, rIndex, phiIndex, zIndex, stepR, stepPhi, stepZ, minZColumnIndex); - phiValue = fInterpolatorPhi->GetValue(r, phi, z, rIndex, phiIndex, zIndex, stepR, stepPhi, stepZ, minZColumnIndex); - zValue = fInterpolatorZ->GetValue(r, phi, z, rIndex, phiIndex, zIndex, stepR, stepPhi, stepZ, minZColumnIndex); -} - -/// Get interpolation -/// \param r -/// \param phi -/// \param z -/// \param rValue -/// \param phiValue -/// \param zValue -/// \param rIndex -/// \param phiIndex -/// \param zIndex -/// \param startR -/// \param startPhi -/// \param startZ -void AliTPCLookUpTable3DInterpolatorIrregularD::GetValue( - Double_t r, Double_t phi, Double_t z, Float_t& rValue, Float_t& phiValue, Float_t& zValue, Int_t rIndex, - Int_t phiIndex, Int_t zIndex, Int_t startR, Int_t startPhi, Int_t startZ) -{ - rValue = fInterpolatorR->GetValue(r, phi, z, rIndex, phiIndex, zIndex, startR, startPhi, startZ); - phiValue = fInterpolatorPhi->GetValue(r, phi, z, rIndex, phiIndex, zIndex, startR, startPhi, startZ); - zValue = fInterpolatorZ->GetValue(r, phi, z, rIndex, phiIndex, zIndex, startR, startPhi, startZ); -} - -// using kdtree -void AliTPCLookUpTable3DInterpolatorIrregularD::GetValue( - Double_t r, Double_t phi, Double_t z, Double_t& rValue, Double_t& phiValue, Double_t& zValue) -{ - rValue = fInterpolatorR->GetValue(r, phi, z); - phiValue = fInterpolatorPhi->GetValue(r, phi, z); - zValue = fInterpolatorZ->GetValue(r, phi, z); -} diff --git a/GPU/TPCSpaceChargeBase/AliTPCLookUpTable3DInterpolatorIrregularD.h b/GPU/TPCSpaceChargeBase/AliTPCLookUpTable3DInterpolatorIrregularD.h deleted file mode 100644 index f7466360f3f85..0000000000000 --- a/GPU/TPCSpaceChargeBase/AliTPCLookUpTable3DInterpolatorIrregularD.h +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright CERN and copyright holders of ALICE O2. This software is -// distributed under the terms of the GNU General Public License v3 (GPL -// Version 3), copied verbatim in the file "COPYING". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file AliTPCLookUpTable3DInterpolatorIrregularD.h -/// \brief Wrap up look-up table with irregular grid -/// -/// \author Rifki Sadikin , Indonesian Institute of Sciences -/// \date Mar 4, 2015 - -#ifndef AliTPCLookUpTable3DInterpolatorIrregularD_H -#define AliTPCLookUpTable3DInterpolatorIrregularD_H - -#include "TMatrixD.h" -#include "AliTPC3DCylindricalInterpolatorIrregular.h" - -class AliTPCLookUpTable3DInterpolatorIrregularD -{ - public: - void SetNR(Int_t nRRow) { fNR = nRRow; } - void SetNPhi(Int_t nPhiSlice) { fNPhi = nPhiSlice; } - void SetNZ(Int_t nZColumn) { fNZ = nZColumn; } - - Int_t GetNR() { return fNR; } - Int_t GetNPhi() { return fNPhi; } - Int_t GetNZ() { return fNZ; } - - void SetRList(TMatrixD** matricesRPoint) { fMatricesRPoint = matricesRPoint; } - void SetPhiList(TMatrixD** matricesPhiPoint) { fMatricesPhiPoint = matricesPhiPoint; } - void SetZList(TMatrixD** matricesZPoint) { fMatricesZPoint = matricesZPoint; } - - void SetLookUpR(TMatrixD** matricesRValue) { fMatricesRValue = matricesRValue; } - void SetLookUpPhi(TMatrixD** matricesPhiValue) { fMatricesPhiValue = matricesPhiValue; } - void SetLookUpZ(TMatrixD** matricesZValue) { fMatricesZValue = matricesZValue; } - - AliTPCLookUpTable3DInterpolatorIrregularD(); - AliTPCLookUpTable3DInterpolatorIrregularD(Int_t nRRow, TMatrixD** matricesRValue, TMatrixD** r, Int_t nPhiSlice, - TMatrixD** matricesPhiValue, TMatrixD** matricesPhiPoint, Int_t nZColumn, - TMatrixD** matricesZValue, TMatrixD** matricesZPoint, Int_t order, - Int_t stepR, Int_t stepZ, Int_t stepPhi, Int_t type); - - ~AliTPCLookUpTable3DInterpolatorIrregularD(); - - void GetValue(Double_t r, Double_t phi, Double_t z, Double_t& rValue, Double_t& phiValue, Double_t& zValue, Int_t rIndex, - Int_t phiIndex, Int_t zIndex, Int_t stepR, Int_t stepPhi, Int_t stepZ); - void GetValue(Double_t r, Double_t phi, Double_t z, Double_t& rValue, Double_t& phiValue, Double_t& zValue, Int_t rIndex, - Int_t phiIndex, Int_t zIndex, Int_t stepR, Int_t stepPhi, Int_t stepZ, Int_t minZColumnIndex); - void GetValue(Double_t r, Double_t phi, Double_t z, Float_t& rValue, Float_t& phiValue, Float_t& zValue, Int_t rIndex, Int_t phiIndex, - Int_t zIndex, Int_t stepR, Int_t stepPhi, Int_t stepZ); - void GetValue(Double_t r, Double_t phi, Double_t z, Double_t& rValue, Double_t& phiValue, Double_t& zValue); - void SetOrder(Int_t order) { fOrder = order; } - void CopyFromMatricesToInterpolator(); - void CopyFromMatricesToInterpolator(Int_t j); - - Int_t GetIrregularGridSize() { return fInterpolatorR->GetIrregularGridSize(); } - void SetIrregularGridSize(Int_t size) - { - fInterpolatorR->SetIrregularGridSize(size); - fInterpolatorPhi->SetIrregularGridSize(size); - fInterpolatorZ->SetIrregularGridSize(size); - } - void SetKernelType(Int_t kernelType) - { - fInterpolatorR->SetKernelType(kernelType); - fInterpolatorPhi->SetKernelType(kernelType); - fInterpolatorZ->SetKernelType(kernelType); - } - Int_t GetKernelType() { return fInterpolatorR->GetKernelType(); } - - private: - Int_t fOrder; ///< Order of interpolation - Int_t fIrregularGridSize; ///< Size of irregular interpolation neighborhood - Int_t fNR; ///< Number of grid in R - Int_t fNPhi; ///< Number of grid in Phi - Int_t fNZ; ///< Number of grid in Z - - TMatrixD** fMatricesRValue = nullptr; //! Irregular interpolator for R-component - AliTPC3DCylindricalInterpolatorIrregular* fInterpolatorPhi = nullptr; //-> Irregular interpolator for Phi-component - AliTPC3DCylindricalInterpolatorIrregular* fInterpolatorZ = nullptr; //-> Irregular interpolator for Z-component - - TMatrixD** fMatricesRPoint = nullptr; //!, Indonesian Institute of Sciences -/// \date Nov 20, 2017 - -#include -#include "AliTPCPoissonSolver.h" - -/// \cond CLASSIMP -ClassImp(AliTPCPoissonSolver); -/// \endcond - -const Double_t AliTPCPoissonSolver::fgkTPCZ0 = 249.7; ///< nominal gating grid position -const Double_t AliTPCPoissonSolver::fgkIFCRadius = 83.5; ///< radius which renders the "18 rod manifold" best -> compare calc. of Jim Thomas -const Double_t AliTPCPoissonSolver::fgkOFCRadius = 254.5; ///< Mean Radius of the Outer Field Cage (252.55 min, 256.45 max) (cm) -const Double_t AliTPCPoissonSolver::fgkZOffSet = 0.2; ///< Offset from CE: calculate all distortions closer to CE as if at this point -const Double_t AliTPCPoissonSolver::fgkCathodeV = -100000.0; ///< Cathode Voltage (volts) -const Double_t AliTPCPoissonSolver::fgkGG = -70.0; ///< Gating Grid voltage (volts) -const Double_t AliTPCPoissonSolver::fgkdvdE = 0.0024; ///< [cm/V] drift velocity dependency on the E field (from Magboltz for NeCO2N2 at standard environment) -const Double_t AliTPCPoissonSolver::fgkEM = -1.602176487e-19 / 9.10938215e-31; ///< charge/mass in [C/kg] -const Double_t AliTPCPoissonSolver::fgke0 = 8.854187817e-12; ///< vacuum permittivity [A·s/(V·m)] - -Double_t AliTPCPoissonSolver::fgExactErr = 1e-4; -Double_t AliTPCPoissonSolver::fgConvergenceError = 1e-3; - -/// constructor -/// -AliTPCPoissonSolver::AliTPCPoissonSolver() - : TNamed("poisson solver", "solver"), - fErrorConvergenceNorm2{new TVectorD(fMgParameters.nMGCycle)}, - fErrorConvergenceNormInf{new TVectorD(fMgParameters.nMGCycle)}, - fError{new TVectorD(fMgParameters.nMGCycle)} - -{ - // default strategy -} - -/// Constructor -/// \param name name of the object -/// \param title title of the object -AliTPCPoissonSolver::AliTPCPoissonSolver(const char* name, const char* title) - : TNamed(name, title), - fErrorConvergenceNorm2{new TVectorD(fMgParameters.nMGCycle)}, - fErrorConvergenceNormInf{new TVectorD(fMgParameters.nMGCycle)}, - fError{new TVectorD(fMgParameters.nMGCycle)} -{ - /// constructor -} - -/// destructor -AliTPCPoissonSolver::~AliTPCPoissonSolver() -{ - /// virtual destructor - delete[] fExactSolution; - delete fErrorConvergenceNorm2; - delete fErrorConvergenceNormInf; - delete fError; -} - -/// Provides poisson solver in 2D -/// -/// Based on the strategy (relaxation, multi grid or FFT) -/// -/// \param matrixV TMatrixD& potential in matrix -/// \param matrixCharge TMatrixD& charge density in matrix (side effect -/// \param nRRow Int_t number of nRRow in the grid -/// \param nZColumn Int_t number of nZColumn in the grid -/// \param maxIteration Int_t maximum iteration for relaxation method -/// -/// \return A fixed number that has nothing to do with what the function does -void AliTPCPoissonSolver::PoissonSolver2D(TMatrixD& matrixV, TMatrixD& matrixCharge, Int_t nRRow, Int_t nZColumn, - Int_t maxIteration) -{ - switch (fStrategy) { - case kMultiGrid: - PoissonMultiGrid2D(matrixV, matrixCharge, nRRow, nZColumn); - break; - default: - PoissonRelaxation2D(matrixV, matrixCharge, nRRow, nZColumn, maxIteration); - } -} - -/// Provides poisson solver in Cylindrical 3D (TPC geometry) -/// -/// Strategy based on parameter settings (fStrategy and fMgParameters)provided -/// * Cascaded multi grid with S.O.R -/// * Geometric MultiGrid -/// * Cycles: V, W, Full -/// * Relaxation: Jacobi, Weighted-Jacobi, Gauss-Seidel -/// * Grid transfer operators: Full, Half -/// * Spectral Methods (TODO) -/// -/// \param matricesV TMatrixD** potential in 3D matrix -/// \param matricesCharge TMatrixD** charge density in 3D matrix (side effect) -/// \param nRRow Int_t number of nRRow in the r direction of TPC -/// \param nZColumn Int_t number of nZColumn in z direction of TPC -/// \param phiSlice Int_t number of phiSlice in phi direction of T{C -/// \param maxIteration Int_t maximum iteration for relaxation method -/// \param symmetry Int_t symmetry or not -/// -/// \pre Charge density distribution in **matricesCharge** is known and boundary values for **matricesV** are set -/// \post Numerical solution for potential distribution is calculated and stored in each rod at **matricesV** -void AliTPCPoissonSolver::PoissonSolver3D(TMatrixD** matricesV, TMatrixD** matricesCharge, - Int_t nRRow, Int_t nZColumn, Int_t phiSlice, Int_t maxIteration, - Int_t symmetry) -{ - switch (fStrategy) { - case kMultiGrid: - if (fMgParameters.isFull3D) { - PoissonMultiGrid3D(matricesV, matricesCharge, nRRow, nZColumn, phiSlice, symmetry); - } else { - PoissonMultiGrid3D2D(matricesV, matricesCharge, nRRow, nZColumn, phiSlice, symmetry); - } - break; - default: - PoissonRelaxation3D(matricesV, matricesCharge, nRRow, nZColumn, phiSlice, maxIteration, symmetry); - } -} - -/// Solve Poisson's Equation by Relaxation Technique in 2D (assuming cylindrical symmetry) -/// -/// Solve Poisson's equation in a cylindrical coordinate system. The matrixV matrix must be filled with the -/// boundary conditions on the first and last nRRow, and the first and last nZColumn. The remainder of the -/// array can be blank or contain a preliminary guess at the solution. The Charge density matrix contains -/// the enclosed spacecharge density at each point. The charge density matrix can be full of zero's if -/// you wish to solve Laplace equation however it should not contain random numbers or you will get -/// random numbers back as a solution. -/// Poisson's equation is solved by iteratively relaxing the matrix to the final solution. In order to -/// speed up the convergence to the best solution, this algorithm does a binary expansion of the solution -/// space. First it solves the problem on a very sparse grid by skipping nRRow and nZColumn in the original -/// matrix. Then it doubles the number of points and solves the problem again. Then it doubles the -/// number of points and solves the problem again. This happens several times until the maximum number -/// of points has been included in the array. -/// -/// NOTE: In order for this algorithm to work, the number of nRRow and nZColumn must be a power of 2 plus one. -/// So nRRow == 2**M + 1 and nZColumn == 2**N + 1. The number of nRRow and nZColumn can be different. -/// -/// Method for relaxation: S.O.R Weighted Jacobi -/// -/// \param matrixV TMatrixD& potential in matrix -/// \param matrixCharge TMatrixD& charge density in matrix (side effect -/// \param nRRow Int_t number of nRRow in the grid -/// \param nZColumn Int_t number of nZColumn in the grid -/// \param maxIteration Int_t maximum iteration for relaxation method -/// -/// \return A fixed number that has nothing to do with what the function does -/// -/// -/// Original code by Jim Thomas (STAR TPC Collaboration) -void AliTPCPoissonSolver::PoissonRelaxation2D(TMatrixD& matrixV, TMatrixD& matrixCharge, Int_t nRRow, Int_t nZColumn, - Int_t maxIteration) -{ - const Float_t gridSizeR = (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (nRRow - 1); - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (nZColumn - 1); - const Float_t ratio = gridSizeR * gridSizeR / (gridSizeZ * gridSizeZ); - - TMatrixD arrayEr(nRRow, nZColumn); - TMatrixD arrayEz(nRRow, nZColumn); - - //Check that number of nRRow and nZColumn is suitable for a binary expansion - - if (!IsPowerOfTwo(nRRow - 1)) { - Error("PoissonRelaxation2D", "PoissonRelaxation - Error in the number of nRRow. Must be 2**M - 1"); - return; - } - - if (!IsPowerOfTwo(nZColumn - 1)) { - Error("PoissonRelaxation2D", "PoissonRelaxation - Error in the number of nZColumn. Must be 2**N - 1"); - return; - } - - // Solve Poisson's equation in cylindrical coordinates by relaxation technique - // Allow for different size grid spacing in R and Z directions - // Use a binary expansion of the size of the matrix to speed up the solution of the problem - - Int_t iOne = (nRRow - 1) / 4; - Int_t jOne = (nZColumn - 1) / 4; - - // Coarse until nLoop - Int_t nLoop = 1 + (int)(0.5 + TMath::Log2((double)TMath::Max(iOne, jOne))); - - // Loop while the matrix expands & the resolution increases. - for (Int_t count = 0; count < nLoop; count++) { - - Float_t tempGridSizeR = gridSizeR * iOne; - Float_t tempRatio = ratio * iOne * iOne / (jOne * jOne); - Float_t tempFourth = 1.0 / (2.0 + 2.0 * tempRatio); - - // Do this the standard C++ way to avoid gcc extensions for Float_t coefficient1[nRRow] - std::vector coefficient1(nRRow); - std::vector coefficient2(nRRow); - - for (Int_t i = iOne; i < nRRow - 1; i += iOne) { - Float_t radius = AliTPCPoissonSolver::fgkIFCRadius + i * gridSizeR; - coefficient1[i] = 1.0 + tempGridSizeR / (2 * radius); - coefficient2[i] = 1.0 - tempGridSizeR / (2 * radius); - } - - TMatrixD sumChargeDensity(nRRow, nZColumn); - - // average charge at the coarse point - for (Int_t i = iOne; i < nRRow - 1; i += iOne) { - Float_t radius = AliTPCPoissonSolver::fgkIFCRadius + iOne * gridSizeR; - for (Int_t j = jOne; j < nZColumn - 1; j += jOne) { - if (iOne == 1 && jOne == 1) { - sumChargeDensity(i, j) = matrixCharge(i, j); - } else { - // Add up all enclosed charge density contributions within 1/2 unit in all directions - Float_t weight = 0.0; - Float_t sum = 0.0; - sumChargeDensity(i, j) = 0.0; - for (Int_t ii = i - iOne / 2; ii <= i + iOne / 2; ii++) { - for (Int_t jj = j - jOne / 2; jj <= j + jOne / 2; jj++) { - if (ii == i - iOne / 2 || ii == i + iOne / 2 || jj == j - jOne / 2 || jj == j + jOne / 2) { - weight = 0.5; - } else { - weight = 1.0; - } - sumChargeDensity(i, j) += matrixCharge(ii, jj) * weight * radius; - sum += weight * radius; - } - } - sumChargeDensity(i, j) /= sum; - } - sumChargeDensity(i, j) *= tempGridSizeR * tempGridSizeR; // just saving a step later on - } - } - - // Iterate on the current level - for (Int_t k = 1; k <= maxIteration; k++) { - // Solve Poisson's Equation - // Over-relaxation index, must be >= 1 but < 2. Arrange for it to evolve from 2 => 1 - // as iteration increase. - Float_t overRelax = 1.0 + TMath::Sqrt(TMath::Cos((k * TMath::PiOver2()) / maxIteration)); - Float_t overRelaxM1 = overRelax - 1.0; - Float_t overRelaxTemp4, overRelaxCoefficient5; - overRelaxTemp4 = overRelax * tempFourth; - overRelaxCoefficient5 = overRelaxM1 / overRelaxTemp4; - - for (Int_t i = iOne; i < nRRow - 1; i += iOne) { - for (Int_t j = jOne; j < nZColumn - 1; j += jOne) { - // S.O.R - // - matrixV(i, j) = (coefficient2[i] * matrixV(i - iOne, j) + tempRatio * (matrixV(i, j - jOne) + matrixV(i, j + jOne)) - overRelaxCoefficient5 * matrixV(i, j) + coefficient1[i] * matrixV(i + iOne, j) + sumChargeDensity(i, j)) * overRelaxTemp4; - } - } - - // if already at maxIteration - // TODO: stop when it converged - if (k == maxIteration) { - - // After full solution is achieved, copy low resolution solution into higher res array - // Interpolate solution - for (Int_t i = iOne; i < nRRow - 1; i += iOne) { - for (Int_t j = jOne; j < nZColumn - 1; j += jOne) { - if (iOne > 1) { - matrixV(i + iOne / 2, j) = (matrixV(i + iOne, j) + matrixV(i, j)) / 2; - if (i == iOne) { - matrixV(i - iOne / 2, j) = (matrixV(0, j) + matrixV(iOne, j)) / 2; - } - } - if (jOne > 1) { - matrixV(i, j + jOne / 2) = (matrixV(i, j + jOne) + matrixV(i, j)) / 2; - if (j == jOne) { - matrixV(i, j - jOne / 2) = (matrixV(i, 0) + matrixV(i, jOne)) / 2; - } - } - if (iOne > 1 && jOne > 1) { - matrixV(i + iOne / 2, j + jOne / 2) = (matrixV(i + iOne, j + jOne) + matrixV(i, j)) / 2; - if (i == iOne) { - matrixV(i - iOne / 2, j - jOne / 2) = (matrixV(0, j - jOne) + matrixV(iOne, j)) / 2; - } - if (j == jOne) { - matrixV(i - iOne / 2, j - jOne / 2) = (matrixV(i - iOne, 0) + matrixV(i, jOne)) / 2; - } - // Note that this leaves a point at the upper left and lower right corners uninitialized. - // -> Not a big deal. - } - } - } - } - } - - iOne = iOne / 2; - if (iOne < 1) { - iOne = 1; - } - jOne = jOne / 2; - if (jOne < 1) { - jOne = 1; - } - sumChargeDensity.Clear(); - } -} - -/// Solve Poisson's Equation by MultiGrid Technique in 2D (assuming cylindrical symmetry) -/// -/// NOTE: In order for this algorithm to work, the number of nRRow and nZColumn must be a power of 2 plus one. -/// So nRRow == 2**M + 1 and nZColumn == 2**N + 1. The number of nRRow and nZColumn can be different. -/// -/// \param matrixV TMatrixD& potential in matrix -/// \param matrixCharge TMatrixD& charge density in matrix (side effect -/// \param nRRow Int_t number of nRRow -/// \param nZColumn Int_t number of nZColumn -/// \param maxIteration Int_t maximum iteration for relaxation method -/// -/// \return A fixed number that has nothing to do with what the function does -void AliTPCPoissonSolver::PoissonMultiGrid2D(TMatrixD& matrixV, TMatrixD& matrixCharge, Int_t nRRow, Int_t nZColumn) -{ - /// Geometry of TPC -- should be use AliTPCParams instead - const Float_t gridSizeR = (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (nRRow - 1); - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (nZColumn - 1); - const Float_t ratio = gridSizeR * gridSizeR / (gridSizeZ * gridSizeZ); - - Int_t nGridRow = 0; // number grid - Int_t nGridCol = 0; // number grid - Int_t nnRow; - Int_t nnCol; - - nnRow = nRRow; - while (nnRow >>= 1) { - nGridRow++; - } - - nnCol = nZColumn; - while (nnCol >>= 1) { - nGridCol++; - } - - //Check that number of nRRow and nZColumn is suitable for multi grid - if (!IsPowerOfTwo(nRRow - 1)) { - Error("PoissonMultiGrid2D", "PoissonMultiGrid - Error in the number of nRRow. Must be 2**M - 1"); - return; - } - if (!IsPowerOfTwo(nZColumn - 1)) { - Error("PoissonMultiGrid2D", "PoissonMultiGrid - Error in the number of nZColumn. Must be 2**N - 1"); - return; - } - - Int_t nLoop = TMath::Max(nGridRow, nGridCol); // Calculate the number of nLoop for the binary expansion - - Info("PoissonMultiGrid2D", "%s", Form("nGridRow=%d, nGridCol=%d, nLoop=%d, nMGCycle=%d", nGridRow, nGridCol, nLoop, fMgParameters.nMGCycle)); - - Float_t h, h2, radius; - Int_t iOne = 1; // in/dex - Int_t jOne = 1; // index - Int_t tnRRow = nRRow, tnZColumn = nZColumn; - Int_t count; - Float_t tempRatio, tempFourth; - - // Vector for storing multi grid array - std::vector tvChargeFMG(nLoop); - std::vector tvArrayV(nLoop); - std::vector tvCharge(nLoop); - std::vector tvResidue(nLoop); - - // Allocate memory for temporary grid - for (count = 1; count <= nLoop; count++) { - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - // if one just address to matrixV - tvResidue[count - 1] = new TMatrixD(tnRRow, tnZColumn); - if (count == 1) { - tvChargeFMG[count - 1] = &matrixCharge; - tvArrayV[count - 1] = &matrixV; - tvCharge[count - 1] = &matrixCharge; - } else { - tvArrayV[count - 1] = new TMatrixD(tnRRow, tnZColumn); - tvCharge[count - 1] = new TMatrixD(tnRRow, tnZColumn); - tvChargeFMG[count - 1] = new TMatrixD(tnRRow, tnZColumn); - Restrict2D(*tvChargeFMG[count - 1], *tvChargeFMG[count - 2], tnRRow, tnZColumn); - } - iOne = 2 * iOne; - jOne = 2 * jOne; - } - - /// full multi grid - if (fMgParameters.cycleType == kFCycle) { - - Info("PoissonMultiGrid2D", "Do full cycle"); - // FMG - // 1) Relax on the coarsest grid - iOne = iOne / 2; - jOne = jOne / 2; - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - h = gridSizeR * count; - h2 = h * h; - tempRatio = ratio * iOne * iOne / (jOne * jOne); - tempFourth = 1.0 / (2.0 + 2.0 * tempRatio); - - std::vector coefficient1(tnRRow); - std::vector coefficient2(tnRRow); - - for (Int_t i = 1; i < tnRRow - 1; i++) { - radius = AliTPCPoissonSolver::fgkIFCRadius + i * h; - coefficient1[i] = 1.0 + h / (2 * radius); - coefficient2[i] = 1.0 - h / (2 * radius); - } - - Relax2D(*tvArrayV[nLoop - 1], *tvChargeFMG[nLoop - 1], tnRRow, tnZColumn, h2, tempFourth, tempRatio, coefficient1, - coefficient2); - - // Do VCycle from nLoop H to h - for (count = nLoop - 2; count >= 0; count--) { - - iOne = iOne / 2; - jOne = jOne / 2; - - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - - Interp2D(*tvArrayV[count], *tvArrayV[count + 1], tnRRow, tnZColumn); - // Copy the relax charge to the tvCharge - *tvCharge[count] = *tvChargeFMG[count]; //copy - //tvCharge[count]->Print(); - // Do V cycle - - for (Int_t mgCycle = 0; mgCycle < fMgParameters.nMGCycle; mgCycle++) { - - VCycle2D(nRRow, nZColumn, count + 1, nLoop, fMgParameters.nPre, fMgParameters.nPost, gridSizeR, ratio, tvArrayV, - tvCharge, tvResidue); - } - } - } else if (fMgParameters.cycleType == kVCycle) { - // 2. VCycle - Info("PoissonMultiGrid2D", "Do V cycle"); - - Int_t gridFrom = 1; - Int_t gridTo = nLoop; - - // Do MGCycle - for (Int_t mgCycle = 0; mgCycle < fMgParameters.nMGCycle; mgCycle++) { - VCycle2D(nRRow, nZColumn, gridFrom, gridTo, fMgParameters.nPre, fMgParameters.nPost, gridSizeR, ratio, tvArrayV, - tvCharge, tvResidue); - } - } else if (fMgParameters.cycleType == kWCycle) { - - // 3. W Cycle (TODO:) - - Int_t gridFrom = 1; - - //nLoop = nLoop >= 4 ? 4 : nLoop; - - Int_t gridTo = nLoop; - //Int_t gamma = 1; - - // Do MGCycle - for (Int_t mgCycle = 0; mgCycle < fMgParameters.nMGCycle; mgCycle++) { - WCycle2D(nRRow, nZColumn, gridFrom, gridTo, fMgParameters.gamma, fMgParameters.nPre, fMgParameters.nPost, - gridSizeR, ratio, tvArrayV, tvCharge, tvResidue); - } - } - - // Deallocate memory - for (count = nLoop; count >= 1; count--) { - // if one just address to matrixV - if (count > 1) { - delete tvArrayV[count - 1]; - delete tvCharge[count - 1]; - delete tvChargeFMG[count - 1]; - } - delete tvResidue[count - 1]; - } -} - -/// 3D - Solve Poisson's Equation in 3D by Relaxation Technique -/// -/// NOTE: In order for this algorithm to work, the number of nRRow and nZColumn must be a power of 2 plus one. -/// The number of nRRow and Z Column can be different. -/// -/// R Row == 2**M + 1 -/// Z Column == 2**N + 1 -/// Phi Slice == Arbitrary but greater than 3 -/// -/// DeltaPhi in Radians -/// -/// SYMMETRY = 0 if no phi symmetries, and no phi boundary conditions -/// = 1 if we have reflection symmetry at the boundaries (eg. sector symmetry or half sector symmetries). -/// -/// \param matricesV TMatrixD** potential in 3D matrix -/// \param matricesCharge TMatrixD** charge density in 3D matrix (side effect) -/// \param nRRow Int_t number of nRRow in the r direction of TPC -/// \param nZColumn Int_t number of nZColumn in z direction of TPC -/// \param phiSlice Int_t number of phiSlice in phi direction of T{C -/// \param maxIteration Int_t maximum iteration for relaxation method -/// \param symmetry Int_t symmetry or not -/// -void AliTPCPoissonSolver::PoissonRelaxation3D(TMatrixD** matricesV, TMatrixD** matricesCharge, - Int_t nRRow, Int_t nZColumn, Int_t phiSlice, Int_t maxIteration, - Int_t symmetry) -{ - - const Float_t gridSizeR = (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (nRRow - 1); - const Float_t gridSizePhi = TMath::TwoPi() / phiSlice; - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (nZColumn - 1); - const Float_t ratioPhi = gridSizeR * gridSizeR / (gridSizePhi * gridSizePhi); - const Float_t ratioZ = gridSizeR * gridSizeR / (gridSizeZ * gridSizeZ); - - Info("PoissonRelaxation3D", "%s", Form("in Poisson Solver 3D relaxation nRRow=%d, cols=%d, phiSlice=%d \n", nRRow, nZColumn, phiSlice)); - // Check that the number of nRRow and nZColumn is suitable for a binary expansion - if (!IsPowerOfTwo((nRRow - 1))) { - Error("PoissonRelaxation3D", "Poisson3DRelaxation - Error in the number of nRRow. Must be 2**M - 1"); - return; - } - if (!IsPowerOfTwo((nZColumn - 1))) { - Error("PoissonRelaxation3D", "Poisson3DRelaxation - Error in the number of nZColumn. Must be 2**N - 1"); - return; - } - if (phiSlice <= 3) { - Error("PoissonRelaxation3D", "Poisson3DRelaxation - Error in the number of phiSlice. Must be larger than 3"); - return; - } - if (phiSlice > 1000) { - Error("PoissonRelaxation3D", "Poisson3D phiSlice > 1000 is not allowed (nor wise) "); - return; - } - - // Solve Poisson's equation in cylindrical coordinates by relaxation technique - // Allow for different size grid spacing in R and Z directions - // Use a binary expansion of the matrix to speed up the solution of the problem - - Int_t nLoop, mPlus, mMinus, signPlus, signMinus; - Int_t iOne = (nRRow - 1) / 4; - Int_t jOne = (nZColumn - 1) / 4; - nLoop = TMath::Max(iOne, jOne); // Calculate the number of nLoop for the binary expansion - nLoop = 1 + (int)(0.5 + TMath::Log2((double)nLoop)); // Solve for N in 2**N - - TMatrixD* matricesSumChargeDensity[1000]; // Create temporary arrays to store low resolution charge arrays - - std::vector coefficient1( - nRRow); // Do this the standard C++ way to avoid gcc extensions for Float_t coefficient1[nRRow] - std::vector coefficient2( - nRRow); // Do this the standard C++ way to avoid gcc extensions for Float_t coefficient1[nRRow] - std::vector coefficient3( - nRRow); // Do this the standard C++ way to avoid gcc extensions for Float_t coefficient1[nRRow] - std::vector coefficient4( - nRRow); // Do this the standard C++ way to avoid gcc extensions for Float_t coefficient1[nRRow] - std::vector overRelaxCoefficient4(nRRow); // Do this the standard C++ way to avoid gcc extensions - std::vector overRelaxCoefficient5(nRRow); // Do this the standard C++ way to avoid gcc extensions - for (Int_t i = 0; i < phiSlice; i++) { - matricesSumChargeDensity[i] = new TMatrixD(nRRow, nZColumn); - } - - ///// Test of Convergence - TMatrixD* prevArrayV[phiSlice]; - - for (Int_t m = 0; m < phiSlice; m++) { - prevArrayV[m] = new TMatrixD(nRRow, nZColumn); - } - ///// - - // START the master loop and do the binary expansion - for (Int_t count = 0; count < nLoop; count++) { - Float_t tempGridSizeR = gridSizeR * iOne; - Float_t tempRatioPhi = ratioPhi * iOne * iOne; - Float_t tempRatioZ = ratioZ * iOne * iOne / (jOne * jOne); - - for (Int_t i = iOne; i < nRRow - 1; i += iOne) { - Float_t radius = AliTPCPoissonSolver::fgkIFCRadius + i * gridSizeR; - coefficient1[i] = 1.0 + tempGridSizeR / (2 * radius); - coefficient2[i] = 1.0 - tempGridSizeR / (2 * radius); - coefficient3[i] = tempRatioPhi / (radius * radius); - coefficient4[i] = 0.5 / (1.0 + tempRatioZ + coefficient3[i]); - } - - for (Int_t m = 0; m < phiSlice; m++) { - TMatrixD& matrixCharge = *matricesCharge[m]; - TMatrixD& sumChargeDensity = *matricesSumChargeDensity[m]; - for (Int_t i = iOne; i < nRRow - 1; i += iOne) { - Float_t radius = AliTPCPoissonSolver::fgkIFCRadius + i * gridSizeR; - for (Int_t j = jOne; j < nZColumn - 1; j += jOne) { - if (iOne == 1 && jOne == 1) { - sumChargeDensity(i, j) = matrixCharge(i, j); - } else { // Add up all enclosed charge density contributions within 1/2 unit in all directions - Float_t weight = 0.0; - Float_t sum = 0.0; - sumChargeDensity(i, j) = 0.0; - for (Int_t ii = i - iOne / 2; ii <= i + iOne / 2; ii++) { - for (Int_t jj = j - jOne / 2; jj <= j + jOne / 2; jj++) { - if (ii == i - iOne / 2 || ii == i + iOne / 2 || jj == j - jOne / 2 || jj == j + jOne / 2) { - weight = 0.5; - } else { - weight = 1.0; - } - sumChargeDensity(i, j) += matrixCharge(ii, jj) * weight * radius; - sum += weight * radius; - } - } - sumChargeDensity(i, j) /= sum; - } - sumChargeDensity(i, j) *= tempGridSizeR * tempGridSizeR; // just saving a step later on - } - } - } - - for (Int_t k = 1; k <= maxIteration; k++) { - if (count == nLoop - 1) { - //// Test of Convergence - for (Int_t m = 0; m < phiSlice; m++) { - (*prevArrayV[m]) = (*matricesV[m]); - } - //// - } - - Float_t overRelax = 1.0 + TMath::Sqrt(TMath::Cos((k * TMath::PiOver2()) / maxIteration)); - Float_t overRelaxM1 = overRelax - 1.0; - - for (Int_t i = iOne; i < nRRow - 1; i += iOne) { - overRelaxCoefficient4[i] = overRelax * coefficient4[i]; - overRelaxCoefficient5[i] = overRelaxM1 / overRelaxCoefficient4[i]; - } - - for (Int_t m = 0; m < phiSlice; m++) { - mPlus = m + 1; - signPlus = 1; - mMinus = m - 1; - signMinus = 1; - if (symmetry == 1) { // Reflection symmetry in phi (e.g. symmetry at sector boundaries, or half sectors, etc.) - if (mPlus > phiSlice - 1) { - mPlus = phiSlice - 2; - } - if (mMinus < 0) { - mMinus = 1; - } - } else if (symmetry == -1) { // Anti-symmetry in phi - if (mPlus > phiSlice - 1) { - mPlus = phiSlice - 2; - signPlus = -1; - } - if (mMinus < 0) { - mMinus = 1; - signMinus = -1; - } - } else { // No Symmetries in phi, no boundaries, the calculation is continuous across all phi - if (mPlus > phiSlice - 1) { - mPlus = m + 1 - phiSlice; - } - if (mMinus < 0) { - mMinus = m - 1 + phiSlice; - } - } - - TMatrixD& matrixV = *matricesV[m]; - TMatrixD& matrixVP = *matricesV[mPlus]; - TMatrixD& matrixVM = *matricesV[mMinus]; - TMatrixD& sumChargeDensity = *matricesSumChargeDensity[m]; - Double_t* matrixVFast = matrixV.GetMatrixArray(); - Double_t* matrixVPFast = matrixVP.GetMatrixArray(); - Double_t* matrixVMFast = matrixVM.GetMatrixArray(); - Double_t* sumChargeDensityFast = sumChargeDensity.GetMatrixArray(); - - if (fStrategy == kRelaxation) { - // slow implementation - for (Int_t i = iOne; i < nRRow - 1; i += iOne) { - for (Int_t j = jOne; j < nZColumn - 1; j += jOne) { - - matrixV(i, j) = (coefficient2[i] * matrixV(i - iOne, j) + tempRatioZ * (matrixV(i, j - jOne) + matrixV(i, j + jOne)) - overRelaxCoefficient5[i] * matrixV(i, j) + coefficient1[i] * matrixV(i + iOne, j) + coefficient3[i] * (signPlus * matrixVP(i, j) + signMinus * matrixVM(i, j)) + sumChargeDensity(i, j)) * overRelaxCoefficient4[i]; - // Note: over-relax the solution at each step. This speeds up the convergence. - } - } - } else { - for (Int_t i = iOne; i < nRRow - 1; i += iOne) { - Double_t* matrixVFastI = &(matrixVFast[i * nZColumn]); - Double_t* matrixVPFastI = &(matrixVPFast[i * nZColumn]); - Double_t* matrixVMFastI = &(matrixVMFast[i * nZColumn]); - Double_t* sumChargeDensityFastI = &(sumChargeDensityFast[i * nZColumn]); - - for (Int_t j = jOne; j < nZColumn - 1; j += jOne) { - Double_t /*resSlow*/ resFast; - - resFast = (coefficient2[i] * matrixVFastI[j - nZColumn * iOne] + tempRatioZ * (matrixVFastI[j - jOne] + matrixVFastI[j + jOne]) - overRelaxCoefficient5[i] * matrixVFastI[j] + coefficient1[i] * matrixVFastI[j + nZColumn * iOne] + coefficient3[i] * (signPlus * matrixVPFastI[j] + signMinus * matrixVMFastI[j]) + sumChargeDensityFastI[j]) * overRelaxCoefficient4[i]; - matrixVFastI[j] = resFast; - // Note: over-relax the solution at each step. This speeds up the convergence. - } // end j - } //end i - } // end phi - - // After full solution is achieved, copy low resolution solution into higher res array - if (k == maxIteration) { - for (Int_t i = iOne; i < nRRow - 1; i += iOne) { - for (Int_t j = jOne; j < nZColumn - 1; j += jOne) { - - if (iOne > 1) { - matrixV(i + iOne / 2, j) = (matrixV(i + iOne, j) + matrixV(i, j)) / 2; - if (i == iOne) { - matrixV(i - iOne / 2, j) = (matrixV(0, j) + matrixV(iOne, j)) / 2; - } - } - if (jOne > 1) { - matrixV(i, j + jOne / 2) = (matrixV(i, j + jOne) + matrixV(i, j)) / 2; - if (j == jOne) { - matrixV(i, j - jOne / 2) = (matrixV(i, 0) + matrixV(i, jOne)) / 2; - } - } - if (iOne > 1 && jOne > 1) { - matrixV(i + iOne / 2, j + jOne / 2) = (matrixV(i + iOne, j + jOne) + matrixV(i, j)) / 2; - if (i == iOne) { - matrixV(i - iOne / 2, j - jOne / 2) = (matrixV(0, j - jOne) + matrixV(iOne, j)) / 2; - } - if (j == jOne) { - matrixV(i - iOne / 2, j - jOne / 2) = (matrixV(i - iOne, 0) + matrixV(i, jOne)) / 2; - } - // Note that this leaves a point at the upper left and lower right corners uninitialized. Not a big deal. - } - } - } - } - } - - if (count == nLoop - 1) { - - (*fErrorConvergenceNormInf)(k - 1) = GetConvergenceError(matricesV, prevArrayV, phiSlice); - (*fError)(k - 1) = GetExactError(matricesV, prevArrayV, phiSlice); - - // if error already achieved then stop mg iteration - fIterations = k - 1; - if ((*fErrorConvergenceNormInf)(k - 1) <= fgConvergenceError) { - Info("PoissonRelaxation3D", "%s", Form("Exact Err: %f, Iteration : %d", (*fError)(k - 1), k - 1)); - break; - } - if (k == maxIteration) { - Info("PoissonRelaxation3D", "%s", Form("Exact Err: %f, Iteration : %d", (*fError)(k - 1), k - 1)); - } - } - } - - iOne = iOne / 2; - if (iOne < 1) { - iOne = 1; - } - jOne = jOne / 2; - if (jOne < 1) { - jOne = 1; - } - } - - for (Int_t k = 0; k < phiSlice; k++) { - matricesSumChargeDensity[k]->Delete(); - } - - for (Int_t m = 0; m < phiSlice; m++) { - delete prevArrayV[m]; - } -} - -/// 3D - Solve Poisson's Equation in 3D by MultiGrid with constant phi slices -/// -/// NOTE: In order for this algorithm to work, the number of nRRow and nZColumn must be a power of 2 plus one. -/// The number of nRRow and Z Column can be different. -/// -/// R Row == 2**M + 1 -/// Z Column == 2**N + 1 -/// Phi Slice == Arbitrary but greater than 3 -/// -/// Solving: \f$ \nabla^{2}V(r,\phi,z) = - f(r,\phi,z) \f$ -/// -/// Algorithm for MultiGrid Full Cycle (FMG) -/// - Relax on the coarsest grid -/// - Do from coarsest to finest -/// - Interpolate potential from coarse -> fine -/// - Do V-Cycle to the current coarse level to the coarsest -/// - Stop if converged -/// -/// DeltaPhi in Radians -/// \param matricesV TMatrixD** potential in 3D matrix \f$ V(r,\phi,z) \f$ -/// \param matricesCharge TMatrixD** charge density in 3D matrix (side effect) \f$ - f(r,\phi,z) \f$ -/// \param nRRow Int_t number of nRRow in the r direction of TPC -/// \param nZColumn Int_t number of nZColumn in z direction of TPC -/// \param phiSlice Int_t number of phiSlice in phi direction of T{C -/// \param maxIteration Int_t maximum iteration for relaxation method (NOT USED) -/// \param symmetry Int_t symmetry (TODO for symmetry = 1) -// -/// SYMMETRY = 0 if no phi symmetries, and no phi boundary condition -/// = 1 if we have reflection symmetry at the boundaries (eg. sector symmetry or half sector symmetries). -/// -void AliTPCPoissonSolver::PoissonMultiGrid3D2D(TMatrixD** matricesV, TMatrixD** matricesCharge, Int_t nRRow, - Int_t nZColumn, Int_t phiSlice, Int_t symmetry) -{ - - const Float_t gridSizeR = - (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (nRRow - 1); // h_{r} - const Float_t gridSizePhi = TMath::TwoPi() / phiSlice; // h_{phi} - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (nZColumn - 1); // h_{z} - const Float_t ratioPhi = - gridSizeR * gridSizeR / (gridSizePhi * gridSizePhi); // ratio_{phi} = gridSize_{r} / gridSize_{phi} - const Float_t ratioZ = gridSizeR * gridSizeR / (gridSizeZ * gridSizeZ); // ratio_{Z} = gridSize_{r} / gridSize_{z} - - // error tolerate - //const Float_t ERR = 1e-8; - Double_t convergenceError; - - Info("PoissonMultiGrid3D2D", "%s", Form("in Poisson Solver 3D multiGrid semi coarsening nRRow=%d, cols=%d, phiSlice=%d \n", nRRow, nZColumn, phiSlice)); - - // Check that the number of nRRow and nZColumn is suitable for a binary expansion - if (!IsPowerOfTwo((nRRow - 1))) { - Error("PoissonMultiGrid3D2D", "Poisson3DMultiGrid - Error in the number of nRRow. Must be 2**M + 1"); - return; - } - if (!IsPowerOfTwo((nZColumn - 1))) { - Error("PoissonMultiGrid3D2D", "Poisson3DMultiGrid - Error in the number of nZColumn. Must be 2**N - 1"); - return; - } - if (phiSlice <= 3) { - Error("PoissonMultiGrid3D2D", "Poisson3DMultiGrid - Error in the number of phiSlice. Must be larger than 3"); - return; - } - if (phiSlice > 1000) { - Error("PoissonMultiGrid3D2D", "Poisson3D phiSlice > 1000 is not allowed (nor wise) "); - return; - } - - // Solve Poisson's equation in cylindrical coordinates by multiGrid technique - // Allow for different size grid spacing in R and Z directions - - Int_t nGridRow = 0; // number grid - Int_t nGridCol = 0; // number grid - Int_t nnRow; - Int_t nnCol; - - nnRow = nRRow; - while (nnRow >>= 1) { - nGridRow++; - } - nnCol = nZColumn; - while (nnCol >>= 1) { - nGridCol++; - } - - Int_t nLoop = TMath::Max(nGridRow, nGridCol); // Calculate the number of nLoop for the binary expansion - nLoop = (nLoop > fMgParameters.maxLoop) ? fMgParameters.maxLoop : nLoop; - Int_t count; - Int_t iOne = 1; // index i in gridSize r (original) - Int_t jOne = 1; // index j in gridSize z (original) - Int_t tnRRow = nRRow, tnZColumn = nZColumn; - std::vector tvChargeFMG(nLoop); // charge is restricted in full multiGrid - std::vector tvArrayV(nLoop); // potential <--> error - std::vector tvCharge(nLoop); // charge <--> residue - std::vector tvResidue(nLoop); // residue calculation - std::vector tvPrevArrayV(nLoop); // error calculation - - for (count = 1; count <= nLoop; count++) { - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - tvResidue[count - 1] = new TMatrixD*[phiSlice]; - tvPrevArrayV[count - 1] = new TMatrixD*[phiSlice]; - for (Int_t k = 0; k < phiSlice; k++) { - tvResidue[count - 1][k] = new TMatrixD(tnRRow, tnZColumn); - tvPrevArrayV[count - 1][k] = new TMatrixD(tnRRow, tnZColumn); - } - - // memory for the finest grid is from parameters - if (count == 1) { - tvChargeFMG[count - 1] = matricesCharge; - tvArrayV[count - 1] = matricesV; - tvCharge[count - 1] = matricesCharge; - } else { - // allocate for coarser grid - tvChargeFMG[count - 1] = new TMatrixD*[phiSlice]; - tvArrayV[count - 1] = new TMatrixD*[phiSlice]; - tvCharge[count - 1] = new TMatrixD*[phiSlice]; - for (Int_t k = 0; k < phiSlice; k++) { - tvArrayV[count - 1][k] = new TMatrixD(tnRRow, tnZColumn); - tvCharge[count - 1][k] = new TMatrixD(tnRRow, tnZColumn); - tvChargeFMG[count - 1][k] = new TMatrixD(tnRRow, tnZColumn); - } - Restrict3D(tvChargeFMG[count - 1], tvChargeFMG[count - 2], tnRRow, tnZColumn, phiSlice, phiSlice); - RestrictBoundary3D(tvArrayV[count - 1], tvArrayV[count - 2], tnRRow, tnZColumn, phiSlice, phiSlice); - } - iOne = 2 * iOne; // doubling - jOne = 2 * jOne; // doubling - } - Float_t h, h2, radius; - Float_t tempRatioPhi, tempRatioZ; - std::vector coefficient1( - nRRow); // coefficient1(nRRow) for storing (1 + h_{r}/2r_{i}) from central differences in r direction - std::vector coefficient2( - nRRow); // coefficient2(nRRow) for storing (1 + h_{r}/2r_{i}) from central differences in r direction - std::vector coefficient3( - nRRow); // coefficient3(nRRow) for storing (1/r_{i}^2) from central differences in phi direction - std::vector coefficient4(nRRow); // coefficient4(nRRow) for storing 1/2 - std::vector inverseCoefficient4(nRRow); // inverse of coefficient4(nRRow) - - // Case full multi grid (FMG) - if (fMgParameters.cycleType == kFCycle) { - - // 1) Relax on the coarsest grid - iOne = iOne / 2; - jOne = jOne / 2; - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - - h = gridSizeR * iOne; - h2 = h * h; - - tempRatioPhi = ratioPhi * iOne * iOne; // Used tobe divided by ( m_one * m_one ) when m_one was != 1 - tempRatioZ = ratioZ * iOne * iOne / (jOne * jOne); - - for (Int_t i = 1; i < tnRRow - 1; i++) { - radius = AliTPCPoissonSolver::fgkIFCRadius + i * h; - coefficient1[i] = 1.0 + h / (2 * radius); - coefficient2[i] = 1.0 - h / (2 * radius); - coefficient3[i] = tempRatioPhi / (radius * radius); - coefficient4[i] = 0.5 / (1.0 + tempRatioZ + coefficient3[i]); - } - // relax on the coarsest level - Relax3D(tvArrayV[nLoop - 1], tvChargeFMG[nLoop - 1], tnRRow, tnZColumn, phiSlice, symmetry, h2, tempRatioZ, - coefficient1, - coefficient2, coefficient3, coefficient4); - // 2) Do multiGrid v-cycle from coarsest to finest - for (count = nLoop - 2; count >= 0; count--) { - // move to finer grid - iOne = iOne / 2; - jOne = jOne / 2; - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - // 2) a) Interpolate potential for h -> 2h (coarse -> fine) - Interp3D(tvArrayV[count], tvArrayV[count + 1], tnRRow, tnZColumn, phiSlice, phiSlice); - // 2) c) Copy the restricted charge to charge for calculation - for (Int_t m = 0; m < phiSlice; m++) { - *tvCharge[count][m] = *tvChargeFMG[count][m]; //copy - } - // 2) c) Do V cycle fMgParameters.nMGCycle times at most - for (Int_t mgCycle = 0; mgCycle < fMgParameters.nMGCycle; mgCycle++) { - // Copy the potential to temp array for convergence calculation - for (Int_t m = 0; m < phiSlice; m++) { - *tvPrevArrayV[count][m] = *tvArrayV[count][m]; //copy - } - // 2) c) i) Call V cycle from grid count+1 (current fine level) to nLoop (coarsest) - VCycle3D2D(nRRow, nZColumn, phiSlice, symmetry, count + 1, nLoop, fMgParameters.nPre, fMgParameters.nPost, - gridSizeR, ratioZ, ratioPhi, tvArrayV, tvCharge, tvResidue, coefficient1, coefficient2, coefficient3, - coefficient4, inverseCoefficient4); - - convergenceError = GetConvergenceError(tvArrayV[count], tvPrevArrayV[count], phiSlice); - - if (count == 0) { - (*fErrorConvergenceNormInf)(mgCycle) = convergenceError; - (*fError)(mgCycle) = GetExactError(matricesV, tvPrevArrayV[count], phiSlice); - } - /// if already converge just break move to finer grid - if (convergenceError <= fgConvergenceError) { - fIterations = mgCycle + 1; - break; - } - } - } - } // Case V multi grid (VMG) - else if (fMgParameters.cycleType == kVCycle) { - Int_t gridFrom = 1; - Int_t gridTo = nLoop; - // do v cycle fMgParameters.nMGCycle from the coarsest to finest - for (Int_t mgCycle = 0; mgCycle < fMgParameters.nMGCycle; mgCycle++) { - // copy to store previous potential - for (Int_t m = 0; m < phiSlice; m++) { - *tvPrevArrayV[0][m] = *tvArrayV[0][m]; //copy - } - // Do V Cycle for constant phiSlice - VCycle3D2D(nRRow, nZColumn, phiSlice, symmetry, gridFrom, gridTo, fMgParameters.nPre, fMgParameters.nPost, - gridSizeR, ratioZ, ratioPhi, tvArrayV, tvCharge, tvResidue, coefficient1, coefficient2, coefficient3, - coefficient4, inverseCoefficient4); - - // convergence error - convergenceError = GetConvergenceError(tvArrayV[0], tvPrevArrayV[0], phiSlice); - (*fErrorConvergenceNormInf)(mgCycle) = convergenceError; - (*fError)(mgCycle) = GetExactError(matricesV, tvPrevArrayV[0], phiSlice); - - // if error already achieved then stop mg iteration - if (convergenceError <= fgConvergenceError) { - fIterations = mgCycle + 1; - break; - } - } - } - // Deallocate memory - for (count = 1; count <= nLoop; count++) { - delete[] tvResidue[count - 1]; - delete[] tvPrevArrayV[count - 1]; - - if (count > 1) { - delete[] tvChargeFMG[count - 1]; - delete[] tvArrayV[count - 1]; - delete[] tvCharge[count - 1]; - } - } -} - -/// 3D - Solve Poisson's Equation in 3D in all direction by MultiGrid -/// -/// NOTE: In order for this algorithm to work, the number of nRRow and nZColumn must be a power of 2 plus one. -/// The number of nRRow and Z Column can be different. -/// -/// R Row == 2**M + 1 -/// Z Column == 2**N + 1 -/// Phi Slices == Arbitrary but greater than 3 -/// -/// Solving: \f$ \nabla^{2}V(r,\phi,z) = - f(r,\phi,z) \f$ -/// -/// Algorithm for MultiGrid Full Cycle (FMG) -/// - Relax on the coarsest grid -/// - Do from coarsest to finest -/// - Interpolate potential from coarse -> fine -/// - Do V-Cycle to the current coarse level to the coarsest -/// - Stop if converged -/// -/// DeltaPhi in Radians -/// \param matricesV TMatrixD** potential in 3D matrix -/// \param matricesCharge TMatrixD** charge density in 3D matrix (side effect) -/// \param nRRow Int_t number of nRRow in the r direction of TPC -/// \param nZColumn Int_t number of nZColumn in z direction of TPC -/// \param phiSlice Int_t number of phiSlice in phi direction of T{C -/// \param maxIteration Int_t maximum iteration for relaxation method -/// \param symmetry Int_t symmetry or not -// -/// SYMMETRY = 0 if no phi symmetries, and no phi boundary condition -/// = 1 if we have reflection symmetry at the boundaries (eg. sector symmetry or half sector symmetries). -/// -void AliTPCPoissonSolver::PoissonMultiGrid3D(TMatrixD** matricesV, TMatrixD** matricesCharge, - Int_t nRRow, Int_t nZColumn, Int_t phiSlice, Int_t symmetry) -{ - - const Float_t gridSizeR = - (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (nRRow - 1); // h_{r} - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (nZColumn - 1); // h_{z} - const Float_t ratioZ = gridSizeR * gridSizeR / (gridSizeZ * gridSizeZ); // ratio_{Z} = gridSize_{r} / gridSize_{z} - - Float_t gridSizePhi = TMath::TwoPi() / phiSlice; // h_{phi} - Float_t h, h2, radius; - Float_t tempRatioPhi, tempRatioZ; - - Float_t convergenceError; // Convergence error - - Info("PoissonMultiGrid3D", "%s", Form("in Poisson Solver 3D multi grid full coarsening nRRow=%d, cols=%d, phiSlice=%d \n", nRRow, nZColumn, phiSlice)); - - // Check that the number of nRRow and nZColumn is suitable for a binary expansion - if (!IsPowerOfTwo((nRRow - 1))) { - Error("PoissonMultiGrid3D", "Poisson3DMultiGrid - Error in the number of nRRow. Must be 2**M + 1"); - return; - } - if (!IsPowerOfTwo((nZColumn - 1))) { - Error("PoissonMultiGrid3D", "Poisson3DMultiGrid - Error in the number of nZColumn. Must be 2**N - 1"); - return; - } - if (phiSlice <= 3) { - Error("PoissonMultiGrid3D", "Poisson3DMultiGrid - Error in the number of phiSlice. Must be larger than 3"); - return; - } - if (phiSlice > 1000) { - Error("PoissonMultiGrid3D", "Poisson3D phiSlice > 1000 is not allowed (nor wise) "); - return; - } - - // Solve Poisson's equation in cylindrical coordinates by multi grid technique - // Allow for different size grid spacing in R and Z directions - - Int_t nGridRow = 0; // number grid - Int_t nGridCol = 0; // number grid - Int_t nGridPhi = 0; - - Int_t nnRow; - Int_t nnCol; - Int_t nnPhi; - - nnRow = nRRow; - while (nnRow >>= 1) { - nGridRow++; - } - - nnCol = nZColumn; - while (nnCol >>= 1) { - nGridCol++; - } - - nnPhi = phiSlice; - - while (nnPhi % 2 == 0) { - nGridPhi++; - nnPhi /= 2; - } - - Info("PoissonMultiGrid3D", "%s", Form("nGridRow=%d, nGridCol=%d, nGridPhi=%d", nGridRow, nGridCol, nGridPhi)); - Int_t nLoop = TMath::Max(nGridRow, nGridCol); // Calculate the number of nLoop for the binary expansion - nLoop = TMath::Max(nLoop, nGridPhi); - - // Vector for storing multi grid array - Int_t iOne = 1; // index i in gridSize r (original) - Int_t jOne = 1; // index j in gridSize z (original) - Int_t kOne = 1; // index k in gridSize phi - Int_t tnRRow = nRRow, tnZColumn = nZColumn, tPhiSlice = phiSlice, otPhiSlice; - - // 1) Memory allocation for multi grid - std::vector tvChargeFMG(nLoop); // charge is restricted in full multiGrid - std::vector tvArrayV(nLoop); // potential <--> error - std::vector tvCharge(nLoop); // charge <--> residue - std::vector tvResidue(nLoop); // residue calculation - std::vector tvPrevArrayV(nLoop); // error calculation - - // these vectors for storing the coefficients in smoother - std::vector coefficient1( - nRRow); // coefficient1(nRRow) for storing (1 + h_{r}/2r_{i}) from central differences in r direction - std::vector coefficient2( - nRRow); // coefficient2(nRRow) for storing (1 + h_{r}/2r_{i}) from central differences in r direction - std::vector coefficient3( - nRRow); // coefficient3(nRRow) for storing (1/r_{i}^2) from central differences in phi direction - std::vector coefficient4(nRRow); // coefficient4(nRRow) for storing 1/2 - std::vector inverseCoefficient4(nRRow); // inverse of coefficient4(nRRow) - - for (Int_t count = 1; count <= nLoop; count++) { - - // tnRRow,tnZColumn in new grid - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - tPhiSlice = kOne == 1 ? phiSlice : phiSlice / kOne; - tPhiSlice = tPhiSlice < nnPhi ? nnPhi : tPhiSlice; - - // allocate memory for residue - tvResidue[count - 1] = new TMatrixD*[tPhiSlice]; - tvPrevArrayV[count - 1] = new TMatrixD*[tPhiSlice]; - for (Int_t k = 0; k < tPhiSlice; k++) { - tvResidue[count - 1][k] = new TMatrixD(tnRRow, tnZColumn); - tvPrevArrayV[count - 1][k] = new TMatrixD(tnRRow, tnZColumn); - } - - // memory for the finest grid is from parameters - if (count == 1) { - tvChargeFMG[count - 1] = matricesCharge; - tvArrayV[count - 1] = matricesV; - tvCharge[count - 1] = matricesCharge; - } else { - // allocate for coarser grid - tvChargeFMG[count - 1] = new TMatrixD*[tPhiSlice]; - tvArrayV[count - 1] = new TMatrixD*[tPhiSlice]; - tvCharge[count - 1] = new TMatrixD*[tPhiSlice]; - for (Int_t k = 0; k < tPhiSlice; k++) { - tvArrayV[count - 1][k] = new TMatrixD(tnRRow, tnZColumn); - tvCharge[count - 1][k] = new TMatrixD(tnRRow, tnZColumn); - tvChargeFMG[count - 1][k] = new TMatrixD(tnRRow, tnZColumn); - } - } - iOne = 2 * iOne; // doubling - jOne = 2 * jOne; // doubling - kOne = 2 * kOne; - } - - // Case full multi grid (FMG) - - if (fMgParameters.cycleType == kFCycle) { - // Restrict the charge to coarser grid - iOne = 2; - jOne = 2; - kOne = 2; - otPhiSlice = phiSlice; - - // 1) Restrict Charge and Boundary to coarser grid - for (Int_t count = 2; count <= nLoop; count++) { - // tnRRow,tnZColumn in new grid - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - tPhiSlice = kOne == 1 ? phiSlice : phiSlice / kOne; - tPhiSlice = tPhiSlice < nnPhi ? nnPhi : tPhiSlice; - - Info("PoissonMultiGrid3D", "%s", Form("Restrict3D, tnRRow=%d, tnZColumn=%d, newPhiSlice=%d, oldPhiSlice=%d\n", tnRRow, tnZColumn, tPhiSlice, otPhiSlice)); - Restrict3D(tvChargeFMG[count - 1], tvChargeFMG[count - 2], tnRRow, tnZColumn, tPhiSlice, otPhiSlice); - // copy boundary values of V - RestrictBoundary3D(tvArrayV[count - 1], tvArrayV[count - 2], tnRRow, tnZColumn, tPhiSlice, otPhiSlice); - otPhiSlice = tPhiSlice; - - iOne = 2 * iOne; // doubling - jOne = 2 * jOne; // doubling - kOne = 2 * kOne; - } - - // Relax on the coarsest grid - // FMG - // 2) Relax on the coarsest grid - - // move to the coarsest + 1 - iOne = iOne / 2; - jOne = jOne / 2; - kOne = kOne / 2; - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - tPhiSlice = kOne == 1 ? phiSlice : phiSlice / kOne; - tPhiSlice = tPhiSlice < nnPhi ? nnPhi : tPhiSlice; - otPhiSlice = tPhiSlice; - - h = gridSizeR * iOne; - h2 = h * h; - gridSizePhi = TMath::TwoPi() / tPhiSlice; // h_{phi} - tempRatioPhi = h * h / (gridSizePhi * gridSizePhi); // ratio_{phi} = gridSize_{r} / gridSize_{phi} - tempRatioZ = ratioZ * iOne * iOne / (jOne * jOne); - for (Int_t i = 1; i < tnRRow - 1; i++) { - radius = AliTPCPoissonSolver::fgkIFCRadius + i * h; - coefficient1[i] = 1.0 + h / (2 * radius); - coefficient2[i] = 1.0 - h / (2 * radius); - coefficient3[i] = tempRatioPhi / (radius * radius); - coefficient4[i] = 0.5 / (1.0 + tempRatioZ + coefficient3[i]); - } - // 3) Relax on the coarsest grid - Relax3D(tvArrayV[nLoop - 1], tvChargeFMG[nLoop - 1], tnRRow, tnZColumn, tPhiSlice, symmetry, h2, tempRatioZ, - coefficient1, - coefficient2, coefficient3, coefficient4); - - // 4) V Cycle from coarsest to finest - for (Int_t count = nLoop - 2; count >= 0; count--) { - // move to finer grid - coefficient1.clear(); - coefficient2.clear(); - coefficient3.clear(); - coefficient4.clear(); - inverseCoefficient4.clear(); - - iOne = iOne / 2; - jOne = jOne / 2; - kOne = kOne / 2; - - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - tPhiSlice = kOne == 1 ? phiSlice : phiSlice / kOne; - tPhiSlice = tPhiSlice < nnPhi ? nnPhi : tPhiSlice; - // 4) a) interpolate from 2h --> h grid - Interp3D(tvArrayV[count], tvArrayV[count + 1], tnRRow, tnZColumn, tPhiSlice, otPhiSlice); - - // Copy the relax charge to the tvCharge - if (count > 0) { - for (Int_t m = 0; m < tPhiSlice; m++) { - *tvCharge[count][m] = *tvChargeFMG[count][m]; //copy - } - } - for (Int_t mgCycle = 0; mgCycle < fMgParameters.nMGCycle; mgCycle++) { - // copy to store previous potential - for (Int_t m = 0; m < tPhiSlice; m++) { - *tvPrevArrayV[count][m] = *tvArrayV[count][m]; //copy - } - - VCycle3D(nRRow, nZColumn, phiSlice, symmetry, count + 1, nLoop, fMgParameters.nPre, fMgParameters.nPost, - gridSizeR, ratioZ, tvArrayV, - tvCharge, tvResidue, coefficient1, coefficient2, coefficient3, coefficient4, inverseCoefficient4); - - /// converge error - convergenceError = GetConvergenceError(tvArrayV[count], tvPrevArrayV[count], tPhiSlice); - //// error counting ///// - if (count == 0) { - (*fErrorConvergenceNormInf)(mgCycle) = convergenceError; - (*fError)(mgCycle) = GetExactError(matricesV, tvPrevArrayV[count], phiSlice); - } - /// if already converge just break move to finer grid - if (convergenceError <= fgConvergenceError) { - fIterations = mgCycle + 1; - break; - } - } - // keep old slice information - otPhiSlice = tPhiSlice; - } - - } else if (fMgParameters.cycleType == kVCycle) { - // V-cycle - Int_t gridFrom = 1; - Int_t gridTo = nLoop; - - for (Int_t mgCycle = 0; mgCycle < fMgParameters.nMGCycle; mgCycle++) { - // copy to store previous potential - for (Int_t m = 0; m < phiSlice; m++) { - *tvPrevArrayV[0][m] = *tvArrayV[0][m]; //copy - } - // Do V Cycle from the coarsest to finest grid - VCycle3D(nRRow, nZColumn, phiSlice, symmetry, gridFrom, gridTo, fMgParameters.nPre, fMgParameters.nPost, - gridSizeR, ratioZ, tvArrayV, tvCharge, tvResidue, - coefficient1, coefficient2, coefficient3, coefficient4, inverseCoefficient4); - // convergence error - convergenceError = GetConvergenceError(tvArrayV[0], tvPrevArrayV[0], phiSlice); - (*fErrorConvergenceNormInf)(mgCycle) = convergenceError; - (*fError)(mgCycle) = GetExactError(matricesV, tvPrevArrayV[0], phiSlice); - // if error already achieved then stop mg iteration - if (convergenceError <= fgConvergenceError) { - //Info("PoissonMultiGrid3D",Form("Exact Err: %f, MG Iteration : %d", (*fError)(mgCycle), mgCycle)); - fIterations = mgCycle + 1; - break; - } - } - } - // deallocate memory for multiGrid - for (Int_t count = 1; count <= nLoop; count++) { - delete[] tvResidue[count - 1]; - delete[] tvPrevArrayV[count - 1]; - if (count > 1) { - delete[] tvChargeFMG[count - 1]; - delete[] tvArrayV[count - 1]; - delete[] tvCharge[count - 1]; - } - } -} - -/// Helper function to check if the integer is equal to a power of two -/// \param i Int_t the number -/// \return 1 if it is a power of two, else 0 -Int_t AliTPCPoissonSolver::IsPowerOfTwo(Int_t i) const -{ - Int_t j = 0; - while (i > 0) { - j += (i & 1); - i = (i >> 1); - } - if (j == 1) { - return (1); // True - } - return (0); // False -} - -/// Relax3D -/// -/// Relaxation operation for multiGrid -/// relaxation used 7 stencil in cylindrical coordinate -/// -/// Using the following equations -/// \f$ U_{i,j,k} = (1 + \frac{1}{r_{i}h_{r}}) U_{i+1,j,k} + (1 - \frac{1}{r_{i}h_{r}}) U_{i+1,j,k} \f$ -/// -/// \param matricesCurrentV TMatrixD** potential in 3D (matrices of matrix) -/// \param matricesCurrentCharge TMatrixD** charge in 3D -/// \param nRRow const Int_t number of nRRow in the r direction of TPC -/// \param nZColumn const Int_t number of nZColumn in z direction of TPC -/// \param phiSlice const Int_t number of phiSlice in phi direction of TPC -/// \param symmetry const Int_t is the cylinder has symmetry -/// \param h2 const Float_t \f$ h_{r}^{2} \f$ -/// \param tempRatioZ const Float_t ration between grid size in z-direction and r-direction -/// \param coefficient1 std::vector coefficient for \f$ V_{x+1,y,z} \f$ -/// \param coefficient2 std::vector coefficient for \f$ V_{x-1,y,z} \f$ -/// \param coefficient3 std::vector coefficient for z -/// \param coefficient4 std::vector coefficient for f(r,\phi,z) -/// -void AliTPCPoissonSolver::Relax3D(TMatrixD** matricesCurrentV, TMatrixD** matricesCurrentCharge, const Int_t tnRRow, - const Int_t tnZColumn, - const Int_t phiSlice, const Int_t symmetry, const Float_t h2, - const Float_t tempRatioZ, std::vector& coefficient1, - std::vector& coefficient2, - std::vector& coefficient3, std::vector& coefficient4) -{ - - Int_t mPlus, mMinus, signPlus, signMinus; - TMatrixD* matrixV; - TMatrixD* matrixVP; - TMatrixD* matrixVM; - TMatrixD* arrayCharge; - - // Gauss-Seidel (Read Black} - if (fMgParameters.relaxType == kGaussSeidel) { - // for each slice - Int_t isw, jsw, msw; - msw = 1; - for (Int_t iPass = 1; iPass <= 2; iPass++, msw = 3 - msw) { - jsw = msw; - for (Int_t m = 0; m < phiSlice; m++, jsw = 3 - jsw) { - mPlus = m + 1; - signPlus = 1; - mMinus = m - 1; - signMinus = 1; - // Reflection symmetry in phi (e.g. symmetry at sector boundaries, or half sectors, etc.) - if (symmetry == 1) { - if (mPlus > phiSlice - 1) { - mPlus = phiSlice - 2; - } - if (mMinus < 0) { - mMinus = 1; - } - } - // Anti-symmetry in phi - else if (symmetry == -1) { - if (mPlus > phiSlice - 1) { - mPlus = phiSlice - 2; - signPlus = -1; - } - if (mMinus < 0) { - mMinus = 1; - signMinus = -1; - } - } else { // No Symmetries in phi, no boundaries, the calculation is continuous across all phi - if (mPlus > phiSlice - 1) { - mPlus = m + 1 - phiSlice; - } - if (mMinus < 0) { - mMinus = m - 1 + phiSlice; - } - } - matrixV = matricesCurrentV[m]; - matrixVP = matricesCurrentV[mPlus]; // slice - matrixVM = matricesCurrentV[mMinus]; // slice - arrayCharge = matricesCurrentCharge[m]; - - isw = jsw; - for (Int_t j = 1; j < tnZColumn - 1; j++, isw = 3 - isw) { - for (Int_t i = isw; i < tnRRow - 1; i += 2) { - //Info("Relax3D",Form("Doing slice %d, z=%d, r=%d", m,j,i)); - (*matrixV)(i, j) = (coefficient2[i] * (*matrixV)(i - 1, j) + tempRatioZ * ((*matrixV)(i, j - 1) + (*matrixV)(i, j + 1)) + coefficient1[i] * (*matrixV)(i + 1, j) + coefficient3[i] * (signPlus * (*matrixVP)(i, j) + signMinus * (*matrixVM)(i, j)) + (h2 * (*arrayCharge)(i, j))) * coefficient4[i]; - } // end cols - } // end nRRow - } // end phi - } // end sweep - } else if (fMgParameters.relaxType == kJacobi) { - // for each slice - for (Int_t m = 0; m < phiSlice; m++) { - - mPlus = m + 1; - signPlus = 1; - mMinus = m - 1; - signMinus = 1; - - // Reflection symmetry in phi (e.g. symmetry at sector boundaries, or half sectors, etc.) - if (symmetry == 1) { - if (mPlus > phiSlice - 1) { - mPlus = phiSlice - 2; - } - if (mMinus < 0) { - mMinus = 1; - } - } - // Anti-symmetry in phi - else if (symmetry == -1) { - if (mPlus > phiSlice - 1) { - mPlus = phiSlice - 2; - signPlus = -1; - } - if (mMinus < 0) { - mMinus = 1; - signMinus = -1; - } - } else { // No Symmetries in phi, no boundaries, the calculation is continuous across all phi - if (mPlus > phiSlice - 1) { - mPlus = m + 1 - phiSlice; - } - if (mMinus < 0) { - mMinus = m - 1 + phiSlice; - } - } - - matrixV = matricesCurrentV[m]; - matrixVP = matricesCurrentV[mPlus]; // slice - matrixVM = matricesCurrentV[mMinus]; // slice - arrayCharge = matricesCurrentCharge[m]; - - // Jacobian - for (Int_t j = 1; j < tnZColumn - 1; j++) { - for (Int_t i = 1; i < tnRRow - 1; i++) { - (*matrixV)(i, j) = (coefficient2[i] * (*matrixV)(i - 1, j) + tempRatioZ * ((*matrixV)(i, j - 1) + (*matrixV)(i, j + 1)) + coefficient1[i] * (*matrixV)(i + 1, j) + coefficient3[i] * (signPlus * (*matrixVP)(i, j) + signMinus * (*matrixVM)(i, j)) + (h2 * (*arrayCharge)(i, j))) * coefficient4[i]; - - } // end cols - } // end nRRow - - } // end phi - - } else { - // Case weighted Jacobi - // TODO - } -} - -/// Relax2D -/// -/// Relaxation operation for multiGrid -/// relaxation used 5 stencil in cylindrical coordinate -/// -/// Using the following equations -/// \f$ U_{i,j,k} = (1 + \frac{1}{r_{i}h_{r}}) U_{i+1,j,k} + (1 - \frac{1}{r_{i}h_{r}}) U_{i+1,j,k} \f$ -/// -/// \param matricesCurrentV TMatrixD& potential in 3D (matrices of matrix) -/// \param matricesCurrentCharge TMatrixD& charge in 3D -/// \param nRRow const Int_t number of nRRow in the r direction of TPC -/// \param nZColumn const Int_t number of nZColumn in z direction of TPC -/// \param phiSlice const Int_t number of phiSlice in phi direction of TPC -/// \param symmetry const Int_t is the cylinder has symmetry -/// \param h2 const Float_t \f$ h_{r}^{2} \f$ -/// \param tempFourth const Float_t coefficient for h -/// \param tempRatio const Float_t ratio between grid size in z-direction and r-direction -/// \param coefficient1 std::vector coefficient for \f$ V_{x+1,y,z} \f$ -/// \param coefficient2 std::vector coefficient for \f$ V_{x-1,y,z} \f$ -/// -void AliTPCPoissonSolver::Relax2D(TMatrixD& matricesCurrentV, TMatrixD& matricesCurrentCharge, const Int_t tnRRow, - const Int_t tnZColumn, - const Float_t h2, const Float_t tempFourth, const Float_t tempRatio, - std::vector& coefficient1, std::vector& coefficient2) -{ - - // Gauss-Seidel - if (fMgParameters.relaxType == kGaussSeidel) { - - Int_t isw, jsw = 1; - for (Int_t iPass = 1; iPass <= 2; iPass++, jsw = 3 - jsw) { - isw = jsw; - for (Int_t j = 1; j < tnZColumn - 1; j++, isw = 3 - isw) { - for (Int_t i = isw; i < tnRRow - 1; i += 2) { - matricesCurrentV(i, j) = tempFourth * (coefficient1[i] * matricesCurrentV(i + 1, j) + - coefficient2[i] * matricesCurrentV(i - 1, j) + - tempRatio * (matricesCurrentV(i, j + 1) + matricesCurrentV(i, j - 1)) + - (h2 * matricesCurrentCharge(i, j))); - } // end cols - } // end nRRow - } // end pass red-black - } else if (fMgParameters.relaxType == kJacobi) { - for (Int_t j = 1; j < tnZColumn - 1; j++) { - for (Int_t i = 1; i < tnRRow - 1; i++) { - matricesCurrentV(i, j) = tempFourth * (coefficient1[i] * matricesCurrentV(i + 1, j) + - coefficient2[i] * matricesCurrentV(i - 1, j) + tempRatio * (matricesCurrentV(i, j + 1) + matricesCurrentV(i, j - 1)) + - (h2 * matricesCurrentCharge(i, j))); - } // end cols - } // end nRRow - } else if (fMgParameters.relaxType == kWeightedJacobi) { - // Weighted Jacobi - // TODO - } -} - -/// Residue3D -/// -/// Compute residue from V(.) where V(.) is numerical potential and f(.). -/// residue used 7 stencil in cylindrical coordinate -/// -/// Using the following equations -/// \f$ U_{i,j,k} = (1 + \frac{1}{r_{i}h_{r}}) U_{i+1,j,k} + (1 - \frac{1}{r_{i}h_{r}}) U_{i+1,j,k} \f$ -/// -/// \param residue TMatrixD** residue in 3D (matrices of matrix) -/// \param matricesCurrentV TMatrixD** potential in 3D (matrices of matrix) -/// \param matricesCurrentCharge TMatrixD** charge in 3D -/// \param nRRow const Int_t number of nRRow in the r direction of TPC -/// \param nZColumn const Int_t number of nZColumn in z direction of TPC -/// \param phiSlice const Int_t number of phiSlice in phi direction of TPC -/// \param symmetry const Int_t is the cylinder has symmetry -/// \param ih2 const Float_t \f$ 1/ h_{r}^{2} \f$ -/// \param tempRatioZ const Float_t ration between grid size in z-direction and r-direction -/// \param coefficient1 std::vector coefficient for \f$ V_{x+1,y,z} \f$ -/// \param coefficient2 std::vector coefficient for \f$ V_{x-1,y,z} \f$ -/// \param coefficient3 std::vector coefficient for z -/// \param inverseCoefficient4 std::vector inverse coefficient for f(r,\phi,z) -/// -void AliTPCPoissonSolver::Residue3D(TMatrixD** residue, TMatrixD** matricesCurrentV, TMatrixD** matricesCurrentCharge, - const Int_t tnRRow, - const Int_t tnZColumn, const Int_t phiSlice, const Int_t symmetry, - const Float_t ih2, - const Float_t tempRatioZ, std::vector& coefficient1, - std::vector& coefficient2, - std::vector& coefficient3, std::vector& inverseCoefficient4) -{ - Int_t mPlus, mMinus, signPlus, signMinus; - for (Int_t m = 0; m < phiSlice; m++) { - - mPlus = m + 1; - signPlus = 1; - mMinus = m - 1; - signMinus = 1; - - // Reflection symmetry in phi (e.g. symmetry at sector boundaries, or half sectors, etc.) - if (symmetry == 1) { - if (mPlus > phiSlice - 1) { - mPlus = phiSlice - 2; - } - if (mMinus < 0) { - mMinus = 1; - } - } - // Anti-symmetry in phi - else if (symmetry == -1) { - if (mPlus > phiSlice - 1) { - mPlus = phiSlice - 2; - signPlus = -1; - } - if (mMinus < 0) { - mMinus = 1; - signMinus = -1; - } - } else { // No Symmetries in phi, no boundaries, the calculation is continuous across all phi - if (mPlus > phiSlice - 1) { - mPlus = m + 1 - phiSlice; - } - if (mMinus < 0) { - mMinus = m - 1 + phiSlice; - } - } - - TMatrixD& arrayResidue = *residue[m]; - TMatrixD& matrixV = *matricesCurrentV[m]; - TMatrixD& matrixVP = *matricesCurrentV[mPlus]; // slice - TMatrixD& matrixVM = *matricesCurrentV[mMinus]; // slice - TMatrixD& arrayCharge = *matricesCurrentCharge[m]; - - for (Int_t j = 1; j < tnZColumn - 1; j++) { - for (Int_t i = 1; i < tnRRow - 1; i++) { - - arrayResidue(i, j) = - ih2 * (coefficient2[i] * matrixV(i - 1, j) + tempRatioZ * (matrixV(i, j - 1) + matrixV(i, j + 1)) + coefficient1[i] * matrixV(i + 1, j) + - coefficient3[i] * (signPlus * matrixVP(i, j) + signMinus * matrixVM(i, j)) - - inverseCoefficient4[i] * matrixV(i, j)) + - arrayCharge(i, j); - - } // end cols - } // end nRRow - - //arrayResidue.Print(); - } -} - -/// Residue2D -/// -/// Compute residue from V(.) where V(.) is numerical potential and f(.). -/// residue used 5 stencil in cylindrical coordinate -/// -/// Using the following equations -/// \f$ U_{i,j,k} = (1 + \frac{1}{r_{i}h_{r}}) U_{i+1,j,k} + (1 - \frac{1}{r_{i}h_{r}}) U_{i+1,j,k} \f$ -/// -/// \param residue TMatrixD& potential in 2D -/// \param matricesCurrentV TMatrixD& potential in 2D -/// \param matricesCurrentCharge TMatrixD& charge in 2D -/// \param nRRow const Int_t number of nRRow in the r direction of TPC -/// \param nZColumn const Int_t number of nZColumn in z direction of TPC -/// \param phiSlice const Int_t number of phiSlice in phi direction of TPC -/// \param symmetry const Int_t is the cylinder has symmetry -/// \param h2 const Float_t \f$ h_{r}^{2} \f$ -/// \param tempFourth const Float_t coefficient for h -/// \param tempRatio const Float_t ratio between grid size in z-direction and r-direction -/// \param coefficient1 std::vector coefficient for \f$ V_{x+1,y,z} \f$ -/// \param coefficient2 std::vector coefficient for \f$ V_{x-1,y,z} \f$ -/// -void AliTPCPoissonSolver::Residue2D(TMatrixD& residue, TMatrixD& matricesCurrentV, TMatrixD& matricesCurrentCharge, - const Int_t tnRRow, - const Int_t tnZColumn, const Float_t ih2, const Float_t inverseTempFourth, - const Float_t tempRatio, std::vector& coefficient1, - std::vector& coefficient2) -{ - for (Int_t i = 1; i < tnRRow - 1; i++) { - for (Int_t j = 1; j < tnZColumn - 1; j++) { - residue(i, j) = ih2 * (coefficient1[i] * matricesCurrentV(i + 1, j) + coefficient2[i] * matricesCurrentV(i - 1, j) + tempRatio * (matricesCurrentV(i, j + 1) + matricesCurrentV(i, j - 1)) - - inverseTempFourth * matricesCurrentV(i, j)) + - matricesCurrentCharge(i, j); - - } // end cols - } // end nRRow - - //Boundary points. - for (Int_t i = 0; i < tnRRow; i++) { - residue(i, 0) = residue(i, tnZColumn - 1) = 0.0; - } - - for (Int_t j = 0; j < tnZColumn; j++) { - residue(0, j) = residue(tnRRow - 1, j) = 0.0; - } -} - -/// Restrict2D -/// -/// Grid transfer operator, restrict from fine -> coarse grid -/// provide full-half weighting -/// -/// \[ \frac{1}{16}\left( \begin{array}{ccc} -/// 1 & 2 & 1 \\ -/// 2 & 4 & 2 \\ -/// 1 & 2 & 1 \end{array} \right) \] -/// -/// \param matricesCurrentCharge TMatrixD& coarse grid (2h) -/// \param residue TMatrixD& fine grid (h) -/// \param nRRow const Int_t number of nRRow in the r direction of TPC -/// \param nZColumn const Int_t number of nZColumn in z direction of TPC -/// -void AliTPCPoissonSolver::Restrict2D(TMatrixD& matricesCurrentCharge, TMatrixD& residue, const Int_t tnRRow, - const Int_t tnZColumn) -{ - - for (Int_t i = 1, ii = 2; i < tnRRow - 1; i++, ii += 2) { - for (Int_t j = 1, jj = 2; j < tnZColumn - 1; j++, jj += 2) { - if (fMgParameters.gtType == kHalf) { - // half - matricesCurrentCharge(i, j) = 0.5 * residue(ii, jj) + - 0.125 * - (residue(ii + 1, jj) + residue(ii - 1, jj) + residue(ii, jj + 1) + - residue(ii, jj - 1)); - - } else - // full - if (fMgParameters.gtType == kFull) { - matricesCurrentCharge(i, j) = 0.25 * residue(ii, jj) + - 0.125 * - (residue(ii + 1, jj) + residue(ii - 1, jj) + residue(ii, jj + 1) + - residue(ii, jj - 1)) + - 0.0625 * - (residue(ii + 1, jj + 1) + residue(ii - 1, jj + 1) + residue(ii + 1, jj - 1) + - residue(ii - 1, jj - 1)); - } - - } // end cols - } // end nRRow - - // boundary - // for boundary - for (Int_t j = 0, jj = 0; j < tnZColumn; j++, jj += 2) { - matricesCurrentCharge(0, j) = residue(0, jj); - matricesCurrentCharge(tnRRow - 1, j) = residue((tnRRow - 1) * 2, jj); - } - - // for boundary - for (Int_t i = 0, ii = 0; i < tnRRow; i++, ii += 2) { - matricesCurrentCharge(i, 0) = residue(ii, 0); - matricesCurrentCharge(i, tnZColumn - 1) = residue(ii, (tnZColumn - 1) * 2); - } -} - -/// RestrictBoundary2D -/// -/// Boundary transfer restrict from fine -> coarse grid -/// -/// \param matricesCurrentCharge TMatrixD& coarse grid (2h) -/// \param residue TMatrixD& fine grid (h) -/// \param nRRow const Int_t number of nRRow in the r direction of TPC -/// \param nZColumn const Int_t number of nZColumn in z direction of TPC -/// -void AliTPCPoissonSolver::RestrictBoundary2D(TMatrixD& matricesCurrentCharge, TMatrixD& residue, const Int_t tnRRow, - const Int_t tnZColumn) -{ - // for boundary - for (Int_t j = 0, jj = 0; j < tnZColumn; j++, jj += 2) { - matricesCurrentCharge(0, j) = residue(0, jj); - matricesCurrentCharge(tnRRow - 1, j) = residue((tnRRow - 1) * 2, jj); - } - - // for boundary - for (Int_t i = 0, ii = 0; i < tnRRow; i++, ii += 2) { - matricesCurrentCharge(i, 0) = residue(ii, 0); - matricesCurrentCharge(i, tnZColumn - 1) = residue(ii, (tnZColumn - 1) * 2); - } -} - -/// Restriction in 3D -/// -/// Restriction is a map from fine grid (h) to coarse grid (2h) -/// -/// In case of 3D -/// Full weighting: -/// \f[ (R u)_{i,j,k} = \frac{1}{2} u_{2i,2j,2k} + \frac{1}{4} S_{1} + \frac{1}{8} S_{2} + \frac{1}{16} S_{3}\f] -/// -/// -/// Restriction in all direction r-phi-z -/// restriction in phi only if oldPhi == 2*newPhi -/// \param matricesCurrentCharge TMatrixD** coarser grid 2h -/// \param residue TMatrixD ** fine grid h -/// \param tnRRow Int_t number of grid in nRRow (in r-direction) for coarser grid should be 2^N + 1, finer grid in 2^{N+1} + 1 -/// \param tnZColumn Int_t number of grid in nZColumn (in z-direction) for coarser grid should be 2^M + 1, finer grid in 2^{M+1} + 1 -/// \param newPhiSlice Int_t number of phiSlice (in phi-direction) for coarser grid -/// \param oldPhiSlice Int_t number of phiSlice (in phi-direction) for finer grid -/// -void AliTPCPoissonSolver::Restrict3D(TMatrixD** matricesCurrentCharge, TMatrixD** residue, const Int_t tnRRow, - const Int_t tnZColumn, - const Int_t newPhiSlice, const Int_t oldPhiSlice) -{ - - Double_t s1, s2, s3; - - if (2 * newPhiSlice == oldPhiSlice) { - - Int_t mPlus, mMinus; - Int_t mm = 0; - - for (Int_t m = 0; m < newPhiSlice; m++, mm += 2) { - - // assuming no symmetry - mPlus = mm + 1; - mMinus = mm - 1; - - if (mPlus > (oldPhiSlice)-1) { - mPlus = mm + 1 - (oldPhiSlice); - } - if (mMinus < 0) { - mMinus = mm - 1 + (oldPhiSlice); - } - - TMatrixD& arrayResidue = *residue[mm]; - TMatrixD& arrayResidueP = *residue[mPlus]; - TMatrixD& arrayResidueM = *residue[mMinus]; // slice - TMatrixD& arrayCharge = *matricesCurrentCharge[m]; - - for (Int_t i = 1, ii = 2; i < tnRRow - 1; i++, ii += 2) { - for (Int_t j = 1, jj = 2; j < tnZColumn - 1; j++, jj += 2) { - - // at the same plane - s1 = arrayResidue(ii + 1, jj) + arrayResidue(ii - 1, jj) + arrayResidue(ii, jj + 1) + - arrayResidue(ii, jj - 1) + arrayResidueP(ii, jj) + arrayResidueM(ii, jj); - s2 = (arrayResidue(ii + 1, jj + 1) + arrayResidue(ii + 1, jj - 1) + arrayResidueP(ii + 1, jj) + - arrayResidueM(ii + 1, jj)) + - (arrayResidue(ii - 1, jj - 1) + arrayResidue(ii - 1, jj + 1) + arrayResidueP(ii - 1, jj) + - arrayResidueM(ii - 1, jj)) + - arrayResidueP(ii, jj - 1) + arrayResidueM(ii, jj + 1) + arrayResidueM(ii, jj - 1) + - arrayResidueP(ii, jj + 1); - - s3 = (arrayResidueP(ii + 1, jj + 1) + arrayResidueP(ii + 1, jj - 1) + arrayResidueM(ii + 1, jj + 1) + - arrayResidueM(ii + 1, jj - 1)) + - (arrayResidueM(ii - 1, jj - 1) + arrayResidueM(ii - 1, jj + 1) + arrayResidueP(ii - 1, jj - 1) + - arrayResidueP(ii - 1, jj + 1)); - - arrayCharge(i, j) = 0.125 * arrayResidue(ii, jj) + 0.0625 * s1 + 0.03125 * s2 + 0.015625 * s3; - } // end cols - } // end nRRow - - // for boundary - for (Int_t j = 0, jj = 0; j < tnZColumn; j++, jj += 2) { - arrayCharge(0, j) = arrayResidue(0, jj); - arrayCharge(tnRRow - 1, j) = arrayResidue((tnRRow - 1) * 2, jj); - } - - // for boundary - for (Int_t i = 0, ii = 0; i < tnRRow; i++, ii += 2) { - arrayCharge(i, 0) = arrayResidue(ii, 0); - arrayCharge(i, tnZColumn - 1) = arrayResidue(ii, (tnZColumn - 1) * 2); - } - } // end phis - - } else { - for (int m = 0; m < newPhiSlice; m++) { - Restrict2D(*matricesCurrentCharge[m], *residue[m], tnRRow, tnZColumn); - } - } -} - -/// Restrict Boundary in 3D -/// -/// Pass boundary information to coarse grid -/// -/// \param matricesCurrentCharge TMatrixD** coarser grid 2h -/// \param residue TMatrixD ** fine grid h -/// \param tnRRow Int_t number of grid in nRRow (in r-direction) for coarser grid should be 2^N + 1, finer grid in 2^{N+1} + 1 -/// \param tnZColumn Int_t number of grid in nZColumn (in z-direction) for coarser grid should be 2^M + 1, finer grid in 2^{M+1} + 1 -/// \param newPhiSlice Int_t number of phiSlice (in phi-direction) for coarser grid -/// \param oldPhiSlice Int_t number of phiSlice (in phi-direction) for finer grid -/// -void AliTPCPoissonSolver::RestrictBoundary3D(TMatrixD** matricesCurrentCharge, TMatrixD** residue, const Int_t tnRRow, - const Int_t tnZColumn, const Int_t newPhiSlice, const Int_t oldPhiSlice) -{ - - // in case of full 3d and the phiSlice is also coarsening - - if (2 * newPhiSlice == oldPhiSlice) { - - for (Int_t m = 0, mm = 0; m < newPhiSlice; m++, mm += 2) { - - TMatrixD& arrayResidue = *residue[mm]; - TMatrixD& arrayCharge = *matricesCurrentCharge[m]; - // for boundary - for (Int_t j = 0, jj = 0; j < tnZColumn; j++, jj += 2) { - arrayCharge(0, j) = arrayResidue(0, jj); - arrayCharge(tnRRow - 1, j) = arrayResidue((tnRRow - 1) * 2, jj); - } - - // for boundary - for (Int_t i = 0, ii = 0; i < tnRRow; i++, ii += 2) { - arrayCharge(i, 0) = arrayResidue(ii, 0); - arrayCharge(i, tnZColumn - 1) = arrayResidue(ii, (tnZColumn - 1) * 2); - } - } // end phis - } else { - for (int m = 0; m < newPhiSlice; m++) { - RestrictBoundary2D(*matricesCurrentCharge[m], *residue[m], tnRRow, tnZColumn); - } - } -} - -/// Prolongation with Addition for 2D -/// -/// Interpolation with addition from coarse level (2h) --> fine level (h) -/// -/// Interpolation in all direction r-phi-z -/// \param matricesCurrentV TMatrixD& fine grid h -/// \param matricesCurrentVC TMatrixD& coarse grid 2h -/// \param tnRRow Int_t number of grid in nRRow (in r-direction) for coarser grid should be 2^N + 1, finer grid in 2^{N+1} + 1 -/// \param tnZColumn Int_t number of grid in nZColumn (in z-direction) for coarser grid should be 2^M + 1, finer grid in 2^{M+1} + 1a -/// -void AliTPCPoissonSolver::AddInterp2D(TMatrixD& matricesCurrentV, TMatrixD& matricesCurrentVC, const Int_t tnRRow, - const Int_t tnZColumn) -{ - for (Int_t j = 2; j < tnZColumn - 1; j += 2) { - for (Int_t i = 2; i < tnRRow - 1; i += 2) { - matricesCurrentV(i, j) = matricesCurrentV(i, j) + matricesCurrentVC(i / 2, j / 2); - } - } - - for (Int_t j = 1; j < tnZColumn - 1; j += 2) { - for (Int_t i = 2; i < tnRRow - 1; i += 2) { - matricesCurrentV(i, j) = - matricesCurrentV(i, j) + 0.5 * (matricesCurrentVC(i / 2, j / 2) + matricesCurrentVC(i / 2, j / 2 + 1)); - } - } - - for (Int_t j = 2; j < tnZColumn - 1; j += 2) { - for (Int_t i = 1; i < tnRRow - 1; i += 2) { - matricesCurrentV(i, j) = - matricesCurrentV(i, j) + 0.5 * (matricesCurrentVC(i / 2, j / 2) + matricesCurrentVC(i / 2 + 1, j / 2)); - } - } - - // only if full - if (fMgParameters.gtType == kFull) { - for (Int_t j = 1; j < tnZColumn - 1; j += 2) { - for (Int_t i = 1; i < tnRRow - 1; i += 2) { - matricesCurrentV(i, j) = - matricesCurrentV(i, j) + 0.25 * (matricesCurrentVC(i / 2, j / 2) + matricesCurrentVC(i / 2, j / 2 + 1) + matricesCurrentVC(i / 2 + 1, j / 2) + matricesCurrentVC(i / 2 + 1, j / 2 + 1)); - } - } - } -} - -/// Prolongation with Addition for 3D -/// -/// Interpolation with addition from coarse level (2h) --> fine level (h) -/// -/// Interpolation in all direction r-phi-z -/// Interpolation in phi only if oldPhi == 2*newPhi -/// \param matricesCurrentV TMatrixD& fine grid h -/// \param matricesCurrentVC TMatrixD& coarse grid 2h -/// \param tnRRow Int_t number of grid in nRRow (in r-direction) for coarser grid should be 2^N + 1, finer grid in 2^{N+1} + 1 -/// \param tnZColumn Int_t number of grid in nZColumn (in z-direction) for coarser grid should be 2^M + 1, finer grid in 2^{M+1} + 1a -/// \param newPhiSlice Int_t number of phiSlice (in phi-direction) for coarser grid -/// \param oldPhiSlice Int_t number of phiSlice (in phi-direction) for finer grid -/// -void AliTPCPoissonSolver::AddInterp3D(TMatrixD** matricesCurrentV, TMatrixD** matricesCurrentVC, const Int_t tnRRow, - const Int_t tnZColumn, - const Int_t newPhiSlice, const Int_t oldPhiSlice) -{ - // Do restrict 2 D for each slice - - //const Float_t h = (AliTPCPoissonSolver::fgkOFCRadius-AliTPCPoissonSolver::fgkIFCRadius) / ((tnRRow-1)/2); // h_{r} - //Float_t radius,ratio; - //std::vector coefficient1((tnRRow-1) / 2 ); // coefficient1(nRRow) for storing (1 + h_{r}/2r_{i}) from central differences in r direction - //std::vector coefficient2((tnRRow-1) / 2); // coefficient2(nRRow) for storing (1 + h_{r}/2r_{i}) from central differences in r direction - - if (newPhiSlice == 2 * oldPhiSlice) { - Int_t mPlus, mmPlus; - Int_t mm = 0; - - for (Int_t m = 0; m < newPhiSlice; m += 2) { - - // assuming no symmetry - mm = m / 2; - mmPlus = mm + 1; - mPlus = m + 1; - - // round - if (mmPlus > (oldPhiSlice)-1) { - mmPlus = mm + 1 - (oldPhiSlice); - } - if (mPlus > (newPhiSlice)-1) { - mPlus = m + 1 - (newPhiSlice); - } - - TMatrixD& fineV = *matricesCurrentV[m]; - TMatrixD& fineVP = *matricesCurrentV[mPlus]; - TMatrixD& coarseV = *matricesCurrentVC[mm]; - TMatrixD& coarseVP = *matricesCurrentVC[mmPlus]; - - for (Int_t j = 2; j < tnZColumn - 1; j += 2) { - for (Int_t i = 2; i < tnRRow - 1; i += 2) { - fineV(i, j) += coarseV(i / 2, j / 2); - // point on corner lines at phi direction - fineVP(i, j) += 0.5 * (coarseV(i / 2, j / 2) + coarseVP(i / 2, j / 2)); - } - } - - for (Int_t j = 1; j < tnZColumn - 1; j += 2) { - for (Int_t i = 2; i < tnRRow - 1; i += 2) { - fineV(i, j) += 0.5 * (coarseV(i / 2, j / 2) + coarseV(i / 2, j / 2 + 1)); - // point on corner lines at phi direction - fineVP(i, j) += 0.25 * (coarseV(i / 2, j / 2) + coarseV(i / 2, j / 2 + 1) + coarseVP(i / 2, j / 2) + - coarseVP(i / 2, j / 2 + 1)); - } - } - - for (Int_t j = 2; j < tnZColumn - 1; j += 2) { - for (Int_t i = 1; i < tnRRow - 1; i += 2) { - fineV(i, j) += 0.5 * (coarseV(i / 2, j / 2) + coarseV(i / 2 + 1, j / 2)); - - // point on line at phi direction - fineVP(i, j) += 0.25 * ((coarseV(i / 2, j / 2) + coarseVP(i / 2, j / 2)) + - (coarseVP(i / 2 + 1, j / 2) + coarseV(i / 2 + 1, j / 2))); - } - } - - for (Int_t j = 1; j < tnZColumn - 1; j += 2) { - for (Int_t i = 1; i < tnRRow - 1; i += 2) { - fineV(i, j) += 0.25 * ((coarseV(i / 2, j / 2) + coarseV(i / 2, j / 2 + 1)) + - (coarseV(i / 2 + 1, j / 2) + coarseV(i / 2 + 1, j / 2 + 1))); - - // point at the center at phi direction - fineVP(i, j) += 0.125 * ((coarseV(i / 2, j / 2) + coarseV(i / 2, j / 2 + 1) + coarseVP(i / 2, j / 2) + - coarseVP(i / 2, j / 2 + 1)) + - (coarseV(i / 2 + 1, j / 2) + coarseV(i / 2 + 1, j / 2 + 1) + - coarseVP(i / 2 + 1, j / 2) + coarseVP(i / 2 + 1, j / 2 + 1))); - } - } - } - - } else { - for (int m = 0; m < newPhiSlice; m++) { - AddInterp2D(*matricesCurrentV[m], *matricesCurrentVC[m], tnRRow, tnZColumn); - } - } -} - -/// Interpolation/Prolongation in 3D -/// -/// Interpolation is a map from coarse grid (h) to fine grid (2h) -/// -/// In case of 3D -/// Full weighting: -/// \f[ (R u)_{i,j,k} = \frac{1}{2} u_{2i,2j,2k} + \frac{1}{4} S_{1} + \frac{1}{8} S_{2} + \frac{1}{16} S_{3}\f] -/// -/// -/// Restriction in all direction r-phi-z -/// restriction in phi only if oldPhi == 2*newPhi -/// \param matricesCurrentV TMatrixD** finer grid h -/// \param curArrayCV TMatrixD ** coarse grid 2h -/// \param tnRRow Int_t number of grid in nRRow (in r-direction) for coarser grid should be 2^N + 1, finer grid in 2^{N+1} + 1 -/// \param tnZColumn Int_t number of grid in nZColumn (in z-direction) for coarser grid should be 2^M + 1, finer grid in 2^{M+1} + 1 -/// \param newPhiSlice Int_t number of phiSlice (in phi-direction) for coarser grid -/// \param oldPhiSlice Int_t number of phiSlice (in phi-direction) for finer grid -/// -void AliTPCPoissonSolver::Interp3D(TMatrixD** matricesCurrentV, TMatrixD** matricesCurrentVC, const Int_t tnRRow, - const Int_t tnZColumn, - const Int_t newPhiSlice, const Int_t oldPhiSlice) -{ - - // Do restrict 2 D for each slice - if (newPhiSlice == 2 * oldPhiSlice) { - Int_t mPlus, mmPlus; - Int_t mm = 0; - - for (Int_t m = 0; m < newPhiSlice; m += 2) { - - // assuming no symmetry - mm = m / 2; - mmPlus = mm + 1; - mPlus = m + 1; - - // round - if (mmPlus > (oldPhiSlice)-1) { - mmPlus = mm + 1 - (oldPhiSlice); - } - if (mPlus > (newPhiSlice)-1) { - mPlus = m + 1 - (newPhiSlice); - } - - TMatrixD& fineV = *matricesCurrentV[m]; - TMatrixD& fineVP = *matricesCurrentV[mPlus]; - TMatrixD& coarseV = *matricesCurrentVC[mm]; - TMatrixD& coarseVP = *matricesCurrentVC[mmPlus]; - - for (Int_t j = 2; j < tnZColumn - 1; j += 2) { - for (Int_t i = 2; i < tnRRow - 1; i += 2) { - fineV(i, j) = coarseV(i / 2, j / 2); - - // point on corner lines at phi direction - fineVP(i, j) = 0.5 * (coarseV(i / 2, j / 2) + coarseVP(i / 2, j / 2)); - } - } - - for (Int_t j = 1; j < tnZColumn - 1; j += 2) { - for (Int_t i = 2; i < tnRRow - 1; i += 2) { - fineV(i, j) = 0.5 * (coarseV(i / 2, j / 2) + coarseV(i / 2, j / 2 + 1)); - - // point on corner lines at phi direction - fineVP(i, j) = 0.25 * (coarseV(i / 2, j / 2) + coarseV(i / 2, j / 2 + 1) + coarseVP(i / 2, j / 2) + - coarseVP(i / 2, j / 2 + 1)); - } - } - - for (Int_t j = 2; j < tnZColumn - 1; j += 2) { - for (Int_t i = 1; i < tnRRow - 1; i += 2) { - fineV(i, j) = 0.5 * (coarseV(i / 2, j / 2) + coarseV(i / 2 + 1, j / 2)); - - // point on line at phi direction - fineVP(i, j) = 0.25 * ((coarseV(i / 2, j / 2) + coarseVP(i / 2, j / 2)) + - (coarseVP(i / 2 + 1, j / 2) + coarseV(i / 2 + 1, j / 2))); - } - } - - for (Int_t j = 1; j < tnZColumn - 1; j += 2) { - for (Int_t i = 1; i < tnRRow - 1; i += 2) { - fineV(i, j) = 0.25 * ((coarseV(i / 2, j / 2) + coarseV(i / 2, j / 2 + 1)) + - (coarseV(i / 2 + 1, j / 2) + coarseV(i / 2 + 1, j / 2 + 1))); - - // point at the center at phi direction - fineVP(i, j) = 0.125 * ((coarseV(i / 2, j / 2) + coarseV(i / 2, j / 2 + 1) + coarseVP(i / 2, j / 2) + - coarseVP(i / 2, j / 2 + 1)) + - (coarseV(i / 2 + 1, j / 2) + coarseV(i / 2 + 1, j / 2 + 1) + - coarseVP(i / 2 + 1, j / 2) + coarseVP(i / 2 + 1, j / 2 + 1))); - } - } - } - - } else { - for (int m = 0; m < newPhiSlice; m++) { - Interp2D(*matricesCurrentV[m], *matricesCurrentVC[m], tnRRow, tnZColumn); - } - } -} - -/// Interpolation/Prolongation in 2D -/// -/// Interpolation is a map from coarse grid (h) to fine grid (2h) -/// -/// In case of 2D -/// Full weighting: -/// \f[ (R u)_{i,j,k} = \frac{1}{2} u_{2i,2j,2k} + \frac{1}{4} S_{1} + \frac{1}{8} S_{2} + \frac{1}{16} S_{3}\f] -/// -/// -/// Restriction in all direction r-phi-z -/// \param matricesCurrentV TMatrixD** finer grid h -/// \param curArrayCV TMatrixD ** coarse grid 2h -/// \param tnRRow Int_t number of grid in nRRow (in r-direction) for coarser grid should be 2^N + 1, finer grid in 2^{N+1} + 1 -/// \param tnZColumn Int_t number of grid in nZColumn (in z-direction) for coarser grid should be 2^M + 1, finer grid in 2^{M+1} + 1 -/// -void AliTPCPoissonSolver::Interp2D(TMatrixD& matricesCurrentV, TMatrixD& matricesCurrentVC, const Int_t tnRRow, - const Int_t tnZColumn) -{ - for (Int_t j = 2; j < tnZColumn - 1; j += 2) { - for (Int_t i = 2; i < tnRRow - 1; i += 2) { - matricesCurrentV(i, j) = matricesCurrentVC(i / 2, j / 2); - } - } - - for (Int_t j = 1; j < tnZColumn - 1; j += 2) { - for (Int_t i = 2; i < tnRRow - 1; i += 2) { - matricesCurrentV(i, j) = 0.5 * (matricesCurrentVC(i / 2, j / 2) + matricesCurrentVC(i / 2, j / 2 + 1)); - } - } - - for (Int_t j = 2; j < tnZColumn - 1; j += 2) { - for (Int_t i = 1; i < tnRRow - 1; i += 2) { - matricesCurrentV(i, j) = 0.5 * (matricesCurrentVC(i / 2, j / 2) + matricesCurrentVC(i / 2 + 1, j / 2)); - } - } - - // only if full - if (fMgParameters.gtType == kFull) { - for (Int_t j = 1; j < tnZColumn - 1; j += 2) { - for (Int_t i = 1; i < tnRRow - 1; i += 2) { - matricesCurrentV(i, j) = 0.25 * - (matricesCurrentVC(i / 2, j / 2) + matricesCurrentVC(i / 2, j / 2 + 1) + - matricesCurrentVC(i / 2 + 1, j / 2) + - matricesCurrentVC(i / 2 + 1, j / 2 + 1)); - } - } - } -} - -/// V-Cycle 2D -/// -/// Implementation non-recursive V-cycle for 2D -/// -/// Algorithms: -/// -/// \param nRRow Int_t number of grid in nRRow (in r-direction) for coarser grid should be 2^N + 1, finer grid in 2^{N+1} + 1 -/// \param nZColumn Int_t number of grid in nZColumn (in z-direction) for coarser grid should be 2^M + 1, finer grid in 2^{M+1} + 1 -/// \param gridFrom const Int_t finest level of grid -/// \param gridTo const Int_t coarsest level of grid -/// \param nPre const Int_t number of smoothing before coarsening -/// \param nPost const Int_t number of smoothing after coarsening -/// \param gridSizeR const Float_t grid size in r direction (OPTION, recalculate) -/// \param ratio const Float_t ratio between square of grid r and grid z (OPTION, recalculate) -/// \param tvArrayV vector vector of V potential in different grids -/// \param tvCharge vector vector of charge distribution in different grids -/// \param tvResidue vector vector of residue calculation in different grids -/// -void AliTPCPoissonSolver::VCycle2D(const Int_t nRRow, const Int_t nZColumn, const Int_t gridFrom, const Int_t gridTo, - const Int_t nPre, const Int_t nPost, const Float_t gridSizeR, const Float_t ratio, - std::vector& tvArrayV, - std::vector& tvCharge, std::vector& tvResidue) -{ - - Float_t h, h2, ih2, tempRatio, tempFourth, inverseTempFourth, radius; - TMatrixD *matricesCurrentV, *matricesCurrentVC; - TMatrixD* matricesCurrentCharge; - TMatrixD* residue; - Int_t iOne, jOne, tnRRow, tnZColumn, count; - iOne = 1 << (gridFrom - 1); - jOne = 1 << (gridFrom - 1); - - matricesCurrentV = nullptr; - matricesCurrentVC = nullptr; - matricesCurrentCharge = nullptr; - residue = nullptr; - - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - - std::vector coefficient1(nRRow); - std::vector coefficient2(nZColumn); - - // 1) Go to coarsest level - for (count = gridFrom; count <= gridTo - 1; count++) { - h = gridSizeR * iOne; - h2 = h * h; - ih2 = 1.0 / h2; - tempRatio = ratio * iOne * iOne / (jOne * jOne); - tempFourth = 1.0 / (2.0 + 2.0 * tempRatio); - inverseTempFourth = 1.0 / tempFourth; - for (Int_t i = 1; i < tnRRow - 1; i++) { - radius = AliTPCPoissonSolver::fgkIFCRadius + i * h; - coefficient1[i] = 1.0 + h / (2 * radius); - coefficient2[i] = 1.0 - h / (2 * radius); - } - matricesCurrentV = tvArrayV[count - 1]; - matricesCurrentCharge = tvCharge[count - 1]; - residue = tvResidue[count - 1]; - - // 1) Pre-Smoothing: Gauss-Seidel Relaxation or Jacobi - for (Int_t jPre = 1; jPre <= nPre; jPre++) { - Relax2D(*matricesCurrentV, *matricesCurrentCharge, tnRRow, tnZColumn, h2, tempFourth, tempRatio, coefficient1, - coefficient2); - } - - // 2) Residue calculation - Residue2D(*residue, *matricesCurrentV, *matricesCurrentCharge, tnRRow, tnZColumn, ih2, inverseTempFourth, tempRatio, - coefficient1, - coefficient2); - - iOne = 2 * iOne; - jOne = 2 * jOne; - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - - matricesCurrentCharge = tvCharge[count]; - matricesCurrentV = tvArrayV[count]; - - //3) Restriction - Restrict2D(*matricesCurrentCharge, *residue, tnRRow, tnZColumn); - - //4) Zeroing coarser V - matricesCurrentV->Zero(); - } - - // 5) coarsest grid - h = gridSizeR * iOne; - h2 = h * h; - tempRatio = ratio * iOne * iOne / (jOne * jOne); - tempFourth = 1.0 / (2.0 + 2.0 * tempRatio); - - for (Int_t i = 1; i < tnRRow - 1; i++) { - radius = AliTPCPoissonSolver::fgkIFCRadius + i * h; - coefficient1[i] = 1.0 + h / (2 * radius); - coefficient2[i] = 1.0 - h / (2 * radius); - } - - Relax2D(*matricesCurrentV, *matricesCurrentCharge, tnRRow, tnZColumn, h2, tempFourth, tempRatio, coefficient1, - coefficient2); - - // Go to finest grid - for (count = gridTo - 1; count >= gridFrom; count--) { - - iOne = iOne / 2; - jOne = jOne / 2; - - h = gridSizeR * iOne; - h2 = h * h; - ih2 = 1.0 / h2; - tempRatio = ratio * iOne * iOne / (jOne * jOne); - tempFourth = 1.0 / (2.0 + 2.0 * tempRatio); - inverseTempFourth = 1.0 / tempFourth; - - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - matricesCurrentCharge = tvCharge[count - 1]; - matricesCurrentV = tvArrayV[count - 1]; - matricesCurrentVC = tvArrayV[count]; - - // 6) Interpolation/Prolongation - AddInterp2D(*matricesCurrentV, *matricesCurrentVC, tnRRow, tnZColumn); - - for (Int_t i = 1; i < tnRRow - 1; i++) { - radius = AliTPCPoissonSolver::fgkIFCRadius + i * h; - coefficient1[i] = 1.0 + h / (2 * radius); - coefficient2[i] = 1.0 - h / (2 * radius); - } - - // 7) Post-Smoothing: Gauss-Seidel Relaxation - for (Int_t jPost = 1; jPost <= nPost; jPost++) { - Relax2D(*matricesCurrentV, *matricesCurrentCharge, tnRRow, tnZColumn, h2, tempFourth, tempRatio, coefficient1, - coefficient2); - } // end post smoothing - - //// DEBUG //// - //Info("VCycle2D",Form("Count %d", count)); - //Info("VCycle2D",Form("Exact Err: %f, MG Iteration : %d", (*fError)(mgCycle), mgCycle)); - //matricesCurrentV->Print(); - //matricesCurrentCharge->Print(); - } -} - -/// W-Cycle 2D -/// -/// Implementation non-recursive W-cycle for 2D -/// -/// Algorithms: -/// -/// \param nRRow Int_t number of grid in nRRow (in r-direction) for coarser grid should be 2^N + 1, finer grid in 2^{N+1} + 1 -/// \param nZColumn Int_t number of grid in nZColumn (in z-direction) for coarser grid should be 2^M + 1, finer grid in 2^{M+1} + 1 -/// \param gridFrom const Int_t finest level of grid -/// \param gridTo const Int_t coarsest level of grid -/// \param gamma const Int_t number of iterations at coarsest level -/// \param nPre const Int_t number of smoothing before coarsening -/// \param nPost const Int_t number of smoothing after coarsening -/// \param gridSizeR const Float_t grid size in r direction (OPTION, recalculate) -/// \param ratio const Float_t ratio between square of grid r and grid z (OPTION, recalculate) -/// \param tvArrayV vector vector of V potential in different grids -/// \param tvCharge vector vector of charge distribution in different grids -/// \param tvResidue vector vector of residue calculation in different grids -/// -void AliTPCPoissonSolver::WCycle2D(const Int_t nRRow, const Int_t nZColumn, const Int_t gridFrom, const Int_t gridTo, - const int gamma, - const Int_t nPre, const Int_t nPost, const Float_t gridSizeR, const Float_t ratio, - std::vector& tvArrayV, - std::vector& tvCharge, std::vector& tvResidue) -{ - - Float_t h, h2, ih2, tempRatio, tempFourth, inverseTempFourth, radius; - TMatrixD *matricesCurrentV, *matricesCurrentVC; - TMatrixD* matricesCurrentCharge; - TMatrixD* residue; - Int_t iOne, jOne, tnRRow, tnZColumn, count; - iOne = 1 << (gridFrom - 1); - jOne = 1 << (gridFrom - 1); - - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - - std::vector coefficient1(nRRow); - std::vector coefficient2(nZColumn); - - // 1) Go to coarsest level - for (count = gridFrom; count <= gridTo - 2; count++) { - h = gridSizeR * iOne; - h2 = h * h; - ih2 = 1.0 / h2; - tempRatio = ratio * iOne * iOne / (jOne * jOne); - tempFourth = 1.0 / (2.0 + 2.0 * tempRatio); - inverseTempFourth = 1.0 / tempFourth; - for (Int_t i = 1; i < tnRRow - 1; i++) { - radius = AliTPCPoissonSolver::fgkIFCRadius + i * h; - coefficient1[i] = 1.0 + h / (2 * radius); - coefficient2[i] = 1.0 - h / (2 * radius); - } - matricesCurrentV = tvArrayV[count - 1]; - matricesCurrentCharge = tvCharge[count - 1]; - residue = tvResidue[count - 1]; - - // 1) Pre-Smoothing: Gauss-Seidel Relaxation or Jacobi - for (Int_t jPre = 1; jPre <= nPre; jPre++) { - Relax2D(*matricesCurrentV, *matricesCurrentCharge, tnRRow, tnZColumn, h2, tempFourth, tempRatio, coefficient1, - coefficient2); - } - - // 2) Residue calculation - Residue2D(*residue, *matricesCurrentV, *matricesCurrentCharge, tnRRow, tnZColumn, ih2, inverseTempFourth, tempRatio, - coefficient1, - coefficient2); - - iOne = 2 * iOne; - jOne = 2 * jOne; - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - - matricesCurrentCharge = tvCharge[count]; - matricesCurrentV = tvArrayV[count]; - - //3) Restriction - Restrict2D(*matricesCurrentCharge, *residue, tnRRow, tnZColumn); - - //4) Zeroing coarser V - matricesCurrentV->Zero(); - } - - // Do V cycle from: gridTo-1 to gridTo gamma times - for (Int_t iGamma = 0; iGamma < gamma; iGamma++) { - VCycle2D(nRRow, nZColumn, gridTo - 1, gridTo, - nPre, nPost, gridSizeR, ratio, tvArrayV, - tvCharge, tvResidue); - } - - // Go to finest grid - for (count = gridTo - 2; count >= gridFrom; count--) { - - iOne = iOne / 2; - jOne = jOne / 2; - - h = gridSizeR * iOne; - h2 = h * h; - ih2 = 1.0 / h2; - tempRatio = ratio * iOne * iOne / (jOne * jOne); - tempFourth = 1.0 / (2.0 + 2.0 * tempRatio); - inverseTempFourth = 1.0 / tempFourth; - - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - matricesCurrentCharge = tvCharge[count - 1]; - matricesCurrentV = tvArrayV[count - 1]; - matricesCurrentVC = tvArrayV[count]; - - // 6) Interpolation/Prolongation - AddInterp2D(*matricesCurrentV, *matricesCurrentVC, tnRRow, tnZColumn); - - for (Int_t i = 1; i < tnRRow - 1; i++) { - radius = AliTPCPoissonSolver::fgkIFCRadius + i * h; - coefficient1[i] = 1.0 + h / (2 * radius); - coefficient2[i] = 1.0 - h / (2 * radius); - } - - // 7) Post-Smoothing: Gauss-Seidel Relaxation - for (Int_t jPost = 1; jPost <= nPost; jPost++) { - Relax2D(*matricesCurrentV, *matricesCurrentCharge, tnRRow, tnZColumn, h2, tempFourth, tempRatio, coefficient1, - coefficient2); - } // end post smoothing - } -} - -/// VCycle 3D2D, V Cycle 3D in multiGrid with constant phiSlice -/// fine-->coarsest-->fine, propagating the residue to correct initial guess of V -/// -/// Algorithm: -/// -/// NOTE: In order for this algorithm to work, the number of nRRow and nZColumn must be a power of 2 plus one. -/// The number of nRRow and Z Column can be different. -/// -/// R Row == 2**M + 1 -/// Z Column == 2**N + 1 -/// Phi Slice == Arbitrary but greater than 3 -/// -/// DeltaPhi in Radians -/// \param nRRow Int_t number of grid in nRRow (in r-direction) for coarser grid should be 2^N + 1, finer grid in 2^{N+1} + 1 -/// \param nZColumn Int_t number of grid in nZColumn (in z-direction) for coarser grid should be 2^M + 1, finer grid in 2^{M+1} + 1 -/// \param gridFrom const Int_t finest level of grid -/// \param gridTo const Int_t coarsest level of grid -/// \param nPre const Int_t number of smoothing before coarsening -/// \param nPost const Int_t number of smoothing after coarsening -/// \param gridSizeR const Float_t grid size in r direction (OPTION, recalculate) -/// \param ratio const Float_t ratio between square of grid r and grid z (OPTION, recalculate) -/// \param tvArrayV vector vector of V potential in different grids -/// \param tvCharge vector vector of charge distribution in different grids -/// \param tvResidue vector vector of residue calculation in different grids -/// \param coefficient1 std::vector& coefficient for relaxation (r direction) -/// \param coefficient2 std::vector& coefficient for relaxation (r direction) -/// \param coefficient3 std::vector& coefficient for relaxation (ratio r/z) -/// \param coefficient4 std::vector& coefficient for relaxation (ratio for grid_r) -/// \param inverseCoefficient4 std::vector& coefficient for relaxation (inverse coefficient4) -/// -void AliTPCPoissonSolver::VCycle3D2D(const Int_t nRRow, const Int_t nZColumn, const Int_t phiSlice, const Int_t symmetry, - const Int_t gridFrom, const Int_t gridTo, const Int_t nPre, const Int_t nPost, - const Float_t gridSizeR, const Float_t ratioZ, const Float_t ratioPhi, - std::vector& tvArrayV, std::vector& tvCharge, - std::vector& tvResidue, std::vector& coefficient1, - std::vector& coefficient2, std::vector& coefficient3, - std::vector& coefficient4, - std::vector& inverseCoefficient4) -{ - - Float_t h, h2, ih2, tempRatioZ, tempRatioPhi, radius; - TMatrixD **matricesCurrentV, **matricesCurrentVC; - TMatrixD** matricesCurrentCharge; - TMatrixD** residue; - Int_t iOne, jOne, tnRRow, tnZColumn, count; - - matricesCurrentV = nullptr; - matricesCurrentVC = nullptr; - matricesCurrentCharge = nullptr; - residue = nullptr; - - iOne = 1 << (gridFrom - 1); - jOne = 1 << (gridFrom - 1); - - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - - for (count = gridFrom; count <= gridTo - 1; count++) { - h = gridSizeR * iOne; - h2 = h * h; - ih2 = 1.0 / h2; - - tempRatioPhi = ratioPhi * iOne * iOne; // Used tobe divided by ( m_one * m_one ) when m_one was != 1 - tempRatioZ = ratioZ * iOne * iOne / (jOne * jOne); - - for (Int_t i = 1; i < tnRRow - 1; i++) { - radius = AliTPCPoissonSolver::fgkIFCRadius + i * h; - coefficient1[i] = 1.0 + h / (2 * radius); - coefficient2[i] = 1.0 - h / (2 * radius); - coefficient3[i] = tempRatioPhi / (radius * radius); - coefficient4[i] = 0.5 / (1.0 + tempRatioZ + coefficient3[i]); - inverseCoefficient4[i] = 1.0 / coefficient4[i]; - } - - matricesCurrentV = tvArrayV[count - 1]; - matricesCurrentCharge = tvCharge[count - 1]; - residue = tvResidue[count - 1]; - - //Info("VCycle3D2D","Before Pre-smoothing"); - //matricesCurrentV->Print(); - - // 1) Pre-Smoothing: Gauss-Seidel Relaxation or Jacobi - for (Int_t jPre = 1; jPre <= nPre; jPre++) { - Relax3D(matricesCurrentV, matricesCurrentCharge, tnRRow, tnZColumn, phiSlice, symmetry, h2, tempRatioZ, - coefficient1, coefficient2, - coefficient3, coefficient4); - } // end pre smoothing - - // 2) Residue calculation - Residue3D(residue, matricesCurrentV, matricesCurrentCharge, tnRRow, tnZColumn, phiSlice, symmetry, ih2, tempRatioZ, - coefficient1, - coefficient2, - coefficient3, inverseCoefficient4); - - iOne = 2 * iOne; - jOne = 2 * jOne; - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - - matricesCurrentCharge = tvCharge[count]; - matricesCurrentV = tvArrayV[count]; - - //3) Restriction - //Restrict2D(*matricesCurrentCharge,*residue,tnRRow,tnZColumn); - Restrict3D(matricesCurrentCharge, residue, tnRRow, tnZColumn, phiSlice, phiSlice); - - //4) Zeroing coarser V - for (Int_t m = 0; m < phiSlice; m++) { - matricesCurrentV[m]->Zero(); - } - } - - // coarsest grid - h = gridSizeR * iOne; - h2 = h * h; - - tempRatioPhi = ratioPhi * iOne * iOne; - tempRatioZ = ratioZ * iOne * iOne / (jOne * jOne); - - for (Int_t i = 1; i < tnRRow - 1; i++) { - radius = AliTPCPoissonSolver::fgkIFCRadius + i * h; - coefficient1[i] = 1.0 + h / (2 * radius); - coefficient2[i] = 1.0 - h / (2 * radius); - coefficient3[i] = tempRatioPhi / (radius * radius); - coefficient4[i] = 0.5 / (1.0 + tempRatioZ + coefficient3[i]); - } - - // 3) Relax on the coarsest grid - Relax3D(matricesCurrentV, matricesCurrentCharge, tnRRow, tnZColumn, phiSlice, symmetry, h2, tempRatioZ, coefficient1, - coefficient2, - coefficient3, coefficient4); - - // back to fine - for (count = gridTo - 1; count >= gridFrom; count--) { - iOne = iOne / 2; - jOne = jOne / 2; - - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - - h = gridSizeR * iOne; - h2 = h * h; - - tempRatioPhi = ratioPhi * iOne * iOne; // Used tobe divided by ( m_one * m_one ) when m_one was != 1 - tempRatioZ = ratioZ * iOne * iOne / (jOne * jOne); - - matricesCurrentCharge = tvCharge[count - 1]; - matricesCurrentV = tvArrayV[count - 1]; - matricesCurrentVC = tvArrayV[count]; - - // 4) Interpolation/Prolongation - AddInterp3D(matricesCurrentV, matricesCurrentVC, tnRRow, tnZColumn, phiSlice, phiSlice); - - for (Int_t i = 1; i < tnRRow - 1; i++) { - radius = AliTPCPoissonSolver::fgkIFCRadius + i * h; - coefficient1[i] = 1.0 + h / (2 * radius); - coefficient2[i] = 1.0 - h / (2 * radius); - coefficient3[i] = tempRatioPhi / (radius * radius); - coefficient4[i] = 0.5 / (1.0 + tempRatioZ + coefficient3[i]); - } - - // 5) Post-Smoothing: Gauss-Seidel Relaxation - for (Int_t jPost = 1; jPost <= nPost; jPost++) { - Relax3D(matricesCurrentV, matricesCurrentCharge, tnRRow, tnZColumn, phiSlice, symmetry, h2, tempRatioZ, - coefficient1, coefficient2, - coefficient3, coefficient4); - } // end post smoothing - } -} - -/// VCycle 3D, V Cycle in multiGrid, fine-->coarsest-->fine, propagating the residue to correct initial guess of V -/// -/// NOTE: In order for this algorithm to work, the number of nRRow and nZColumn must be a power of 2 plus one. -/// The number of nRRow and Z Column can be different. -/// -/// R Row == 2**M + 1 -/// Z Column == 2**N + 1 -/// Phi Slice == Arbitrary but greater than 3 -/// -/// DeltaPhi in Radians -/// -/// \param nRRow Int_t number of nRRow in the r direction of TPC -/// \param nZColumn Int_t number of nZColumn in z direction of TPC -/// \param phiSlice Int_t number of phiSlice in phi direction of T{C -/// \param symmetry Int_t symmetry or not -/// \param gridFrom const Int_t finest level of grid -/// \param gridTo const Int_t coarsest level of grid -/// \param nPre const Int_t number of smoothing before coarsening -/// \param nPost const Int_t number of smoothing after coarsening -/// \param gridSizeR const Float_t grid size in r direction (OPTION, recalculate) -/// \param ratioz const Float_t ratio between square of grid r and grid z (OPTION, recalculate) -/// \param tvArrayV vector vector of V potential in different grids -/// \param tvCharge vector vector of charge distribution in different grids -/// \param tvResidue vector vector of residue calculation in different grids -/// \param coefficient1 std::vector& coefficient for relaxation (r direction) -/// \param coefficient2 std::vector& coefficient for relaxation (r direction) -/// \param coefficient3 std::vector& coefficient for relaxation (ratio r/z) -/// \param coefficient4 std::vector& coefficient for relaxation (ratio for grid_r) -/// \param inverseCoefficient4 std::vector& coefficient for relaxation (inverse coefficient4) -/// -void AliTPCPoissonSolver::VCycle3D(const Int_t nRRow, const Int_t nZColumn, const Int_t phiSlice, const Int_t symmetry, - const Int_t gridFrom, const Int_t gridTo, - const Int_t nPre, const Int_t nPost, const Float_t gridSizeR, const Float_t ratioZ, - std::vector& tvArrayV, std::vector& tvCharge, - std::vector& tvResidue, - std::vector& coefficient1, std::vector& coefficient2, - std::vector& coefficient3, - std::vector& coefficient4, std::vector& inverseCoefficient4) -{ - - Float_t h, h2, ih2, tempRatioZ, tempRatioPhi, radius, tempGridSizePhi; - TMatrixD **matricesCurrentV, **matricesCurrentVC; - TMatrixD** matricesCurrentCharge; - TMatrixD** residue; - Int_t iOne, jOne, kOne, tnRRow, tnZColumn, tPhiSlice, otPhiSlice, count, nnPhi; - - matricesCurrentV = nullptr; - matricesCurrentVC = nullptr; - matricesCurrentCharge = nullptr; - residue = nullptr; - - iOne = 1 << (gridFrom - 1); - jOne = 1 << (gridFrom - 1); - kOne = 1 << (gridFrom - 1); - - nnPhi = phiSlice; - - while (nnPhi % 2 == 0) { - nnPhi /= 2; - } - - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - tPhiSlice = kOne == 1 ? phiSlice : phiSlice / kOne; - tPhiSlice = tPhiSlice < nnPhi ? nnPhi : tPhiSlice; - - //Info("VCycle3D",Form("Grid information: tnRRow=%d, tcols=%d, tPhiSlice=%d\n", tnRRow,tnZColumn,tPhiSlice)); - - for (count = gridFrom; count <= gridTo - 1; count++) { - otPhiSlice = tPhiSlice; - - h = gridSizeR * iOne; - h2 = h * h; - ih2 = 1.0 / h2; - tempGridSizePhi = TMath::TwoPi() / tPhiSlice; // phi now is multiGrid - - tempRatioPhi = h * h / (tempGridSizePhi * tempGridSizePhi); // ratio_{phi} = gridSize_{r} / gridSize_{phi} - - tempRatioZ = ratioZ * iOne * iOne / (jOne * jOne); - - for (Int_t i = 1; i < tnRRow - 1; i++) { - radius = AliTPCPoissonSolver::fgkIFCRadius + i * h; - coefficient1[i] = 1.0 + h / (2 * radius); - coefficient2[i] = 1.0 - h / (2 * radius); - coefficient3[i] = tempRatioPhi / (radius * radius); - coefficient4[i] = 0.5 / (1.0 + tempRatioZ + coefficient3[i]); - inverseCoefficient4[i] = 1.0 / coefficient4[i]; - } - - matricesCurrentV = tvArrayV[count - 1]; - matricesCurrentCharge = tvCharge[count - 1]; - residue = tvResidue[count - 1]; - - //Info("VCycle3D","Before Pre-smoothing"); - //matricesCurrentV->Print(); - - // 1) Pre-Smoothing: Gauss-Seidel Relaxation or Jacobi - for (Int_t jPre = 1; jPre <= nPre; jPre++) { - Relax3D(matricesCurrentV, matricesCurrentCharge, tnRRow, tnZColumn, tPhiSlice, symmetry, h2, tempRatioZ, - coefficient1, coefficient2, coefficient3, coefficient4); - } // end pre smoothing - - // 2) Residue calculation - - Residue3D(residue, matricesCurrentV, matricesCurrentCharge, tnRRow, tnZColumn, tPhiSlice, symmetry, ih2, tempRatioZ, - coefficient1, coefficient2, coefficient3, inverseCoefficient4); - - iOne = 2 * iOne; - jOne = 2 * jOne; - kOne = 2 * kOne; - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - tPhiSlice = phiSlice / kOne; - tPhiSlice = tPhiSlice < nnPhi ? nnPhi : tPhiSlice; - - matricesCurrentCharge = tvCharge[count]; - matricesCurrentV = tvArrayV[count]; - //3) Restriction - Restrict3D(matricesCurrentCharge, residue, tnRRow, tnZColumn, tPhiSlice, otPhiSlice); - - //4) Zeroing coarser V - for (Int_t m = 0; m < tPhiSlice; m++) { - matricesCurrentV[m]->Zero(); - } - } - - // coarsest grid - h = gridSizeR * iOne; - h2 = h * h; - tempGridSizePhi = TMath::TwoPi() / tPhiSlice; // phi now is multiGrid - - tempRatioPhi = h * h / (tempGridSizePhi * tempGridSizePhi); // ratio_{phi} = gridSize_{r} / gridSize_{phi} - tempRatioZ = ratioZ * iOne * iOne / (jOne * jOne); - - for (Int_t i = 1; i < tnRRow - 1; i++) { - radius = AliTPCPoissonSolver::fgkIFCRadius + i * h; - coefficient1[i] = 1.0 + h / (2 * radius); - coefficient2[i] = 1.0 - h / (2 * radius); - coefficient3[i] = tempRatioPhi / (radius * radius); - coefficient4[i] = 0.5 / (1.0 + tempRatioZ + coefficient3[i]); - } - - // 3) Relax on the coarsest grid - Relax3D(matricesCurrentV, matricesCurrentCharge, tnRRow, tnZColumn, tPhiSlice, symmetry, h2, tempRatioZ, coefficient1, - coefficient2, coefficient3, coefficient4); - - // back to fine - for (count = gridTo - 1; count >= gridFrom; count--) { - otPhiSlice = tPhiSlice; - - iOne = iOne / 2; - jOne = jOne / 2; - kOne = kOne / 2; - - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - tPhiSlice = kOne == 1 ? phiSlice : phiSlice / kOne; - tPhiSlice = tPhiSlice < nnPhi ? nnPhi : tPhiSlice; - - h = gridSizeR * iOne; - h2 = h * h; - tempGridSizePhi = TMath::TwoPi() / tPhiSlice; // phi now is multiGrid - - tempRatioPhi = h * h / (tempGridSizePhi * tempGridSizePhi); // ratio_{phi} = gridSize_{r} / gridSize_{phi} - - tempRatioZ = ratioZ * iOne * iOne / (jOne * jOne); - - matricesCurrentCharge = tvCharge[count - 1]; - matricesCurrentV = tvArrayV[count - 1]; - matricesCurrentVC = tvArrayV[count]; - - // 4) Interpolation/Prolongation - - AddInterp3D(matricesCurrentV, matricesCurrentVC, tnRRow, tnZColumn, tPhiSlice, otPhiSlice); - - for (Int_t i = 1; i < tnRRow - 1; i++) { - radius = AliTPCPoissonSolver::fgkIFCRadius + i * h; - coefficient1[i] = 1.0 + h / (2 * radius); - coefficient2[i] = 1.0 - h / (2 * radius); - coefficient3[i] = tempRatioPhi / (radius * radius); - coefficient4[i] = 0.5 / (1.0 + tempRatioZ + coefficient3[i]); - } - - // 5) Post-Smoothing: Gauss-Seidel Relaxation - for (Int_t jPost = 1; jPost <= nPost; jPost++) { - Relax3D(matricesCurrentV, matricesCurrentCharge, tnRRow, tnZColumn, tPhiSlice, symmetry, h2, tempRatioZ, - coefficient1, coefficient2, - coefficient3, coefficient4); - } - } -} - -/// -/// Set matrix exact solution for relative error calculation -/// -/// \param exactSolution TMatrixD** pointer to exact solution (potential) in 3D -/// \param fPhiSlices const Int_t number of phi slices -/// -void AliTPCPoissonSolver::SetExactSolution(TMatrixD** exactSolution, const Int_t fPhiSlices) -{ - Double_t maxAbs; - fExactSolution = exactSolution; - fExactPresent = kTRUE; - fMaxExact = 0.0; - for (Int_t m = 0; m < fPhiSlices; m++) { - maxAbs = TMath::Max(TMath::Abs((*fExactSolution[m]).Max()), TMath::Abs((*fExactSolution[m]).Min())); - if (maxAbs > fMaxExact) { - fMaxExact = maxAbs; - } - } -} - -/// -/// Relative error calculation: comparison with exact solution -/// -/// \param matricesCurrentV TMatrixD** current potential (numerical solution) -/// \param tempArrayV TMatrixD** temporary matrix for calculating error -/// \param nRRow Int_t number of grid in nRRow (in r-direction) for coarser grid should be 2^N + 1, finer grid in 2^{N+1} + 1 -/// \param nZColumn Int_t number of grid in nZColumn (in z-direction) for coarser grid should be 2^M + 1, finer grid in 2^{M+1} + 1 -/// \param phiSlice const Int_t phi slices -/// -Double_t AliTPCPoissonSolver::GetExactError(TMatrixD** matricesCurrentV, TMatrixD** tempArrayV, const Int_t phiSlice) -{ - Double_t error = 0.0; - - if (fExactPresent == kTRUE) { - for (Int_t m = 0; m < phiSlice; m++) { - (*tempArrayV[m]) = (*fExactSolution[m]) - (*matricesCurrentV[m]); - (*tempArrayV[m]) *= 1.0 / GetMaxExact(); - if (tempArrayV[m]->E2Norm() > error) { - error = tempArrayV[m]->E2Norm(); - } - //printf("%f\n",tempArrayV[m]->E2Norm(); - } - } - return error; -} - -/// -/// Relative error calculation: comparison with exact solution -/// -/// \param matricesCurrentV TMatrixD** current potential (numerical solution) -/// \param tempArrayV TMatrixD** temporary matrix for calculating error -/// \param nRRow Int_t number of grid in nRRow (in r-direction) for coarser grid should be 2^N + 1, finer grid in 2^{N+1} + 1 -/// \param nZColumn Int_t number of grid in nZColumn (in z-direction) for coarser grid should be 2^M + 1, finer grid in 2^{M+1} + 1 -/// \param phiSlice const Int_t phi slices -/// -Double_t - AliTPCPoissonSolver::GetConvergenceError(TMatrixD** matricesCurrentV, TMatrixD** prevArrayV, const Int_t phiSlice) -{ - Double_t error = 0.0; - - for (Int_t m = 0; m < phiSlice; m++) { - - // absolute - (*prevArrayV[m]) = (*prevArrayV[m]) - (*matricesCurrentV[m]); - - if (prevArrayV[m]->E2Norm() > error) { - error = prevArrayV[m]->E2Norm(); - } - } - return error; -} - -///////////////////// interface for GPU /////////////////// - -/// VCycle 3D2D, V Cycle 3D in multiGrid with constant phiSlice -/// fine-->coarsest-->fine, propagating the residue to correct initial guess of V -/// -/// Algorithm: -/// -/// NOTE: In order for this algorithm to work, the number of nRRow and nZColumn must be a power of 2 plus one. -/// The number of nRRow and Z Column can be different. -/// -/// R Row == 2**M + 1 -/// Z Column == 2**N + 1 -/// Phi Slice == Arbitrary but greater than 3 -/// -/// DeltaPhi in Radians -/// \param nRRow Int_t number of grid in nRRow (in r-direction) for coarser grid should be 2^N + 1, finer grid in 2^{N+1} + 1 -/// \param nZColumn Int_t number of grid in nZColumn (in z-direction) for coarser grid should be 2^M + 1, finer grid in 2^{M+1} + 1 -/// \param gridFrom const Int_t finest level of grid -/// \param gridTo const Int_t coarsest level of grid -/// \param nPre const Int_t number of smoothing before coarsening -/// \param nPost const Int_t number of smoothing after coarsening -/// \param gridSizeR const Float_t grid size in r direction (OPTION, recalculate) -/// \param ratio const Float_t ratio between square of grid r and grid z (OPTION, recalculate) -/// \param tvArrayV vector vector of V potential in different grids -/// \param tvCharge vector vector of charge distribution in different grids -/// \param tvResidue vector vector of residue calculation in different grids -/// \param coefficient1 std::vector& coefficient for relaxation (r direction) -/// \param coefficient2 std::vector& coefficient for relaxation (r direction) -/// \param coefficient3 std::vector& coefficient for relaxation (ratio r/z) -/// \param coefficient4 std::vector& coefficient for relaxation (ratio for grid_r) -/// \param inverseCoefficient4 std::vector& coefficient for relaxation (inverse coefficient4) -/// -void AliTPCPoissonSolver::VCycle3D2DGPU( - const Int_t nRRow, const Int_t nZColumn, const Int_t phiSlice, const Int_t symmetry, - const Int_t gridFrom, const Int_t gridTo, const Int_t nPre, const Int_t nPost, const Float_t gridSizeR, - const Float_t ratioZ, const Float_t ratioPhi, std::vector& tvArrayV, - std::vector& tvCharge, std::vector& tvResidue, std::vector& coefficient1, - std::vector& coefficient2, std::vector& coefficient3, std::vector& coefficient4, - std::vector& inverseCoefficient4) -{ - Float_t h, h2, ih2, tempRatioZ, tempRatioPhi, radius; - TMatrixD **matricesCurrentV, **matricesCurrentVC; - TMatrixD** matricesCurrentCharge; - TMatrixD** residue; - Int_t iOne, jOne, tnRRow, tnZColumn, count; - - matricesCurrentV = nullptr; - matricesCurrentVC = nullptr; - matricesCurrentCharge = nullptr; - residue = nullptr; - - iOne = 1 << (gridFrom - 1); - jOne = 1 << (gridFrom - 1); - - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - - for (count = gridFrom; count <= gridTo - 1; count++) { - h = gridSizeR * iOne; - h2 = h * h; - ih2 = 1.0 / h2; - - tempRatioPhi = ratioPhi * iOne * iOne; // Used tobe divided by ( m_one * m_one ) when m_one was != 1 - tempRatioZ = ratioZ * iOne * iOne / (jOne * jOne); - - for (Int_t i = 1; i < tnRRow - 1; i++) { - radius = AliTPCPoissonSolver::fgkIFCRadius + i * h; - coefficient1[i] = 1.0 + h / (2 * radius); - coefficient2[i] = 1.0 - h / (2 * radius); - coefficient3[i] = tempRatioPhi / (radius * radius); - coefficient4[i] = 0.5 / (1.0 + tempRatioZ + coefficient3[i]); - inverseCoefficient4[i] = 1.0 / coefficient4[i]; - } - - matricesCurrentV = tvArrayV[count - 1]; - matricesCurrentCharge = tvCharge[count - 1]; - residue = tvResidue[count - 1]; - - //Info("VCycle3D2DGPU","Before Pre-smoothing"); - //matricesCurrentV->Print(); - - // 1) Pre-Smoothing: Gauss-Seidel Relaxation or Jacobi - for (Int_t jPre = 1; jPre <= nPre; jPre++) { - Relax3D(matricesCurrentV, matricesCurrentCharge, tnRRow, tnZColumn, phiSlice, symmetry, h2, tempRatioZ, - coefficient1, coefficient2, - coefficient3, coefficient4); - } // end pre smoothing - - // 2) Residue calculation - Residue3D(residue, matricesCurrentV, matricesCurrentCharge, tnRRow, tnZColumn, phiSlice, symmetry, ih2, tempRatioZ, - coefficient1, - coefficient2, - coefficient3, inverseCoefficient4); - - iOne = 2 * iOne; - jOne = 2 * jOne; - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - - matricesCurrentCharge = tvCharge[count]; - matricesCurrentV = tvArrayV[count]; - - //3) Restriction - //Restrict2D(*matricesCurrentCharge,*residue,tnRRow,tnZColumn); - Restrict3D(matricesCurrentCharge, residue, tnRRow, tnZColumn, phiSlice, phiSlice); - - //4) Zeroing coarser V - for (Int_t m = 0; m < phiSlice; m++) { - matricesCurrentV[m]->Zero(); - } - } - - // coarsest grid - h = gridSizeR * iOne; - h2 = h * h; - - tempRatioPhi = ratioPhi * iOne * iOne; - tempRatioZ = ratioZ * iOne * iOne / (jOne * jOne); - - for (Int_t i = 1; i < tnRRow - 1; i++) { - radius = AliTPCPoissonSolver::fgkIFCRadius + i * h; - coefficient1[i] = 1.0 + h / (2 * radius); - coefficient2[i] = 1.0 - h / (2 * radius); - coefficient3[i] = tempRatioPhi / (radius * radius); - coefficient4[i] = 0.5 / (1.0 + tempRatioZ + coefficient3[i]); - } - - // 3) Relax on the coarsest grid - Relax3D(matricesCurrentV, matricesCurrentCharge, tnRRow, tnZColumn, phiSlice, symmetry, h2, tempRatioZ, coefficient1, - coefficient2, - coefficient3, coefficient4); - - // back to fine - for (count = gridTo - 1; count >= gridFrom; count--) { - iOne = iOne / 2; - jOne = jOne / 2; - - tnRRow = iOne == 1 ? nRRow : nRRow / iOne + 1; - tnZColumn = jOne == 1 ? nZColumn : nZColumn / jOne + 1; - - h = gridSizeR * iOne; - h2 = h * h; - - tempRatioPhi = ratioPhi * iOne * iOne; // Used tobe divided by ( m_one * m_one ) when m_one was != 1 - tempRatioZ = ratioZ * iOne * iOne / (jOne * jOne); - - matricesCurrentCharge = tvCharge[count - 1]; - matricesCurrentV = tvArrayV[count - 1]; - matricesCurrentVC = tvArrayV[count]; - - // 4) Interpolation/Prolongation - AddInterp3D(matricesCurrentV, matricesCurrentVC, tnRRow, tnZColumn, phiSlice, phiSlice); - - for (Int_t i = 1; i < tnRRow - 1; i++) { - radius = AliTPCPoissonSolver::fgkIFCRadius + i * h; - coefficient1[i] = 1.0 + h / (2 * radius); - coefficient2[i] = 1.0 - h / (2 * radius); - coefficient3[i] = tempRatioPhi / (radius * radius); - coefficient4[i] = 0.5 / (1.0 + tempRatioZ + coefficient3[i]); - } - - // 5) Post-Smoothing: Gauss-Seidel Relaxation - for (Int_t jPost = 1; jPost <= nPost; jPost++) { - Relax3D(matricesCurrentV, matricesCurrentCharge, tnRRow, tnZColumn, phiSlice, symmetry, h2, tempRatioZ, - coefficient1, coefficient2, - coefficient3, coefficient4); - } // end post smoothing - } -} diff --git a/GPU/TPCSpaceChargeBase/AliTPCPoissonSolver.h b/GPU/TPCSpaceChargeBase/AliTPCPoissonSolver.h deleted file mode 100644 index 5aead1e7b555c..0000000000000 --- a/GPU/TPCSpaceChargeBase/AliTPCPoissonSolver.h +++ /dev/null @@ -1,219 +0,0 @@ -// Copyright CERN and copyright holders of ALICE O2. This software is -// distributed under the terms of the GNU General Public License v3 (GPL -// Version 3), copied verbatim in the file "COPYING". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file AliTPCPoissonSolver.h -/// \brief This class provides implementation of Poisson Eq -/// solver by MultiGrid Method -/// -/// -/// -/// \author Rifki Sadikin , Indonesian Institute of Sciences -/// \date Nov 20, 2017 - -#ifndef ALITPCPOISSONSOLVER_H -#define ALITPCPOISSONSOLVER_H - -#include -#include "TMatrixD.h" -#include "TVectorD.h" - -class AliTPCPoissonSolver : public TNamed -{ - public: - ///< Enumeration of Poisson Solver Strategy Type - enum StrategyType { - kRelaxation = 0, ///< S.O.R Cascaded MultiGrid - kMultiGrid = 1, ///< Geometric MG - kFastRelaxation = 2 ///< Spectral (TODO) - }; - - ///< Enumeration of Cycles Type - enum CycleType { - kVCycle = 0, ///< V Cycle - kWCycle = 1, ///< W Cycle (TODO) - kFCycle = 2 ///< Full Cycle - }; - - ///< Fine -> Coarse Grid transfer operator types - enum GridTransferType { - kHalf = 0, ///< Half weighting - kFull = 1, ///< Full weighting - }; - - ///< Smoothing (Relax) operator types - enum RelaxType { - kJacobi = 0, ///< Jacobi (5 Stencil 2D, 7 Stencil 3D_ - kWeightedJacobi = 1, ///< (TODO) - kGaussSeidel = 2 ///< Gauss Seidel 2D (2 Color, 5 Stencil), 3D (7 Stencil) - }; - - ///< Coarse -> fine operator types (TODO: Interp and Restrict in one packet, just one enumeration) - enum InterpType { - kHalfInterp = 0, ///< Half bi linear interpolation - kFullInterp = 1 ///< Full bi linear interpolation - }; - - ///< Parameters choice for MultiGrid algorithm - struct MGParameters { - Bool_t isFull3D; ///< TRUE: full coarsening, FALSE: semi coarsening - CycleType cycleType; ///< cycleType follow CycleType - GridTransferType gtType; ///< gtType grid transfer type follow GridTransferType - RelaxType relaxType; ///< relaxType follow RelaxType - Int_t gamma; ///< number of iteration at coarsest level - Int_t nPre; ///< number of iteration for pre smoothing - Int_t nPost; ///< number of iteration for post smoothing - Int_t nMGCycle; ///< number of multi grid cycle (V type) - Int_t maxLoop; ///< the number of tree-deep of multi grid - - // default values - MGParameters() - { - isFull3D = kFALSE; - cycleType = kFCycle; - gtType = kFull; // default full - relaxType = kGaussSeidel; // default relaxation method - nPre = 2; - nPost = 2; - nMGCycle = 200; - maxLoop = 6; - } - }; - - AliTPCPoissonSolver(); - AliTPCPoissonSolver(const char* name, const char* title); -#if (defined(__CINT__) || defined(__ROOTCINT__)) && !defined(__CLING__) - ~AliTPCPoissonSolver(); -#else - ~AliTPCPoissonSolver() override; -#endif - void PoissonSolver2D(TMatrixD& matrixV, TMatrixD& chargeDensity, Int_t nRRow, Int_t nZColumn, Int_t maxIterations); - void PoissonSolver3D(TMatrixD** matricesV, TMatrixD** matricesChargeDensities, Int_t nRRow, Int_t nZColumn, - Int_t phiSlice, Int_t maxIterations, Int_t symmetry); - - void SetStrategy(StrategyType strategy) { fStrategy = strategy; } - StrategyType GetStrategy() { return fStrategy; } - - static const Double_t fgkTPCZ0; ///< nominal gating grid position - static const Double_t fgkIFCRadius; ///< Mean Radius of the Inner Field Cage ( 82.43 min, 83.70 max) (cm) - static const Double_t fgkOFCRadius; ///< Mean Radius of the Outer Field Cage (252.55 min, 256.45 max) (cm) - static const Double_t fgkZOffSet; ///< Offset from CE: calculate all distortions closer to CE as if at this point - static const Double_t fgkCathodeV; ///< Cathode Voltage (volts) - static const Double_t fgkGG; ///< Gating Grid voltage (volts) - static const Double_t fgkdvdE; ///< [cm/V] drift velocity dependency on the E field (from Magboltz for NeCO2N2 at standard environment) - static const Double_t fgkEM; ///< charge/mass in [C/kg] - static const Double_t fgke0; ///< vacuum permittivity [A·s/(V·m)] - - static Double_t fgExactErr; ///< Error tolerated - static Double_t fgConvergenceError; ///< Error tolerated - Int_t fIterations; ///< number of maximum iteration - MGParameters fMgParameters; ///< parameters multi grid - - void SetExactSolution(TMatrixD** exactSolution, const Int_t fPhiSlices); - void SetCycleType(AliTPCPoissonSolver::CycleType cycleType) { fMgParameters.cycleType = cycleType; } - - private: - AliTPCPoissonSolver(const AliTPCPoissonSolver&); // not implemented - AliTPCPoissonSolver& operator=(const AliTPCPoissonSolver&); // not implemented - StrategyType fStrategy = kMultiGrid; ///< strategy used default multiGrid - TMatrixD** fExactSolution = nullptr; //!& vectorCoefficient1, - std::vector& vectorCoefficient2); - void Relax3D(TMatrixD** currentMatricesV, TMatrixD** matricesCharge, const Int_t tnRRow, const Int_t tnZColumn, - const Int_t phiSlice, const Int_t symmetry, const Float_t h2, const Float_t tempRatioZ, - std::vector& vectorCoefficient1, std::vector& vectorCoefficient2, - std::vector& vectorCoefficient3, - std::vector& vectorCoefficient4); - void Residue2D(TMatrixD& residue, TMatrixD& matrixV, TMatrixD& matrixCharge, - const Int_t tnRRow, const Int_t tnZColumn, const Float_t ih2, const Float_t iTempFourth, - const Float_t tempRatio, std::vector& vectorCoefficient1, - std::vector& vectorCoefficient2); - void Residue3D(TMatrixD** residue, TMatrixD** currentMatricesV, TMatrixD** matricesCharge, const Int_t tnRRow, - const Int_t tnZColumn, const Int_t phiSlice, const Int_t symmetry, const Float_t ih2, - const Float_t tempRatio, std::vector& vectorCoefficient1, - std::vector& vectorCoefficient2, - std::vector& vectorCoefficient3, std::vector& vectorInverseCoefficient4); - void Restrict2D(TMatrixD& matrixCharge, TMatrixD& residue, const Int_t tnRRow, const Int_t tnZColumn); - void Restrict3D(TMatrixD** matricesCharge, TMatrixD** residue, const Int_t tnRRow, const Int_t tnZColumn, - const Int_t newPhiSlice, const Int_t oldPhiSlice); - void RestrictBoundary2D(TMatrixD& matrixCharge, TMatrixD& residue, const Int_t tnRRow, const Int_t tnZColumn); - void RestrictBoundary3D(TMatrixD** matricesCharge, TMatrixD** residue, const Int_t tnRRow, const Int_t tnZColumn, - const Int_t newPhiSlice, const Int_t oldPhiSlice); - - void AddInterp2D(TMatrixD& matrixV, TMatrixD& matrixVC, const Int_t tnRRow, const Int_t tnZColumn); - void AddInterp3D(TMatrixD** currentMatricesV, TMatrixD** currentMatricesVC, const Int_t tnRRow, const Int_t tnZColumn, - const Int_t newPhiSlice, const Int_t oldPhiSlice); - void Interp2D(TMatrixD& matrixV, TMatrixD& matrixVC, const Int_t tnRRow, const Int_t tnZColumn); - - void Interp3D(TMatrixD** currentMatricesV, TMatrixD** currentMatricesVC, const Int_t tnRRow, const Int_t tnZColumn, - const Int_t newPhiSlice, const Int_t oldPhiSlice); - void VCycle2D(const Int_t nRRow, const Int_t nZColumn, const Int_t gridFrom, const Int_t gridTo, const Int_t nPre, - const Int_t nPost, const Float_t gridSizeR, const Float_t ratio, std::vector& tvArrayV, - std::vector& tvCharge, std::vector& tvResidue); - void WCycle2D(const Int_t nRRow, const Int_t nZColumn, const Int_t gridFrom, const Int_t gridTo, const Int_t gamma, - const Int_t nPre, const Int_t nPost, const Float_t gridSizeR, const Float_t ratio, - std::vector& tvArrayV, - std::vector& tvCharge, std::vector& tvResidue); - void - VCycle3D(const Int_t nRRow, const Int_t nZColumn, const Int_t phiSlice, const Int_t symmetry, const Int_t gridFrom, - const Int_t gridTo, const Int_t nPre, const Int_t nPost, const Float_t gridSizeR, const Float_t ratioZ, - std::vector& tvArrayV, std::vector& tvCharge, - std::vector& tvResidue, std::vector& vectorCoefficient1, - std::vector& vectorCoefficient2, - std::vector& vectorCoefficient3, std::vector& vectorCoefficient4, - std::vector& vectorInverseCoefficient4); - void VCycle3D2D(const Int_t nRRow, const Int_t nZColumn, const Int_t phiSlice, const Int_t symmetry, - const Int_t gridFrom, const Int_t gridTo, const Int_t nPre, const Int_t nPost, - const Float_t gridSizeR, - const Float_t ratioZ, const Float_t ratioPhi, std::vector& tvArrayV, - std::vector& tvCharge, std::vector& tvResidue, - std::vector& vectorCoefficient1, - std::vector& vectorCoefficient2, std::vector& vectorCoefficient3, - std::vector& vectorCoefficient4, - std::vector& vectorInverseCoefficient4); - void VCycle3D2DGPU(const Int_t nRRow, const Int_t nZColumn, const Int_t phiSlice, const Int_t symmetry, - const Int_t gridFrom, const Int_t gridTo, const Int_t nPre, - const Int_t nPost, const Float_t gridSizeR, const Float_t ratioZ, const Float_t ratioPhi, - std::vector& tvArrayV, std::vector& tvCharge, - std::vector& tvResidue, std::vector& vectorCoefficient1, - std::vector& vectorCoefficient2, - std::vector& vectorCoefficient3, std::vector& vectorCoefficient4, - std::vector& vectorInverseCoefficient4); - Double_t GetExactError(TMatrixD** currentMatricesV, TMatrixD** tempArrayV, const Int_t phiSlice); - Double_t GetConvergenceError(TMatrixD** currentMatricesV, TMatrixD** prevArrayV, const Int_t phiSlice); - Double_t fMaxExact; - Bool_t fExactPresent = kFALSE; - /// \cond CLASSIMP -#if defined(ROOT_VERSION_CODE) && ROOT_VERSION_CODE >= ROOT_VERSION(6, 0, 0) - ClassDefOverride(AliTPCPoissonSolver, 5); -#else - ClassDefNV(AliTPCPoissonSolver, 5); -#endif - /// \endcond -}; - -#endif diff --git a/GPU/TPCSpaceChargeBase/AliTPCSpaceCharge3DCalc.cxx b/GPU/TPCSpaceChargeBase/AliTPCSpaceCharge3DCalc.cxx deleted file mode 100644 index 14dea14dbc555..0000000000000 --- a/GPU/TPCSpaceChargeBase/AliTPCSpaceCharge3DCalc.cxx +++ /dev/null @@ -1,5315 +0,0 @@ -// Copyright CERN and copyright holders of ALICE O2. This software is -// distributed under the terms of the GNU General Public License v3 (GPL -// Version 3), copied verbatim in the file "COPYING". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file AliTPCSpaceCharge3DCalc.cxx -/// \brief This class provides distortion and correction map with integration following electron drift -/// -/// \author Rifki Sadikin , Indonesian Institute of Sciences -/// \date Nov 20, 2017 - -#include "TStopwatch.h" -#include "TMath.h" -#include "AliTPCSpaceCharge3DCalc.h" - -/// \cond CLASSIMP -ClassImp(AliTPCSpaceCharge3DCalc); -/// \endcond - -/// Construction for AliTPCSpaceCharge3DCalc class -/// Default values -/// ~~~ -/// fInterpolationOrder = 5; // interpolation cubic spline with 5 points -/// fNRRows = 129; -/// fNPhiSlices = 180; // the maximum of phi-slices so far = (8 per sector) -/// fNZColumns = 129; // the maximum on column-slices so ~ 2cm slicing -/// ~~~ -AliTPCSpaceCharge3DCalc::AliTPCSpaceCharge3DCalc() -{ - InitAllocateMemory(); -} - -/// Member values from params -/// -/// \param nRRow Int_t number of grid in r direction -/// \param nZColumn Int_t number of grid in z direction -/// \param nPhiSlice Int_t number of grid in \f$ \phi \f$ direction -/// -AliTPCSpaceCharge3DCalc::AliTPCSpaceCharge3DCalc(Int_t nRRow, - Int_t nZColumn, Int_t nPhiSlice) - : fNRRows(nRRow), - fNPhiSlices(nPhiSlice), - fNZColumns(nZColumn) -{ - InitAllocateMemory(); -} - -/// Construction for AliTPCSpaceCharge3DCalc class -/// Member values from params -/// -/// \param nRRow Int_t number of grid in r direction -/// \param nZColumn Int_t number of grid in z direction -/// \param nPhiSlice Int_t number of grid in \f$ \phi \f$ direction -/// \param interpolationOrder Int_t order of interpolation -/// \param strategy Int_t strategy for global distortion -/// \param rbfKernelType Int_t strategy for global distortion -/// -AliTPCSpaceCharge3DCalc::AliTPCSpaceCharge3DCalc( - Int_t nRRow, Int_t nZColumn, Int_t nPhiSlice, Int_t interpolationOrder, - Int_t irregularGridSize, Int_t rbfKernelType) - : fNRRows(nRRow), - fNPhiSlices(nPhiSlice), - fNZColumns(nZColumn), - fInterpolationOrder(interpolationOrder), - fIrregularGridSize(irregularGridSize), - fRBFKernelType(rbfKernelType) -{ - InitAllocateMemory(); -} - -/// Memory allocation for working/output memory -/// -void AliTPCSpaceCharge3DCalc::InitAllocateMemory() -{ - fPoissonSolver = new AliTPCPoissonSolver(); - - fListR = new Double_t[fNRRows]; - fListPhi = new Double_t[fNPhiSlices]; - fListZ = new Double_t[fNZColumns]; - fListZA = new Double_t[fNZColumns]; - fListZC = new Double_t[fNZColumns]; - - // allocate for boundary - /// TODO: fListPotentialBoundary arrays are never used in the code. Remove? - // Int_t len = 2 * fNPhiSlices * (fNZColumns + fNRRows) - (4 * fNPhiSlices); - // fListPotentialBoundaryA = new Double_t[len]; - // fListPotentialBoundaryC = new Double_t[len]; - - Int_t phiSlicesPerSector = fNPhiSlices / kNumSector; - const Float_t gridSizeR = (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (fNRRows - 1); - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (fNZColumns - 1); - const Float_t gridSizePhi = TMath::TwoPi() / fNPhiSlices; - - for (Int_t k = 0; k < fNPhiSlices; k++) { - fListPhi[k] = gridSizePhi * k; - } - for (Int_t i = 0; i < fNRRows; i++) { - fListR[i] = AliTPCPoissonSolver::fgkIFCRadius + i * gridSizeR; - } - for (Int_t j = 0; j < fNZColumns; j++) { - fListZ[j] = (j * gridSizeZ); - fListZA[j] = (j * gridSizeZ); - fListZC[j] = (j * gridSizeZ); - } - - fMatrixIntDistDrEzA = new TMatrixD*[fNPhiSlices]; - fMatrixIntDistDPhiREzA = new TMatrixD*[fNPhiSlices]; - fMatrixIntDistDzA = new TMatrixD*[fNPhiSlices]; - - fMatrixIntDistDrEzC = new TMatrixD*[fNPhiSlices]; - fMatrixIntDistDPhiREzC = new TMatrixD*[fNPhiSlices]; - fMatrixIntDistDzC = new TMatrixD*[fNPhiSlices]; - - fMatrixErOverEzA = new TMatrixD*[fNPhiSlices]; - fMatrixEPhiOverEzA = new TMatrixD*[fNPhiSlices]; - fMatrixDeltaEzA = new TMatrixD*[fNPhiSlices]; - - fMatrixErOverEzC = new TMatrixD*[fNPhiSlices]; - fMatrixEPhiOverEzC = new TMatrixD*[fNPhiSlices]; - fMatrixDeltaEzC = new TMatrixD*[fNPhiSlices]; - - fMatrixIntCorrDrEzA = new TMatrixD*[fNPhiSlices]; - fMatrixIntCorrDPhiREzA = new TMatrixD*[fNPhiSlices]; - fMatrixIntCorrDzA = new TMatrixD*[fNPhiSlices]; - - fMatrixIntCorrDrEzC = new TMatrixD*[fNPhiSlices]; - fMatrixIntCorrDPhiREzC = new TMatrixD*[fNPhiSlices]; - fMatrixIntCorrDzC = new TMatrixD*[fNPhiSlices]; - - fMatrixIntCorrDrEzIrregularA = new TMatrixD*[fNPhiSlices]; - fMatrixIntCorrDPhiREzIrregularA = new TMatrixD*[fNPhiSlices]; - fMatrixIntCorrDzIrregularA = new TMatrixD*[fNPhiSlices]; - fMatrixRListIrregularA = new TMatrixD*[fNPhiSlices]; - fMatrixPhiListIrregularA = new TMatrixD*[fNPhiSlices]; - fMatrixZListIrregularA = new TMatrixD*[fNPhiSlices]; - - fMatrixIntCorrDrEzIrregularC = new TMatrixD*[fNPhiSlices]; - fMatrixIntCorrDPhiREzIrregularC = new TMatrixD*[fNPhiSlices]; - fMatrixIntCorrDzIrregularC = new TMatrixD*[fNPhiSlices]; - fMatrixRListIrregularC = new TMatrixD*[fNPhiSlices]; - fMatrixPhiListIrregularC = new TMatrixD*[fNPhiSlices]; - fMatrixZListIrregularC = new TMatrixD*[fNPhiSlices]; - - fMatrixChargeA = new TMatrixD*[fNPhiSlices]; - fMatrixChargeC = new TMatrixD*[fNPhiSlices]; - fMatrixChargeInverseA = new TMatrixD*[fNPhiSlices]; - fMatrixChargeInverseC = new TMatrixD*[fNPhiSlices]; - fMatrixPotentialA = new TMatrixD*[fNPhiSlices]; - fMatrixPotentialC = new TMatrixD*[fNPhiSlices]; - - for (Int_t k = 0; k < fNPhiSlices; k++) { - - fMatrixIntDistDrEzA[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixIntDistDPhiREzA[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixIntDistDzA[k] = new TMatrixD(fNRRows, fNZColumns); - - fMatrixIntDistDrEzC[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixIntDistDPhiREzC[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixIntDistDzC[k] = new TMatrixD(fNRRows, fNZColumns); - - fMatrixIntCorrDrEzA[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixIntCorrDPhiREzA[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixIntCorrDzA[k] = new TMatrixD(fNRRows, fNZColumns); - - fMatrixIntCorrDrEzC[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixIntCorrDPhiREzC[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixIntCorrDzC[k] = new TMatrixD(fNRRows, fNZColumns); - - fMatrixErOverEzA[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixEPhiOverEzA[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixDeltaEzA[k] = new TMatrixD(fNRRows, fNZColumns); - - fMatrixErOverEzC[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixEPhiOverEzC[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixDeltaEzC[k] = new TMatrixD(fNRRows, fNZColumns); - - fMatrixIntCorrDrEzIrregularA[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixIntCorrDPhiREzIrregularA[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixIntCorrDzIrregularA[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixRListIrregularA[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixPhiListIrregularA[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixZListIrregularA[k] = new TMatrixD(fNRRows, fNZColumns); - - fMatrixIntCorrDrEzIrregularC[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixIntCorrDPhiREzIrregularC[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixIntCorrDzIrregularC[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixRListIrregularC[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixPhiListIrregularC[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixZListIrregularC[k] = new TMatrixD(fNRRows, fNZColumns); - - fMatrixChargeA[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixChargeC[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixChargeInverseA[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixChargeInverseC[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixPotentialA[k] = new TMatrixD(fNRRows, fNZColumns); - fMatrixPotentialC[k] = new TMatrixD(fNRRows, fNZColumns); - } - - fLookupIntDistA = - new AliTPCLookUpTable3DInterpolatorD( - fNRRows, fMatrixIntDistDrEzA, fListR, fNPhiSlices, fMatrixIntDistDPhiREzA, fListPhi, - fNZColumns, fMatrixIntDistDzA, fListZA, fInterpolationOrder); - fLookupIntDistC = - new AliTPCLookUpTable3DInterpolatorD( - fNRRows, fMatrixIntDistDrEzC, fListR, fNPhiSlices, fMatrixIntDistDPhiREzC, fListPhi, - fNZColumns, fMatrixIntDistDzC, fListZC, fInterpolationOrder); - fLookupIntCorrA = - new AliTPCLookUpTable3DInterpolatorD( - fNRRows, fMatrixIntCorrDrEzA, fListR, fNPhiSlices, fMatrixIntCorrDPhiREzA, fListPhi, - fNZColumns, fMatrixIntCorrDzA, fListZA, fInterpolationOrder); - fLookupIntCorrC = - new AliTPCLookUpTable3DInterpolatorD( - fNRRows, fMatrixIntCorrDrEzC, fListR, fNPhiSlices, fMatrixIntCorrDPhiREzC, fListPhi, - fNZColumns, fMatrixIntCorrDzC, fListZC, fInterpolationOrder); - - fLookupIntENoDriftA = - new AliTPCLookUpTable3DInterpolatorD( - fNRRows, fMatrixErOverEzA, fListR, fNPhiSlices, fMatrixEPhiOverEzA, fListPhi, - fNZColumns, fMatrixDeltaEzA, fListZA, fInterpolationOrder); - fLookupIntENoDriftC = - new AliTPCLookUpTable3DInterpolatorD( - fNRRows, fMatrixErOverEzC, fListR, fNPhiSlices, fMatrixEPhiOverEzC, fListPhi, - fNZColumns, fMatrixDeltaEzC, fListZC, fInterpolationOrder); - fLookupIntCorrIrregularA = - new AliTPCLookUpTable3DInterpolatorIrregularD( - fNRRows, fMatrixIntCorrDrEzIrregularA, fMatrixRListIrregularA, fNPhiSlices, - fMatrixIntCorrDPhiREzIrregularA, fMatrixPhiListIrregularA, fNZColumns, - fMatrixIntCorrDzIrregularA, fMatrixZListIrregularA, 2, GetIrregularGridSize(), - GetIrregularGridSize(), GetIrregularGridSize(), 1); - - fLookupIntCorrIrregularC = - new AliTPCLookUpTable3DInterpolatorIrregularD( - fNRRows, fMatrixIntCorrDrEzIrregularC, fMatrixRListIrregularC, fNPhiSlices, - fMatrixIntCorrDPhiREzIrregularC, fMatrixPhiListIrregularC, fNZColumns, - fMatrixIntCorrDzIrregularC, fMatrixZListIrregularC, 2, GetIrregularGridSize(), - GetIrregularGridSize(), GetIrregularGridSize(), 1); - - fInterpolatorChargeA = new AliTPC3DCylindricalInterpolator(); - fInterpolatorChargeC = new AliTPC3DCylindricalInterpolator(); - fInterpolatorPotentialA = new AliTPC3DCylindricalInterpolator(); - fInterpolatorPotentialC = new AliTPC3DCylindricalInterpolator(); - fInterpolatorInverseChargeA = new AliTPC3DCylindricalInterpolator(); - fInterpolatorInverseChargeC = new AliTPC3DCylindricalInterpolator(); - - fInterpolatorChargeA->SetNR(fNRRows); - fInterpolatorChargeA->SetNZ(fNZColumns); - fInterpolatorChargeA->SetNPhi(fNPhiSlices); - fInterpolatorChargeA->SetNGridPoints(); - fInterpolatorChargeA->SetRList(fListR); - fInterpolatorChargeA->SetZList(fListZA); - fInterpolatorChargeA->SetPhiList(fListPhi); - fInterpolatorChargeA->SetOrder(fInterpolationOrder); - - fInterpolatorChargeC->SetNR(fNRRows); - fInterpolatorChargeC->SetNZ(fNZColumns); - fInterpolatorChargeC->SetNPhi(fNPhiSlices); - fInterpolatorChargeC->SetNGridPoints(); - fInterpolatorChargeC->SetRList(fListR); - fInterpolatorChargeC->SetZList(fListZC); - fInterpolatorChargeC->SetPhiList(fListPhi); - fInterpolatorChargeC->SetOrder(fInterpolationOrder); - - fInterpolatorPotentialA->SetNR(fNRRows); - fInterpolatorPotentialA->SetNZ(fNZColumns); - fInterpolatorPotentialA->SetNPhi(fNPhiSlices); - fInterpolatorPotentialA->SetNGridPoints(); - fInterpolatorPotentialA->SetRList(fListR); - fInterpolatorPotentialA->SetZList(fListZA); - fInterpolatorPotentialA->SetPhiList(fListPhi); - fInterpolatorPotentialA->SetOrder(fInterpolationOrder); - - fInterpolatorPotentialC->SetNR(fNRRows); - fInterpolatorPotentialC->SetNZ(fNZColumns); - fInterpolatorPotentialC->SetNPhi(fNPhiSlices); - fInterpolatorPotentialC->SetNGridPoints(); - fInterpolatorPotentialC->SetRList(fListR); - fInterpolatorPotentialC->SetZList(fListZA); - fInterpolatorPotentialC->SetPhiList(fListPhi); - fInterpolatorPotentialC->SetOrder(fInterpolationOrder); - - fInterpolatorInverseChargeA->SetNR(fNRRows); - fInterpolatorInverseChargeA->SetNZ(fNZColumns); - fInterpolatorInverseChargeA->SetNPhi(fNPhiSlices); - fInterpolatorInverseChargeA->SetNGridPoints(); - fInterpolatorInverseChargeA->SetRList(fListR); - fInterpolatorInverseChargeA->SetZList(fListZA); - fInterpolatorInverseChargeA->SetPhiList(fListPhi); - fInterpolatorInverseChargeA->SetOrder(fInterpolationOrder); - - fInterpolatorInverseChargeC->SetNR(fNRRows); - fInterpolatorInverseChargeC->SetNZ(fNZColumns); - fInterpolatorInverseChargeC->SetNPhi(fNPhiSlices); - fInterpolatorInverseChargeC->SetNGridPoints(); - fInterpolatorInverseChargeC->SetRList(fListR); - fInterpolatorInverseChargeC->SetZList(fListZC); - fInterpolatorInverseChargeC->SetPhiList(fListPhi); - fInterpolatorInverseChargeC->SetOrder(fInterpolationOrder); - - fLookupDistA = - new AliTPCLookUpTable3DInterpolatorD( - fNRRows, nullptr, fListR, fNPhiSlices, nullptr, fListPhi, fNZColumns, nullptr, fListZA, - fInterpolationOrder); - - fLookupDistC = - new AliTPCLookUpTable3DInterpolatorD( - fNRRows, nullptr, fListR, fNPhiSlices, nullptr, fListPhi, fNZColumns, nullptr, fListZA, - fInterpolationOrder); - - fLookupCorrA = - new AliTPCLookUpTable3DInterpolatorD( - fNRRows, nullptr, fListR, fNPhiSlices, nullptr, fListPhi, fNZColumns, nullptr, fListZA, - fInterpolationOrder); - - fLookupCorrC = - new AliTPCLookUpTable3DInterpolatorD( - fNRRows, nullptr, fListR, fNPhiSlices, nullptr, fListPhi, fNZColumns, nullptr, fListZA, - fInterpolationOrder); - - fLookupInverseDistA = - new AliTPCLookUpTable3DInterpolatorD( - fNRRows, nullptr, fListR, fNPhiSlices, nullptr, fListPhi, fNZColumns, nullptr, fListZA, - fInterpolationOrder); - - fLookupInverseDistC = - new AliTPCLookUpTable3DInterpolatorD( - fNRRows, nullptr, fListR, fNPhiSlices, nullptr, fListPhi, fNZColumns, nullptr, fListZA, - fInterpolationOrder); - - fLookupElectricFieldA = - new AliTPCLookUpTable3DInterpolatorD( - fNRRows, nullptr, fListR, fNPhiSlices, nullptr, fListPhi, fNZColumns, nullptr, fListZA, - fInterpolationOrder); - - fLookupElectricFieldC = - new AliTPCLookUpTable3DInterpolatorD( - fNRRows, nullptr, fListR, fNPhiSlices, nullptr, fListPhi, fNZColumns, nullptr, fListZA, - fInterpolationOrder); - - fLookupIntCorrIrregularA->SetKernelType(fRBFKernelType); - fLookupIntCorrIrregularC->SetKernelType(fRBFKernelType); -} -/// Destruction for AliTPCSpaceCharge3DCalc -/// Deallocate memory for lookup table and charge distribution -/// -AliTPCSpaceCharge3DCalc::~AliTPCSpaceCharge3DCalc() -{ - - if (fPoissonSolver != nullptr) { - delete fPoissonSolver; - } - - for (Int_t k = 0; k < fNPhiSlices; k++) { - delete fMatrixIntDistDrEzA[k]; - delete fMatrixIntDistDPhiREzA[k]; - delete fMatrixIntDistDzA[k]; - delete fMatrixIntDistDrEzC[k]; - delete fMatrixIntDistDPhiREzC[k]; - delete fMatrixIntDistDzC[k]; - delete fMatrixIntCorrDrEzA[k]; - delete fMatrixIntCorrDPhiREzA[k]; - delete fMatrixIntCorrDzA[k]; - delete fMatrixIntCorrDrEzC[k]; - delete fMatrixIntCorrDPhiREzC[k]; - delete fMatrixIntCorrDzC[k]; - delete fMatrixErOverEzA[k]; - delete fMatrixEPhiOverEzA[k]; - delete fMatrixDeltaEzA[k]; - delete fMatrixErOverEzC[k]; - delete fMatrixEPhiOverEzC[k]; - delete fMatrixDeltaEzC[k]; - delete fMatrixIntCorrDrEzIrregularA[k]; - delete fMatrixIntCorrDPhiREzIrregularA[k]; - delete fMatrixIntCorrDzIrregularA[k]; - delete fMatrixRListIrregularA[k]; - delete fMatrixPhiListIrregularA[k]; - delete fMatrixZListIrregularA[k]; - delete fMatrixIntCorrDrEzIrregularC[k]; - delete fMatrixIntCorrDPhiREzIrregularC[k]; - delete fMatrixIntCorrDzIrregularC[k]; - delete fMatrixRListIrregularC[k]; - delete fMatrixPhiListIrregularC[k]; - delete fMatrixZListIrregularC[k]; - delete fMatrixChargeA[k]; - delete fMatrixChargeC[k]; - delete fMatrixChargeInverseA[k]; - delete fMatrixChargeInverseC[k]; - - delete fMatrixPotentialA[k]; - delete fMatrixPotentialC[k]; - } - delete[] fListR; - delete[] fListPhi; - delete[] fListZ; - delete[] fListZA; - delete[] fListZC; - - delete fLookupIntDistA; - delete fLookupIntDistC; - delete fLookupIntENoDriftA; - delete fLookupIntENoDriftC; - delete fLookupIntCorrA; - delete fLookupIntCorrC; - delete fLookupIntCorrIrregularA; - delete fLookupIntCorrIrregularC; - delete fLookupDistA; - delete fLookupDistC; - delete fLookupInverseDistA; - delete fLookupInverseDistC; - delete fLookupElectricFieldA; - delete fLookupElectricFieldC; - delete fInterpolatorChargeA; - delete fInterpolatorPotentialA; - delete fInterpolatorChargeC; - delete fInterpolatorPotentialC; - delete fInterpolatorInverseChargeA; - delete fInterpolatorInverseChargeC; - - delete fHistogram3DSpaceCharge; - delete fHistogram3DSpaceChargeA; - delete fHistogram3DSpaceChargeC; - - delete fFormulaBoundaryIFCA; - delete fFormulaBoundaryIFCC; - delete fFormulaBoundaryOFCA; - delete fFormulaBoundaryOFCC; - delete fFormulaBoundaryROCA; - delete fFormulaBoundaryROCC; - delete fFormulaBoundaryCE; - - delete fFormulaPotentialV; - delete fFormulaChargeRho; - - delete fFormulaEPhi; - delete fFormulaEr; - delete fFormulaEz; - - /// TODO: fListPotentialBoundary arrays are never used in the code. Remove? - // delete[] fListPotentialBoundaryA; - // delete[] fListPotentialBoundaryC; -} - -/// Creating look-up tables of Correction/Distortion by integration following -/// drift line, input from space charge 3d histogram (fSpaceCharge3D) and boundary values are filled with zeroes -/// -/// TODO: provide an interface for setting boundary values -/// -/// The algorithm and implementations of this function is the following: -/// -/// Do for each side A,C -/// -/// 1) Solving \f$ \nabla^2 \Phi(r,\phi,z) = - \rho(r,\phi,z)\f$ -/// ~~~ Calling poisson solver -/// fPoissonSolver->PoissonSolver3D( matricesV, matricesCharge, nRRow, nZColumn, phiSlice, maxIteration, symmetry ) ; -/// ~~~ -/// -/// 2) Get the electric field \f$ \vec{E} = - \nabla \Phi(r,\phi,z) \f$ -/// ~~~ -/// ElectricField( matricesV, matricesEr, matricesEPhi, matricesEz, nRRow, nZColumn, phiSlice, -/// gridSizeR, gridSizePhi ,gridSizeZ,symmetry, AliTPCPoissonSolver::fgkIFCRadius); -/// ~~~ -/// -/// 3) Calculate local distortion and correction, using Langevin formula -/// ~~~ cxx -/// LocalDistCorrDz (matricesEr, matricesEPhi, matricesEz, -/// matricesDistDrDz, matricesDistDPhiRDz, matricesDistDz, -/// matricesCorrDrDz, matricesCorrDPhiRDz, matricesCorrDz, -/// nRRow, nZColumn, phiSlice, gridSizeZ, ezField); -/// ~~~ -/// -/// 4) Integrate distortion by following the drift line -/// -/// 5) Fill look up table for Integral distortion -/// -/// 6) Fill look up table for Integral correction -/// -/// \param nRRow Int_t Number of nRRow in r-direction -/// \param nZColumn Int_t Number of nZColumn in z-direction -/// \param phiSlice Int_t Number of phi slice in \f$ phi \f$ direction -/// \param maxIteration Int_t Maximum iteration for poisson solver -/// \param stoppingConvergence Convergence error stopping condition for poisson solver -/// -/// \post Lookup tables for distortion: -/// ~~~ -/// fLookUpIntDistDrEz,fLookUpIntDistDPhiREz,fLookUpIntDistDz -/// ~~~ -/// and correction: -/// ~~~ -/// fLookUpIntCorrDrEz,fLookUpIntCorrDPhiREz,fLookUpIntCorrDz -/// ~~~ -/// are initialized -/// -void AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz( - Int_t nRRow, Int_t nZColumn, Int_t phiSlice, Int_t maxIteration, Double_t stoppingConvergence) -{ - Int_t phiSlicesPerSector = phiSlice / kNumSector; - const Float_t gridSizeR = (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (nRRow - 1); - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (nZColumn - 1); - const Float_t gridSizePhi = TMath::TwoPi() / phiSlice; - const Double_t ezField = (AliTPCPoissonSolver::fgkCathodeV - AliTPCPoissonSolver::fgkGG) / AliTPCPoissonSolver::fgkTPCZ0; // = ALICE Electric Field (V/cm) Magnitude ~ -400 V/cm; - - // local variables - Float_t radius0, phi0, z0; - - // memory allocation for temporary matrices: - // potential (boundary values), charge distribution - TMatrixD **matricesV, *matricesCharge[phiSlice]; - TMatrixD *matricesEr[phiSlice], *matricesEPhi[phiSlice], *matricesEz[phiSlice]; - TMatrixD *matricesDistDrDz[phiSlice], *matricesDistDPhiRDz[phiSlice], *matricesDistDz[phiSlice]; - TMatrixD *matricesCorrDrDz[phiSlice], *matricesCorrDPhiRDz[phiSlice], *matricesCorrDz[phiSlice]; - TMatrixD *matricesGDistDrDz[phiSlice], *matricesGDistDPhiRDz[phiSlice], *matricesGDistDz[phiSlice]; - TMatrixD *matricesGCorrDrDz[phiSlice], *matricesGCorrDPhiRDz[phiSlice], *matricesGCorrDz[phiSlice]; - - for (Int_t k = 0; k < phiSlice; k++) { - //matricesV[k] = new TMatrixD(nRRow, nZColumn); - matricesCharge[k] = new TMatrixD(nRRow, nZColumn); - matricesEr[k] = new TMatrixD(nRRow, nZColumn); - matricesEPhi[k] = new TMatrixD(nRRow, nZColumn); - matricesEz[k] = new TMatrixD(nRRow, nZColumn); - matricesDistDrDz[k] = new TMatrixD(nRRow, nZColumn); - matricesDistDPhiRDz[k] = new TMatrixD(nRRow, nZColumn); - matricesDistDz[k] = new TMatrixD(nRRow, nZColumn); - matricesCorrDrDz[k] = new TMatrixD(nRRow, nZColumn); - matricesCorrDPhiRDz[k] = new TMatrixD(nRRow, nZColumn); - matricesCorrDz[k] = new TMatrixD(nRRow, nZColumn); - matricesGDistDrDz[k] = new TMatrixD(nRRow, nZColumn); - matricesGDistDPhiRDz[k] = new TMatrixD(nRRow, nZColumn); - matricesGDistDz[k] = new TMatrixD(nRRow, nZColumn); - matricesGCorrDrDz[k] = new TMatrixD(nRRow, nZColumn); - matricesGCorrDPhiRDz[k] = new TMatrixD(nRRow, nZColumn); - matricesGCorrDz[k] = new TMatrixD(nRRow, nZColumn); - } - - // list of point as used in the poisson relaxation and the interpolation (for interpolation) - Double_t rList[nRRow], zList[nZColumn], phiList[phiSlice]; - - // pointer to current TF1 for potential boundary values - TF1* f1BoundaryIFC = nullptr; - TF1* f1BoundaryOFC = nullptr; - TF1* f1BoundaryROC = nullptr; - TStopwatch w; - - for (Int_t k = 0; k < phiSlice; k++) { - phiList[k] = gridSizePhi * k; - } - for (Int_t i = 0; i < nRRow; i++) { - rList[i] = AliTPCPoissonSolver::fgkIFCRadius + i * gridSizeR; - } - for (Int_t j = 0; j < nZColumn; j++) { - zList[j] = j * gridSizeZ; - } - - // allocate look up local distortion - AliTPCLookUpTable3DInterpolatorD* lookupLocalDist = - new AliTPCLookUpTable3DInterpolatorD( - nRRow, matricesDistDrDz, rList, phiSlice, matricesDistDPhiRDz, phiList, nZColumn, matricesDistDz, - zList, fInterpolationOrder); - - // allocate look up local correction - AliTPCLookUpTable3DInterpolatorD* lookupLocalCorr = - new AliTPCLookUpTable3DInterpolatorD( - nRRow, matricesCorrDrDz, rList, phiSlice, matricesCorrDPhiRDz, phiList, nZColumn, matricesCorrDz, - zList, fInterpolationOrder); - - // allocate look up for global distortion - AliTPCLookUpTable3DInterpolatorD* lookupGlobalDist = - new AliTPCLookUpTable3DInterpolatorD( - nRRow, matricesGDistDrDz, rList, phiSlice, matricesGDistDPhiRDz, phiList, nZColumn, matricesGDistDz, - zList, fInterpolationOrder); - // allocate look up for global distortion - AliTPCLookUpTable3DInterpolatorD* lookupGlobalCorr = - new AliTPCLookUpTable3DInterpolatorD( - nRRow, matricesGCorrDrDz, rList, phiSlice, matricesGCorrDPhiRDz, phiList, nZColumn, matricesGCorrDz, - zList, fInterpolationOrder); - - // should be set, in another place - const Int_t symmetry = 0; // fSymmetry - - // for irregular - TMatrixD** matricesIrregularDrDz = nullptr; - TMatrixD** matricesIrregularDPhiRDz = nullptr; - TMatrixD** matricesIrregularDz = nullptr; - TMatrixD** matricesPhiIrregular = nullptr; - TMatrixD** matricesRIrregular = nullptr; - TMatrixD** matricesZIrregular = nullptr; - - // for charge - TMatrixD** matricesLookUpCharge = nullptr; - AliTPC3DCylindricalInterpolator* chargeInterpolator = nullptr; - AliTPC3DCylindricalInterpolator* potentialInterpolator = nullptr; - /// TODO: fListPotentialBoundary arrays are never used in the code. Remove? - // Double_t* potentialBoundary = nullptr; - TMatrixD* matrixV; - TMatrixD* matrixCharge; - // for potential - TMatrixD** matricesVPotential; - - Int_t pIndex = 0; - - // do if look up table haven't be initialized - if (!fInitLookUp) { - // initialize for working memory - for (Int_t side = 0; side < 2; side++) { - // zeroing global distortion/correction - for (Int_t k = 0; k < phiSlice; k++) { - matricesDistDrDz[k]->Zero(); - matricesDistDPhiRDz[k]->Zero(); - matricesDistDz[k]->Zero(); - matricesCorrDrDz[k]->Zero(); - matricesCorrDPhiRDz[k]->Zero(); - matricesCorrDz[k]->Zero(); - - matricesGDistDrDz[k]->Zero(); - matricesGDistDPhiRDz[k]->Zero(); - matricesGDistDz[k]->Zero(); - matricesGCorrDrDz[k]->Zero(); - matricesGCorrDPhiRDz[k]->Zero(); - matricesGCorrDz[k]->Zero(); - } - if (side == 0) { - matricesIrregularDrDz = fMatrixIntCorrDrEzIrregularA; - matricesIrregularDPhiRDz = fMatrixIntCorrDPhiREzIrregularA; - matricesIrregularDz = fMatrixIntCorrDzIrregularA; - - matricesPhiIrregular = fMatrixPhiListIrregularA; - matricesRIrregular = fMatrixRListIrregularA; - matricesZIrregular = fMatrixZListIrregularA; - matricesLookUpCharge = fMatrixChargeA; - - matricesV = fMatrixPotentialA; - chargeInterpolator = fInterpolatorChargeA; - potentialInterpolator = fInterpolatorPotentialA; - fLookupDistA->SetLookUpR(matricesDistDrDz); - fLookupDistA->SetLookUpPhi(matricesDistDPhiRDz); - fLookupDistA->SetLookUpZ(matricesDistDz); - fLookupCorrA->SetLookUpR(matricesCorrDrDz); - fLookupCorrA->SetLookUpPhi(matricesCorrDPhiRDz); - fLookupCorrA->SetLookUpZ(matricesCorrDz); - - fLookupElectricFieldA->SetLookUpR(matricesEr); - fLookupElectricFieldA->SetLookUpPhi(matricesEPhi); - fLookupElectricFieldA->SetLookUpZ(matricesEz); - - /// TODO: fListPotentialBoundary arrays are never used in the code. Remove? - // potentialBoundary = fListPotentialBoundaryA; - f1BoundaryIFC = fFormulaBoundaryIFCA; - f1BoundaryOFC = fFormulaBoundaryOFCA; - f1BoundaryROC = fFormulaBoundaryROCA; - } else { - matricesIrregularDrDz = fMatrixIntCorrDrEzIrregularC; - matricesIrregularDPhiRDz = fMatrixIntCorrDPhiREzIrregularC; - matricesIrregularDz = fMatrixIntCorrDzIrregularC; - matricesPhiIrregular = fMatrixPhiListIrregularC; - matricesRIrregular = fMatrixRListIrregularC; - matricesZIrregular = fMatrixZListIrregularC; - matricesLookUpCharge = fMatrixChargeC; - matricesV = fMatrixPotentialC; - chargeInterpolator = fInterpolatorChargeC; - potentialInterpolator = fInterpolatorPotentialC; - fLookupDistC->SetLookUpR(matricesDistDrDz); - fLookupDistC->SetLookUpPhi(matricesDistDPhiRDz); - fLookupDistC->SetLookUpZ(matricesDistDz); - fLookupCorrC->SetLookUpR(matricesCorrDrDz); - fLookupCorrC->SetLookUpPhi(matricesCorrDPhiRDz); - fLookupCorrC->SetLookUpZ(matricesCorrDz); - fLookupElectricFieldC->SetLookUpR(matricesEr); - fLookupElectricFieldC->SetLookUpPhi(matricesEPhi); - fLookupElectricFieldC->SetLookUpZ(matricesEz); - - /// TODO: fListPotentialBoundary arrays are never used in the code. Remove? - // potentialBoundary = fListPotentialBoundaryC; - f1BoundaryIFC = fFormulaBoundaryIFCC; - f1BoundaryOFC = fFormulaBoundaryOFCC; - f1BoundaryROC = fFormulaBoundaryROCC; - } - - // fill the potential boundary - // guess the initial potential - // fill also charge - //pIndex = 0; - - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step = 0: Fill Boundary and Charge Densities")); - for (Int_t k = 0; k < phiSlice; k++) { - phi0 = k * gridSizePhi; - matrixV = matricesV[k]; - matrixCharge = matricesCharge[k]; - for (Int_t i = 0; i < nRRow; i++) { - radius0 = AliTPCPoissonSolver::fgkIFCRadius + i * gridSizeR; - for (Int_t j = 0; j < nZColumn; j++) { - z0 = j * gridSizeZ; - (*matrixCharge)(i, j) = chargeInterpolator->GetValue(rList[i], phiList[k], zList[j]); - (*matrixV)(i, j) = 0.0; // fill zeros - - if (fFormulaPotentialV == nullptr) { - // boundary IFC - if (i == 0) { - if (f1BoundaryIFC != nullptr) { - if (TF2* f2BoundaryIFC = dynamic_cast(f1BoundaryIFC)) { - (*matrixV)(i, j) = f2BoundaryIFC->Eval(z0, phi0); - } else { - (*matrixV)(i, j) = f1BoundaryIFC->Eval(z0); - } - } - } - if (i == (nRRow - 1)) { - if (f1BoundaryOFC != nullptr) { - if (TF2* f2BoundaryOFC = dynamic_cast(f1BoundaryOFC)) { - (*matrixV)(i, j) = f2BoundaryOFC->Eval(z0, phi0); - } else { - (*matrixV)(i, j) = f1BoundaryOFC->Eval(z0); - } - } - } - if (j == 0) { - if (fFormulaBoundaryCE) { - if (TF2* f2FormulaBoundaryCE = dynamic_cast(fFormulaBoundaryCE)) { - (*matrixV)(i, j) = f2FormulaBoundaryCE->Eval(radius0, phi0); - } else { - (*matrixV)(i, j) = fFormulaBoundaryCE->Eval(radius0); - } - } - } - if (j == (nZColumn - 1)) { - if (f1BoundaryROC != nullptr) { - if (TF2* f2BoundaryROC = dynamic_cast(f1BoundaryROC)) { - (*matrixV)(i, j) = f2BoundaryROC->Eval(radius0, phi0); - } else { - (*matrixV)(i, j) = f1BoundaryROC->Eval(radius0); - } - } - } - } else { - if ((i == 0) || (i == (nRRow - 1)) || (j == 0) || (j == (nZColumn - 1))) { - (*matrixV)(i, j) = fFormulaPotentialV->Eval(radius0, phi0, z0); - } - } - } - } - } - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step 0: Preparing Charge interpolator: %f\n", w.CpuTime())); - AliTPCPoissonSolver::fgConvergenceError = stoppingConvergence; - - //fPoissonSolver->SetStrategy(AliTPCPoissonSolver::kMultiGrid); - //(fPoissonSolver->fMgParameters).cycleType = AliTPCPoissonSolver::kFCycle; - //(fPoissonSolver->fMgParameters).isFull3D = kFALSE; - //(fPoissonSolver->fMgParameters).nMGCycle = maxIteration; - //(fPoissonSolver->fMgParameters).maxLoop = 6; - - w.Start(); - fPoissonSolver->PoissonSolver3D(matricesV, matricesCharge, nRRow, nZColumn, phiSlice, maxIteration, symmetry); - w.Stop(); - - potentialInterpolator->SetValue(matricesV); - potentialInterpolator->InitCubicSpline(); - - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step 1: Poisson solver: %f\n", w.CpuTime())); - if (side == 0) { - myProfile.poissonSolverTime = w.CpuTime(); - } - if (side == 0) { - myProfile.iteration = fPoissonSolver->fIterations; - } - - w.Start(); - ElectricField(matricesV, - matricesEr, matricesEPhi, matricesEz, nRRow, nZColumn, phiSlice, - gridSizeR, gridSizePhi, gridSizeZ, symmetry, AliTPCPoissonSolver::fgkIFCRadius); - w.Stop(); - - myProfile.electricFieldTime = w.CpuTime(); - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step 2: Electric Field Calculation: %f\n", w.CpuTime())); - w.Start(); - LocalDistCorrDz(matricesEr, matricesEPhi, matricesEz, - matricesDistDrDz, matricesDistDPhiRDz, matricesDistDz, - matricesCorrDrDz, matricesCorrDPhiRDz, matricesCorrDz, - nRRow, nZColumn, phiSlice, gridSizeZ, ezField); - w.Stop(); - myProfile.localDistortionTime = w.CpuTime(); - - // copy to interpolator - if (side == 0) { - lookupLocalDist->CopyFromMatricesToInterpolator(); - lookupLocalCorr->CopyFromMatricesToInterpolator(); - fLookupDistA->CopyFromMatricesToInterpolator(); - fLookupCorrA->CopyFromMatricesToInterpolator(); - fLookupElectricFieldA->CopyFromMatricesToInterpolator(); - } else { - lookupLocalDist->CopyFromMatricesToInterpolator(); - lookupLocalCorr->CopyFromMatricesToInterpolator(); - fLookupDistC->CopyFromMatricesToInterpolator(); - fLookupCorrC->CopyFromMatricesToInterpolator(); - fLookupElectricFieldC->CopyFromMatricesToInterpolator(); - } - - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step 3: Local distortion and correction: %f\n", w.CpuTime())); - w.Start(); - if (fIntegrationStrategy == kNaive) { - IntegrateDistCorrDriftLineDz( - lookupLocalDist, - matricesGDistDrDz, matricesGDistDPhiRDz, matricesGDistDz, - lookupLocalCorr, - matricesGCorrDrDz, matricesGCorrDPhiRDz, matricesGCorrDz, - matricesIrregularDrDz, matricesIrregularDPhiRDz, matricesIrregularDz, - matricesRIrregular, matricesPhiIrregular, matricesZIrregular, - nRRow, nZColumn, phiSlice, rList, phiList, zList); - } else { - IntegrateDistCorrDriftLineDzWithLookUp( - lookupLocalDist, - matricesGDistDrDz, matricesGDistDPhiRDz, matricesGDistDz, - lookupLocalCorr, - matricesGCorrDrDz, matricesGCorrDPhiRDz, matricesGCorrDz, - nRRow, nZColumn, phiSlice, rList, phiList, zList); - } - - w.Stop(); - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step 4: Global correction/distortion: %f\n", w.CpuTime())); - myProfile.globalDistortionTime = w.CpuTime(); - - //// copy to 1D interpolator ///// - lookupGlobalDist->CopyFromMatricesToInterpolator(); - if (fCorrectionType == 0) { - lookupGlobalCorr->CopyFromMatricesToInterpolator(); - } - //// - - w.Stop(); - - if (side == 0) { - - w.Start(); - FillLookUpTable(lookupGlobalDist, - fMatrixIntDistDrEzA, fMatrixIntDistDPhiREzA, fMatrixIntDistDzA, - nRRow, nZColumn, phiSlice, rList, phiList, zList); - - if (fCorrectionType == 0) { - FillLookUpTable(lookupGlobalCorr, - fMatrixIntCorrDrEzA, fMatrixIntCorrDPhiREzA, fMatrixIntCorrDzA, - nRRow, nZColumn, phiSlice, rList, phiList, zList); - } - - fLookupIntDistA->CopyFromMatricesToInterpolator(); - if (fCorrectionType == 0) { - fLookupIntCorrA->CopyFromMatricesToInterpolator(); - } else { - fLookupIntCorrIrregularA->CopyFromMatricesToInterpolator(); - } - - w.Stop(); - myProfile.interpolationInitTime = w.CpuTime(); - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step 5: Filling up the look up: %f\n", w.CpuTime())); - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", " A side done"); - } - if (side == 1) { - w.Start(); - FillLookUpTable(lookupGlobalDist, - fMatrixIntDistDrEzC, fMatrixIntDistDPhiREzC, fMatrixIntDistDzC, - nRRow, nZColumn, phiSlice, rList, phiList, zList); - - if (fCorrectionType == 0) { - FillLookUpTable(lookupGlobalCorr, - fMatrixIntCorrDrEzC, fMatrixIntCorrDPhiREzC, fMatrixIntCorrDzC, - nRRow, nZColumn, phiSlice, rList, phiList, zList); - } - - fLookupIntDistC->CopyFromMatricesToInterpolator(); - if (fCorrectionType == 0) { - fLookupIntCorrC->CopyFromMatricesToInterpolator(); - } else { - fLookupIntCorrIrregularC->CopyFromMatricesToInterpolator(); - } - w.Stop(); - myProfile.interpolationInitTime = w.CpuTime(); - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", " C side done"); - } - } - - fInitLookUp = kTRUE; - } - - // memory de-allocation for temporary matrices - for (Int_t k = 0; k < phiSlice; k++) { - //delete matricesV[k]; - delete matricesCharge[k]; - delete matricesEr[k]; - delete matricesEPhi[k]; - delete matricesEz[k]; - delete matricesDistDrDz[k]; - delete matricesDistDPhiRDz[k]; - delete matricesDistDz[k]; - - delete matricesCorrDrDz[k]; - delete matricesCorrDPhiRDz[k]; - delete matricesCorrDz[k]; - delete matricesGDistDrDz[k]; - delete matricesGDistDPhiRDz[k]; - delete matricesGDistDz[k]; - - delete matricesGCorrDrDz[k]; - delete matricesGCorrDPhiRDz[k]; - delete matricesGCorrDz[k]; - } - delete lookupLocalDist; - delete lookupLocalCorr; - delete lookupGlobalDist; - delete lookupGlobalCorr; -} - -// outdated, to be removed once modifications in aliroot are pushed -/// Creating look-up tables of Correction/Distortion by integration following -/// drift line with known distributions for potential and space charge. -/// -/// -/// \param nRRow Int_t number of grid in row direction -/// \param nZColumn Int_t number of grid in z direction -/// \param phiSlice Int_t number of slices in phi direction -/// \param maxIteration Int_t max iteration for convergence -/// \param stopConvergence Double_t stopping criteria for convergence -/// \param matricesDistDrDzA TMatrixD** local r distortion (output) A side -/// \param matricesDistDPhiRDzA TMatrixD** local r phi distortion (output) A side -/// \param matricesDistDzA TMatrixD** local z distortion (output) A side -/// \param matricesCorrDrDzA TMatrixD** local r correction (output) A side -/// \param matricesCorrDPhiRDzA TMatrixD** local r phi correction (output) A side -/// \param matricesCorrDzA TMatrixD** local z correction (output) A side -/// \param matricesDistDrDzC TMatrixD** local r distortion (output) C side -/// \param matricesDistDPhiRDzC TMatrixD** local r phi distortion (output) C side -/// \param matricesDistDzC TMatrixD** local z distortion (output) C side -/// \param matricesCorrDrDzC TMatrixD** local r phi correction (output) C side -/// \param matricesCorrDPhiRDzC TMatrixD** local r phi correction (output) C side -/// \param matricesCorrDzC TMatrixD** local z correction (output) C side -/// -/// \post Lookup tables for distortion: -/// ~~~ -/// fLookUpIntDistDrEz,fLookUpIntDistDPhiREz,fLookUpIntDistDz -/// ~~~ -/// and correction: -/// ~~~ -/// fLookUpIntCorrDrEz,fLookUpIntCorrDPhiREz,fLookUpIntCorrDz -/// ~~~ -/// are initialized -/// -void AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz( - Int_t nRRow, Int_t nZColumn, Int_t phiSlice, Int_t maxIteration, Double_t stopConvergence, - TMatrixD** matricesErA, TMatrixD** matricesEPhiA, TMatrixD** matricesEzA, - TMatrixD** matricesErC, TMatrixD** matricesEPhiC, TMatrixD** matricesEzC, - TMatrixD** matricesDistDrDzA, TMatrixD** matricesDistDPhiRDzA, TMatrixD** matricesDistDzA, - TMatrixD** matricesCorrDrDzA, TMatrixD** matricesCorrDPhiRDzA, TMatrixD** matricesCorrDzA, - TMatrixD** matricesDistDrDzC, TMatrixD** matricesDistDPhiRDzC, TMatrixD** matricesDistDzC, - TMatrixD** matricesCorrDrDzC, TMatrixD** matricesCorrDPhiRDzC, TMatrixD** matricesCorrDzC, - TFormula* intErDzTestFunction, TFormula* intEPhiRDzTestFunction, TFormula* intDzTestFunction) -{ - Int_t phiSlicesPerSector = phiSlice / kNumSector; - const Float_t gridSizeR = (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (nRRow - 1); - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (nZColumn - 1); - const Float_t gridSizePhi = TMath::TwoPi() / phiSlice; - const Double_t ezField = (AliTPCPoissonSolver::fgkCathodeV - AliTPCPoissonSolver::fgkGG) / AliTPCPoissonSolver::fgkTPCZ0; // = ALICE Electric Field (V/cm) Magnitude ~ -400 V/cm; - - // local variables - Float_t radius0, phi0, z0; - - // memory allocation for temporary matrices: - // potential (boundary values), charge distribution - TMatrixD *matricesV[phiSlice], *matricesCharge[phiSlice]; - TMatrixD *matricesEr[phiSlice], *matricesEPhi[phiSlice], *matricesEz[phiSlice]; - TMatrixD *matricesDistDrDz[phiSlice], *matricesDistDPhiRDz[phiSlice], *matricesDistDz[phiSlice]; - TMatrixD *matricesCorrDrDz[phiSlice], *matricesCorrDPhiRDz[phiSlice], *matricesCorrDz[phiSlice]; - TMatrixD *matricesGDistDrDz[phiSlice], *matricesGDistDPhiRDz[phiSlice], *matricesGDistDz[phiSlice]; - TMatrixD *matricesGCorrDrDz[phiSlice], *matricesGCorrDPhiRDz[phiSlice], *matricesGCorrDz[phiSlice]; - - for (Int_t k = 0; k < phiSlice; k++) { - matricesV[k] = new TMatrixD(nRRow, nZColumn); - matricesCharge[k] = new TMatrixD(nRRow, nZColumn); - matricesEr[k] = new TMatrixD(nRRow, nZColumn); - matricesEPhi[k] = new TMatrixD(nRRow, nZColumn); - matricesEz[k] = new TMatrixD(nRRow, nZColumn); - matricesDistDrDz[k] = new TMatrixD(nRRow, nZColumn); - matricesDistDPhiRDz[k] = new TMatrixD(nRRow, nZColumn); - matricesDistDz[k] = new TMatrixD(nRRow, nZColumn); - matricesCorrDrDz[k] = new TMatrixD(nRRow, nZColumn); - matricesCorrDPhiRDz[k] = new TMatrixD(nRRow, nZColumn); - matricesCorrDz[k] = new TMatrixD(nRRow, nZColumn); - matricesGDistDrDz[k] = new TMatrixD(nRRow, nZColumn); - matricesGDistDPhiRDz[k] = new TMatrixD(nRRow, nZColumn); - matricesGDistDz[k] = new TMatrixD(nRRow, nZColumn); - matricesGCorrDrDz[k] = new TMatrixD(nRRow, nZColumn); - matricesGCorrDPhiRDz[k] = new TMatrixD(nRRow, nZColumn); - matricesGCorrDz[k] = new TMatrixD(nRRow, nZColumn); - } - - // list of point as used in the poisson relaxation and the interpolation (for interpolation) - Double_t rList[nRRow], zList[nZColumn], phiList[phiSlice]; - - // pointer to current TF1 for potential boundary values - TF1* f1BoundaryIFC = nullptr; - TF1* f1BoundaryOFC = nullptr; - TF1* f1BoundaryROC = nullptr; - TStopwatch w; - - for (Int_t k = 0; k < phiSlice; k++) { - phiList[k] = gridSizePhi * k; - } - for (Int_t i = 0; i < nRRow; i++) { - rList[i] = AliTPCPoissonSolver::fgkIFCRadius + i * gridSizeR; - } - for (Int_t j = 0; j < nZColumn; j++) { - zList[j] = j * gridSizeZ; - } - - // allocate look up local distortion - AliTPCLookUpTable3DInterpolatorD* lookupLocalDist = - new AliTPCLookUpTable3DInterpolatorD( - nRRow, matricesDistDrDz, rList, phiSlice, matricesDistDPhiRDz, phiList, nZColumn, matricesDistDz, - zList, fInterpolationOrder); - - // allocate look up local correction - AliTPCLookUpTable3DInterpolatorD* lookupLocalCorr = - new AliTPCLookUpTable3DInterpolatorD( - nRRow, matricesCorrDrDz, rList, phiSlice, matricesCorrDPhiRDz, phiList, nZColumn, matricesCorrDz, - zList, fInterpolationOrder); - - // allocate look up for global distortion - AliTPCLookUpTable3DInterpolatorD* lookupGlobalDist = - new AliTPCLookUpTable3DInterpolatorD( - nRRow, matricesGDistDrDz, rList, phiSlice, matricesGDistDPhiRDz, phiList, nZColumn, matricesGDistDz, - zList, fInterpolationOrder); - // allocate look up for global distortion - AliTPCLookUpTable3DInterpolatorD* lookupGlobalCorr = - new AliTPCLookUpTable3DInterpolatorD( - nRRow, matricesGCorrDrDz, rList, phiSlice, matricesGCorrDPhiRDz, phiList, nZColumn, matricesGCorrDz, - zList, fInterpolationOrder); - - // should be set, in another place - const Int_t symmetry = 0; // fSymmetry - - // for irregular - TMatrixD** matricesIrregularDrDz = nullptr; - TMatrixD** matricesIrregularDPhiRDz = nullptr; - TMatrixD** matricesIrregularDz = nullptr; - TMatrixD** matricesPhiIrregular = nullptr; - TMatrixD** matricesRIrregular = nullptr; - TMatrixD** matricesZIrregular = nullptr; - - // for charge - TMatrixD** matricesLookUpCharge = nullptr; - AliTPC3DCylindricalInterpolator* chargeInterpolator = nullptr; - AliTPC3DCylindricalInterpolator* potentialInterpolator = nullptr; - /// TODO: fListPotentialBoundary arrays are never used in the code. Remove? - // Double_t* potentialBoundary = nullptr; - TMatrixD* matrixV; - TMatrixD* matrixCharge; - Int_t pIndex = 0; - - // do if look up table haven't be initialized - if (!fInitLookUp) { - // initialize for working memory - for (Int_t side = 0; side < 2; side++) { - // zeroing global distortion/correction - for (Int_t k = 0; k < phiSlice; k++) { - matricesDistDrDz[k]->Zero(); - matricesDistDPhiRDz[k]->Zero(); - matricesDistDz[k]->Zero(); - matricesCorrDrDz[k]->Zero(); - matricesCorrDPhiRDz[k]->Zero(); - matricesCorrDz[k]->Zero(); - - matricesGDistDrDz[k]->Zero(); - matricesGDistDPhiRDz[k]->Zero(); - matricesGDistDz[k]->Zero(); - matricesGCorrDrDz[k]->Zero(); - matricesGCorrDPhiRDz[k]->Zero(); - matricesGCorrDz[k]->Zero(); - } - if (side == 0) { - matricesIrregularDrDz = fMatrixIntCorrDrEzIrregularA; - matricesIrregularDPhiRDz = fMatrixIntCorrDPhiREzIrregularA; - matricesIrregularDz = fMatrixIntCorrDzIrregularA; - - matricesPhiIrregular = fMatrixPhiListIrregularA; - matricesRIrregular = fMatrixRListIrregularA; - matricesZIrregular = fMatrixZListIrregularA; - matricesLookUpCharge = fMatrixChargeA; - chargeInterpolator = fInterpolatorChargeA; - potentialInterpolator = fInterpolatorPotentialA; - fLookupDistA->SetLookUpR(matricesDistDrDzA); - fLookupDistA->SetLookUpPhi(matricesDistDPhiRDzA); - fLookupDistA->SetLookUpZ(matricesDistDzA); - lookupLocalDist->SetLookUpR(matricesDistDrDzA); - lookupLocalDist->SetLookUpPhi(matricesDistDPhiRDzA); - lookupLocalDist->SetLookUpZ(matricesDistDzA); - - lookupLocalCorr->SetLookUpR(matricesCorrDrDzA); - lookupLocalCorr->SetLookUpPhi(matricesCorrDPhiRDzA); - lookupLocalCorr->SetLookUpZ(matricesCorrDzA); - - fLookupElectricFieldA->SetLookUpR(matricesErA); - fLookupElectricFieldA->SetLookUpPhi(matricesEPhiA); - fLookupElectricFieldA->SetLookUpZ(matricesEzA); - - /// TODO: fListPotentialBoundary arrays are never used in the code. Remove? - // potentialBoundary = fListPotentialBoundaryA; - f1BoundaryIFC = fFormulaBoundaryIFCA; - f1BoundaryOFC = fFormulaBoundaryOFCA; - f1BoundaryROC = fFormulaBoundaryROCA; - } else { - matricesIrregularDrDz = fMatrixIntCorrDrEzIrregularC; - matricesIrregularDPhiRDz = fMatrixIntCorrDPhiREzIrregularC; - matricesIrregularDz = fMatrixIntCorrDzIrregularC; - matricesPhiIrregular = fMatrixPhiListIrregularC; - matricesRIrregular = fMatrixRListIrregularC; - matricesZIrregular = fMatrixZListIrregularC; - matricesLookUpCharge = fMatrixChargeC; - chargeInterpolator = fInterpolatorChargeC; - potentialInterpolator = fInterpolatorPotentialC; - fLookupDistC->SetLookUpR(matricesDistDrDzC); - fLookupDistC->SetLookUpPhi(matricesDistDPhiRDzC); - fLookupDistC->SetLookUpZ(matricesDistDzC); - fLookupElectricFieldC->SetLookUpR(matricesErC); - fLookupElectricFieldC->SetLookUpPhi(matricesEPhiC); - fLookupElectricFieldC->SetLookUpZ(matricesEzC); - - lookupLocalDist->SetLookUpR(matricesDistDrDzC); - lookupLocalDist->SetLookUpPhi(matricesDistDPhiRDzC); - lookupLocalDist->SetLookUpZ(matricesDistDzC); - - lookupLocalCorr->SetLookUpR(matricesCorrDrDzC); - lookupLocalCorr->SetLookUpPhi(matricesCorrDPhiRDzC); - lookupLocalCorr->SetLookUpZ(matricesCorrDzC); - - /// TODO: fListPotentialBoundary arrays are never used in the code. Remove? - // potentialBoundary = fListPotentialBoundaryC; - f1BoundaryIFC = fFormulaBoundaryIFCC; - f1BoundaryOFC = fFormulaBoundaryOFCC; - f1BoundaryROC = fFormulaBoundaryROCC; - } - - // fill the potential boundary - // guess the initial potential - // fill also charge - //pIndex = 0; - - //Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz","%s",Form("Step = 0: Fill Boundary and Charge Densities")); - for (Int_t k = 0; k < phiSlice; k++) { - phi0 = k * gridSizePhi; - matrixV = matricesV[k]; - matrixCharge = matricesCharge[k]; - for (Int_t i = 0; i < nRRow; i++) { - radius0 = AliTPCPoissonSolver::fgkIFCRadius + i * gridSizeR; - for (Int_t j = 0; j < nZColumn; j++) { - z0 = j * gridSizeZ; - (*matrixCharge)(i, j) = chargeInterpolator->GetValue(rList[i], phiList[k], zList[j]); - (*matrixV)(i, j) = fFormulaPotentialV->Eval(radius0, phi0, z0); - } - } - } - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step 0: Preparing Charge interpolator: %f\n", w.CpuTime())); - //AliTPCPoissonSolver::fgConvergenceError = stoppingConvergence; - - //fPoissonSolver->SetStrategy(AliTPCPoissonSolver::kMultiGrid); - //(fPoissonSolver->fMgParameters).cycleType = AliTPCPoissonSolver::kFCycle; - //(fPoissonSolver->fMgParameters).isFull3D = kFALSE; - //(fPoissonSolver->fMgParameters).nMGCycle = maxIteration; - //(fPoissonSolver->fMgParameters).maxLoop = 6; - - w.Start(); - //fPoissonSolver->PoissonSolver3D(matricesV, matricesCharge, nRRow, nZColumn, phiSlice, maxIteration, - // symmetry); - w.Stop(); - - potentialInterpolator->SetValue(matricesV); - potentialInterpolator->InitCubicSpline(); - - // copy to interpolator - if (side == 0) { - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step 1: Poisson solver: %f\n", w.CpuTime())); - w.Start(); - ElectricField(matricesV, - matricesErA, matricesEPhiA, matricesEzA, nRRow, nZColumn, phiSlice, - gridSizeR, gridSizePhi, gridSizeZ, symmetry, AliTPCPoissonSolver::fgkIFCRadius); - w.Stop(); - - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step 2: Electric Field Calculation: %f\n", w.CpuTime())); - w.Start(); - LocalDistCorrDz(matricesErA, matricesEPhiA, matricesEzA, - matricesDistDrDzA, matricesDistDPhiRDzA, matricesDistDzA, - matricesCorrDrDzA, matricesCorrDPhiRDzA, matricesCorrDzA, - nRRow, nZColumn, phiSlice, gridSizeZ, ezField); - //LocalDistCorrDz(intErDzTestFunction, intEPhiRDzTestFunction, intDzTestFunction, - // GetEzFormula(), rList, phiList, zList, - // matricesDistDrDzA, matricesDistDPhiRDzA, matricesDistDzA, - // matricesCorrDrDzA, matricesCorrDPhiRDzA, matricesCorrDzA, - // nRRow, nZColumn, phiSlice, gridSizeZ, ezField); - w.Stop(); - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step 3: Local distortion and correction: %f\n", w.CpuTime())); - - lookupLocalDist->CopyFromMatricesToInterpolator(); - lookupLocalCorr->CopyFromMatricesToInterpolator(); - fLookupDistA->CopyFromMatricesToInterpolator(); - fLookupElectricFieldA->CopyFromMatricesToInterpolator(); - } else { - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step 1: Poisson solver: %f\n", w.CpuTime())); - w.Start(); - ElectricField(matricesV, - matricesErC, matricesEPhiC, matricesEzC, nRRow, nZColumn, phiSlice, - gridSizeR, gridSizePhi, gridSizeZ, symmetry, AliTPCPoissonSolver::fgkIFCRadius); - w.Stop(); - - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step 2: Electric Field Calculation: %f\n", w.CpuTime())); - w.Start(); - LocalDistCorrDz(matricesErC, matricesEPhiC, matricesEzC, - matricesDistDrDzC, matricesDistDPhiRDzC, matricesDistDzC, - matricesCorrDrDzC, matricesCorrDPhiRDzC, matricesCorrDzC, - nRRow, nZColumn, phiSlice, gridSizeZ, ezField); - //LocalDistCorrDz(intErDzTestFunction, intEPhiRDzTestFunction, intDzTestFunction, - // GetEzFormula(), rList, phiList, zList, - // matricesDistDrDzC, matricesDistDPhiRDzC, matricesDistDzC, - // matricesCorrDrDzC, matricesCorrDPhiRDzC, matricesCorrDzC, - // nRRow, nZColumn, phiSlice, gridSizeZ, ezField); - w.Stop(); - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step 3: Local distortion and correction: %f\n", w.CpuTime())); - lookupLocalDist->CopyFromMatricesToInterpolator(); - lookupLocalCorr->CopyFromMatricesToInterpolator(); - fLookupDistC->CopyFromMatricesToInterpolator(); - fLookupElectricFieldC->CopyFromMatricesToInterpolator(); - } - - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step 3: Local distortion and correction: %f\n", w.CpuTime())); - w.Start(); - if (fIntegrationStrategy == kNaive) { - IntegrateDistCorrDriftLineDz( - lookupLocalDist, - matricesGDistDrDz, matricesGDistDPhiRDz, matricesGDistDz, - lookupLocalCorr, - matricesGCorrDrDz, matricesGCorrDPhiRDz, matricesGCorrDz, - matricesIrregularDrDz, matricesIrregularDPhiRDz, matricesIrregularDz, - matricesRIrregular, matricesPhiIrregular, matricesZIrregular, - nRRow, nZColumn, phiSlice, rList, phiList, zList); - } else { - IntegrateDistCorrDriftLineDzWithLookUp( - lookupLocalDist, - matricesGDistDrDz, matricesGDistDPhiRDz, matricesGDistDz, - lookupLocalCorr, - matricesGCorrDrDz, matricesGCorrDPhiRDz, matricesGCorrDz, - nRRow, nZColumn, phiSlice, rList, phiList, zList); - } - - w.Stop(); - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step 4: Global correction/distortion: %f\n", w.CpuTime())); - - //// copy to 1D interpolator ///// - lookupGlobalDist->CopyFromMatricesToInterpolator(); - if (fCorrectionType == 0) { - lookupGlobalCorr->CopyFromMatricesToInterpolator(); - } - //// - - w.Start(); - //// copy to 1D interpolator ///// - lookupGlobalDist->CopyFromMatricesToInterpolator(); - lookupGlobalCorr->CopyFromMatricesToInterpolator(); - //// - - w.Stop(); - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step 5: Filling up the look up: %f\n", w.CpuTime())); - - if (side == 0) { - FillLookUpTable(lookupGlobalDist, - fMatrixIntDistDrEzA, fMatrixIntDistDPhiREzA, fMatrixIntDistDzA, - nRRow, nZColumn, phiSlice, rList, phiList, zList); - - FillLookUpTable(lookupGlobalCorr, - fMatrixIntCorrDrEzA, fMatrixIntCorrDPhiREzA, fMatrixIntCorrDzA, - nRRow, nZColumn, phiSlice, rList, phiList, zList); - - fLookupIntDistA->CopyFromMatricesToInterpolator(); - if (fCorrectionType == 0) { - fLookupIntCorrA->CopyFromMatricesToInterpolator(); - } else { - fLookupIntCorrIrregularA->CopyFromMatricesToInterpolator(); - } - - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", " A side done"); - } - if (side == 1) { - FillLookUpTable(lookupGlobalDist, - fMatrixIntDistDrEzC, fMatrixIntDistDPhiREzC, fMatrixIntDistDzC, - nRRow, nZColumn, phiSlice, rList, phiList, zList); - - FillLookUpTable(lookupGlobalCorr, - fMatrixIntCorrDrEzC, fMatrixIntCorrDPhiREzC, fMatrixIntCorrDzC, - nRRow, nZColumn, phiSlice, rList, phiList, zList); - - fLookupIntDistC->CopyFromMatricesToInterpolator(); - if (fCorrectionType == 0) { - fLookupIntCorrC->CopyFromMatricesToInterpolator(); - } else { - fLookupIntCorrIrregularC->CopyFromMatricesToInterpolator(); - } - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", " C side done"); - } - } - - fInitLookUp = kTRUE; - } - - // memory de-allocation for temporary matrices - for (Int_t k = 0; k < phiSlice; k++) { - delete matricesV[k]; - delete matricesCharge[k]; - delete matricesEr[k]; - delete matricesEPhi[k]; - delete matricesEz[k]; - delete matricesDistDrDz[k]; - delete matricesDistDPhiRDz[k]; - delete matricesDistDz[k]; - - delete matricesCorrDrDz[k]; - delete matricesCorrDPhiRDz[k]; - delete matricesCorrDz[k]; - delete matricesGDistDrDz[k]; - delete matricesGDistDPhiRDz[k]; - delete matricesGDistDz[k]; - - delete matricesGCorrDrDz[k]; - delete matricesGCorrDPhiRDz[k]; - delete matricesGCorrDz[k]; - } - delete lookupLocalDist; - delete lookupLocalCorr; - delete lookupGlobalDist; - delete lookupGlobalCorr; -} - -/// Creating look-up tables of Correction/Distortion by integration following -/// drift line with known distributions for potential and space charge. -/// -/// -/// \param nRRow Int_t number of grid in row direction -/// \param nZColumn Int_t number of grid in z direction -/// \param phiSlice Int_t number of slices in phi direction -/// \param maxIteration Int_t max iteration for convergence -/// \param stopConvergence Double_t stopping criteria for convergence -/// \param matricesCorrDzC TMatrixD** local z correction (output) C side -/// \param intErDzTestFunction TFormula* analytic function for closed integration of Er in z direction -/// \param intEPhiRDzTestFunction TFormula* analytic function for closed integration of EPhi in z direction -/// \param intDzTestFunction TFormula* analytic function for closed integration of Ez in z direction -/// -/// \post Lookup tables for distortion: -/// ~~~ -/// fLookUpIntDistDrEz,fLookUpIntDistDPhiREz,fLookUpIntDistDz -/// ~~~ -/// and correction: -/// ~~~ -/// fLookUpIntCorrDrEz,fLookUpIntCorrDPhiREz,fLookUpIntCorrDz -/// ~~~ -/// are initialized -/// -void AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz( - Int_t nRRow, Int_t nZColumn, Int_t phiSlice, Int_t maxIteration, Double_t stopConvergence, - TFormula* intErDzTestFunction, TFormula* intEPhiRDzTestFunction, TFormula* intDzTestFunction) -{ - Int_t phiSlicesPerSector = phiSlice / kNumSector; - const Float_t gridSizeR = (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (nRRow - 1); - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (nZColumn - 1); - const Float_t gridSizePhi = TMath::TwoPi() / phiSlice; - const Double_t ezField = (AliTPCPoissonSolver::fgkCathodeV - AliTPCPoissonSolver::fgkGG) / AliTPCPoissonSolver::fgkTPCZ0; // = ALICE Electric Field (V/cm) Magnitude ~ -400 V/cm; - - // local variables - Float_t radius0, phi0, z0; - - // memory allocation for temporary matrices: - // potential (boundary values), charge distribution - TMatrixD *matricesV[phiSlice], *matricesCharge[phiSlice]; - TMatrixD *matricesEr[phiSlice], *matricesEPhi[phiSlice], *matricesEz[phiSlice]; - TMatrixD *matricesDistDrDz[phiSlice], *matricesDistDPhiRDz[phiSlice], *matricesDistDz[phiSlice]; - TMatrixD *matricesCorrDrDz[phiSlice], *matricesCorrDPhiRDz[phiSlice], *matricesCorrDz[phiSlice]; - TMatrixD *matricesGDistDrDz[phiSlice], *matricesGDistDPhiRDz[phiSlice], *matricesGDistDz[phiSlice]; - TMatrixD *matricesGCorrDrDz[phiSlice], *matricesGCorrDPhiRDz[phiSlice], *matricesGCorrDz[phiSlice]; - - for (Int_t k = 0; k < phiSlice; k++) { - matricesV[k] = new TMatrixD(nRRow, nZColumn); - matricesCharge[k] = new TMatrixD(nRRow, nZColumn); - matricesEr[k] = new TMatrixD(nRRow, nZColumn); - matricesEPhi[k] = new TMatrixD(nRRow, nZColumn); - matricesEz[k] = new TMatrixD(nRRow, nZColumn); - matricesDistDrDz[k] = new TMatrixD(nRRow, nZColumn); - matricesDistDPhiRDz[k] = new TMatrixD(nRRow, nZColumn); - matricesDistDz[k] = new TMatrixD(nRRow, nZColumn); - matricesCorrDrDz[k] = new TMatrixD(nRRow, nZColumn); - matricesCorrDPhiRDz[k] = new TMatrixD(nRRow, nZColumn); - matricesCorrDz[k] = new TMatrixD(nRRow, nZColumn); - matricesGDistDrDz[k] = new TMatrixD(nRRow, nZColumn); - matricesGDistDPhiRDz[k] = new TMatrixD(nRRow, nZColumn); - matricesGDistDz[k] = new TMatrixD(nRRow, nZColumn); - matricesGCorrDrDz[k] = new TMatrixD(nRRow, nZColumn); - matricesGCorrDPhiRDz[k] = new TMatrixD(nRRow, nZColumn); - matricesGCorrDz[k] = new TMatrixD(nRRow, nZColumn); - } - - // list of point as used in the poisson relaxation and the interpolation (for interpolation) - Double_t rList[nRRow], zList[nZColumn], phiList[phiSlice]; - - // pointer to current TF1 for potential boundary values - TF1* f1BoundaryIFC = nullptr; - TF1* f1BoundaryOFC = nullptr; - TF1* f1BoundaryROC = nullptr; - TStopwatch w; - - for (Int_t k = 0; k < phiSlice; k++) { - phiList[k] = gridSizePhi * k; - } - for (Int_t i = 0; i < nRRow; i++) { - rList[i] = AliTPCPoissonSolver::fgkIFCRadius + i * gridSizeR; - } - for (Int_t j = 0; j < nZColumn; j++) { - zList[j] = j * gridSizeZ; - } - - // allocate look up local distortion - AliTPCLookUpTable3DInterpolatorD* lookupLocalDist = - new AliTPCLookUpTable3DInterpolatorD( - nRRow, matricesDistDrDz, rList, phiSlice, matricesDistDPhiRDz, phiList, nZColumn, matricesDistDz, - zList, fInterpolationOrder); - - // allocate look up local correction - AliTPCLookUpTable3DInterpolatorD* lookupLocalCorr = - new AliTPCLookUpTable3DInterpolatorD( - nRRow, matricesCorrDrDz, rList, phiSlice, matricesCorrDPhiRDz, phiList, nZColumn, matricesCorrDz, - zList, fInterpolationOrder); - - // allocate look up for global distortion - AliTPCLookUpTable3DInterpolatorD* lookupGlobalDist = - new AliTPCLookUpTable3DInterpolatorD( - nRRow, matricesGDistDrDz, rList, phiSlice, matricesGDistDPhiRDz, phiList, nZColumn, matricesGDistDz, - zList, fInterpolationOrder); - // allocate look up for global distortion - AliTPCLookUpTable3DInterpolatorD* lookupGlobalCorr = - new AliTPCLookUpTable3DInterpolatorD( - nRRow, matricesGCorrDrDz, rList, phiSlice, matricesGCorrDPhiRDz, phiList, nZColumn, matricesGCorrDz, - zList, fInterpolationOrder); - - // should be set, in another place - const Int_t symmetry = 0; // fSymmetry - - // for irregular - TMatrixD** matricesIrregularDrDz = nullptr; - TMatrixD** matricesIrregularDPhiRDz = nullptr; - TMatrixD** matricesIrregularDz = nullptr; - TMatrixD** matricesPhiIrregular = nullptr; - TMatrixD** matricesRIrregular = nullptr; - TMatrixD** matricesZIrregular = nullptr; - - // for charge - TMatrixD** matricesLookUpCharge = nullptr; - AliTPC3DCylindricalInterpolator* chargeInterpolator = nullptr; - AliTPC3DCylindricalInterpolator* potentialInterpolator = nullptr; - /// TODO: fListPotentialBoundary arrays are never used in the code. Remove? - // Double_t* potentialBoundary = nullptr; - TMatrixD* matrixV; - TMatrixD* matrixCharge; - - // do if look up table haven't be initialized - if (!fInitLookUp) { - // initialize for working memory - for (Int_t side = 0; side < 2; side++) { - // zeroing global distortion/correction - for (Int_t k = 0; k < phiSlice; k++) { - matricesDistDrDz[k]->Zero(); - matricesDistDPhiRDz[k]->Zero(); - matricesDistDz[k]->Zero(); - matricesCorrDrDz[k]->Zero(); - matricesCorrDPhiRDz[k]->Zero(); - matricesCorrDz[k]->Zero(); - - matricesGDistDrDz[k]->Zero(); - matricesGDistDPhiRDz[k]->Zero(); - matricesGDistDz[k]->Zero(); - matricesGCorrDrDz[k]->Zero(); - matricesGCorrDPhiRDz[k]->Zero(); - matricesGCorrDz[k]->Zero(); - } - - if (side == 0) { - matricesIrregularDrDz = fMatrixIntCorrDrEzIrregularA; - matricesIrregularDPhiRDz = fMatrixIntCorrDPhiREzIrregularA; - matricesIrregularDz = fMatrixIntCorrDzIrregularA; - - matricesPhiIrregular = fMatrixPhiListIrregularA; - matricesRIrregular = fMatrixRListIrregularA; - matricesZIrregular = fMatrixZListIrregularA; - matricesLookUpCharge = fMatrixChargeA; - - chargeInterpolator = fInterpolatorChargeA; - potentialInterpolator = fInterpolatorPotentialA; - fLookupDistA->SetLookUpR(matricesDistDrDz); - fLookupDistA->SetLookUpPhi(matricesDistDPhiRDz); - fLookupDistA->SetLookUpZ(matricesDistDz); - - fLookupElectricFieldA->SetLookUpR(matricesEr); - fLookupElectricFieldA->SetLookUpPhi(matricesEPhi); - fLookupElectricFieldA->SetLookUpZ(matricesEz); - - /// TODO: fListPotentialBoundary arrays are never used in the code. Remove? - // potentialBoundary = fListPotentialBoundaryA; - f1BoundaryIFC = fFormulaBoundaryIFCA; - f1BoundaryOFC = fFormulaBoundaryOFCA; - f1BoundaryROC = fFormulaBoundaryROCA; - } else { - matricesIrregularDrDz = fMatrixIntCorrDrEzIrregularC; - matricesIrregularDPhiRDz = fMatrixIntCorrDPhiREzIrregularC; - matricesIrregularDz = fMatrixIntCorrDzIrregularC; - matricesPhiIrregular = fMatrixPhiListIrregularC; - matricesRIrregular = fMatrixRListIrregularC; - matricesZIrregular = fMatrixZListIrregularC; - matricesLookUpCharge = fMatrixChargeC; - - chargeInterpolator = fInterpolatorChargeC; - potentialInterpolator = fInterpolatorPotentialC; - fLookupDistC->SetLookUpR(matricesDistDrDz); - fLookupDistC->SetLookUpPhi(matricesDistDPhiRDz); - fLookupDistC->SetLookUpZ(matricesDistDz); - fLookupElectricFieldC->SetLookUpR(matricesEr); - fLookupElectricFieldC->SetLookUpPhi(matricesEPhi); - fLookupElectricFieldC->SetLookUpZ(matricesEz); - - /// TODO: fListPotentialBoundary arrays are never used in the code. Remove? - // potentialBoundary = fListPotentialBoundaryC; - f1BoundaryIFC = fFormulaBoundaryIFCC; - f1BoundaryOFC = fFormulaBoundaryOFCC; - f1BoundaryROC = fFormulaBoundaryROCC; - } - // fill the potential boundary - // guess the initial potential - // fill also charge - //pIndex = 0; - - //Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz","%s",Form("Step = 0: Fill Boundary and Charge Densities")); - for (Int_t k = 0; k < phiSlice; k++) { - phi0 = k * gridSizePhi; - matrixV = matricesV[k]; - matrixCharge = matricesCharge[k]; - for (Int_t i = 0; i < nRRow; i++) { - radius0 = AliTPCPoissonSolver::fgkIFCRadius + i * gridSizeR; - for (Int_t j = 0; j < nZColumn; j++) { - z0 = j * gridSizeZ; - (*matrixCharge)(i, j) = chargeInterpolator->GetValue(rList[i], phiList[k], zList[j]); - (*matrixV)(i, j) = fFormulaPotentialV->Eval(radius0, phi0, z0); - } - } - } - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step 0: Preparing Charge interpolator: %f\n", w.CpuTime())); - - potentialInterpolator->SetValue(matricesV); - potentialInterpolator->InitCubicSpline(); - - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step 1: Poisson solver: %f\n", w.CpuTime())); - w.Start(); - w.Stop(); - - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step 2: Electric Field Calculation: %f\n", w.CpuTime())); - w.Start(); - w.Stop(); - - // copy to interpolator - LocalDistCorrDz(intErDzTestFunction, intEPhiRDzTestFunction, intDzTestFunction, - GetEzFormula(), rList, phiList, zList, - matricesDistDrDz, matricesDistDPhiRDz, matricesDistDz, - matricesCorrDrDz, matricesCorrDPhiRDz, matricesCorrDz, - nRRow, nZColumn, phiSlice, gridSizeZ, ezField); - w.Stop(); - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step 3: Local distortion and correction: %f\n", w.CpuTime())); - lookupLocalDist->CopyFromMatricesToInterpolator(); - lookupLocalCorr->CopyFromMatricesToInterpolator(); - if (side == 0) { - fLookupDistA->CopyFromMatricesToInterpolator(); - fLookupElectricFieldA->CopyFromMatricesToInterpolator(); - } else { - fLookupDistC->CopyFromMatricesToInterpolator(); - fLookupElectricFieldC->CopyFromMatricesToInterpolator(); - } - - w.Start(); - - IntegrateDistCorrDriftLineDz(intErDzTestFunction, intEPhiRDzTestFunction, intDzTestFunction, GetEzFormula(), ezField, - matricesGDistDrDz, matricesGDistDPhiRDz, matricesGDistDz, - matricesGCorrDrDz, matricesGCorrDPhiRDz, matricesGCorrDz, - matricesIrregularDrDz, matricesIrregularDPhiRDz, matricesIrregularDz, - matricesRIrregular, matricesPhiIrregular, matricesZIrregular, - nRRow, nZColumn, phiSlice, rList, phiList, zList); - - w.Stop(); - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step 4: Global correction/distortion: %f\n", w.CpuTime())); - w.Start(); - - //// copy to 1D interpolator ///// - lookupGlobalDist->CopyFromMatricesToInterpolator(); - lookupGlobalCorr->CopyFromMatricesToInterpolator(); - //// - - w.Stop(); - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Step 5: Filling up the look up: %f\n", w.CpuTime())); - - if (side == 0) { - FillLookUpTable(lookupGlobalDist, - fMatrixIntDistDrEzA, fMatrixIntDistDPhiREzA, fMatrixIntDistDzA, - nRRow, nZColumn, phiSlice, rList, phiList, zList); - - FillLookUpTable(lookupGlobalCorr, - fMatrixIntCorrDrEzA, fMatrixIntCorrDPhiREzA, fMatrixIntCorrDzA, - nRRow, nZColumn, phiSlice, rList, phiList, zList); - - fLookupIntDistA->CopyFromMatricesToInterpolator(); - if (fCorrectionType == 0) { - fLookupIntCorrA->CopyFromMatricesToInterpolator(); - } else { - fLookupIntCorrIrregularA->CopyFromMatricesToInterpolator(); - } - - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", " A side done"); - } - if (side == 1) { - FillLookUpTable(lookupGlobalDist, - fMatrixIntDistDrEzC, fMatrixIntDistDPhiREzC, fMatrixIntDistDzC, - nRRow, nZColumn, phiSlice, rList, phiList, zList); - - FillLookUpTable(lookupGlobalCorr, - fMatrixIntCorrDrEzC, fMatrixIntCorrDPhiREzC, fMatrixIntCorrDzC, - nRRow, nZColumn, phiSlice, rList, phiList, zList); - - fLookupIntDistC->CopyFromMatricesToInterpolator(); - if (fCorrectionType == 0) { - fLookupIntCorrC->CopyFromMatricesToInterpolator(); - } else { - fLookupIntCorrIrregularC->CopyFromMatricesToInterpolator(); - } - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", " C side done"); - } - } - - fInitLookUp = kTRUE; - } - - // memory de-allocation for temporary matrices - for (Int_t k = 0; k < phiSlice; k++) { - delete matricesV[k]; - delete matricesCharge[k]; - delete matricesEr[k]; - delete matricesEPhi[k]; - delete matricesEz[k]; - delete matricesDistDrDz[k]; - delete matricesDistDPhiRDz[k]; - delete matricesDistDz[k]; - - delete matricesCorrDrDz[k]; - delete matricesCorrDPhiRDz[k]; - delete matricesCorrDz[k]; - delete matricesGDistDrDz[k]; - delete matricesGDistDPhiRDz[k]; - delete matricesGDistDz[k]; - - delete matricesGCorrDrDz[k]; - delete matricesGCorrDPhiRDz[k]; - delete matricesGCorrDz[k]; - } - delete lookupLocalDist; - delete lookupLocalCorr; - delete lookupGlobalDist; - delete lookupGlobalCorr; -} -/// Creating look-up tables of Correction/Distortion by linear integration -/// on z line -/// -/// \param nRRow Int_t number of grid in row direction -/// \param nZColumn Int_t number of grid in z direction -/// \param phiSlice Int_t number of slices in phi direction -/// \param maxIteration Int_t max iteration for convergence -/// \param stoppingConvergence Double_t stopping criteria for convergence -/// \post Lookup tables for distortion: -/// ~~~ -/// fLookUpIntDistDrEz,fLookUpIntDistDPhiREz,fLookUpIntDistDz -/// ~~~ fo -/// and correction: -/// ~~~ -/// fLookUpIntCorrDrEz,fLookUpIntCorrDPhiREz,fLookUpIntCorrDz -/// ~~~ -/// are initialized -/// -void AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoisson(Int_t nRRow, Int_t nZColumn, Int_t phiSlice, Int_t maxIteration, - Double_t stoppingConvergence) -{ - // Compute grid size for all direction - Int_t phiSlicesPerSector = phiSlice / kNumSector; - const Float_t gridSizeR = (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (nRRow - 1); - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (nZColumn - 1); - const Float_t gridSizePhi = TMath::TwoPi() / phiSlice; - const Double_t ezField = (AliTPCPoissonSolver::fgkCathodeV - AliTPCPoissonSolver::fgkGG) / AliTPCPoissonSolver::fgkTPCZ0; // = ALICE Electric Field (V/cm) Magnitude ~ -400 V/cm; - - // local variables - Float_t radius0, phi0, z0; - - // memory allocation for temporary matrices: - // potential (boundary values), charge distribution - - TMatrixD *matricesV[phiSlice], *matricesCharge[phiSlice]; - TMatrixD *matricesEr[phiSlice], *matricesEPhi[phiSlice], *matricesEz[phiSlice]; - - for (Int_t k = 0; k < phiSlice; k++) { - matricesEr[k] = new TMatrixD(nRRow, nZColumn); - matricesEPhi[k] = new TMatrixD(nRRow, nZColumn); - matricesEz[k] = new TMatrixD(nRRow, nZColumn); - matricesV[k] = new TMatrixD(nRRow, nZColumn); - matricesCharge[k] = new TMatrixD(nRRow, nZColumn); - } - - // list of point as used in the poisson relaxation and the interpolation (for interpolation) - Double_t rList[nRRow], zList[nZColumn], phiList[phiSlice]; - - for (Int_t k = 0; k < phiSlice; k++) { - phiList[k] = gridSizePhi * k; - } - for (Int_t i = 0; i < nRRow; i++) { - rList[i] = AliTPCPoissonSolver::fgkIFCRadius + i * gridSizeR; - } - for (Int_t j = 0; j < nZColumn; j++) { - zList[j] = j * gridSizeZ; - } - // should be set, in another place - const Int_t symmetry = 0; - // do if look up table haven't be initialized - if (!fInitLookUp) { - for (Int_t side = 0; side < 2; side++) { - for (Int_t k = 0; k < phiSlice; k++) { - TMatrixD* mV = matricesV[k]; - TMatrixD* mCharge = matricesCharge[k]; - phi0 = phiList[k]; - for (Int_t i = 0; i < nRRow; i++) { - radius0 = rList[i]; - for (Int_t j = 0; j < nZColumn; j++) { - z0 = zList[j]; - if (side == 1) { - z0 = -TMath::Abs(zList[j]); - } - if (fHistogram3DSpaceCharge != nullptr) { - // * Boundary values and charge distribution setup - (*mV)(i, j) = 0.0; - (*mCharge)(i, j) = -1 * InterpolatePhi(fHistogram3DSpaceCharge, phi0, radius0, z0); - } - } - } - } - AliTPCLookUpTable3DInterpolatorD* lookupEField = - new AliTPCLookUpTable3DInterpolatorD( - nRRow, - matricesEr, - rList, phiSlice, - matricesEPhi, - phiList, nZColumn, - matricesEz, - zList, - fInterpolationOrder); - - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "Step 1: Solving poisson solver"); - fPoissonSolver->PoissonSolver3D(matricesV, matricesCharge, nRRow, nZColumn, phiSlice, maxIteration, symmetry); - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "Step 2: Calculate electric field"); - CalculateEField( - matricesV, - matricesEr, - matricesEPhi, - matricesEz, - nRRow, - nZColumn, - phiSlice, - maxIteration, - symmetry); - lookupEField->CopyFromMatricesToInterpolator(); - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "Step 3: Fill the look up table"); - - if (side == 0) { - FillLookUpTable(lookupEField, - fMatrixErOverEzA, fMatrixEPhiOverEzA, fMatrixDeltaEzA, - nRRow, nZColumn, phiSlice, rList, phiList, zList); - fLookupIntENoDriftA->CopyFromMatricesToInterpolator(); - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", " A side done"); - } - if (side == 1) { - FillLookUpTable(lookupEField, - fMatrixErOverEzC, fMatrixEPhiOverEzC, fMatrixDeltaEzC, - nRRow, nZColumn, phiSlice, rList, phiList, zList); - fLookupIntENoDriftC->CopyFromMatricesToInterpolator(); - - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", " C side done"); - } - delete lookupEField; - } - fInitLookUp = kTRUE; - } - - for (Int_t k = 0; k < phiSlice; k++) { - delete matricesV[k]; - delete matricesCharge[k]; - delete matricesEr[k]; - delete matricesEPhi[k]; - delete matricesEz[k]; - } -} -/// Force creating look-up table of Correction/Distortion by integration following -/// drift line. -/// -/// \param nRRow Int_t Number of nRRow in r-direction -/// \param nZColumn Int_t Number of nZColumn in z-direction -/// \param phiSlice Int_t Number of phi slices in \f$ phi \f$ direction -/// \param maxIteration Int_t Maximum iteration for poisson solver -/// \param stoppingConvergence Convergence error stopping condition for poisson solver -/// -void AliTPCSpaceCharge3DCalc::ForceInitSpaceCharge3DPoissonIntegralDz(Int_t nRRow, Int_t nZColumn, Int_t phiSlice, - Int_t maxIteration, Double_t stoppingConvergence) -{ - fInitLookUp = kFALSE; - InitSpaceCharge3DPoissonIntegralDz(nRRow, nZColumn, phiSlice, maxIteration, stoppingConvergence); -} -/// Electric field Calculation: -/// -/// -/// \param matricesV -/// \param matricesEr -/// \param matricesEPhi -/// \param matricesEz -/// \param nRRow -/// \param nZColumn -/// \param phiSlice -/// \param gridSizeR -/// \param gridSizePhi -/// \param gridSizeZ -/// \param symmetry -/// \param innerRadius -/// -/// \pre Matrix matricesV is assumed had been calculated by Poisson solver -/// \post Results of E-fields are calculated by measuring gradient at potential distribution -/// -/// -/// * Differentiate potential on all direction (r,z and phi) -/// * Non-boundary -> Central difference (3 stencil) TODO: 5 Stencil -/// -/// \f$ \nabla_{r} V(r_{i},\phi_{j},z_{k}) \approx -( V_{i+1,j,k} - V_{i-1,j,k}) / (2* h_{r}) \f$ -/// -/// \f$ -\nabla_{\phi} V(r_{i},\phi_{j},z_{k}) \approx -( V_{i,j-1,k} - V_{i,j+1,k}) / (2* r_{j} * h_{\phi}) \f$ -/// -/// \f$ -\nabla_{z} V(r_{i},\phi_{j},z_{k}) \approx -( V_{i,j,k+1} - V_{i,j,k-1}) / (2* h_{z}) \f$ -/// -/// ~~~ cxx -/// matrixEr(i,j) = -1 * ( arrayV(i+1,j) - arrayV(i-1,j) ) / (2*gridSizeR); // r direction -/// matrixEz(i,j) = -1 * ( arrayV(i,j+1) - arrayV(i,j-1) ) / (2*gridSizeZ) ; // z direction -/// matrixEPhi(i,j) = -1 * (signPlus * arrayVP(i,j) - signMinus * arrayVM(i,j) ) / (2*radius*gridSizePhi) -/// ~~~ -/// -/// * Boundary -> Forward/Backward difference (3 stencil) TODO: 5 Stencil -/// -/// \f$ -\nabla_{r} V(r_{0},\phi_{j},z_{k}) \approx -( -0.5 V_{2,j,k} + 2 V_{1,j,k} - 1.5 * V_{0,j,k}) / h_{r} \f$ -/// -/// \f$ -\nabla_{r} V(r_{nRRow - 1},\phi_{j},z_{k}) \approx -( 1.5 V_{nRRow-1,j,k} - 2.0 V_{nRRow-2,j,k} + 0.5 V_{nRRow -3,j,k}) / h_{\phi} \f$ -/// -void AliTPCSpaceCharge3DCalc::ElectricField(TMatrixD** matricesV, TMatrixD** matricesEr, TMatrixD** matricesEPhi, - TMatrixD** matricesEz, const Int_t nRRow, const Int_t nZColumn, - const Int_t phiSlice, - const Float_t gridSizeR, const Float_t gridSizePhi, - const Float_t gridSizeZ, - const Int_t symmetry, const Float_t innerRadius) -{ - Float_t radius; - Int_t mPlus, mMinus, signPlus, signMinus; - for (Int_t m = 0; m < phiSlice; m++) { - mPlus = m + 1; - signPlus = 1; - mMinus = m - 1; - signMinus = 1; - if (symmetry == 1) { // Reflection symmetry in phi (e.g. symmetry at sector boundaries, or half sectors, etc.) - if (mPlus > phiSlice - 1) { - mPlus = phiSlice - 2; - } - if (mMinus < 0) { - mMinus = 1; - } - } else if (symmetry == -1) { // Anti-symmetry in phi - if (mPlus > phiSlice - 1) { - mPlus = phiSlice - 2; - signPlus = -1; - } - if (mMinus < 0) { - mMinus = 1; - signMinus = -1; - } - } else { // No Symmetries in phi, no boundaries, the calculations is continuous across all phi - if (mPlus > phiSlice - 1) { - mPlus = m + 1 - phiSlice; - } - if (mMinus < 0) { - mMinus = m - 1 + phiSlice; - } - } - - TMatrixD& arrayVP = *matricesV[mPlus]; - TMatrixD& arrayVM = *matricesV[mMinus]; - TMatrixD& arrayV = *matricesV[m]; - TMatrixD& matrixEr = *matricesEr[m]; - TMatrixD& matrixEz = *matricesEz[m]; - TMatrixD& matrixEPhi = *matricesEPhi[m]; - - // for non-boundary V - for (Int_t i = 1; i < nRRow - 1; i++) { - radius = innerRadius + i * gridSizeR; - for (Int_t j = 1; j < nZColumn - 1; j++) { - matrixEr(i, j) = -1 * (arrayV(i + 1, j) - arrayV(i - 1, j)) / (2 * gridSizeR); // r direction - matrixEz(i, j) = -1 * (arrayV(i, j + 1) - arrayV(i, j - 1)) / (2 * gridSizeZ); // z direction - matrixEPhi(i, j) = -1 * (signPlus * arrayVP(i, j) - signMinus * arrayVM(i, j)) / - (2 * radius * gridSizePhi); // phi direction - } - } - - // for boundary-r - for (Int_t j = 0; j < nZColumn; j++) { - matrixEr(0, j) = -1 * (-0.5 * arrayV(2, j) + 2.0 * arrayV(1, j) - 1.5 * arrayV(0, j)) / - gridSizeR; // forward difference - matrixEr(nRRow - 1, j) = - -1 * (1.5 * arrayV(nRRow - 1, j) - 2.0 * arrayV(nRRow - 2, j) + 0.5 * arrayV(nRRow - 3, j)) / - gridSizeR; // backward difference - } - - for (Int_t i = 0; i < nRRow; i += nRRow - 1) { - radius = innerRadius + i * gridSizeR; - for (Int_t j = 1; j < nZColumn - 1; j++) { - matrixEz(i, j) = -1 * (arrayV(i, j + 1) - arrayV(i, j - 1)) / (2 * gridSizeZ); // z direction - matrixEPhi(i, j) = -1 * (signPlus * arrayVP(i, j) - signMinus * arrayVM(i, j)) / - (2 * radius * gridSizePhi); // phi direction - } - } - - // for boundary-z - for (Int_t i = 0; i < nRRow; i++) { - matrixEz(i, 0) = -1 * (-0.5 * arrayV(i, 2) + 2.0 * arrayV(i, 1) - 1.5 * arrayV(i, 0)) / gridSizeZ; - matrixEz(i, nZColumn - 1) = - -1 * - (1.5 * arrayV(i, nZColumn - 1) - 2.0 * arrayV(i, nZColumn - 2) + 0.5 * arrayV(i, nZColumn - 3)) / - gridSizeZ; - } - - for (Int_t i = 1; i < nRRow - 1; i++) { - radius = innerRadius + i * gridSizeR; - for (Int_t j = 0; j < nZColumn; j += nZColumn - 1) { - matrixEr(i, j) = -1 * (arrayV(i + 1, j) - arrayV(i - 1, j)) / (2 * gridSizeR); // r direction - matrixEPhi(i, j) = -1 * (signPlus * arrayVP(i, j) - signMinus * arrayVM(i, j)) / - (2 * radius * gridSizePhi); // phi direction - } - } - - // corner points for EPhi - for (Int_t i = 0; i < nRRow; i += nRRow - 1) { - radius = innerRadius + i * gridSizeR; - for (Int_t j = 0; j < nZColumn; j += nZColumn - 1) { - matrixEPhi(i, j) = -1 * (signPlus * arrayVP(i, j) - signMinus * arrayVM(i, j)) / - (2 * radius * gridSizePhi); // phi direction - } - } - } -} -/// -/// Local distortion and correction, calculate local distortion/correction -/// based on simplified langevin equation, see internal note ALICE-INT-2010-016. -/// -/// Local Distortion -/// -/// Local distortion is calculated based on formulation in ALICE-INT-2010-016, this function assume that -/// electric field \f$\vec{E}(r_{i},z_{j},\phi_{m})\f$ is provided. -/// -/// First, we calculate integration of the Electric field in z-direction for all direction. -/// Assumption: \f$ z_{0} \f$ is location of CE (Central Electrode) and \f$ z_{nZColumn - 1} \f$ is location of End Plate. -/// -/// This integration is in \f$z\f$ direction we can only use trapezoidal rule. -/// -/// Let suppose we want to calculate local distortion at \f$(r_{i},z_{j},\phi_{m})\f$. -/// Assume \f$\vec{E}(r_{i},z_{j+1},\phi_{m}) \f$ and \f$\vec{E}(r_{i},z_{j},\phi_{m}) \f$ are known, see Figure \ref fig1 (a), -/// -/// \anchor fig1 -/// ![Local Distortion](localdist.png) -/// -/// Than we can calculate definite integrations for each directions in respect of $z$ from \f$ z_{j} \f$ to \f$ z_{j + 1} \f$ as follows: -/// -/// \f$ \int^{z_{j+1}}_{z_{j}} \frac{E_{r}}{E_{z}}(r_{i},z_{j},\phi_{m}) dzDist \approx \frac{-1}{\mathrm{ezField}} \frac{h_{z}}{2.0} \left( E_{r}(r_{i},z_{j},\phi_{m}) + E_{r}(r_{i},z_{j+1},\phi_{m}) \right)\f$ -/// -/// \f$ \int^{z_{j+1}}_{z_{j}} \frac{E_{\phi}}{E_{z}}(r_{i},z_{j},\phi_{m}) dzDist \approx \frac{-1}{\mathrm{ezField}} \frac{h_{z}}{2.0} \left( E_{\phi}(r_{i},z_{j},\phi_{m}) + E_{\phi}(r_{i},z_{j+1},\phi_{m}) \right)\f$ -/// -/// \f$ \int^{z_{j+1}}_{z_{j}} E_{z}(r_{i},z_{j},\phi_{m}) dzDist \approx \frac{h_{z}}{2.0} \left( E_{z}(r_{i},z_{j},\phi_{m}) + E_{z}(r_{i},z_{j+1},\phi_{m}) \right) \f$ -/// -/// Code sample at \ref impllocaldist is an implementation of the local integration of electric field. -/// -/// \anchor impllocaldist -/// ~~~ -/// Double_t ezField = (AliTPCPoissonSolver::fgkCathodeV-AliTPCPoissonSolver::fgkGG)/AliTPCPoissonSolver::fgkTPCZ0; // = Electric Field (V/cm) Magnitude ~ -400 V/cm; -/// -/// localIntErOverEz = (gridSizeZ/2.0)*((*eR)(i,j)+(*eR)(i,j+1))/(ezField + (*eZ)(i,j)) ; -/// localIntEPhiOverEz = (gridSizeZ/2.0)*((*ePhi)(i,j)+(*ePhi)(i,j+1))/(ezField + (*eZ)(i,j)) ; -/// localIntDeltaEz = (gridSizeZ/2.0)*((*eZ)(i,j)+(*eZ)(i,j+1)) ; -/// ~~~ -/// -/// -/// After we have local integrations for electric fields in each direction, -/// local distortion \f$\hat{\delta}(r_{i},z_{j},\phi_{m})\f$ is calculated by simplified Langevin equation (see Figure \ref1 (b) for illustration): -/// -/// \f$ \hat{\delta}_{rE}(r_{i},z_{j},\phi_{m}) = c_{0} \int^{z_{j+1}}_{z_{j}} \frac{E_{r}}{E_{z}} dzDist + c_{1} \int^{z_{j+1}}_{z_{j}} \frac{E_{\phi}}{E_{z}} dzDist \f$ -/// -/// ~~~ -/// (*distDrDz)(i,j) = fC0*localIntErOverEz + fC1*localIntEPhiOverEz; -/// ~~~ -/// -/// \f$ r\hat{\delta}_{\phi E}(r_{i},z_{j},\phi_{m}) = - c_{1} \int^{z_{j+1}}_{z_{j}} \frac{E_{j}}{E_{j}} dzDist + c_{0} \int^{z_{j+1}}_{j_{j}} \frac{E_{\phi}}{E_{z}} dzDist \f$ -/// -/// ~~~ -/// (*distDPhiRDz)(i,j) = fC0*localIntEPhiOverEz - fC1*localIntErOverEz ; -/// ~~~ -/// -/// \f$ \hat{\delta}_{z}(r_{i},z_{j},\phi_{m}) = \int_{z_{j}}^{z_{j+1}} \frac{v^{\prime}(E)}{v_{0}} (E - E_{0}) dzDist\f$ -/// -/// ~~~ -/// (*distDz)(i,j) = localIntDeltaEz*-1*AliTPCPoissonSolver::fgkdvdE; -/// ~~~ -/// -/// Where \f$c_{0}\f$ and \f$c_{1}\f$ are constants (see the ALICE-INT-2010-016 for further details). -/// -/// Local correction -/// -/// Local correction is computed as local distortion where the electric fields are in opposite direction (see Figure \ref fig2 (a)). -/// -/// \anchor fig2 -/// ![Local Correction](localcorr.png) -/// -/// Let suppose we want to calculate local correction at \f$(r_{i},\mathbf{z_{j+1}},\phi_{m})\f$. -/// Assume \f$\vec{E}(r_{i},z_{j+1},\phi_{m}) \f$ and \f$\vec{E}(r_{i},z_{j},\phi_{m}) \f$ are known. -/// -/// Than we can calculate definite integrations for each directions in respect of \f$z\f$ from \f$ z_{j+1} \f$ to \f$ z_{j} \f$ as follows: -/// -/// \f$ \int^{z_{j}}_{z_{j+1}} \frac{E_{r}}{E_{z}}(r_{i},z_{j},\phi_{m}) dzDist \approx -1 * \frac{-1}{\mathrm{ezField}} \frac{h_{z}}{2.0} \left( E_{r}(r_{i},z_{j},\phi_{m}) + E_{r}(r_{i},z_{j+1},\phi_{m}) \right)\f$ -/// -/// \f$ \int^{z_{j}}_{z_{j+1}} \frac{E_{\phi}}{E_{z}}(r_{i},z_{j},\phi_{m}) dzDist \approx -1 * \frac{-1}{\mathrm{ezField}} \frac{h_{z}}{2.0} \left( E_{\phi}(r_{i},z_{j},\phi_{m}) + E_{\phi}(r_{i},z_{j+1},\phi_{m}) \right)\f$ -/// -/// \f$ \int^{z_{j}}_{z_{j+1}} E_{z}(r_{i},z_{j},\phi_{m}) dzDist \approx -1 * \frac{h_{z}}{2.0} \left( E_{z}(r_{i},z_{j},\phi_{m}) + E_{z}(r_{i},z_{j+1},\phi_{m}) \right) \f$ -/// -/// Local correction at \f$\hat{\delta'}(r_{i},\mathbf{z_{j+1}},\phi_{m})\f$ is calculated by simplified Langevin equation (see Figure \ref fig2 (b) for illustration): -/// -/// \f$ \hat{\delta'}_{rE}(r_{i},z_{j+1},\phi_{m}) = c_{0} \int^{z_{j}}_{z_{j+1}} \frac{E_{r}}{E_{z}} dzDist + c_{1} \int^{z_{j-1}}_{z_{j}} \frac{E_{\phi}}{E_{z}} dzDist \f$ -/// -/// \f$ r\hat{\delta'}_{\phi E}(r_{i},z_{j+1},\phi_{m}) = - c_{1} \int^{z_{j}}_{z_{j+1}} \frac{E_{j}}{E_{j}} dzDist + c_{0} \int^{z_{j-1}}_{j_{k}} \frac{E_{\phi}}{E_{z}} dzDist \f$ -/// -/// \f$ \hat{\delta'}_{z}(r_{i},z_{j+1},\phi_{m}) = \int_{z_{j}}^{z_{j+1}} \frac{v^{\prime}(E)}{v_{0}} (E - E_{0}) dzDist\f$ -/// -/// For implementation, we use the fact that -/// -/// \f$ \hat{\delta'}_{rE}(r_{i},z_{j+1},\phi_{m}) = -1 * \hat{\delta}_{rE}(r_{i},z_{j},\phi_{m}) \f$ -/// -/// \f$ r\hat{\delta'}_{\phi E}(r_{i},z_{j+1},\phi_{m}) = -1 * r\hat{\delta}_{\phi E}(r_{i},z_{j},\phi_{m}) \f$ -/// -/// \f$ \hat{\delta'}_{z}(r_{i},z_{j+1},\phi_{m}) = -1 * \hat{\delta}_{z}(r_{i},z_{j},\phi_{m}) \f$ -/// -/// ~~~ -/// (*corrDrDz)(i,j+1) = -1* (*distDrDz)(i,j) ; -/// (*corrDPhiRDz)(i,j+1) = -1* (*distDPhiRDz)(i,j); -/// (*corrDz)(i,j+1) = -1* (*distDz)(i,j); -/// ~~~ -/// -/// \param matricesEr TMatrixD** electric field for \f$r\f$ component -/// \param matricesEPhi TMatrixD** electric field for \f$\phi\f$ component -/// \param matricesEz TMatrixD** electric field for \f$z\f$ component -/// \param matricesDistDrDz TMatrixD** local distortion \f$\hat{\delta}_{r}\f$ -/// \param matricesDistDPhiRDz TMatrixD** local distortion \f$r \hat{\delta}_{\phi}\f$ -/// \param matricesDistDz TMatrixD** local distortion \f$ \hat{\delta}_{z}\f$ -/// \param matricesCorrDrDz TMatrixD** local correction \f$\hat{\delta}_{r}\f$ -/// \param matricesCorrDPhiRDz TMatrixD** local correction \f$r \hat{\delta}_{\phi}\f$ -/// \param matricesCorrDz TMatrixD** local correction \f$ \hat{\delta}_{z}\f$ -/// \param nRRow Int_t Number of nRRow in r-direction -/// \param nZColumn Int_t Number of nZColumn in z-direction -/// \param phiSlice Int_t Number of phi slices in \f$ phi \f$ direction -/// \param gridSizeZ const Float_t grid size in z direction -/// \param ezField const Double_t ezField calculated from the invoking operation -/// -/// \pre matricesEr, matricesEPhi, matrices Ez assume already been calculated -/// \post Local distortion and correction are computed according simplified Langevin equation -/// ~~~ -/// matricesDistDrDz,matricesDistDPhiRDz,matricesDistDz -/// ~~~ -/// and correction: -/// ~~~ -/// matricesCorrDrDz,matricesCorrDPhiRDz,matricesCorrDz -/// ~~~ -/// -void AliTPCSpaceCharge3DCalc::LocalDistCorrDz(TMatrixD** matricesEr, TMatrixD** matricesEPhi, TMatrixD** matricesEz, - TMatrixD** matricesDistDrDz, TMatrixD** matricesDistDPhiRDz, - TMatrixD** matricesDistDz, - TMatrixD** matricesCorrDrDz, TMatrixD** matricesCorrDPhiRDz, - TMatrixD** matricesCorrDz, - const Int_t nRRow, const Int_t nZColumn, const Int_t phiSlice, - const Float_t gridSizeZ, - const Double_t ezField) -{ - Float_t localIntErOverEz = 0.0; - Float_t localIntEPhiOverEz = 0.0; - Float_t localIntDeltaEz = 0.0; - TMatrixD* eR; - TMatrixD* ePhi; - TMatrixD* eZ; - TMatrixD* distDrDz; - TMatrixD* distDPhiRDz; - TMatrixD* distDz; - TMatrixD* corrDrDz; - TMatrixD* corrDPhiRDz; - TMatrixD* corrDz; - - // Initialization for j == column-1 integration is 0.0 - for (Int_t m = 0; m < phiSlice; m++) { - distDrDz = matricesDistDrDz[m]; - distDPhiRDz = matricesDistDPhiRDz[m]; - distDz = matricesDistDz[m]; - - corrDrDz = matricesCorrDrDz[m]; - corrDPhiRDz = matricesCorrDPhiRDz[m]; - corrDz = matricesCorrDz[m]; - - for (Int_t i = 0; i < nRRow; i++) { - (*distDrDz)(i, nZColumn - 1) = 0.0; - (*distDPhiRDz)(i, nZColumn - 1) = 0.0; - (*distDz)(i, nZColumn - 1) = 0.0; - - (*corrDrDz)(i, 0) = 0.0; - (*corrDPhiRDz)(i, 0) = 0.0; - (*corrDz)(i, 0) = 0.0; - } - } - - // for this case - // use trapezoidal rule assume no ROC displacement - for (Int_t m = 0; m < phiSlice; m++) { - eR = matricesEr[m]; - ePhi = matricesEPhi[m]; - eZ = matricesEz[m]; - distDrDz = matricesDistDrDz[m]; - distDPhiRDz = matricesDistDPhiRDz[m]; - distDz = matricesDistDz[m]; - - corrDrDz = matricesCorrDrDz[m]; - corrDPhiRDz = matricesCorrDPhiRDz[m]; - corrDz = matricesCorrDz[m]; - - for (Int_t j = 0; j < nZColumn - 1; j++) { - for (Int_t i = 0; i < nRRow; i++) { - double eZ0 = ezField + (*eZ)(i, j); - double eZ1 = ezField + (*eZ)(i, j + 1); - localIntErOverEz = (gridSizeZ * 0.5) * ((*eR)(i, j) / eZ0 + (*eR)(i, j + 1) / eZ1); - localIntEPhiOverEz = (gridSizeZ * 0.5) * ((*ePhi)(i, j) / eZ0 + (*ePhi)(i, j + 1) / eZ1); - localIntDeltaEz = (gridSizeZ * 0.5) * ((*eZ)(i, j) + (*eZ)(i, j + 1)); - - (*distDrDz)(i, j) = fC0 * localIntErOverEz + fC1 * localIntEPhiOverEz; - (*distDPhiRDz)(i, j) = fC0 * localIntEPhiOverEz - fC1 * localIntErOverEz; - (*distDz)(i, j) = localIntDeltaEz * -1 * AliTPCPoissonSolver::fgkdvdE; - - (*corrDrDz)(i, j + 1) = -1 * (*distDrDz)(i, j); - (*corrDPhiRDz)(i, j + 1) = -1 * (*distDPhiRDz)(i, j); - (*corrDz)(i, j + 1) = -1 * (*distDz)(i, j); - } - } - } -} -/// -/// Local distortion and correction, calculate local distortion/correction for known analitics functions -/// based on simplified langevin equation, see internal note ALICE-INT-2010-016. -/// -/// \param matricesEPhi TMatrixD** electric field for \f$\phi\f$ component -/// \param matricesEz TMatrixD** electric field for \f$z\f$ component -/// \param matricesDistDrDz TMatrixD** local distortion \f$\hat{\delta}_{r}\f$ -/// \param matricesDistDPhiRDz TMatrixD** local distortion \f$r \hat{\delta}_{\phi}\f$ -/// \param matricesDistDz TMatrixD** local distortion \f$ \hat{\delta}_{z}\f$ -/// \param matricesCorrDrDz TMatrixD** local correction \f$\hat{\delta}_{r}\f$ -/// \param matricesCorrDPhiRDz TMatrixD** local correction \f$r \hat{\delta}_{\phi}\f$ -/// \param matricesCorrDz TMatrixD** local correction \f$ \hat{\delta}_{z}\f$ -/// \param nRRow Int_t Number of nRRow in r-direction -/// \param nZColumn Int_t Number of nZColumn in z-direction -/// \param phiSlice Int_t Number of phi slices in \f$ phi \f$ direction -/// \param gridSizeZ const Float_t grid size in z direction -/// \param ezField const Double_t ezField calculated from the invoking operation -/// -/// \pre matricesEr, matricesEPhi, matrices Ez assume already been calculated -/// \post Local distortion and correction are computed according simplified Langevin equation -/// ~~~ -/// matricesDistDrDz,matricesDistDPhiRDz,matricesDistDz -/// ~~~ -/// and correction: -/// ~~~ -/// matricesCorrDrDz,matricesCorrDPhiRDz,matricesCorrDz -/// ~~~ -/// -void AliTPCSpaceCharge3DCalc::LocalDistCorrDz(TFormula* intErDzFunction, TFormula* intEPhiRDzFunction, TFormula* intDzFunction, - TFormula* ezFunction, Double_t* rList, Double_t* phiList, Double_t* zList, TMatrixD** matricesDistDrDz, - TMatrixD** matricesDistDPhiRDz, TMatrixD** matricesDistDz, TMatrixD** matricesCorrDrDz, - TMatrixD** matricesCorrDPhiRDz, TMatrixD** matricesCorrDz, const Int_t nRRow, const Int_t nZColumn, - const Int_t phiSlice, const Float_t gridSizeZ, const Double_t ezField) -{ - Float_t localIntErOverEz = 0.0; - Float_t localIntEPhiOverEz = 0.0; - Float_t localIntDeltaEz = 0.0; - TMatrixD* distDrDz; - TMatrixD* distDPhiRDz; - TMatrixD* distDz; - TMatrixD* corrDrDz; - TMatrixD* corrDPhiRDz; - TMatrixD* corrDz; - - Double_t radius, phi, z0, z1; - - // Initialization for j == column-1 integration is 0.0 - for (Int_t m = 0; m < phiSlice; m++) { - distDrDz = matricesDistDrDz[m]; - distDPhiRDz = matricesDistDPhiRDz[m]; - distDz = matricesDistDz[m]; - - corrDrDz = matricesCorrDrDz[m]; - corrDPhiRDz = matricesCorrDPhiRDz[m]; - corrDz = matricesCorrDz[m]; - - for (Int_t i = 0; i < nRRow; i++) { - (*distDrDz)(i, nZColumn - 1) = 0.0; - (*distDPhiRDz)(i, nZColumn - 1) = 0.0; - (*distDz)(i, nZColumn - 1) = 0.0; - - (*corrDrDz)(i, 0) = 0.0; - (*corrDPhiRDz)(i, 0) = 0.0; - (*corrDz)(i, 0) = 0.0; - } - } - - // for this case - // use trapezoidal rule assume no ROC displacement - for (Int_t m = 0; m < phiSlice; m++) { - phi = phiList[m]; - distDrDz = matricesDistDrDz[m]; - distDPhiRDz = matricesDistDPhiRDz[m]; - distDz = matricesDistDz[m]; - - corrDrDz = matricesCorrDrDz[m]; - corrDPhiRDz = matricesCorrDPhiRDz[m]; - corrDz = matricesCorrDz[m]; - - for (Int_t i = 0; i < nRRow; i++) { - radius = rList[i]; - - for (Int_t j = 0; j < nZColumn - 1; j++) { - z0 = zList[j]; - z1 = zList[j + 1]; - - localIntErOverEz = (intErDzFunction->Eval(radius, phi, z1) - intErDzFunction->Eval(radius, phi, z0)) / (ezField + ezFunction->Eval(radius, phi, z0)); - localIntEPhiOverEz = (intEPhiRDzFunction->Eval(radius, phi, z1) - intEPhiRDzFunction->Eval(radius, phi, z0)) / (ezField + ezFunction->Eval(radius, phi, z0)); - localIntDeltaEz = intDzFunction->Eval(radius, phi, z1) - intDzFunction->Eval(radius, phi, z0); - - (*distDrDz)(i, j) = fC0 * localIntErOverEz + fC1 * localIntEPhiOverEz; - (*distDPhiRDz)(i, j) = fC0 * localIntEPhiOverEz - fC1 * localIntErOverEz; - (*distDz)(i, j) = localIntDeltaEz * -1 * AliTPCPoissonSolver::fgkdvdE; - - (*corrDrDz)(i, j + 1) = -1 * (*distDrDz)(i, j); - (*corrDPhiRDz)(i, j + 1) = -1 * (*distDPhiRDz)(i, j); - (*corrDz)(i, j + 1) = -1 * (*distDz)(i, j); - } - } - } -} - -/// IntegrateDistCorrDriftLineDz, integration of local distortion by following electron drift -/// See explanation at LocalDistCorrDz -/// -/// -/// \param matricesEr TMatrixD** electric field for \f$r\f$ component -/// \param matricesEPhi TMatrixD** electric field for \f$\phi\f$ component -/// \param matricesEz TMatrixD** electric field for \f$z\f$ component -/// \param matricesCorrDrDz TMatrixD** local correction \f$\hat{\delta}_{r}\f$ -/// \param matricesCorrDPhiRDz TMatrixD** local correction \f$r \hat{\delta}_{\phi}\f$ -/// \param matricesCorrDz TMatrixD** local correction \f$ \hat{\delta}_{z}\f$ -/// \param nRRow Int_t Number of nRRow in r-direction -/// \param nZColumn Int_t Number of nZColumn in z-direction -/// \param phiSlice Int_t Number of phi slices in \f$ phi \f$ direction -/// \param gridSizeZ const Float_t grid size in z direction -/// \param ezField const Double_t ezField calculate from the invoking operation -/// -/// \pre matricesEr, matricesEPhi, matrices Ez are provided -/// \post Local correction are computed according simplified Langevin equation -/// ~~~ -/// matricesCorrDz,matricesCorrDPhiRDz,matricesDistDz -/// ~~~ -/// -void AliTPCSpaceCharge3DCalc::IntegrateDistCorrDriftLineDz( - AliTPCLookUpTable3DInterpolatorD* lookupLocalDist, - TMatrixD** matricesGDistDrDz, - TMatrixD** matricesGDistDPhiRDz, TMatrixD** matricesGDistDz, - AliTPCLookUpTable3DInterpolatorD* lookupLocalCorr, - TMatrixD** matricesGCorrDrDz, TMatrixD** matricesGCorrDPhiRDz, TMatrixD** matricesGCorrDz, - TMatrixD** matricesGCorrIrregularDrDz, TMatrixD** matricesGCorrIrregularDPhiRDz, - TMatrixD** matricesGCorrIrregularDz, - TMatrixD** matricesRIrregular, TMatrixD** matricesPhiIrregular, TMatrixD** matricesZIrregular, - const Int_t nRRow, const Int_t nZColumn, const Int_t phiSlice, - const Double_t* rList, const Double_t* phiList, const Double_t* zList) -{ - - Float_t drDist, dPhi, dzDist, ddR, ddRPhi, ddZ; - Float_t radius0, phi0, z0, radius, phi, z, radiusCorrection; - radiusCorrection = 0.0; - radius = 0.0; - TMatrixD* mDistDrDz; - TMatrixD* mDistDPhiRDz; - TMatrixD* mDistDz; - TMatrixD* mCorrDrDz; - TMatrixD* mCorrDPhiRDz; - TMatrixD* mCorrDz; - TMatrixD* mCorrIrregularDrDz; - TMatrixD* mCorrIrregularDPhiRDz; - TMatrixD* mCorrIrregularDz; - TMatrixD* mRIrregular; - TMatrixD* mPhiIrregular; - TMatrixD* mZIrregular; - Int_t j = nZColumn - 1; - z0 = zList[j]; - - for (Int_t m = 0; m < phiSlice; m++) { - phi0 = phiList[m]; - - mDistDrDz = matricesGDistDrDz[m]; - mDistDPhiRDz = matricesGDistDPhiRDz[m]; - mDistDz = matricesGDistDz[m]; - - // - mCorrDrDz = matricesGCorrDrDz[m]; - mCorrDPhiRDz = matricesGCorrDPhiRDz[m]; - mCorrDz = matricesGCorrDz[m]; - - mCorrIrregularDrDz = matricesGCorrIrregularDrDz[m]; - mCorrIrregularDPhiRDz = matricesGCorrIrregularDPhiRDz[m]; - mCorrIrregularDz = matricesGCorrIrregularDz[m]; - - mRIrregular = matricesRIrregular[m]; - mPhiIrregular = matricesPhiIrregular[m]; - mZIrregular = matricesZIrregular[m]; - - for (Int_t i = 0; i < nRRow; i++) { - // do from j to 0 - // follow the drift - radius0 = rList[i]; - phi = phi0; - - /// - (*mDistDrDz)(i, j) = 0.; - (*mDistDPhiRDz)(i, j) = 0.; - (*mDistDz)(i, j) = 0.; - - (*mCorrDrDz)(i, j) = 0.; - (*mCorrDPhiRDz)(i, j) = 0.; - (*mCorrDz)(i, j) = 0.; - - //////////////// use irregular grid look up table for correction - if (fCorrectionType == kIrregularInterpolator) { - (*mCorrIrregularDrDz)(i, j) = 0.0; - (*mCorrIrregularDPhiRDz)(i, j) = 0.0; - (*mCorrIrregularDz)(i, j) = -0.0; - - // distorted point - (*mRIrregular)(i, j) = radius0; - (*mPhiIrregular)(i, j) = phi0; - (*mZIrregular)(i, j) = z0; - } - /////////////// - } - } - - // from j one column near end cap - for (j = nZColumn - 2; j >= 0; j--) { - - z0 = zList[j]; - for (Int_t m = 0; m < phiSlice; m++) { - phi0 = phiList[m]; - - mDistDrDz = matricesGDistDrDz[m]; - mDistDPhiRDz = matricesGDistDPhiRDz[m]; - mDistDz = matricesGDistDz[m]; - - // - mCorrDrDz = matricesGCorrDrDz[m]; - mCorrDPhiRDz = matricesGCorrDPhiRDz[m]; - mCorrDz = matricesGCorrDz[m]; - - mCorrIrregularDrDz = matricesGCorrIrregularDrDz[m]; - mCorrIrregularDPhiRDz = matricesGCorrIrregularDPhiRDz[m]; - mCorrIrregularDz = matricesGCorrIrregularDz[m]; - - mRIrregular = matricesRIrregular[m]; - mPhiIrregular = matricesPhiIrregular[m]; - mZIrregular = matricesZIrregular[m]; - - for (Int_t i = 0; i < nRRow; i++) { - // do from j to 0 - // follow the drift - radius0 = rList[i]; - phi = phi0; - radius = radius0; - - drDist = 0.0; - dPhi = 0.0; - dzDist = 0.0; - - // follow the drift line from z=j --> nZColumn - 1 - for (Int_t jj = j; jj < nZColumn; jj++) { - // interpolation the local distortion for current position - // phi += ddRPhi / radius; - phi = phi0 + dPhi; - radius = radius0 + drDist; - z = zList[jj] + dzDist; - - // regulate phi - while (phi < 0.0) { - phi = TMath::TwoPi() + phi; - } - while (phi > TMath::TwoPi()) { - phi = phi - TMath::TwoPi(); - } - - lookupLocalDist->GetValue(radius, phi, z, ddR, ddRPhi, ddZ); - - // add local distortion - drDist += ddR; - dPhi += (ddRPhi / radius); - dzDist += ddZ; - } - // set the global distortion after following the electron drift - (*mDistDrDz)(i, j) = drDist; - (*mDistDPhiRDz)(i, j) = dPhi * radius0; - (*mDistDz)(i, j) = dzDist; - /////////////// use irregular grid look up table for correction - // set - if (fCorrectionType == kIrregularInterpolator) { - // values should be negative of distortions - (*mCorrIrregularDrDz)(i, j) = -drDist; - (*mCorrIrregularDPhiRDz)(i, j) = -1 * dPhi * (radius0 + drDist); - (*mCorrIrregularDz)(i, j) = -dzDist; - - // distorted point - (*mRIrregular)(i, j) = radius0 + drDist; - (*mPhiIrregular)(i, j) = phi0 + dPhi; - (*mZIrregular)(i, j) = z0 + dzDist; - } - /////////////// - - // put the radius to the original value - if (fCorrectionType == kRegularInterpolator) { - if (j == nZColumn - 2) { - radiusCorrection = radius0; - } - - // get global correction from j+1 - drDist = (*mCorrDrDz)(i, j + 1); - dPhi = (*mCorrDPhiRDz)(i, j + 1) / radius0; - dzDist = (*mCorrDz)(i, j + 1); - - radiusCorrection = radius0 + drDist; - phi = phi0 + dPhi; - z = zList[j + 1] + dzDist; - - while (phi < 0.0) { - phi = TMath::TwoPi() + phi; - } - while (phi > TMath::TwoPi()) { - phi = phi - TMath::TwoPi(); - } - - lookupLocalCorr->GetValue(radiusCorrection, phi, z, ddR, ddRPhi, ddZ); - - drDist += ddR; - dzDist += ddZ; - dPhi += ddRPhi / radiusCorrection; - - (*mCorrDrDz)(i, j) = drDist; - (*mCorrDPhiRDz)(i, j) = dPhi * radius0; - (*mCorrDz)(i, j) = dzDist; - } - } - } - } -} - -// oudated, to be removed once changes in aliroot are pushed -/// follow the drift for exact function -/// -/// \param intDrDzF -/// \param intDPhiDzF -/// \param intDzDzF -/// \param ezField -/// \param matricesGDistDrDz -/// \param matricesGDistDPhiRDz -/// \param matricesGDistDz -/// \param matricesGCorrDrDz -/// \param matricesGCorrDPhiRDz -/// \param matricesGCorrDz -/// \param matricesGCorrIrregularDrDz -/// \param matricesGCorrIrregularDPhiRDz -/// \param matricesGCorrIrregularDz -/// \param matricesRIrregular -/// \param matricesPhiIrregular -/// \param matricesZIrregular -/// \param nRRow -/// \param nZColumn -/// \param phiSlice -/// \param rList -/// \param phiList -/// \param zList -void AliTPCSpaceCharge3DCalc::IntegrateDistCorrDriftLineDz( - TFormula* intDrDzF, TFormula* intDPhiDzF, TFormula* intDzDzF, const Double_t ezField, - TMatrixD** matricesGDistDrDz, TMatrixD** matricesGDistDPhiRDz, TMatrixD** matricesGDistDz, - TMatrixD** matricesGCorrDrDz, TMatrixD** matricesGCorrDPhiRDz, TMatrixD** matricesGCorrDz, - TMatrixD** matricesGCorrIrregularDrDz, TMatrixD** matricesGCorrIrregularDPhiRDz, TMatrixD** matricesGCorrIrregularDz, TMatrixD** matricesRIrregular, - TMatrixD** matricesPhiIrregular, TMatrixD** matricesZIrregular, const Int_t nRRow, const Int_t nZColumn, - const Int_t phiSlice, const Double_t* rList, const Double_t* phiList, const Double_t* zList) -{ - Float_t drDist, dPhi, dzDist, ddR, ddRPhi, ddZ; - Float_t radius0, phi0, z0, radius, phi, z, radiusCorrection, z1; - - Float_t localIntErOverEz = 0.0; - Float_t localIntEPhiOverEz = 0.0; - Float_t localIntDeltaEz = 0.0; - // set parameters for function - // hard coded, will be modified after pull at AliRoot - - TFormula* ezF = GetEzFormula(); - radiusCorrection = 0.0; - radius = 0.0; - TMatrixD* mDistDrDz; - TMatrixD* mDistDPhiRDz; - TMatrixD* mDistDz; - TMatrixD* mCorrDrDz; - TMatrixD* mCorrDPhiRDz; - TMatrixD* mCorrDz; - TMatrixD* mCorrIrregularDrDz; - TMatrixD* mCorrIrregularDPhiRDz; - TMatrixD* mCorrIrregularDz; - TMatrixD* mRIrregular; - TMatrixD* mPhiIrregular; - TMatrixD* mZIrregular; - Int_t j = nZColumn - 1; - z0 = zList[j]; - - for (Int_t m = 0; m < phiSlice; m++) { - phi0 = phiList[m]; - - mDistDrDz = matricesGDistDrDz[m]; - mDistDPhiRDz = matricesGDistDPhiRDz[m]; - mDistDz = matricesGDistDz[m]; - - // - mCorrDrDz = matricesGCorrDrDz[m]; - mCorrDPhiRDz = matricesGCorrDPhiRDz[m]; - mCorrDz = matricesGCorrDz[m]; - - mCorrIrregularDrDz = matricesGCorrIrregularDrDz[m]; - mCorrIrregularDPhiRDz = matricesGCorrIrregularDPhiRDz[m]; - mCorrIrregularDz = matricesGCorrIrregularDz[m]; - - mRIrregular = matricesRIrregular[m]; - mPhiIrregular = matricesPhiIrregular[m]; - mZIrregular = matricesZIrregular[m]; - - for (Int_t i = 0; i < nRRow; i++) { - // follow the drift - radius0 = rList[i]; - phi = phi0; - radius = radius0; - /// - (*mDistDrDz)(i, j) = 0.0; - (*mDistDPhiRDz)(i, j) = 0.0; - (*mDistDz)(i, j) = 0.0; - - //////////////// use irregular grid look up table for correction - // set - (*mCorrIrregularDrDz)(i, j) = 0.0; - (*mCorrIrregularDPhiRDz)(i, j) = 0.0; - (*mCorrIrregularDz)(i, j) = 0.0; - - // distorted point - (*mRIrregular)(i, j) = radius0; - (*mPhiIrregular)(i, j) = phi0; - (*mZIrregular)(i, j) = z0; - /////////////// - } - } - - // from j one column near end cap - for (j = nZColumn - 2; j >= 0; j--) { - - z0 = zList[j]; - - for (Int_t m = 0; m < phiSlice; m++) { - phi0 = phiList[m]; - - mDistDrDz = matricesGDistDrDz[m]; - mDistDPhiRDz = matricesGDistDPhiRDz[m]; - mDistDz = matricesGDistDz[m]; - - // - mCorrDrDz = matricesGCorrDrDz[m]; - mCorrDPhiRDz = matricesGCorrDPhiRDz[m]; - mCorrDz = matricesGCorrDz[m]; - - mCorrIrregularDrDz = matricesGCorrIrregularDrDz[m]; - mCorrIrregularDPhiRDz = matricesGCorrIrregularDPhiRDz[m]; - mCorrIrregularDz = matricesGCorrIrregularDz[m]; - - mRIrregular = matricesRIrregular[m]; - mPhiIrregular = matricesPhiIrregular[m]; - mZIrregular = matricesZIrregular[m]; - - for (Int_t i = 0; i < nRRow; i++) { - // do from j to 0 - // follow the drift - radius0 = rList[i]; - phi = phi0; - radius = radius0; - - drDist = 0.0; - dPhi = 0.0; - dzDist = 0.0; - ddRPhi = 0.0; - - // follow the drift line from z=j --> nZColumn - 1 - for (Int_t jj = j; jj < nZColumn; jj++) { - // interpolation the local distortion for current position - phi = phi0 + dPhi; - radius = radius0 + drDist; - z = zList[jj] + dzDist; - z1 = z + (zList[j + 1] - zList[j]); - // regulate phi - while (phi < 0.0) { - phi = TMath::TwoPi() + phi; - } - while (phi > TMath::TwoPi()) { - phi = phi - TMath::TwoPi(); - } - - //lookupLocalDist->GetValue(radius, phi, z, ddR, ddRPhi, ddZ); - localIntErOverEz = (intDrDzF->Eval(radius, phi, z1) - intDrDzF->Eval(radius, phi, z)) / (ezField + ezF->Eval(radius, phi, z)); - localIntEPhiOverEz = (intDPhiDzF->Eval(radius, phi, z1) - intDPhiDzF->Eval(radius, phi, z)) / (ezField + ezF->Eval(radius, phi, z)); - localIntDeltaEz = intDzDzF->Eval(radius, phi, z1) - intDzDzF->Eval(radius, phi, z); - - ddR = fC0 * localIntErOverEz + fC1 * localIntEPhiOverEz; - ddRPhi = fC0 * localIntEPhiOverEz - fC1 * localIntErOverEz; - ddZ = -1 * localIntDeltaEz * AliTPCPoissonSolver::fgkdvdE; - - drDist += ddR; - dPhi += (ddRPhi / radius); - dzDist += ddZ; - - // add local distortion - } - // set the global distortion after following the electron drift - (*mDistDrDz)(i, j) = drDist; - (*mDistDPhiRDz)(i, j) = dPhi * radius0; - (*mDistDz)(i, j) = dzDist; - /////////////// use irregular grid look up table for correction - // use oppsite directions of distortion - (*mCorrIrregularDrDz)(i, j) = -drDist; - (*mCorrIrregularDPhiRDz)(i, j) = -dPhi * (radius0 + drDist); - (*mCorrIrregularDz)(i, j) = -dzDist; - - // distorted point - (*mRIrregular)(i, j) = radius0 + drDist; - (*mPhiIrregular)(i, j) = phi0 + dPhi; - (*mZIrregular)(i, j) = z0 + dzDist; - /////////////// - - // put the radius to the original value - if (j == nZColumn - 2) { - radiusCorrection = radius0; - } - - // get global correction from j+1 - drDist = (*mCorrDrDz)(i, j + 1); - dzDist = (*mCorrDz)(i, j + 1); - - radiusCorrection = radius0 + drDist; - dPhi = (*mCorrDPhiRDz)(i, j + 1) / radius0; - //dPhi = (*mCorrDPhiRDz)(i, j + 1) /radiusCorrection; - phi = phi0 + dPhi; - z = zList[j + 1] + dzDist; - z1 = z - (zList[j + 1] - zList[j]); - - while (phi < 0.0) { - phi = TMath::TwoPi() + phi; - } - while (phi > TMath::TwoPi()) { - phi = phi - TMath::TwoPi(); - } - - //lookupLocalCorr->GetValue(radiusCorrection, phi, z, ddR, ddRPhi, ddZ); - localIntErOverEz = (intDrDzF->Eval(radiusCorrection, phi, z1) - intDrDzF->Eval(radiusCorrection, phi, z)) / (ezField + intDzDzF->Eval(radiusCorrection, phi, z)); - localIntEPhiOverEz = (intDPhiDzF->Eval(radiusCorrection, phi, z1) - intDPhiDzF->Eval(radiusCorrection, phi, z)) / (ezField + intDzDzF->Eval(radiusCorrection, phi, z)); - localIntDeltaEz = intDzDzF->Eval(radiusCorrection, phi, z1) - intDzDzF->Eval(radiusCorrection, phi, z); - - ddR = fC0 * localIntErOverEz + fC1 * localIntEPhiOverEz; - ddRPhi = fC0 * localIntEPhiOverEz - fC1 * localIntErOverEz; - ddZ = -1 * localIntDeltaEz * AliTPCPoissonSolver::fgkdvdE; - - drDist += ddR; - dzDist += ddZ; - dPhi += ddRPhi / radiusCorrection; - - (*mCorrDrDz)(i, j) = drDist; - (*mCorrDPhiRDz)(i, j) = dPhi * radius0; - (*mCorrDz)(i, j) = dzDist; - } - } - } -} - -/// follow the drift for exact function -/// -/// \param intDrDzF -/// \param intDPhiDzF -/// \param intDzDzF -/// \param ezField -/// \param matricesGDistDrDz -/// \param matricesGDistDPhiRDz -/// \param matricesGDistDz -/// \param matricesGCorrDrDz -/// \param matricesGCorrDPhiRDz -/// \param matricesGCorrDz -/// \param matricesGCorrIrregularDrDz -/// \param matricesGCorrIrregularDPhiRDz -/// \param matricesGCorrIrregularDz -/// \param matricesRIrregular -/// \param matricesPhiIrregular -/// \param matricesZIrregular -/// \param nRRow -/// \param nZColumn -/// \param phiSlice -/// \param rList -/// \param phiList -/// \param zList -void AliTPCSpaceCharge3DCalc::IntegrateDistCorrDriftLineDz( - TFormula* intDrDzF, TFormula* intDPhiDzF, TFormula* intDzDzF, TFormula* ezF, const Double_t ezField, - TMatrixD** matricesGDistDrDz, TMatrixD** matricesGDistDPhiRDz, TMatrixD** matricesGDistDz, - TMatrixD** matricesGCorrDrDz, TMatrixD** matricesGCorrDPhiRDz, TMatrixD** matricesGCorrDz, - TMatrixD** matricesGCorrIrregularDrDz, TMatrixD** matricesGCorrIrregularDPhiRDz, TMatrixD** matricesGCorrIrregularDz, TMatrixD** matricesRIrregular, - TMatrixD** matricesPhiIrregular, TMatrixD** matricesZIrregular, const Int_t nRRow, const Int_t nZColumn, - const Int_t phiSlice, const Double_t* rList, const Double_t* phiList, const Double_t* zList) -{ - - Float_t drDist, dPhi, dzDist, ddR, ddRPhi, ddZ; - Float_t radius0, phi0, z0, radius, phi, z, radiusCorrection, z1; - - Float_t localIntErOverEz = 0.0; - Float_t localIntEPhiOverEz = 0.0; - Float_t localIntDeltaEz = 0.0; - - radiusCorrection = 0.0; - radius = 0.0; - TMatrixD* mDistDrDz; - TMatrixD* mDistDPhiRDz; - TMatrixD* mDistDz; - TMatrixD* mCorrDrDz; - TMatrixD* mCorrDPhiRDz; - TMatrixD* mCorrDz; - TMatrixD* mCorrIrregularDrDz; - TMatrixD* mCorrIrregularDPhiRDz; - TMatrixD* mCorrIrregularDz; - TMatrixD* mRIrregular; - TMatrixD* mPhiIrregular; - TMatrixD* mZIrregular; - Int_t j = nZColumn - 1; - z0 = zList[j]; - - for (Int_t m = 0; m < phiSlice; m++) { - phi0 = phiList[m]; - - mDistDrDz = matricesGDistDrDz[m]; - mDistDPhiRDz = matricesGDistDPhiRDz[m]; - mDistDz = matricesGDistDz[m]; - - // - mCorrDrDz = matricesGCorrDrDz[m]; - mCorrDPhiRDz = matricesGCorrDPhiRDz[m]; - mCorrDz = matricesGCorrDz[m]; - - mCorrIrregularDrDz = matricesGCorrIrregularDrDz[m]; - mCorrIrregularDPhiRDz = matricesGCorrIrregularDPhiRDz[m]; - mCorrIrregularDz = matricesGCorrIrregularDz[m]; - - mRIrregular = matricesRIrregular[m]; - mPhiIrregular = matricesPhiIrregular[m]; - mZIrregular = matricesZIrregular[m]; - - for (Int_t i = 0; i < nRRow; i++) { - // follow the drift - radius0 = rList[i]; - phi = phi0; - radius = radius0; - /// - (*mDistDrDz)(i, j) = 0.0; - (*mDistDPhiRDz)(i, j) = 0.0; - (*mDistDz)(i, j) = 0.0; - - //////////////// use irregular grid look up table for correction - // set - (*mCorrIrregularDrDz)(i, j) = 0.0; - (*mCorrIrregularDPhiRDz)(i, j) = 0.0; - (*mCorrIrregularDz)(i, j) = 0.0; - - // distorted point - (*mRIrregular)(i, j) = radius0; - (*mPhiIrregular)(i, j) = phi0; - (*mZIrregular)(i, j) = z0; - /////////////// - } - } - - // from j one column near end cap - for (j = nZColumn - 2; j >= 0; j--) { - - z0 = zList[j]; - - for (Int_t m = 0; m < phiSlice; m++) { - phi0 = phiList[m]; - - mDistDrDz = matricesGDistDrDz[m]; - mDistDPhiRDz = matricesGDistDPhiRDz[m]; - mDistDz = matricesGDistDz[m]; - - // - mCorrDrDz = matricesGCorrDrDz[m]; - mCorrDPhiRDz = matricesGCorrDPhiRDz[m]; - mCorrDz = matricesGCorrDz[m]; - - mCorrIrregularDrDz = matricesGCorrIrregularDrDz[m]; - mCorrIrregularDPhiRDz = matricesGCorrIrregularDPhiRDz[m]; - mCorrIrregularDz = matricesGCorrIrregularDz[m]; - - mRIrregular = matricesRIrregular[m]; - mPhiIrregular = matricesPhiIrregular[m]; - mZIrregular = matricesZIrregular[m]; - - for (Int_t i = 0; i < nRRow; i++) { - // do from j to 0 - // follow the drift - radius0 = rList[i]; - phi = phi0; - radius = radius0; - - drDist = 0.0; - dPhi = 0.0; - dzDist = 0.0; - ddRPhi = 0.0; - - // follow the drift line from z=j --> nZColumn - 1 - for (Int_t jj = j; jj < nZColumn; jj++) { - // interpolation the local distortion for current position - phi = phi0 + dPhi; - radius = radius0 + drDist; - z = zList[jj] + dzDist; - z1 = z + (zList[j + 1] - zList[j]); - // regulate phi - while (phi < 0.0) { - phi = TMath::TwoPi() + phi; - } - while (phi > TMath::TwoPi()) { - phi = phi - TMath::TwoPi(); - } - - //lookupLocalDist->GetValue(radius, phi, z, ddR, ddRPhi, ddZ); - localIntErOverEz = (intDrDzF->Eval(radius, phi, z1) - intDrDzF->Eval(radius, phi, z)) / (ezField + ezF->Eval(radius, phi, z)); - localIntEPhiOverEz = (intDPhiDzF->Eval(radius, phi, z1) - intDPhiDzF->Eval(radius, phi, z)) / (ezField + ezF->Eval(radius, phi, z)); - localIntDeltaEz = intDzDzF->Eval(radius, phi, z1) - intDzDzF->Eval(radius, phi, z); - - ddR = fC0 * localIntErOverEz + fC1 * localIntEPhiOverEz; - ddRPhi = fC0 * localIntEPhiOverEz - fC1 * localIntErOverEz; - ddZ = -1 * localIntDeltaEz * AliTPCPoissonSolver::fgkdvdE; - - drDist += ddR; - dPhi += (ddRPhi / radius); - dzDist += ddZ; - - // add local distortion - } - // set the global distortion after following the electron drift - (*mDistDrDz)(i, j) = drDist; - (*mDistDPhiRDz)(i, j) = dPhi * radius0; - (*mDistDz)(i, j) = dzDist; - /////////////// use irregular grid look up table for correction - // use oppsite directions of distortion - (*mCorrIrregularDrDz)(i, j) = -drDist; - (*mCorrIrregularDPhiRDz)(i, j) = -dPhi * (radius0 + drDist); - (*mCorrIrregularDz)(i, j) = -dzDist; - - // distorted point - (*mRIrregular)(i, j) = radius0 + drDist; - (*mPhiIrregular)(i, j) = phi0 + dPhi; - (*mZIrregular)(i, j) = z0 + dzDist; - /////////////// - - // put the radius to the original value - if (j == nZColumn - 2) { - radiusCorrection = radius0; - } - - // get global correction from j+1 - drDist = (*mCorrDrDz)(i, j + 1); - dzDist = (*mCorrDz)(i, j + 1); - - radiusCorrection = radius0 + drDist; - dPhi = (*mCorrDPhiRDz)(i, j + 1) / radius0; - //dPhi = (*mCorrDPhiRDz)(i, j + 1) /radiusCorrection; - phi = phi0 + dPhi; - z = zList[j + 1] + dzDist; - z1 = z - (zList[j + 1] - zList[j]); - - while (phi < 0.0) { - phi = TMath::TwoPi() + phi; - } - while (phi > TMath::TwoPi()) { - phi = phi - TMath::TwoPi(); - } - - //lookupLocalCorr->GetValue(radiusCorrection, phi, z, ddR, ddRPhi, ddZ); - localIntErOverEz = (intDrDzF->Eval(radiusCorrection, phi, z1) - intDrDzF->Eval(radiusCorrection, phi, z)) / (ezField + intDzDzF->Eval(radiusCorrection, phi, z)); - localIntEPhiOverEz = (intDPhiDzF->Eval(radiusCorrection, phi, z1) - intDPhiDzF->Eval(radiusCorrection, phi, z)) / (ezField + intDzDzF->Eval(radiusCorrection, phi, z)); - localIntDeltaEz = intDzDzF->Eval(radiusCorrection, phi, z1) - intDzDzF->Eval(radiusCorrection, phi, z); - - ddR = fC0 * localIntErOverEz + fC1 * localIntEPhiOverEz; - ddRPhi = fC0 * localIntEPhiOverEz - fC1 * localIntErOverEz; - ddZ = -1 * localIntDeltaEz * AliTPCPoissonSolver::fgkdvdE; // two times? - - drDist += ddR; - dzDist += ddZ; - dPhi += ddRPhi / radiusCorrection; - - (*mCorrDrDz)(i, j) = drDist; - (*mCorrDPhiRDz)(i, j) = dPhi * radius0; - (*mCorrDz)(i, j) = dzDist; - } - } - } -} - -/// See explanation at LocalDistCorrDz -/// -/// -/// \param matricesEr TMatrixD** electric field for \f$r\f$ component -/// \param matricesEPhi TMatrixD** electric field for \f$\phi\f$ component -/// \param matricesEz TMatrixD** electric field for \f$z\f$ component -/// \param matricesCorrDrDz TMatrixD** local correction \f$\hat{\delta}_{r}\f$ -/// \param matricesCorrDPhiRDz TMatrixD** local correction \f$r \hat{\delta}_{\phi}\f$ -/// \param matricesCorrDz TMatrixD** local correction \f$ \hat{\delta}_{z}\f$ -/// \param nRRow Int_t Number of nRRow in r-direction -/// \param nZColumn Int_t Number of nZColumn in z-direction -/// \param phiSlice Int_t Number of phi slices in \f$ phi \f$ direction -/// \param gridSizeZ const Float_t grid size in z direction -/// \param ezField const Double_t ezField calculate from the invoking operation -/// -/// \pre matricesEr, matricesEPhi, matrices Ez are provided -/// \post Local correction are computed according simplified Langevin equation -/// ~~~ -/// matricesCorrDz,matricesCorrDPhiRDz,matricesDistDz -/// ~~~ -void AliTPCSpaceCharge3DCalc::IntegrateDistCorrDriftLineDzWithLookUp(AliTPCLookUpTable3DInterpolatorD* lookupLocalDist, TMatrixD** matricesGDistDrDz, TMatrixD** matricesGDistDPhiRDz, TMatrixD** matricesGDistDz, AliTPCLookUpTable3DInterpolatorD* lookupLocalCorr, TMatrixD** matricesGCorrDrDz, TMatrixD** matricesGCorrDPhiRDz, TMatrixD** matricesGCorrDz, const Int_t nRRow, const Int_t nZColumn, const Int_t phiSlice, Double_t* rList, Double_t* phiList, Double_t* zList) -{ - - Float_t drDist, dRPhi, dPhi, dzDist, ddR, ddRPhi, ddPhi, ddZ; - Float_t radius0, phi0, z0, radius, phi, z, radiusCorrection; - TMatrixD* mDistDrDz; - TMatrixD* mDistDPhiRDz; - TMatrixD* mDistDz; - TMatrixD* mCorrDrDz; - TMatrixD* mCorrDPhiRDz; - TMatrixD* mCorrDz; - - // allocate look up for temporal - AliTPCLookUpTable3DInterpolatorD* lookupGlobalDistTemp = - new AliTPCLookUpTable3DInterpolatorD( - nRRow, matricesGDistDrDz, rList, phiSlice, matricesGDistDPhiRDz, phiList, nZColumn, matricesGDistDz, - zList, 1); - - Int_t j = nZColumn - 1; - for (Int_t m = 0; m < phiSlice; m++) { - phi0 = phiList[m]; - - mDistDrDz = matricesGDistDrDz[m]; - mDistDPhiRDz = matricesGDistDPhiRDz[m]; - mDistDz = matricesGDistDz[m]; - - // - mCorrDrDz = matricesGCorrDrDz[m]; - mCorrDPhiRDz = matricesGCorrDPhiRDz[m]; - mCorrDz = matricesGCorrDz[m]; - - for (Int_t i = 0; i < nRRow; i++) { - /// - (*mDistDrDz)(i, j) = 0.0; - (*mDistDPhiRDz)(i, j) = 0.0; - (*mDistDz)(i, j) = 0.0; - - (*mCorrDrDz)(i, j) = 0.0; - (*mCorrDPhiRDz)(i, j) = 0.0; - (*mCorrDz)(i, j) = 0.0; - } - } - - drDist = 0.0; - dRPhi = 0.0; - dPhi = 0.0; - dzDist = 0.0; - // from j one column near end cap - for (j = nZColumn - 2; j >= 0; j--) { - - z0 = zList[j]; - for (Int_t m = 0; m < phiSlice; m++) { - phi0 = phiList[m]; - - mDistDrDz = matricesGDistDrDz[m]; - mDistDPhiRDz = matricesGDistDPhiRDz[m]; - mDistDz = matricesGDistDz[m]; - - // - mCorrDrDz = matricesGCorrDrDz[m]; - mCorrDPhiRDz = matricesGCorrDPhiRDz[m]; - mCorrDz = matricesGCorrDz[m]; - - for (Int_t i = 0; i < nRRow; i++) { - - // do from j to 0 - // follow the drift - radius0 = rList[i]; - - lookupLocalDist->GetValue(radius0, phi0, z0, ddR, ddRPhi, ddZ); - ddPhi = ddRPhi / radius0; - - if (j < nZColumn - 2) { - phi = phi0 + ddPhi; - radius = radius0 + ddR; - z = zList[j + 1] + ddZ; - - lookupGlobalDistTemp->GetValue(radius, phi, z, drDist, dRPhi, dzDist); - dPhi = dRPhi / radius; - } - - (*mDistDrDz)(i, j) = drDist + ddR; - (*mDistDPhiRDz)(i, j) = (dPhi + ddPhi) * radius0; - (*mDistDz)(i, j) = dzDist + ddZ; - - // copy to 1D for being able to interpolate at next step - if (j > 0) { - (*mDistDrDz)(i, j - 1) = drDist + ddR; - (*mDistDPhiRDz)(i, j - 1) = (dPhi + ddPhi) * radius0; - (*mDistDz)(i, j - 1) = dzDist + ddZ; - } - - // get global correction from j+1 - drDist = (*mCorrDrDz)(i, j + 1); - dPhi = (*mCorrDPhiRDz)(i, j + 1) / radius0; - dzDist = (*mCorrDz)(i, j + 1); - - radiusCorrection = radius0 + drDist; - phi = phi0 + dPhi; - z = zList[j + 1] + dzDist; - - while (phi < 0.0) { - phi = TMath::TwoPi() + phi; - } - while (phi > TMath::TwoPi()) { - phi = phi - TMath::TwoPi(); - } - - lookupLocalCorr->GetValue(radiusCorrection, phi, z, ddR, ddRPhi, ddZ); - - drDist += ddR; - dzDist += ddZ; - dPhi += (ddRPhi / radiusCorrection); - - (*mCorrDrDz)(i, j) = drDist; - (*mCorrDPhiRDz)(i, j) = dPhi * radius0; - (*mCorrDz)(i, j) = dzDist; - } - } - - lookupGlobalDistTemp->CopyFromMatricesToInterpolator(j); - if (j > 0) { - lookupGlobalDistTemp->CopyFromMatricesToInterpolator(j - 1); - } - } - delete lookupGlobalDistTemp; -} - -/// -/// \param lookupGlobal -/// \param lookupRDz -/// \param lookupPhiRDz -/// \param lookupDz -/// \param nRRow -/// \param nZColumn -/// \param phiSlice -/// \param rList -/// \param phiList -/// \param zList -void AliTPCSpaceCharge3DCalc::FillLookUpTable(AliTPCLookUpTable3DInterpolatorD* lookupGlobal, TMatrixD** lookupRDz, - TMatrixD** lookupPhiRDz, TMatrixD** lookupDz, const Int_t nRRow, - const Int_t nZColumn, const Int_t phiSlice, const Double_t* rList, - const Double_t* phiList, const Double_t* zList) -{ - Double_t r, phi, z; - TMatrixD* mR; - TMatrixD* mPhiR; - TMatrixD* mDz; - - /// * Interpolate basicLookup tables; once for each rod, then sum the results - for (Int_t k = 0; k < fNPhiSlices; k++) { - phi = fListPhi[k]; - - mR = lookupRDz[k]; - mPhiR = lookupPhiRDz[k]; - mDz = lookupDz[k]; - for (Int_t j = 0; j < fNZColumns; j++) { - z = fListZ[j]; // Symmetric solution in Z that depends only on ABS(Z) - - for (Int_t i = 0; i < fNRRows; i++) { - r = fListR[i]; - - lookupGlobal->GetValue(r, phi, z, (*mR)(i, j), (*mPhiR)(i, j), (*mDz)(i, j)); - } - } - } -} -/// -/// \param x -/// \param roc -/// \param dx -void AliTPCSpaceCharge3DCalc::GetDistortionCyl(const Float_t x[], Short_t roc, Float_t dx[]) const -{ - GetDistortionCylAC(x, roc, dx); -} -/// -/// \param x -/// \param roc -/// \param dx -void AliTPCSpaceCharge3DCalc::GetDistortionCylAC(const Float_t x[], Short_t roc, Float_t dx[]) const -{ - if (!fInitLookUp) { - Info("AliTPCSpaceCharge3DCalc::GetDistortionCylAC", "Lookup table was not initialized! Please run AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz(nRRows, nZColumns, nPhiSlices) to perform the initialization. Returning distortion vector (0, 0, 0)."); - for (int i = 0; i < 3; ++i) { - dx[i] = 0.; - } - return; - } - - Float_t dR, dRPhi, dZ; - Double_t r, phi, z; - Int_t sign; - - r = x[0]; - phi = x[1]; - if (phi < 0) { - phi += TMath::TwoPi(); // Table uses phi from 0 to 2*Pi - } - if (phi > TMath::TwoPi()) { - phi = phi - TMath::TwoPi(); // Table uses phi from 0 to 2*Pi - } - z = x[2]; // Create temporary copy of x[2] - - if ((roc % 36) < 18) { - sign = 1; // (TPC A side) - } else { - sign = -1; // (TPC C side) - } - - if (sign == 1 && z < AliTPCPoissonSolver::fgkZOffSet) { - z = AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if (sign == -1 && z > -AliTPCPoissonSolver::fgkZOffSet) { - z = -AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if ((sign == 1 && z < -1e-16) || (sign == -1 && z > -1e-16)) { // just a consistency check - Error("AliTPCSpaceCharge3DCalc::GetDistortionCylAC", "ROC number does not correspond to z coordinate! Calculation of distortions is most likely wrong!"); - } - - if (z > -1e-16) { - fLookupIntDistA->GetValue(r, phi, z, dR, dRPhi, dZ); - } else { - fLookupIntDistC->GetValue(r, phi, -1 * z, dR, dRPhi, dZ); - dZ = -1 * dZ; - } - - dx[0] = fCorrectionFactor * dR; - dx[1] = fCorrectionFactor * dRPhi; - dx[2] = fCorrectionFactor * - dZ; // z distortion - (scaled with drift velocity dependency on the Ez field and the overall scaling factor) -} -/// Get Correction from irregular table -/// -/// \param x -/// \param roc -/// \param dx -void AliTPCSpaceCharge3DCalc::GetCorrectionCylACIrregular(const Float_t x[], Short_t roc, Float_t dx[]) const -{ - if (!fInitLookUp) { - Info("AliTPCSpaceCharge3DCalc::GetCorrectionCylACIrregular", "Lookup table was not initialized! Please run AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz(nRRows, nZColumns, nPhiSlices) to perform the initialization. Returning correction vector (0, 0, 0)."); - for (int i = 0; i < 3; ++i) { - dx[i] = 0.; - } - return; - } - - Double_t dR, dRPhi, dZ; - Double_t r, phi, z; - Int_t sign; - const Float_t gridSizeR = (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (fNRRows - 1); - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (fNZColumns - 1); - const Float_t gridSizePhi = TMath::TwoPi() / fNPhiSlices; - - r = x[0]; - phi = x[1]; - z = x[2]; // Create temporary copy of x[2] - - if ((roc % 36) < 18) { - sign = 1; // (TPC A side) - } else { - sign = -1; // (TPC C side) - } - - if (sign == 1 && z < AliTPCPoissonSolver::fgkZOffSet) { - z = AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if (sign == -1 && z > -AliTPCPoissonSolver::fgkZOffSet) { - z = -AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if ((sign == 1 && z < 0) || (sign == -1 && z > 0)) { // just a consistency check - Error("AliTPCSpaceChargeCalc3D::GetCorrectionCylACIrregular", "ROC number does not correspond to z coordinate! Calculation of distortions is most likely wrong!"); - } - - // get distortion from irregular table - - if (z > 0) { - fLookupIntCorrIrregularA->GetValue(r, phi, z, dR, dRPhi, dZ); - } else { - fLookupIntCorrIrregularC->GetValue(r, phi, -z, dR, dRPhi, dZ); - dZ = -1 * dZ; - } - - dx[0] = fCorrectionFactor * dR; - dx[1] = fCorrectionFactor * dRPhi; - dx[2] = fCorrectionFactor * - dZ; // z distortion - (scaled with drift velocity dependency on the Ez field and the overall scaling factor) -} - -/// Get Correction from irregular table -/// -/// \param x -/// \param roc -/// \param dx -void AliTPCSpaceCharge3DCalc::GetCorrectionCylACIrregular(const Float_t x[], Short_t roc, Float_t dx[], const Int_t side) const -{ - if (!fInitLookUp) { - Info("AliTPCSpaceCharge3DCalc::GetCorrectionCylACIrregular", "Lookup table was not initialized! Please run AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz(nRRows, nZColumns, nPhiSlices) to perform the initialization. Returning correction vector (0, 0, 0)."); - for (int i = 0; i < 3; ++i) { - dx[i] = 0.; - } - return; - } - - Double_t dR, dRPhi, dZ; - Double_t r, phi, z; - Int_t sign; - const Float_t gridSizeR = (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (fNRRows - 1); - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (fNZColumns - 1); - const Float_t gridSizePhi = TMath::TwoPi() / fNPhiSlices; - - r = x[0]; - phi = x[1]; - z = x[2]; // Create temporary copy of x[2] - - if ((roc % 36) < 18) { - sign = 1; // (TPC A side) - } else { - sign = -1; // (TPC C side) - } - - if (sign == 1 && z < AliTPCPoissonSolver::fgkZOffSet) { - z = AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if (sign == -1 && z > -AliTPCPoissonSolver::fgkZOffSet) { - z = -AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if ((sign == 1 && z < 0) || (sign == -1 && z > 0)) { // just a consistency check - Error("AliTPCSpaceChargeCalc3D::GetCorrectionCylACIrregular", "ROC number does not correspond to z coordinate! Calculation of distortions is most likely wrong!"); - } - - // get distortion from irregular table - - if (side == 0) { - fLookupIntCorrIrregularA->GetValue(r, phi, z, dR, dRPhi, dZ); - } else { - fLookupIntCorrIrregularC->GetValue(r, phi, -z, dR, dRPhi, dZ); - dZ = -1 * dZ; - } - - dx[0] = fCorrectionFactor * dR; - dx[1] = fCorrectionFactor * dRPhi; - dx[2] = fCorrectionFactor * - dZ; // z distortion - (scaled with drift velocity dependency on the Ez field and the overall scaling factor) -} - -/// Get correction regular grid by following electron -/// -/// \param x -/// \param roc -/// \param dx -void AliTPCSpaceCharge3DCalc::GetCorrectionCylAC(const Float_t x[], Short_t roc, Float_t dx[]) const -{ - if (!fInitLookUp) { - Info("AliTPCSpaceCharge3DCalc::GetDistortionCylAC", "Lookup table was not initialized! Please run AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz(nRRows, nZColumns, nPhiSlices) to perform the initialization. Returning correction vector (0, 0, 0)."); - for (int i = 0; i < 3; ++i) { - dx[i] = 0.; - } - return; - } - - Float_t dR, dRPhi, dZ; - Double_t r, phi, z; - Int_t sign; - - r = x[0]; - phi = x[1]; - if (phi < 0) { - phi += TMath::TwoPi(); // Table uses phi from 0 to 2*Pi - } - if (phi > TMath::TwoPi()) { - phi = phi - TMath::TwoPi(); // Table uses phi from 0 to 2*Pi - } - z = x[2]; // Create temporary copy of x[2] - - if ((roc % 36) < 18) { - sign = 1; // (TPC A side) - } else { - sign = -1; // (TPC C side) - } - - if (sign == 1 && z < AliTPCPoissonSolver::fgkZOffSet) { - z = AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if (sign == -1 && z > -AliTPCPoissonSolver::fgkZOffSet) { - z = -AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if ((sign == 1 && z < -1e-16) || (sign == -1 && z > -1e-16)) { // just a consistency check - Error("AliTPCSpaceChargeCalc3D::GetCorrectionCylAC", "ROC number does not correspond to z coordinate! Calculation of distortions is most likely wrong!"); - } - - if (z > -1e-16) { - fLookupIntCorrA->GetValue(r, phi, z, dR, dRPhi, dZ); - } else { - fLookupIntCorrC->GetValue(r, phi, -z, dR, dRPhi, dZ); - dZ = -1 * dZ; - } - dx[0] = fCorrectionFactor * dR; - dx[1] = fCorrectionFactor * dRPhi; - dx[2] = fCorrectionFactor * - dZ; // z distortion - (scaled with drift velocity dependency on the Ez field and the overall scaling factor) -} -void AliTPCSpaceCharge3DCalc::GetDistortion(const Float_t x[], Short_t roc, Float_t dx[]) const -{ - Float_t pCyl[3]; // a point in cylindrical coordinate - Float_t dCyl[3]; // distortion - - pCyl[0] = TMath::Sqrt(x[0] * x[0] + x[1] * x[1]); - pCyl[1] = TMath::ATan2(x[1], x[0]); - - // normalize phi - while (pCyl[1] > TMath::Pi()) { - pCyl[1] -= TMath::TwoPi(); - } - while (pCyl[1] < -TMath::Pi()) { - pCyl[1] += TMath::TwoPi(); - } - - pCyl[2] = x[2]; // Create temporary copy of x[2] - - GetDistortionCylAC(pCyl, roc, dCyl); - - // Calculate distorted position - if (pCyl[0] > 0.0) { - //pCyl[0] = pCyl[0] + fCorrectionFactor * dCyl[0]; - pCyl[1] = pCyl[1] + fCorrectionFactor * dCyl[1] / pCyl[0]; - pCyl[0] = pCyl[0] + fCorrectionFactor * dCyl[0]; - } - - dCyl[2] = fCorrectionFactor * dCyl[2]; - - // distortion in x,y and z - dx[0] = (pCyl[0] * TMath::Cos(pCyl[1]) - x[0]); - dx[1] = (pCyl[0] * TMath::Sin(pCyl[1]) - x[1]); - dx[2] = dCyl[2]; -} -/// -/// \param x -/// \param roc -/// \param dx -void AliTPCSpaceCharge3DCalc::GetCorrectionCyl(const Float_t x[], Short_t roc, Float_t dx[]) const -{ - if (fCorrectionType == kRegularInterpolator) { - GetCorrectionCylAC(x, roc, dx); - } else { - GetCorrectionCylACIrregular(x, roc, dx); - } -} -/// -/// \param x -/// \param roc -/// \param dx -void AliTPCSpaceCharge3DCalc::GetCorrection(const Float_t x[], Short_t roc, Float_t dx[]) const -{ - Float_t pCyl[3]; // a point in cylindrical coordinate - Float_t dCyl[3]; // distortion - - pCyl[0] = TMath::Sqrt(x[0] * x[0] + x[1] * x[1]); - pCyl[1] = TMath::ATan2(x[1], x[0]); - pCyl[2] = x[2]; // Create temporary copy of x[2] - - if (fCorrectionType == kRegularInterpolator) { - while (pCyl[1] > TMath::Pi()) { - pCyl[1] -= TMath::TwoPi(); - } - while (pCyl[1] < -TMath::Pi()) { - pCyl[1] += TMath::TwoPi(); - } - - GetCorrectionCylAC(pCyl, roc, dCyl); - } else { - GetCorrectionCylACIrregular(pCyl, roc, dCyl); - } - - // Calculate distorted position - if (pCyl[0] > 0.0) { - //pCyl[0] = pCyl[0] + fCorrectionFactor * dCyl[0]; - pCyl[1] = pCyl[1] + fCorrectionFactor * dCyl[1] / pCyl[0]; - pCyl[0] = pCyl[0] + fCorrectionFactor * dCyl[0]; - } - - dCyl[2] = fCorrectionFactor * dCyl[2]; - - // distortion in x,y and z - dx[0] = (pCyl[0] * TMath::Cos(pCyl[1]) - x[0]); - dx[1] = (pCyl[0] * TMath::Sin(pCyl[1]) - x[1]); - dx[2] = dCyl[2]; -} - -/// -/// \param x -/// \param roc -/// \param dx -void AliTPCSpaceCharge3DCalc::GetCorrection(const Float_t x[], Short_t roc, Float_t dx[], const Int_t side) const -{ - Float_t pCyl[3]; // a point in cylindrical coordinate - Float_t dCyl[3]; // distortion - - pCyl[0] = TMath::Sqrt(x[0] * x[0] + x[1] * x[1]); - pCyl[1] = TMath::ATan2(x[1], x[0]); - pCyl[2] = x[2]; // Create temporary copy of x[2] - - if (fCorrectionType == kRegularInterpolator) { - while (pCyl[1] > TMath::Pi()) { - pCyl[1] -= TMath::TwoPi(); - } - while (pCyl[1] < -TMath::Pi()) { - pCyl[1] += TMath::TwoPi(); - } - - GetCorrectionCylAC(pCyl, roc, dCyl); - } else { - GetCorrectionCylACIrregular(pCyl, roc, dCyl, side); - } - - // Calculate distorted position - if (pCyl[0] > 0.0) { - pCyl[0] = pCyl[0] + fCorrectionFactor * dCyl[0]; - pCyl[1] = pCyl[1] + fCorrectionFactor * dCyl[1] / pCyl[0]; - } - - dCyl[2] = fCorrectionFactor * dCyl[2]; - - // distortion in x,y and z - dx[0] = (pCyl[0] * TMath::Cos(pCyl[1]) - x[0]); - dx[1] = (pCyl[0] * TMath::Sin(pCyl[1]) - x[1]); - dx[2] = dCyl[2]; -} - -/// Use 3D space charge map as an optional input -/// The layout of the input histogram is assumed to be: (phi,r,z) -/// Density histogram is expected to bin in C/m^3 -/// -/// Standard histogram interpolation is used in order to use the density at center of bin -/// -/// \param hisSpaceCharge3D -/// \param norm -void AliTPCSpaceCharge3DCalc::SetInputSpaceCharge(TH3* hisSpaceCharge3D, Double_t norm) -{ - fHistogram3DSpaceCharge = hisSpaceCharge3D; - fInitLookUp = kFALSE; - - Info("AliTPCSpaceCharge3DCalc:SetInputSpaceCharge", "Set Input Space Charge by 3D"); - - Double_t radius0, z0, phi0; - TMatrixD* charge; - for (Int_t iSide = 0; iSide < 2; iSide++) { - for (Int_t k = 0; k < fNPhiSlices; k++) { - if (iSide == 0) { - charge = fMatrixChargeA[k]; - } else { - charge = fMatrixChargeC[k]; - } - - phi0 = fListPhi[k]; - - for (Int_t i = 0; i < fNRRows; i++) { - radius0 = fListR[i]; - - for (Int_t j = 0; j < fNZColumns; j++) { - if (iSide == 0) { - z0 = fListZ[j]; - } else { - z0 = -fListZ[j]; - } - - (*charge)(i, j) = norm * InterpolatePhi(hisSpaceCharge3D, phi0, radius0, z0); - - } // end j - } // end i - } // end phi - } - - fInterpolatorChargeA->SetValue(fMatrixChargeA); - if (fInterpolationOrder > 2) { - fInterpolatorChargeA->InitCubicSpline(); - } - fInterpolatorChargeC->SetValue(fMatrixChargeC); - if (fInterpolationOrder > 2) { - fInterpolatorChargeC->InitCubicSpline(); - } -} - -/// SetInputCharge -/// -/// \param hisSpaceCharge3D TH3* histogram for space charge -/// \param norm Double_t norm/weight -/// \param side Int_t side = 0 => side A, side = 1 => side C -/// -/// side effects: create Charge interpolator -void AliTPCSpaceCharge3DCalc::SetInputSpaceCharge(TH3* hisSpaceCharge3D, Double_t norm, Int_t side) -{ - if (side == 0) { - fHistogram3DSpaceChargeA = hisSpaceCharge3D; - } else { - fHistogram3DSpaceChargeC = hisSpaceCharge3D; - } - - Double_t rMin = hisSpaceCharge3D->GetYaxis()->GetBinCenter(0); - Double_t rMax = hisSpaceCharge3D->GetYaxis()->GetBinUpEdge(hisSpaceCharge3D->GetYaxis()->GetNbins()); - Double_t zMin = hisSpaceCharge3D->GetZaxis()->GetBinCenter(0); - Double_t zMax = hisSpaceCharge3D->GetZaxis()->GetBinCenter(hisSpaceCharge3D->GetZaxis()->GetNbins()); - Double_t radius0, z0, phi0; - TMatrixD* charge; - - for (Int_t k = 0; k < fNPhiSlices; k++) { - if (side == 0) { - charge = fMatrixChargeA[k]; - } else { - charge = fMatrixChargeC[k]; - } - - phi0 = fListPhi[k]; - for (Int_t i = 0; i < fNRRows; i++) { - radius0 = fListR[i]; - for (Int_t j = 0; j < fNZColumns; j++) { - z0 = fListZ[j]; - - if (radius0 > rMin && radius0 < rMax && z0 > zMin && z0 < zMax) { - (*charge)(i, j) = norm * InterpolatePhi(hisSpaceCharge3D, phi0, radius0, z0); - } - } // end j - } // end i - } // end phi - - if (side == 0) { - fInterpolatorChargeA->SetValue(fMatrixChargeA); - fInterpolatorChargeA->InitCubicSpline(); - } else { - fInterpolatorChargeC->SetValue(fMatrixChargeC); - fInterpolatorChargeC->InitCubicSpline(); - } - - fInitLookUp = kFALSE; -} - -/// InterpolationPhi is only used for reading from TH3F (since it is not cylindrical) -/// -/// \param r -/// \param z -/// \return -Double_t AliTPCSpaceCharge3DCalc::InterpolatePhi(const TH3* h3, const Double_t phi, const Double_t r, const Double_t z) -{ - - Int_t ubx = h3->GetXaxis()->FindBin(phi); - if (phi < h3->GetXaxis()->GetBinCenter(ubx)) { - ubx -= 1; - } - Int_t obx = ubx + 1; - Int_t uby = h3->GetYaxis()->FindBin(r); - if (r < h3->GetYaxis()->GetBinCenter(uby)) { - uby -= 1; - } - Int_t oby = uby + 1; - Int_t ubz = h3->GetZaxis()->FindBin(z); - if (z < h3->GetZaxis()->GetBinCenter(ubz)) { - ubz -= 1; - } - Int_t obz = ubz + 1; - - if (uby <= 0 || ubz <= 0 || - oby > h3->GetYaxis()->GetNbins() || obz > h3->GetZaxis()->GetNbins()) { - return 0; - } - - if (ubx <= 0) { - ubx = h3->GetXaxis()->GetNbins(); - } - - if (obx > h3->GetXaxis()->GetNbins()) { - obx = 1; - } - - Double_t xw = h3->GetXaxis()->GetBinCenter(obx) - h3->GetXaxis()->GetBinCenter(ubx); - Double_t yw = h3->GetYaxis()->GetBinCenter(oby) - h3->GetYaxis()->GetBinCenter(uby); - Double_t zw = h3->GetZaxis()->GetBinCenter(obz) - h3->GetZaxis()->GetBinCenter(ubz); - Double_t xd = (phi - h3->GetXaxis()->GetBinCenter(ubx)) / xw; - Double_t yd = (r - h3->GetYaxis()->GetBinCenter(uby)) / yw; - Double_t zd = (z - h3->GetZaxis()->GetBinCenter(ubz)) / zw; - Double_t v[] = {h3->GetBinContent(ubx, uby, ubz), h3->GetBinContent(ubx, uby, obz), - h3->GetBinContent(ubx, oby, ubz), h3->GetBinContent(ubx, oby, obz), - h3->GetBinContent(obx, uby, ubz), h3->GetBinContent(obx, uby, obz), - h3->GetBinContent(obx, oby, ubz), h3->GetBinContent(obx, oby, obz)}; - Double_t i1 = v[0] * (1 - zd) + v[1] * zd; - Double_t i2 = v[2] * (1 - zd) + v[3] * zd; - Double_t j1 = v[4] * (1 - zd) + v[5] * zd; - Double_t j2 = v[6] * (1 - zd) + v[7] * zd; - Double_t w1 = i1 * (1 - yd) + i2 * yd; - Double_t w2 = j1 * (1 - yd) + j2 * yd; - Double_t result = w1 * (1 - xd) + w2 * xd; - return result; -} -/// returns the (input) space charge density at a given point according -/// Note: input in [cm], output in [C/m^3/e0] !! -Float_t AliTPCSpaceCharge3DCalc::GetSpaceChargeDensity(Float_t r, Float_t phi, Float_t z) -{ - while (phi < 0) { - phi += TMath::TwoPi(); - } - while (phi > TMath::TwoPi()) { - phi -= TMath::TwoPi(); - } - - const Int_t order = 1; // - - const Float_t x[] = {r, phi, z}; - Float_t sc = 0; - if (z > -1e-16) { - sc = GetChargeCylAC(x, 0); - } else { - sc = GetChargeCylAC(x, 18); - } - - return sc; -} -/// returns the (input) space charge density at a given point according -/// Note: input in [cm], output in [C/m^3/e0] !! -Float_t AliTPCSpaceCharge3DCalc::GetPotential(Float_t r, Float_t phi, Float_t z) -{ - while (phi < 0) { - phi += TMath::TwoPi(); - } - while (phi > TMath::TwoPi()) { - phi -= TMath::TwoPi(); - } - - const Int_t order = 1; // - - const Float_t x[] = {r, phi, z}; - Float_t v = 0; - if (z > -1e-16) { - v = GetPotentialCylAC(x, 0); - } else { - v = GetPotentialCylAC(x, 18); - } - - return v; -} -/// -/// -/// \param matricesDistDrDz TMatrixD ** matrix of global distortion drDist (r direction) -/// \param matricesDistDPhiRDz TMatrixD ** matrix of global distortion dRPhi (phi r direction) -/// \param matricesDistDz TMatrixD ** matrix of global distortion dzDist (z direction) -/// \param rList Double_t * points of r in the grid (ascending mode) -/// \param zList Double_t * points of z in the grid (ascending mode) -/// \param phiList Double_t * points of phi in the grid (ascending mode) -/// \param nRRow Int_t number of grid in r direction -/// \param nZColumn Int_t number of grid in z direction -/// \param phiSlice Int_t number of grid in phi direction -/// \param nStep Int_t number of step to calculate local dist -/// -void AliTPCSpaceCharge3DCalc::InverseGlobalToLocalDistortionGlobalInvTable( - TMatrixD** matricesDistDrDz, TMatrixD** matricesDistDPhiRDz, TMatrixD** matricesDistDz, Double_t* rList, - Double_t* zList, Double_t* phiList, const Int_t nRRow, const Int_t nZColumn, const Int_t phiSlice, - const Int_t nStep, const Bool_t useCylAC, Int_t stepR, Int_t stepZ, Int_t stepPhi, Int_t type) -{ - Double_t z, phi, r, zAfter, zPrevious, ddR, ddRPhi, ddZ, zl, drDist, dRPhi, dzDist, ddPhi, dPhi, deltaZ, r0, z0, phi0; - Float_t x[3], dx[3], pdx[3]; - Int_t roc; - const Float_t gridSizeR = (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (nRRow - 1); - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (nZColumn - 1); - const Float_t gridSizePhi = TMath::TwoPi() / phiSlice; - TMatrixD* distDrDz; - TMatrixD* distDPhiRDz; - TMatrixD* distDz; - - // correction build up for inverse flow - TMatrixD* corrDrDz; - TMatrixD* corrDPhiRDz; - TMatrixD* corrDz; - TMatrixD* listR; - TMatrixD* listPhi; - TMatrixD* listZ; - TMatrixD* matricesCorrDrDz[phiSlice]; - TMatrixD* matricesCorrDPhiRDz[phiSlice]; - TMatrixD* matricesCorrDz[phiSlice]; - TMatrixD* matricesRList[phiSlice]; - TMatrixD* matricesPhiList[phiSlice]; - TMatrixD* matricesZList[phiSlice]; - - for (Int_t m = 0; m < phiSlice; m++) { - matricesCorrDrDz[m] = new TMatrixD(nRRow, nZColumn); - matricesCorrDPhiRDz[m] = new TMatrixD(nRRow, nZColumn); - matricesCorrDz[m] = new TMatrixD(nRRow, nZColumn); - - matricesRList[m] = new TMatrixD(nRRow, nZColumn); - matricesPhiList[m] = new TMatrixD(nRRow, nZColumn); - matricesZList[m] = new TMatrixD(nRRow, nZColumn); - } - - AliTPCLookUpTable3DInterpolatorIrregularD* lookupInverseCorr = new AliTPCLookUpTable3DInterpolatorIrregularD( - nRRow, matricesCorrDrDz, matricesRList, phiSlice, matricesCorrDPhiRDz, - matricesPhiList, nZColumn, matricesCorrDz, matricesZList, 2, - stepR, stepZ, stepPhi, type); - - lookupInverseCorr->SetKernelType(GetRBFKernelType()); - - for (Int_t k = 0; k < phiSlice; k++) { - distDrDz = matricesDistDrDz[k]; - distDPhiRDz = matricesDistDPhiRDz[k]; - distDz = matricesDistDz[k]; - - listR = matricesRList[k]; - listPhi = matricesPhiList[k]; - listZ = matricesZList[k]; - - for (Int_t i = 0; i < nRRow; i++) { - (*distDrDz)(i, nZColumn - 1) = 0.0; - (*distDPhiRDz)(i, nZColumn - 1) = 0.0; - (*distDz)(i, nZColumn - 1) = 0.0; - - for (Int_t j = 0; j < nZColumn; j++) { - (*listR)(i, j) = rList[i]; - (*listPhi)(i, j) = phiList[k]; - (*listZ)(i, j) = zList[j]; - } - } - } - - // 1) create global correction - deltaZ = (zList[1] - zList[0]); - Int_t iAnchor, kAnchor, zAnchor; - - for (Int_t j = nZColumn - 2; j >= 0; j--) { - - roc = 0; // FIXME - for (Int_t k = 0; k < phiSlice; k++) { - - corrDrDz = matricesCorrDrDz[k]; - corrDPhiRDz = matricesCorrDPhiRDz[k]; - corrDz = matricesCorrDz[k]; - - listR = matricesRList[k]; - listPhi = matricesPhiList[k]; - listZ = matricesZList[k]; - - for (Int_t i = 0; i < nRRow; i++) { - // get global distortion - - r = rList[i]; - phi = phiList[k]; - z = zList[j]; - - drDist = 0.0; - dzDist = 0.0; - dRPhi = 0.0; - - x[0] = r; - x[1] = phi; - x[2] = z; - - if (useCylAC == kTRUE) { - GetDistortionCylAC(x, roc, dx); - } else { - GetDistortionCyl(x, roc, dx); - } - - drDist = dx[0]; - dzDist = dx[2]; - dRPhi = dx[1]; - - r = rList[i]; - phi = phiList[k]; - z = zList[j]; - - (*corrDrDz)(i, j + 1) = -drDist; - (*corrDz)(i, j + 1) = -dzDist; - (*corrDPhiRDz)(i, j + 1) = -dRPhi; - - (*listR)(i, j + 1) = r + drDist; - (*listPhi)(i, j + 1) = phi + dRPhi / r; - (*listZ)(i, j + 1) = z + dzDist; - } - } - lookupInverseCorr->CopyFromMatricesToInterpolator(j + 1); - } - // 2) calculate local distortion - for (Int_t j = nZColumn - 2; j >= 0; j--) { - roc = 0; // FIXME - for (Int_t k = 0; k < phiSlice; k++) { - distDrDz = matricesDistDrDz[k]; - distDPhiRDz = matricesDistDPhiRDz[k]; - distDz = matricesDistDz[k]; - for (Int_t i = 0; i < nRRow; i++) { - // get global distortion - r = rList[i]; - phi = phiList[k]; - z = zList[j]; - drDist = 0.0; - dzDist = 0.0; - dRPhi = 0.0; - - if (j < nZColumn - 2) { - // get global distortion of this point - x[0] = r; - x[1] = phi; - x[2] = z; - if (useCylAC == kTRUE) { - GetDistortionCylAC(x, roc, dx); - } else { - GetDistortionCyl(x, roc, dx); - } - - r0 = r + dx[0]; - z0 = zList[j + 1] + dx[2]; - phi0 = phi + (dx[1] / r); - iAnchor = TMath::FloorNint((r0 - AliTPCPoissonSolver::fgkIFCRadius) / gridSizeR); - kAnchor = TMath::FloorNint(phi0 / gridSizePhi); - zAnchor = TMath::FloorNint(z0 / gridSizeZ); - - if (j > nZColumn - (GetIrregularGridSize() + 2)) { - lookupInverseCorr->GetValue(r0, phi0, z0, drDist, dRPhi, dzDist, iAnchor, kAnchor, zAnchor, - nRRow / 4 + 1, phiSlice / 4 + 1, 1, 0); - } else { - lookupInverseCorr->GetValue(r0, phi0, z0, drDist, dRPhi, dzDist, iAnchor, kAnchor, zAnchor, - nRRow / 4 + 1, phiSlice / 4 + 1, GetIrregularGridSize(), 0); - } - - phi0 = phi0 + ((dRPhi) / r0); - r0 = r0 + (drDist); - z0 += dzDist; - - x[0] = r0; - x[1] = phi0; - x[2] = z0; - - if (phi0 < 0) { - phi0 = TMath::TwoPi() + phi0; - } - if (phi0 > TMath::TwoPi()) { - phi0 = phi0 - TMath::TwoPi(); - } - - if (useCylAC == kTRUE) { - GetDistortionCylAC(x, roc, pdx); - } else { - GetDistortionCyl(x, roc, pdx); - } - - drDist = (dx[0] - pdx[0]); - dzDist = (dx[2] - pdx[2]); - dRPhi = (dx[1] - pdx[1]); - - } else if (j == (nZColumn - 2)) { - - x[0] = r; - x[1] = phi; - x[2] = zList[j]; - if (useCylAC == kTRUE) { - GetDistortionCylAC(x, roc, dx); - } else { - GetDistortionCyl(x, roc, dx); - } - - x[2] = zList[j + 1]; - if (useCylAC == kTRUE) { - GetDistortionCylAC(x, roc, pdx); - } else { - GetDistortionCyl(x, roc, pdx); - } - drDist = (dx[0] - pdx[0]); - dzDist = (dx[2] - pdx[2]); - dRPhi = (dx[1] - pdx[1]); - } - - (*distDrDz)(i, j) = drDist; - (*distDz)(i, j) = dzDist; - (*distDPhiRDz)(i, j) = dRPhi; - } - } - } - - for (Int_t m = 0; m < phiSlice; m++) { - delete matricesCorrDrDz[m]; - delete matricesCorrDPhiRDz[m]; - delete matricesCorrDz[m]; - delete matricesRList[m]; - delete matricesPhiList[m]; - delete matricesZList[m]; - } - delete lookupInverseCorr; -} -/// -/// \param matricesEr -/// \param matricesEPhi -/// \param matricesEz -/// \param matricesInvLocalIntErDz -/// \param matricesInvLocalIntEPhiDz -/// \param matricesInvLocalIntEz -/// \param matricesDistDrDz -/// \param matricesDistDPhiRDz -/// \param matricesDistDz -/// \param rList -/// \param zList -/// \param phiList -/// \param nRRow -/// \param nZColumn -/// \param phiSlice -void AliTPCSpaceCharge3DCalc::InverseLocalDistortionToElectricField( - TMatrixD** matricesEr, TMatrixD** matricesEPhi, TMatrixD** matricesEz, - TMatrixD** matricesInvLocalIntErDz, TMatrixD** matricesInvLocalIntEPhiDz, - TMatrixD** matricesInvLocalIntEz, TMatrixD** matricesDistDrDz, TMatrixD** matricesDistDPhiRDz, - TMatrixD** matricesDistDz, Double_t* rList, Double_t* zList, Double_t* phiList, - const Int_t nRRow, const Int_t nZColumn, const Int_t phiSlice) -{ - // calculate integral - Float_t localIntErOverEz, localIntEPhiOverEz, localIntDeltaEz, z2; - Double_t r; - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (nZColumn - 1); - const Double_t ezField = (AliTPCPoissonSolver::fgkCathodeV - AliTPCPoissonSolver::fgkGG) / AliTPCPoissonSolver::fgkTPCZ0; // = ALICE Electric Field (V/cm) Magnitude ~ -400 V/cm; - - TMatrixD* distDrDz; - TMatrixD* distDz; - TMatrixD* distDPhiRDz; - TMatrixD* tDistDz; - TMatrixD* tDistDPhiRDz; - TMatrixD* tDistDrDz; - Float_t c02c12 = fC0 * fC0 + fC1 * fC1; - - // solve local integration - for (Int_t j = 0; j < nZColumn; j++) { - for (Int_t k = 0; k < phiSlice; k++) { - distDrDz = matricesDistDrDz[k]; - distDz = matricesDistDz[k]; - distDPhiRDz = matricesDistDPhiRDz[k]; - - tDistDrDz = matricesInvLocalIntErDz[k]; - tDistDz = matricesInvLocalIntEz[k]; - tDistDPhiRDz = matricesInvLocalIntEPhiDz[k]; - for (Int_t i = 0; i < nRRow; i++) { - localIntErOverEz = fC0 * (*distDrDz)(i, j) - fC1 * (*distDPhiRDz)(i, j); - localIntErOverEz = localIntErOverEz / (fC0 * fC0 + fC1 * fC1); - localIntEPhiOverEz = ((*distDrDz)(i, j) - (fC0 * localIntErOverEz)) / fC1; - localIntDeltaEz = -1 * (*distDz)(i, j) / AliTPCPoissonSolver::fgkdvdE; // two times? - (*tDistDrDz)(i, j) = localIntErOverEz; - (*tDistDPhiRDz)(i, j) = localIntEPhiOverEz; - (*tDistDz)(i, j) = localIntDeltaEz; - } - } - } - TMatrixD* mEPhi; - TMatrixD* mEr; - TMatrixD* mEz; - - // use central-backward-forward difference for calculating Electric field component - for (Int_t m = 0; m < phiSlice; m++) { - mEPhi = matricesEPhi[m]; - mEr = matricesEr[m]; - mEz = matricesEz[m]; - distDrDz = matricesInvLocalIntErDz[m]; - distDPhiRDz = matricesInvLocalIntEPhiDz[m]; - distDz = matricesInvLocalIntEz[m]; - for (Int_t i = 0; i < nRRow; i++) { - (*mEr)(i, 0) = ((*distDrDz)(i, 0) / gridSizeZ) * -1 * ezField; - (*mEPhi)(i, 0) = ((*distDPhiRDz)(i, 0) / gridSizeZ) * -1 * ezField; - (*mEz)(i, 0) = ((*distDz)(i, 0) / gridSizeZ); - (*mEr)(i, nZColumn - 1) = - ((-0.5 * (*distDrDz)(i, nZColumn - 3) + 1.5 * (*distDrDz)(i, nZColumn - 2)) / gridSizeZ) * ezField; - (*mEPhi)(i, nZColumn - 1) = - ((-0.5 * (*distDPhiRDz)(i, nZColumn - 3) + 1.5 * (*distDPhiRDz)(i, nZColumn - 2)) / gridSizeZ) * - ezField; - (*mEz)(i, nZColumn - 1) = - (-0.5 * (*distDz)(i, nZColumn - 3) + 1.5 * (*distDz)(i, nZColumn - 2)) / gridSizeZ; - } - - for (Int_t i = 0; i < nRRow; i++) { - for (Int_t j = 1; j < nZColumn - 1; j++) { - (*mEr)(i, j) = (((*distDrDz)(i, j) + (*distDrDz)(i, j - 1)) / (2 * gridSizeZ)) * - ezField; // z direction - (*mEPhi)(i, j) = (((*distDPhiRDz)(i, j) + (*distDPhiRDz)(i, j - 1)) / (2 * gridSizeZ)) * - ezField; // z direction - (*mEz)(i, j) = ((*distDz)(i, j) + (*distDz)(i, j - 1)) / (2 * gridSizeZ); // z direction - } - } - } -} -/// Inverse Electric Field to Charge -/// using partial differential -/// -/// \param matricesCharge -/// \param matricesEr -/// \param matricesEPhi -/// \param matricesEz -/// \param rList -/// \param zList -/// \param phiList -/// \param nRRow -/// \param nZColumn -/// \param phiSlice -void AliTPCSpaceCharge3DCalc::InverseElectricFieldToCharge( - TMatrixD** matricesCharge, TMatrixD** matricesEr, TMatrixD** matricesEPhi, TMatrixD** matricesEz, - Double_t* rList, Double_t* zList, Double_t* phiList, const Int_t nRRow, - const Int_t nZColumn, const Int_t phiSlice) -{ - - Float_t radius; - Double_t drDist, dzDist, dPhi; - Int_t mPlus, mMinus, mPlus2, mMinus2, signPlus, signMinus; - Int_t symmetry = 0; - const Float_t gridSizeR = (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (nRRow - 1); - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (nZColumn - 1); - const Float_t gridSizePhi = TMath::TwoPi() / phiSlice; - - for (Int_t m = 0; m < phiSlice; m++) { - mPlus = m + 1; - signPlus = 1; - mMinus = m - 1; - signMinus = 1; - mPlus2 = m + 2; - mMinus2 = m - 2; - if (symmetry == 1) { // Reflection symmetry in phi (e.g. symmetry at sector boundaries, or half sectors, etc.) - if (mPlus > phiSlice - 1) { - mPlus = phiSlice - 2; - } - if (mMinus < 0) { - mMinus = 1; - } - } else if (symmetry == -1) { // Anti-symmetry in phi - if (mPlus > phiSlice - 1) { - mPlus = phiSlice - 2; - signPlus = -1; - } - if (mMinus < 0) { - mMinus = 1; - signMinus = -1; - } - } else { // No Symmetries in phi, no boundaries, the calculations is continuous across all phi - if (mPlus > phiSlice - 1) { - mPlus = m + 1 - phiSlice; - } - if (mMinus < 0) { - mMinus = m - 1 + phiSlice; - } - if (mPlus2 > phiSlice - 1) { - mPlus2 = m + 2 - phiSlice; - } - if (mMinus2 < 0) { - mMinus2 = m - 2 + phiSlice; - } - } - - TMatrixD& matrixCharge = *matricesCharge[m]; - TMatrixD& matrixEr = *matricesEr[m]; - TMatrixD& matrixEz = *matricesEz[m]; - TMatrixD& matrixEPhi = *matricesEPhi[m]; - TMatrixD& matrixEPhiM = *matricesEPhi[mMinus]; - TMatrixD& matrixEPhiP = *matricesEPhi[mPlus]; - TMatrixD& matrixEPhiM2 = *matricesEPhi[mMinus2]; - TMatrixD& matrixEPhiP2 = *matricesEPhi[mPlus2]; - - // for non-boundary V - for (Int_t i = 2; i < nRRow - 2; i++) { - radius = AliTPCPoissonSolver::fgkIFCRadius + i * gridSizeR; - for (Int_t j = 2; j < nZColumn - 2; j++) { - drDist = (-matrixEr(i + 2, j) + 8 * matrixEr(i + 1, j) - 8 * matrixEr(i - 1, j) + matrixEr(i - 2, j)) / - (12 * gridSizeR); // r direction - dzDist = (-matrixEz(i, j + 2) + 8 * matrixEz(i, j + 1) - 8 * matrixEz(i, j - 1) + matrixEz(i, j - 2)) / - (12 * gridSizeZ); // r direction - dPhi = (-matrixEPhiP2(i, j) + 8 * matrixEPhiP(i, j) - 8 * matrixEPhiM(i, j) + matrixEPhiM2(i, j)) / - (12 * gridSizePhi); // phi - - matrixCharge(i, j) = -1 * (matrixEr(i, j) / radius + drDist + dPhi / radius + dzDist); - } - } - - // for boundary in r - for (Int_t j = 2; j < nZColumn - 2; j++) { - - // r near inner radius - // for index r[0] - radius = AliTPCPoissonSolver::fgkIFCRadius; - drDist = (-(11.0 / 6.0) * matrixEr(0, j) + (3.0 * matrixEr(1, j)) - (1.5 * matrixEr(2, j)) + - ((1.0 / 3.0) * matrixEr(3, j))) / - gridSizeR; // forward difference - - // drDist = ( -(1.5)*matrixEr(0,j) + (2.0*matrixEr(1,j)) - (0.5*matrixEr(2,j)) ) / gridSizeR; - - dzDist = (-matrixEz(0, j + 2) + 8 * matrixEz(0, j + 1) - 8 * matrixEz(0, j - 1) + matrixEz(0, j - 2)) / - (12.0 * gridSizeZ); // z direction - dPhi = (-matrixEPhiP2(0, j) + 8 * matrixEPhiP(0, j) - 8 * matrixEPhiM(0, j) + matrixEPhiM2(0, j)) / - (12.0 * gridSizePhi); - - matrixCharge(0, j) = -1 * (matrixEr(0, j) / radius + drDist + dPhi / radius + dzDist); - - // index use central difference 3-point center - radius = AliTPCPoissonSolver::fgkIFCRadius + gridSizeR; - // drDist = (-matrixEr(3,j) +6.0*matrixEr(2,j) - 3.0*matrixEr(1,j) - 2*matrixEr(0,j) ) / (6.0*gridSizeR) ; // forward difference - drDist = (matrixEr(2, j) - matrixEr(0, j)) / (2.0 * gridSizeR); - - dzDist = (-matrixEz(1, j + 2) + 8 * matrixEz(1, j + 1) - 8 * matrixEz(1, j - 1) + matrixEz(1, j - 2)) / - (12 * gridSizeZ); // z direction - dPhi = (-matrixEPhiP2(1, j) + 8 * matrixEPhiP(1, j) - 8 * matrixEPhiM(1, j) + matrixEPhiM2(1, j)) / - (12 * gridSizePhi); - matrixCharge(1, j) = -1 * (matrixEr(1, j) / radius + drDist + dPhi / radius + dzDist); - - // index use central difference 3-point center - radius = AliTPCPoissonSolver::fgkIFCRadius + (nRRow - 2) * gridSizeR; - // drDist = (2.0 * matrixEr(nRRow - 1,j) + 3.0*matrixEr(nRRow - 2,j) - 6.0*matrixEr(nRRow -3,j) + matrixEr(nRRow-4,j) ) / (6.0*gridSizeR) ; - drDist = (matrixEr(nRRow - 1, j) - matrixEr(nRRow - 3, j)) / (2.0 * gridSizeR); - - dzDist = (-matrixEz(nRRow - 2, j + 2) + 8 * matrixEz(nRRow - 2, j + 1) - 8 * matrixEz(nRRow - 2, j - 1) + - matrixEz(nRRow - 2, j - 2)) / - (12 * gridSizeZ); - dPhi = (-matrixEPhiP2(nRRow - 2, j) + 8 * matrixEPhiP(nRRow - 2, j) - 8 * matrixEPhiM(nRRow - 2, j) + - matrixEPhiM2(nRRow - 2, j)) / - (12.0 * gridSizePhi); - matrixCharge(nRRow - 2, j) = -1 * (matrixEr(nRRow - 2, j) / radius + drDist + dPhi / radius + dzDist); - - // index r[nRRow -1] backward difference - radius = AliTPCPoissonSolver::fgkIFCRadius + (nRRow - 1) * gridSizeR; - //drDist = ( 1.5*matrixEr(nRRow-1,j) - 2.0*matrixEr(nRRow-2,j) + 0.5*matrixEr(nRRow-3,j) ) / gridSizeR ; // backward difference - drDist = - (-(11.0 / 6.0) * matrixEr(nRRow - 1, j) + (3.0 * matrixEr(nRRow - 2, j)) - - (1.5 * matrixEr(nRRow - 3, j)) + - ((1.0 / 3.0) * matrixEr(nRRow - 4, j))) / - (-1 * gridSizeR); - - //dzDist = ( matrixEz(nRRow-1,j+1) - matrixEz(nRRow-1,j-1) ) / (2*gridSizeZ) ; // z direction - dzDist = (-matrixEz(nRRow - 1, j + 2) + 8 * matrixEz(nRRow - 1, j + 1) - 8 * matrixEz(nRRow - 1, j - 1) + - matrixEz(nRRow - 1, j - 2)) / - (12 * gridSizeZ); - - dPhi = (-matrixEPhiP2(nRRow - 1, j) + 8 * matrixEPhiP(nRRow - 1, j) - 8 * matrixEPhiM(nRRow - 1, j) + - matrixEPhiM2(nRRow - 1, j)) / - (12 * gridSizePhi); - matrixCharge(nRRow - 1, j) = -1 * (matrixEr(nRRow - 1, j) / radius + drDist + dPhi / radius + dzDist); - } - - // boundary z - for (Int_t i = 2; i < nRRow - 2; i++) { - // z[0] - radius = AliTPCPoissonSolver::fgkIFCRadius + i * gridSizeR; - dzDist = (-(11.0 / 6.0) * matrixEz(i, 0) + (3.0 * matrixEz(i, 1)) - (1.5 * matrixEz(i, 2)) + - ((1.0 / 3.0) * matrixEz(i, 3))) / - (1 * gridSizeZ); // forward difference - drDist = (-matrixEr(i + 2, 0) + 8 * matrixEr(i + 1, 0) - 8 * matrixEr(i - 1, 0) + matrixEr(i - 2, 0)) / - (12 * gridSizeR); // z direction - dPhi = (-matrixEPhiP2(i, 0) + 8 * matrixEPhiP(i, 0) - 8 * matrixEPhiM(i, 0) + matrixEPhiM2(i, 0)) / - (12 * gridSizePhi); - matrixCharge(i, 0) = -1 * (matrixEr(i, 0) / radius + drDist + dPhi / radius + dzDist); - - dzDist = (matrixEz(i, 2) - matrixEz(i, 0)) / (2.0 * gridSizeZ); // forward difference - - drDist = (-matrixEr(i + 2, 1) + 8 * matrixEr(i + 1, 1) - 8 * matrixEr(i - 1, 1) + matrixEr(i - 2, 1)) / - (12 * gridSizeR); // z direction - dPhi = (-matrixEPhiP2(i, 1) + 8 * matrixEPhiP(i, 1) - 8 * matrixEPhiM(i, 1) + matrixEPhiM2(i, 1)) / - (12 * gridSizePhi); - matrixCharge(i, 1) = -1 * (matrixEr(i, 1) / radius + drDist + dPhi / radius + dzDist); - - dzDist = (matrixEz(i, nZColumn - 1) - matrixEz(i, nZColumn - 3)) / (2.0 * gridSizeZ); // forward difference - - drDist = (-matrixEr(i + 2, nZColumn - 2) + 8 * matrixEr(i + 1, nZColumn - 2) - - 8 * matrixEr(i - 1, nZColumn - 2) + - matrixEr(i - 2, nZColumn - 2)) / - (12 * gridSizeR); // z direction - dPhi = (-matrixEPhiP2(i, nZColumn - 2) + 8 * matrixEPhiP(i, nZColumn - 2) - - 8 * matrixEPhiM(i, nZColumn - 2) + - matrixEPhiM2(i, nZColumn - 2)) / - (12 * gridSizePhi); - matrixCharge(i, nZColumn - 2) = -1 * (matrixEr(i, nZColumn - 2) / radius + drDist + dPhi / radius + dzDist); - - dzDist = (-(11.0 / 6.0) * matrixEz(i, nZColumn - 1) + (3.0 * matrixEz(i, nZColumn - 2)) - - (1.5 * matrixEz(i, nZColumn - 3)) + ((1.0 / 3.0) * matrixEz(i, nZColumn - 4))) / - (-gridSizeZ); // backward difference - drDist = (-matrixEr(i + 2, nZColumn - 1) + 8 * matrixEr(i + 1, nZColumn - 1) - - 8 * matrixEr(i - 1, nZColumn - 1) + - matrixEr(i - 2, nZColumn - 1)) / - (12 * gridSizeR); // z direction - dPhi = (-matrixEPhiP2(i, nZColumn - 1) + 8 * matrixEPhiP(i, nZColumn - 1) - - 8 * matrixEPhiM(i, nZColumn - 1) + - matrixEPhiM2(i, nZColumn - 1)) / - (12 * gridSizePhi); - matrixCharge(i, nZColumn - 1) = -1 * (matrixEr(i, nZColumn - 1) / radius + drDist + dPhi / radius + dzDist); - } - // for corner points - // corner points for EPhi - radius = AliTPCPoissonSolver::fgkIFCRadius; - drDist = - (-0.5 * matrixEr(2, 0) + 2.0 * matrixEr(1, 0) - 1.5 * matrixEr(0, 0)) / gridSizeR; // forward difference - dzDist = - (-0.5 * matrixEz(0, 2) + 2.0 * matrixEz(0, 1) - 1.5 * matrixEz(0, 0)) / gridSizeZ; // forward difference - dPhi = (-matrixEPhiP2(0, 0) + 8 * matrixEPhiP(0, 0) - 8 * matrixEPhiM(0, 0) + matrixEPhiM2(0, 0)) / - (12 * gridSizePhi); - matrixCharge(0, 0) = -1 * (matrixEr(0, 0) / radius + drDist + dPhi / radius + dzDist); - drDist = - (-0.5 * matrixEr(2, 1) + 2.0 * matrixEr(1, 1) - 1.5 * matrixEr(0, 1)) / gridSizeR; // forward difference - dzDist = (matrixEz(0, 2) - matrixEz(0, 0)) / (2.0 * gridSizeZ); // forward difference - dPhi = (-matrixEPhiP2(0, 1) + 8 * matrixEPhiP(0, 1) - 8 * matrixEPhiM(0, 1) + matrixEPhiM2(0, 1)) / - (12 * gridSizePhi); - matrixCharge(0, 1) = -1 * (matrixEr(0, 1) / radius + drDist + dPhi / radius + dzDist); - drDist = - (-0.5 * matrixEr(2, nZColumn - 2) + 2.0 * matrixEr(1, nZColumn - 2) - 1.5 * matrixEr(0, nZColumn - 2)) / - gridSizeR; // forward difference - dzDist = (2.0 * matrixEz(0, nZColumn - 1) + 3.0 * matrixEz(0, nZColumn - 2) - 6.0 * matrixEz(0, nZColumn - 3) + - matrixEz(0, nZColumn - 4)) / - (6.0 * gridSizeZ); // backward difference - dPhi = (-matrixEPhiP2(0, nZColumn - 2) + 8 * matrixEPhiP(0, nZColumn - 2) - 8 * matrixEPhiM(0, nZColumn - 2) + - matrixEPhiM2(0, nZColumn - 2)) / - (12 * gridSizePhi); - matrixCharge(0, nZColumn - 2) = -1 * (matrixEr(0, nZColumn - 2) / radius + drDist + dPhi / radius + dzDist); - drDist = - (-0.5 * matrixEr(2, nZColumn - 1) + 2.0 * matrixEr(1, nZColumn - 1) - 1.5 * matrixEr(0, nZColumn - 1)) / - gridSizeR; // forward difference - dzDist = (1.5 * matrixEz(0, nZColumn - 1) - 2.0 * matrixEz(0, nZColumn - 2) + 0.5 * matrixEz(0, nZColumn - 3)) / - gridSizeZ; // backward difference - dPhi = (-matrixEPhiP2(0, nZColumn - 1) + 8 * matrixEPhiP(0, nZColumn - 1) - 8 * matrixEPhiM(0, nZColumn - 1) + - matrixEPhiM2(0, nZColumn - 1)) / - (12 * gridSizePhi); - matrixCharge(0, nZColumn - 1) = -1 * (matrixEr(0, nZColumn - 1) / radius + drDist + dPhi / radius + dzDist); - - radius = AliTPCPoissonSolver::fgkIFCRadius + gridSizeR; - drDist = (-matrixEr(3, 0) + 6.0 * matrixEr(2, 0) - 3.0 * matrixEr(1, 0) - 2 * matrixEr(0, 0)) / - (6.0 * gridSizeR); // forward difference - dzDist = - (-0.5 * matrixEz(1, 2) + 2.0 * matrixEz(1, 1) - 1.5 * matrixEz(1, 0)) / gridSizeZ; // forward difference - dPhi = (-matrixEPhiP2(1, 0) + 8 * matrixEPhiP(1, 0) - 8 * matrixEPhiM(1, 0) + matrixEPhiM2(1, 0)) / - (12 * gridSizePhi); - matrixCharge(1, 0) = -1 * (matrixEr(1, 0) / radius + drDist + dPhi / radius + dzDist); - drDist = (-matrixEr(3, 1) + 6.0 * matrixEr(2, 1) - 3.0 * matrixEr(1, 1) - 2 * matrixEr(0, 1)) / - (6.0 * gridSizeR); // forward difference - dzDist = (-matrixEz(1, 3) + 6.0 * matrixEz(1, 2) - 3.0 * matrixEz(1, 1) - 2 * matrixEz(1, 0)) / - (6.0 * gridSizeZ); // forward difference - dPhi = (-matrixEPhiP2(1, 1) + 8 * matrixEPhiP(1, 1) - 8 * matrixEPhiM(1, 1) + matrixEPhiM2(1, 1)) / - (12 * gridSizePhi); - matrixCharge(1, 1) = -1 * (matrixEr(1, 1) / radius + drDist + dPhi / radius + dzDist); - drDist = (-matrixEr(3, nZColumn - 2) + 6.0 * matrixEr(2, nZColumn - 2) - 3.0 * matrixEr(1, nZColumn - 2) - - 2 * matrixEr(0, nZColumn - 2)) / - (6.0 * gridSizeR); // forward difference - dzDist = (2.0 * matrixEz(1, nZColumn - 1) + 3.0 * matrixEz(1, nZColumn - 2) - 6.0 * matrixEz(1, nZColumn - 3) + - matrixEz(1, nZColumn - 4)) / - (6.0 * gridSizeZ); // backward difference - dPhi = (-matrixEPhiP2(1, nZColumn - 2) + 8 * matrixEPhiP(1, nZColumn - 2) - 8 * matrixEPhiM(1, nZColumn - 2) + - matrixEPhiM2(1, nZColumn - 2)) / - (12 * gridSizePhi); - matrixCharge(1, nZColumn - 2) = -1 * (matrixEr(1, nZColumn - 2) / radius + drDist + dPhi / radius + dzDist); - - drDist = (-matrixEr(3, nZColumn - 1) + 6.0 * matrixEr(2, nZColumn - 1) - 3.0 * matrixEr(1, nZColumn - 1) - - 2 * matrixEr(0, nZColumn - 1)) / - (6.0 * gridSizeR); // forward difference - dzDist = (1.5 * matrixEz(1, nZColumn - 1) - 2.0 * matrixEz(1, nZColumn - 2) + 0.5 * matrixEz(1, nZColumn - 3)) / - gridSizeZ; // backward difference - dPhi = (-matrixEPhiP2(1, nZColumn - 1) + 8 * matrixEPhiP(1, nZColumn - 1) - 8 * matrixEPhiM(1, nZColumn - 1) + - matrixEPhiM2(1, nZColumn - 1)) / - (12 * gridSizePhi); - matrixCharge(1, nZColumn - 1) = -1 * (matrixEr(1, nZColumn - 1) / radius + drDist + dPhi / radius + dzDist); - - radius = AliTPCPoissonSolver::fgkIFCRadius + (nRRow - 2) * gridSizeR; - drDist = (2.0 * matrixEr(nRRow - 1, 0) + 3.0 * matrixEr(nRRow - 2, 0) - 6.0 * matrixEr(nRRow - 3, 0) + - matrixEr(nRRow - 4, 0)) / - (6.0 * gridSizeR); // backward difference - dzDist = (-0.5 * matrixEz(nRRow - 2, 2) + 2.0 * matrixEz(nRRow - 2, 1) - 1.5 * matrixEz(nRRow - 2, 0)) / - gridSizeZ; // forward difference - dPhi = (-matrixEPhiP2(nRRow - 2, 0) + 8 * matrixEPhiP(nRRow - 2, 0) - 8 * matrixEPhiM(nRRow - 2, 0) + - matrixEPhiM2(nRRow - 2, 0)) / - (12 * gridSizePhi); - - matrixCharge(nRRow - 2, 0) = -1 * (matrixEr(nRRow - 2, 0) / radius + drDist + dPhi / radius + dzDist); - drDist = (2.0 * matrixEr(nRRow - 1, 1) + 3.0 * matrixEr(nRRow - 2, 1) - 6.0 * matrixEr(nRRow - 3, 1) + - matrixEr(nRRow - 4, 1)) / - (6.0 * gridSizeR); // backward difference - dzDist = (-matrixEz(nRRow - 2, 3) + 6.0 * matrixEz(nRRow - 2, 2) - 3.0 * matrixEz(nRRow - 2, 1) - - 2 * matrixEz(nRRow - 2, 0)) / - (6.0 * gridSizeZ); // forward difference - dPhi = (-matrixEPhiP2(nRRow - 2, 1) + 8 * matrixEPhiP(nRRow - 2, 1) - 8 * matrixEPhiM(nRRow - 2, 1) + - matrixEPhiM2(nRRow - 2, 1)) / - (12 * gridSizePhi); - matrixCharge(nRRow - 2, 1) = -1 * (matrixEr(nRRow - 2, 1) / radius + drDist + dPhi / radius + dzDist); - drDist = (2.0 * matrixEr(nRRow - 1, nZColumn - 2) + 3.0 * matrixEr(nRRow - 2, nZColumn - 2) - - 6.0 * matrixEr(nRRow - 3, nZColumn - 2) + matrixEr(nRRow - 4, nZColumn - 2)) / - (6.0 * gridSizeR); // backward difference - dzDist = (2.0 * matrixEz(nRRow - 2, nZColumn - 1) + 3.0 * matrixEz(nRRow - 2, nZColumn - 2) - - 6.0 * matrixEz(nRRow - 2, nZColumn - 3) + matrixEz(nRRow - 2, nZColumn - 4)) / - (6.0 * gridSizeZ); // backward difference - dPhi = (-matrixEPhiP2(nRRow - 2, nZColumn - 2) + 8 * matrixEPhiP(nRRow - 2, nZColumn - 2) - - 8 * matrixEPhiM(nRRow - 2, nZColumn - 2) + matrixEPhiM2(nRRow - 2, nZColumn - 2)) / - (12 * gridSizePhi); - matrixCharge(nRRow - 2, nZColumn - 2) = - -1 * (matrixEr(nRRow - 2, nZColumn - 2) / radius + drDist + dPhi / radius + dzDist); - drDist = (2.0 * matrixEr(nRRow - 1, nZColumn - 1) + 3.0 * matrixEr(nRRow - 2, nZColumn - 1) - - 6.0 * matrixEr(nRRow - 3, nZColumn - 1) + matrixEr(nRRow - 4, nZColumn - 1)) / - (6.0 * gridSizeR); // backward difference - dzDist = (1.5 * matrixEz(0, nZColumn - 1) - 2.0 * matrixEz(0, nZColumn - 2) + 0.5 * matrixEz(0, nZColumn - 3)) / - gridSizeZ; // backward difference - dPhi = (-matrixEPhiP2(nRRow - 2, nZColumn - 1) + 8 * matrixEPhiP(nRRow - 2, nZColumn - 1) - - 8 * matrixEPhiM(nRRow - 2, nZColumn - 1) + matrixEPhiM2(nRRow - 2, nZColumn - 1)) / - (12 * gridSizePhi); - - matrixCharge(nRRow - 2, nZColumn - 1) = - -1 * (matrixEr(nRRow - 2, nZColumn - 1) / radius + drDist + dPhi / radius + dzDist); - radius = AliTPCPoissonSolver::fgkIFCRadius + (nRRow - 1) * gridSizeR; - drDist = (1.5 * matrixEr(nRRow - 1, 0) - 2.0 * matrixEr(nRRow - 2, 0) + 0.5 * matrixEr(nRRow - 3, 0)) / - gridSizeR; // backward difference - dzDist = (-0.5 * matrixEz(nRRow - 1, 2) + 2.0 * matrixEz(nRRow - 1, 1) - 1.5 * matrixEz(nRRow - 1, 0)) / - gridSizeZ; // forward difference - dPhi = (-matrixEPhiP2(nRRow - 1, 0) + 8 * matrixEPhiP(nRRow - 1, 0) - 8 * matrixEPhiM(nRRow - 1, 0) + - matrixEPhiM2(nRRow - 1, 0)) / - (12 * gridSizePhi); - matrixCharge(nRRow - 1, 0) = -1 * (matrixEr(nRRow - 1, 0) / radius + drDist + dPhi / radius + dzDist); - drDist = (1.5 * matrixEr(nRRow - 1, 1) - 2.0 * matrixEr(nRRow - 2, 1) + 0.5 * matrixEr(nRRow - 3, 1)) / - gridSizeR; // backward difference - dzDist = (-matrixEz(nRRow - 1, 3) + 6.0 * matrixEz(nRRow - 1, 2) - 3.0 * matrixEz(nRRow - 1, 1) - - 2 * matrixEz(nRRow - 1, 0)) / - (6.0 * gridSizeZ); // forward difference - dPhi = (-matrixEPhiP2(nRRow - 1, 1) + 8 * matrixEPhiP(nRRow - 1, 1) - 8 * matrixEPhiM(nRRow - 1, 1) + - matrixEPhiM2(nRRow - 1, 1)) / - (12 * gridSizePhi); - matrixCharge(nRRow - 1, 1) = -1 * (matrixEr(nRRow - 1, 1) / radius + drDist + dPhi / radius + dzDist); - - drDist = (1.5 * matrixEr(nRRow - 1, nZColumn - 2) - 2.0 * matrixEr(nRRow - 2, nZColumn - 2) + - 0.5 * matrixEr(nRRow - 3, nZColumn - 2)) / - gridSizeR; // backward difference - dzDist = (2.0 * matrixEz(nRRow - 1, nZColumn - 1) + 3.0 * matrixEz(nRRow - 1, nZColumn - 2) - - 6.0 * matrixEz(nRRow - 1, nZColumn - 3) + matrixEz(nRRow - 1, nZColumn - 4)) / - (6.0 * gridSizeZ); // backward difference - dPhi = (-matrixEPhiP2(nRRow - 1, nZColumn - 2) + 8 * matrixEPhiP(nRRow - 1, nZColumn - 2) - - 8 * matrixEPhiM(nRRow - 1, nZColumn - 2) + matrixEPhiM2(nRRow - 1, nZColumn - 2)) / - (12 * gridSizePhi); - matrixCharge(nRRow - 1, nZColumn - 2) = - -1 * (matrixEr(nRRow - 1, nZColumn - 2) / radius + drDist + dPhi / radius + dzDist); - - drDist = (1.5 * matrixEr(nRRow - 1, nZColumn - 1) - 2.0 * matrixEr(nRRow - 2, nZColumn - 1) + - 0.5 * matrixEr(nRRow - 3, nZColumn - 1)) / - gridSizeR; // backward difference - dzDist = (1.5 * matrixEz(nRRow - 1, nZColumn - 1) - 2.0 * matrixEz(nRRow - 1, nZColumn - 2) + - 0.5 * matrixEz(nRRow - 1, nZColumn - 3)) / - gridSizeZ; // backward difference - - dPhi = (-matrixEPhiP2(nRRow - 1, nZColumn - 1) + 8 * matrixEPhiP(nRRow - 1, nZColumn - 1) - - 8 * matrixEPhiM(nRRow - 1, nZColumn - 1) + matrixEPhiM2(nRRow - 1, nZColumn - 1)) / - (12 * gridSizePhi); - - matrixCharge(nRRow - 1, nZColumn - 1) = - -1 * (matrixEr(nRRow - 1, nZColumn - 1) / radius + drDist + dPhi / radius + dzDist); - } -} -/// -/// \param matricesCharge -/// \param matricesEr -/// \param matricesEPhi -/// \param matricesEz -/// \param matricesInvLocalIntErDz -/// \param matricesInvLocalIntEPhiDz -/// \param matricesInvLocalEz -/// \param matricesDistDrDz -/// \param matricesDistDPhiRDz -/// \param matricesDistDz -/// \param nRRow -/// \param nZColumn -/// \param phiSlice -/// \param nSize -/// \param useCylAC -/// \param stepR -/// \param stepZ -/// \param stepPhi -/// \param interpType -/// \param inverseType -void AliTPCSpaceCharge3DCalc::InverseDistortionMaps( - TMatrixD** matricesCharge, TMatrixD** matricesEr, TMatrixD** matricesEPhi, TMatrixD** matricesEz, - TMatrixD** matricesInvLocalIntErDz, TMatrixD** matricesInvLocalIntEPhiDz, TMatrixD** matricesInvLocalEz, - TMatrixD** matricesDistDrDz, TMatrixD** matricesDistDPhiRDz, TMatrixD** matricesDistDz, - const Int_t nRRow, const Int_t nZColumn, const Int_t phiSlice, const Int_t nSize, - const Bool_t useCylAC, Int_t stepR, Int_t stepZ, Int_t stepPhi, Int_t interpType) -{ - // can inverse after lookup table for global distortion been calculated - Double_t* rList = new Double_t[nRRow]; - Double_t* zList = new Double_t[nZColumn]; - Double_t* phiList = new Double_t[phiSlice]; - const Float_t gridSizeR = (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (nRRow - 1); - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (nZColumn - 1); - const Float_t gridSizePhi = TMath::TwoPi() / phiSlice; - - for (Int_t k = 0; k < phiSlice; k++) { - phiList[k] = gridSizePhi * k; - } - for (Int_t i = 0; i < nRRow; i++) { - rList[i] = AliTPCPoissonSolver::fgkIFCRadius + i * gridSizeR; - } - for (Int_t j = 0; j < nZColumn; j++) { - zList[j] = (j * gridSizeZ); - } - // memory allocation - if (fInitLookUp) { - // 1) get local distortion - InverseGlobalToLocalDistortionGlobalInvTable(matricesDistDrDz, matricesDistDPhiRDz, matricesDistDz, rList, - zList, phiList, nRRow, nZColumn, phiSlice, nSize, useCylAC, - stepR, stepZ, stepPhi, interpType); - - fLookupInverseDistA->SetLookUpR(matricesDistDrDz); - fLookupInverseDistA->SetLookUpPhi(matricesDistDPhiRDz); - fLookupInverseDistA->SetLookUpZ(matricesDistDz); - fLookupInverseDistA->CopyFromMatricesToInterpolator(); - - // 2) calculate local integral - InverseLocalDistortionToElectricField(matricesEr, matricesEPhi, matricesEz, matricesInvLocalIntErDz, - matricesInvLocalIntEPhiDz, matricesInvLocalEz, - matricesDistDrDz, matricesDistDPhiRDz, matricesDistDz, rList, zList, - phiList, nRRow, nZColumn, phiSlice); - // 3) get potential from electric field assuming zero boundaries - InverseElectricFieldToCharge(matricesCharge, matricesEr, matricesEPhi, matricesEz, rList, zList, phiList, nRRow, - nZColumn, phiSlice); - } - - // copy charge inverse here just for side A (TODO: do for side C) - for (Int_t k = 0; k < phiSlice; k++) { - *(fMatrixChargeInverseA[k]) = *(matricesCharge[k]); - } - fInterpolatorInverseChargeA->SetValue(fMatrixChargeInverseA); - fInterpolatorInverseChargeA->InitCubicSpline(); - - delete[] zList; - delete[] rList; - delete[] phiList; -} -/// CalculateEField (New Version: with reorganization of modules) -/// Calculate E field based on look-up table created by Poisson Solver -/// * Differentiate V(r) and solve for E(r) using special equations for the first and last row -/// * Integrate E(r)/E(z) from point of origin to pad plane -/// * Differentiate V(r) and solve for E(phi) -/// * Integrate E(phi)/E(z) from point of origin to pad plane -/// * Differentiate V(r) and solve for E(z) using special equations for the first and last row -/// * Integrate (E(z)-Ez(ROC)) from point of origin to pad plane -/// -/// \param matricesV TMatrixD** 3D matrix representing calculated potential -/// \param matricesErOverEz TMatrix** 3D matrix representing e-field at Er/Ez -/// \param matricesEPhiOverEz TMatrix** 3D matrix representing e-field at EPhi/Ez -/// \param matricesDeltaZ TMatrix** 3D matrix representing e-field at DeltaZ -/// \param nRRow Int_t number of nRRow (in R direction) -/// \param nZColumn Int_t number of nZColumn (in Z direction) -/// \param phiSlice Int_t number of (phi slices in phi direction) -/// \param symmetry Int_t symmetry? -/// \param rocDisplace rocDisplacement -/// -/// \pre Matrix matricesV is assumed had been calculated by Poisson solver -/// \post Results of Integration and Derivations for E-field calculation are stored in matricesErOverEz, matricesEPhiOverEz, matricesDeltaZ -/// -void AliTPCSpaceCharge3DCalc::CalculateEField( - TMatrixD** matricesV, TMatrixD** matricesErOverEz, TMatrixD** matricesEPhiOverEz, - TMatrixD** matricesDeltaEz, const Int_t nRRow, const Int_t nZColumn, const Int_t phiSlice, - const Int_t symmetry, Bool_t rocDisplacement) -{ - - const Double_t ezField = (AliTPCPoissonSolver::fgkCathodeV - AliTPCPoissonSolver::fgkGG) / AliTPCPoissonSolver::fgkTPCZ0; // = ALICE Electric Field (V/cm) Magnitude ~ -400 V/cm; - const Float_t gridSizeR = (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (nRRow - 1); - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (nZColumn - 1); - const Float_t gridSizePhi = TMath::TwoPi() / phiSlice; - TMatrixD *matricesEr[phiSlice], *matricesEz[phiSlice], *matricesEPhi[phiSlice]; - - //Allocate memory for electric field r,z, phi direction - for (Int_t k = 0; k < phiSlice; k++) { - matricesEr[k] = new TMatrixD(nRRow, nZColumn); - matricesEz[k] = new TMatrixD(nRRow, nZColumn); - matricesEPhi[k] = new TMatrixD(nRRow, nZColumn); - } - - //Differentiate V(r) and solve for E(r) using special equations for the first and last row - TStopwatch w; - w.Start(); - - ElectricField(matricesV, matricesEr, matricesEPhi, matricesEz, nRRow, nZColumn, - phiSlice, gridSizeR, gridSizePhi, gridSizeZ, symmetry, AliTPCPoissonSolver::fgkIFCRadius); - - w.Stop(); - Info("AliTPCSpaceCharge3DCalc::InitSpaceCharge3DPoissonIntegralDz", "%s", Form("Time for calculation E-field CPU = %f s\n", w.CpuTime())); - - //Integrate E(r)/E(z) from point of origin to pad plane - - IntegrateEz(matricesErOverEz, matricesEr, nRRow, nZColumn, phiSlice, ezField); - IntegrateEz(matricesEPhiOverEz, matricesEPhi, nRRow, nZColumn, phiSlice, ezField); - IntegrateEz(matricesDeltaEz, matricesEz, nRRow, nZColumn, phiSlice, -1.0); - - // calculate z distortion from the integrated Delta Ez residuals - // and include the equivalence (Volt to cm) of the ROC shift !! - for (Int_t m = 0; m < phiSlice; m++) { - TMatrixD& arrayV = *matricesV[m]; - TMatrixD& deltaEz = *matricesDeltaEz[m]; - - for (Int_t j = 0; j < nZColumn; j++) { - for (Int_t i = 0; i < nRRow; i++) { - // Scale the Ez distortions with the drift velocity -> delivers cm - deltaEz(i, j) = deltaEz(i, j) * AliTPCPoissonSolver::fgkdvdE; - // ROC Potential in cm equivalent - Double_t dzROCShift = arrayV(i, nZColumn - 1) / ezField; - if (rocDisplacement) { - deltaEz(i, j) = deltaEz(i, j) + dzROCShift; // add the ROC mis alignment - } - } - } - } - // clear the temporary arrays lists - - for (Int_t k = 0; k < phiSlice; k++) { - delete matricesEr[k]; - delete matricesEz[k]; - delete matricesEPhi[k]; - } -} -/// -/// Integrate at z direction Ez for electron drift calculation -/// -/// -/// \param matricesExOverEz TMatrixD** 3D matrix representing ExOverEz -/// \param matricesEx TMatrix** 3D matrix representing e-field at x direction -/// \param nRRow const Int_t number of nRRow (in R direction) -/// \param nZColumn const Int_t number of nZColumn (in Z direction) -/// \param phiSlice const Int_t number of (phiSlice in phi direction) -/// \param ezField const Double_t Electric field in z direction -/// -/// \pre matricesEx is assumed already been calculated by ElectricFieldCalculation -/// \post Matrix matricesExOverEz is calculated by integration of matricesEx -/// -void AliTPCSpaceCharge3DCalc::IntegrateEz( - TMatrixD** matricesExOverEz, TMatrixD** matricesEx, const Int_t nRRow, const Int_t nZColumn, - const Int_t phiSlice, const Double_t ezField) -{ - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (nZColumn - 1); - for (Int_t m = 0; m < phiSlice; m++) { - TMatrixD& eXoverEz = *matricesExOverEz[m]; - TMatrixD& arrayEx = *matricesEx[m]; - - for (Int_t j = nZColumn - 1; j >= 0; j--) { - for (Int_t i = 0; i < nRRow; i++) { - - /// Calculate integration from int^{0}_{j} (TODO: Split the integration) - if (j < nZColumn - 3) { - eXoverEz(i, j) = eXoverEz(i, j + 2) + - (gridSizeZ / 3.0) * (arrayEx(i, j) + 4 * arrayEx(i, j + 1) + arrayEx(i, j + 2)) / - (-1 * ezField); - } else { - if (j == nZColumn - 3) { - eXoverEz(i, j) = (gridSizeZ / 3.0) * (arrayEx(i, nZColumn - 3) + 4 * arrayEx(i, nZColumn - 2) + arrayEx(i, nZColumn - 1)) / (-1 * ezField); - } - if (j == nZColumn - 2) { - eXoverEz(i, j) = - (gridSizeZ / 3.0) * (1.5 * arrayEx(i, nZColumn - 2) + 1.5 * arrayEx(i, nZColumn - 1)) / - (-1 * ezField); - } - if (j == nZColumn - 1) { - eXoverEz(i, j) = 0.0; - } - } - } - } - } -} -/// GetCorrection from no-drift -/// -/// \param x Float_t point origin -/// \param roc -/// \param dx -void AliTPCSpaceCharge3DCalc::GetCorrectionCylNoDrift(const Float_t x[], const Short_t roc, Float_t dx[]) -{ - /// Calculates the correction due the Space Charge effect within the TPC drift volume - - if (!fInitLookUp) { - Info("AliTPCSpaceCharge3DCalc::", "Lookup table was not initialized! Performing the initialization now ..."); - // InitSpaceCharge3DDistortion(); - return; - } - - Float_t intEr, intEPhi, intDEz; - Double_t r, phi, z; - Int_t sign; - - r = x[0]; - phi = x[1]; - if (phi < 0) { - phi += TMath::TwoPi(); // Table uses phi from 0 to 2*Pi - } - if (phi > TMath::TwoPi()) { - phi -= TMath::TwoPi(); - } - - z = x[2]; // Create temporary copy of x[2] - - if ((roc % 36) < 18) { - sign = 1; // (TPC A side) - } else { - sign = -1; // (TPC C side) - } - - if (sign == 1 && z < AliTPCPoissonSolver::fgkZOffSet) { - z = AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if (sign == -1 && z > -AliTPCPoissonSolver::fgkZOffSet) { - z = -AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if ((sign == 1 && z < 0) || (sign == -1 && z > 0)) { // just a consistency check - Error("AliTPCSpaceCharge3DCalc::", "ROC number does not correspond to z coordinate! Calculation of distortions is most likely wrong!"); - } - - if (sign == -1 && z < 0.0) { - printf("call C side\n"); - fLookupIntENoDriftC->GetValue(r, phi, z, intEr, intEPhi, intDEz); - } else { - fLookupIntENoDriftA->GetValue(r, phi, z, intEr, intEPhi, intDEz); - } - - // Calculate distorted position - if (r > 0.0) { - phi = phi + fCorrectionFactor * (fC0 * intEPhi - fC1 * intEr) / r; - r = r + fCorrectionFactor * (fC0 * intEr + fC1 * intEPhi); - } - Double_t dzDist = intDEz * fCorrectionFactor * AliTPCPoissonSolver::fgkdvdE; - - // Calculate correction in cartesian coordinates - dx[0] = -(r - x[0]); - dx[1] = -(phi - x[1]); - dx[2] = -dzDist; // z distortion - (scaled with drift velocity dependency on the Ez field and the overall scaling factor) -} -/// -/// \param x -/// \param roc -/// \param dx -void AliTPCSpaceCharge3DCalc::GetDistortionCylNoDrift(const Float_t x[], Short_t roc, Float_t dx[]) -{ - /// This function delivers the distortion values dx in respect to the initial coordinates x - /// roc represents the TPC read out chamber (offline numbering convention) - - GetCorrectionCylNoDrift(x, roc, dx); - for (Int_t j = 0; j < 3; ++j) { - dx[j] = -dx[j]; - } -} -/// inverse for no drift -/// inverse from global distortion to local distortion -/// -/// \param matricesDistDrDz -/// \param matricesDistDPhiRDz -/// \param matricesDistDz -/// \param rList -/// \param zList -/// \param phiList -/// \param nRRow -/// \param nZColumn -/// \param phiSlice -void AliTPCSpaceCharge3DCalc::InverseGlobalToLocalDistortionNoDrift( - TMatrixD** matricesDistDrDz, TMatrixD** matricesDistDPhiRDz, TMatrixD** matricesDistDz, - Double_t* rList, Double_t* zList, Double_t* phiList, - const Int_t nRRow, const Int_t nZColumn, const Int_t phiSlice) -{ - - Double_t z, phi, r, zAfter, zPrevious, ddR, ddRPhi, ddZ, drDist, dRPhi, dzDist; - Float_t x[3], dx[3], pdx[3], dxp1[3], dxp2[3]; - Int_t roc; - TMatrixD* distDrDz; - TMatrixD* distDPhiRDz; - TMatrixD* distDz; - - for (Int_t k = 0; k < phiSlice; k++) { - distDrDz = matricesDistDrDz[k]; - distDPhiRDz = matricesDistDPhiRDz[k]; - distDz = matricesDistDz[k]; - for (Int_t i = 0; i < nRRow; i++) { - (*distDrDz)(i, nZColumn - 1) = 0.0; - (*distDPhiRDz)(i, nZColumn - 1) = 0.0; - (*distDz)(i, nZColumn - 1) = 0.0; - } - } - - for (Int_t j = nZColumn - 2; j >= 0; j--) { - roc = 0; // FIXME - for (Int_t k = 0; k < phiSlice; k++) { - - distDrDz = matricesDistDrDz[k]; - distDPhiRDz = matricesDistDPhiRDz[k]; - distDz = matricesDistDz[k]; - for (Int_t i = 0; i < nRRow; i++) { - // get global distortion - - r = rList[i]; - phi = phiList[k]; - z = zList[j]; - zPrevious = zList[j + 1]; - //zAfter = zList[j-1]; - - (*distDrDz)(i, j) = 0.0; - (*distDPhiRDz)(i, j) = 0.0; - (*distDz)(i, j) = 0.0; - drDist = 0.0; - dRPhi = 0.0; - dzDist = 0.0; - - r = rList[i]; - phi = phiList[k]; - z = zList[j]; - - x[0] = r; - x[1] = phi; - x[2] = z; - - GetDistortionCylNoDrift(x, roc, dx); - - //x[0] = x[0] + drDist; - //x[1] = x[1] + dRPhi/r; - x[2] = zPrevious; - - GetDistortionCylNoDrift(x, roc, pdx); - - (*distDrDz)(i, j) = (dx[0] - pdx[0]); - (*distDPhiRDz)(i, j) = (dx[1] - pdx[1]) * r; - (*distDz)(i, j) = (dx[2] - pdx[2]); - } - } - } -} -/// -/// \param matricesCharge -/// \param matricesEr -/// \param matricesEPhi -/// \param matricesEz -/// \param matricesInvLocalIntErDz -/// \param matricesInvLocalIntEPhiDz -/// \param matricesInvLocalEz -/// \param matricesDistDrDz -/// \param matricesDistDPhiRDz -/// \param matricesDistDz -/// \param nRRow -/// \param nZColumn -/// \param phiSlice -void AliTPCSpaceCharge3DCalc::InverseDistortionMapsNoDrift( - TMatrixD** matricesCharge, TMatrixD** matricesEr, TMatrixD** matricesEPhi, TMatrixD** matricesEz, - TMatrixD** matricesInvLocalIntErDz, TMatrixD** matricesInvLocalIntEPhiDz, TMatrixD** matricesInvLocalEz, - TMatrixD** matricesDistDrDz, TMatrixD** matricesDistDPhiRDz, TMatrixD** matricesDistDz, - const Int_t nRRow, const Int_t nZColumn, const Int_t phiSlice) -{ - // can inverse after lookup table for global distortion been calculated - Double_t* rList = new Double_t[nRRow]; - Double_t* zList = new Double_t[nZColumn]; - Double_t* phiList = new Double_t[phiSlice]; - const Float_t gridSizeR = (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (nRRow - 1); - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (nZColumn - 1); - const Float_t gridSizePhi = TMath::TwoPi() / phiSlice; - - for (Int_t k = 0; k < phiSlice; k++) { - phiList[k] = gridSizePhi * k; - } - for (Int_t i = 0; i < nRRow; i++) { - rList[i] = AliTPCPoissonSolver::fgkIFCRadius + i * gridSizeR; - } - for (Int_t j = 0; j < nZColumn; j++) { - zList[j] = (j * gridSizeZ); - } - // memory allocation - if (fInitLookUp) { - // 1) get local distortion - InverseGlobalToLocalDistortionNoDrift(matricesDistDrDz, matricesDistDPhiRDz, matricesDistDz, rList, zList, - phiList, nRRow, nZColumn, phiSlice); - // 2) calculate local integral - InverseLocalDistortionToElectricField(matricesEr, matricesEPhi, matricesEz, matricesInvLocalIntErDz, - matricesInvLocalIntEPhiDz, matricesInvLocalEz, - matricesDistDrDz, matricesDistDPhiRDz, matricesDistDz, rList, zList, - phiList, nRRow, nZColumn, phiSlice); - // 3) get potential from electric field assuming zero boundaries - InverseElectricFieldToCharge(matricesCharge, matricesEr, matricesEPhi, matricesEz, rList, zList, phiList, nRRow, - nZColumn, phiSlice); - } - delete[] zList; - delete[] rList; - delete[] phiList; -} -/// -/// \param matricesChargeA -/// \param matricesChargeC -/// \param spaceChargeHistogram3D -/// \param nRRow -/// \param nZColumn -/// \param phiSlice -void AliTPCSpaceCharge3DCalc::GetChargeDensity( - TMatrixD** matricesChargeA, TMatrixD** matricesChargeC, const TH3* spaceChargeHistogram3D, - const Int_t nRRow, const Int_t nZColumn, const Int_t phiSlice) -{ - Int_t phiSlicesPerSector = phiSlice / kNumSector; - const Float_t gridSizeR = (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (nRRow - 1); - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (nZColumn - 1); - const Float_t gridSizePhi = TMath::TwoPi() / phiSlice; - const Double_t ezField = (AliTPCPoissonSolver::fgkCathodeV - AliTPCPoissonSolver::fgkGG) / AliTPCPoissonSolver::fgkTPCZ0; // = ALICE Electric Field (V/cm) Magnitude ~ -400 V/cm; - // local variables - Float_t radius0, phi0, z0; - // list of point as used in the poisson relaxation and the interpolation (for interpolation) - Double_t rList[nRRow], zList[nZColumn], phiList[phiSlice]; - for (Int_t k = 0; k < phiSlice; k++) { - phiList[k] = gridSizePhi * k; - } - for (Int_t i = 0; i < nRRow; i++) { - rList[i] = AliTPCPoissonSolver::fgkIFCRadius + i * gridSizeR; - } - for (Int_t j = 0; j < nZColumn; j++) { - zList[j] = j * gridSizeZ; - } - - TMatrixD* mCharge; - for (Int_t side = 0; side < 2; side++) { - for (Int_t k = 0; k < phiSlice; k++) { - if (side == 0) { - mCharge = matricesChargeA[k]; - } else { - mCharge = matricesChargeC[k]; - } - - phi0 = phiList[k]; - for (Int_t i = 0; i < nRRow; i++) { - radius0 = rList[i]; - for (Int_t j = 0; j < nZColumn; j++) { - z0 = zList[j]; - if (side == 1) { - z0 = -TMath::Abs(zList[j]); - } - if (spaceChargeHistogram3D != nullptr) { - (*mCharge)(i, j) = InterpolatePhi(spaceChargeHistogram3D, phi0, radius0, z0); - //InterpolatePhi(spaceChargeHistogram3D,phi0,radius0,z0); - } - } - } - } - } -} -/// -/// \param x -/// \param roc -/// \return -Double_t AliTPCSpaceCharge3DCalc::GetChargeCylAC(const Float_t x[], Short_t roc) const -{ - Double_t r, phi, z; - Int_t sign; - - r = x[0]; - phi = x[1]; - if (phi < 0) { - phi += TMath::TwoPi(); // Table uses phi from 0 to 2*Pi - } - if (phi > TMath::TwoPi()) { - phi = phi - TMath::TwoPi(); // Table uses phi from 0 to 2*Pi - } - z = x[2]; // Create temporary copy of x[2] - - if ((roc % 36) < 18) { - sign = 1; // (TPC A side) - } else { - sign = -1; // (TPC C side) - } - - if (sign == 1 && z < AliTPCPoissonSolver::fgkZOffSet) { - z = AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if (sign == -1 && z > -AliTPCPoissonSolver::fgkZOffSet) { - z = -AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if ((sign == 1 && z < 0) || (sign == -1 && z > 0)) { // just a consistency check - Error("AliTPCSpaceCharge3DCalc::", "ROC number does not correspond to z coordinate! Calculation of distortions is most likely wrong!"); - } - - if (z > -1e-16) { - return fInterpolatorChargeA->GetValue(r, phi, z); - } else { - return fInterpolatorChargeC->GetValue(r, phi, -z); - } -} -/// -/// \param x -/// \param roc -/// \return -Double_t AliTPCSpaceCharge3DCalc::GetPotentialCylAC(const Float_t x[], Short_t roc) const -{ - Double_t r, phi, z; - Int_t sign; - - r = x[0]; - phi = x[1]; - if (phi < 0) { - phi += TMath::TwoPi(); // Table uses phi from 0 to 2*Pi - } - if (phi > TMath::TwoPi()) { - phi = phi - TMath::TwoPi(); // Table uses phi from 0 to 2*Pi - } - z = x[2]; // Create temporary copy of x[2] - - if ((roc % 36) < 18) { - sign = 1; // (TPC A side) - } else { - sign = -1; // (TPC C side) - } - - if (sign == 1 && z < AliTPCPoissonSolver::fgkZOffSet) { - z = AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if (sign == -1 && z > -AliTPCPoissonSolver::fgkZOffSet) { - z = -AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if ((sign == 1 && z < 0) || (sign == -1 && z > 0)) { // just a consistency check - Error("AliTPCSpaceCharge3DCalc::", "ROC number does not correspond to z coordinate! Calculation of distortions is most likely wrong!"); - } - - if (z > -1e-16) { - return fInterpolatorPotentialA->GetValue(r, phi, z); - } else { - return fInterpolatorPotentialC->GetValue(r, phi, -z); - } -} -/// chargeInverse -/// -/// \param x -/// \param roc -/// \return -Double_t AliTPCSpaceCharge3DCalc::GetInverseChargeCylAC(const Float_t x[], Short_t roc) const -{ - Double_t r, phi, z; - Int_t sign; - - r = x[0]; - phi = x[1]; - if (phi < 0) { - phi += TMath::TwoPi(); // Table uses phi from 0 to 2*Pi - } - if (phi > TMath::TwoPi()) { - phi = phi - TMath::TwoPi(); // Table uses phi from 0 to 2*Pi - } - z = x[2]; // Create temporary copy of x[2] - - if ((roc % 36) < 18) { - sign = 1; // (TPC A side) - } else { - sign = -1; // (TPC C side) - } - - if (sign == 1 && z < AliTPCPoissonSolver::fgkZOffSet) { - z = AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if (sign == -1 && z > -AliTPCPoissonSolver::fgkZOffSet) { - z = -AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if ((sign == 1 && z < 0) || (sign == -1 && z > 0)) { // just a consistency check - Error("AliTPCSpaceCharge3DCalc::", "ROC number does not correspond to z coordinate! Calculation of distortions is most likely wrong!"); - } - - if (z > -1e-6) { - return fInterpolatorInverseChargeA->GetValue(r, phi, z); - } else { - return fInterpolatorInverseChargeC->GetValue(r, phi, z); - } -} - -/// -/// \param x -/// \param roc -/// \param dx -void AliTPCSpaceCharge3DCalc::GetLocalDistortionCyl(const Float_t x[], Short_t roc, Float_t dx[]) -{ - Float_t dR, dRPhi, dZ; - Double_t r, phi, z; - Int_t sign; - - r = x[0]; - phi = x[1]; - if (phi < 0) { - phi += TMath::TwoPi(); // Table uses phi from 0 to 2*Pi - } - if (phi > TMath::TwoPi()) { - phi = phi - TMath::TwoPi(); // Table uses phi from 0 to 2*Pi - } - z = x[2]; // Create temporary copy of x[2] - - if ((roc % 36) < 18) { - sign = 1; // (TPC A side) - } else { - sign = -1; // (TPC C side) - } - - if (sign == 1 && z < AliTPCPoissonSolver::fgkZOffSet) { - z = AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if (sign == -1 && z > -AliTPCPoissonSolver::fgkZOffSet) { - z = -AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if ((sign == 1 && z < -1e-16) || (sign == -1 && z > -1e-16)) { // just a consistency check - Error("AliTPCSpaceCharge3DCalc::", "ROC number does not correspond to z coordinate! Calculation of distortions is most likely wrong!"); - } - - if (z > -1e-16) { - fLookupDistA->GetValue(r, phi, z, dR, dRPhi, dZ); - } else { - fLookupDistC->GetValue(r, phi, -z, dR, dRPhi, dZ); - dZ = -1 * dZ; - } - - dx[0] = fCorrectionFactor * dR; - dx[1] = fCorrectionFactor * dRPhi; - dx[2] = fCorrectionFactor * - dZ; // z distortion - (scaled with drift velocity dependency on the Ez field and the overall scaling factor) -} - -/// -/// \param x -/// \param roc -/// \param dx -void AliTPCSpaceCharge3DCalc::GetLocalCorrectionCyl(const Float_t x[], Short_t roc, Float_t dx[]) -{ - Float_t dR, dRPhi, dZ; - Double_t r, phi, z; - Int_t sign; - - r = x[0]; - phi = x[1]; - if (phi < 0) { - phi += TMath::TwoPi(); // Table uses phi from 0 to 2*Pi - } - if (phi > TMath::TwoPi()) { - phi = phi - TMath::TwoPi(); // Table uses phi from 0 to 2*Pi - } - z = x[2]; // Create temporary copy of x[2] - - if ((roc % 36) < 18) { - sign = 1; // (TPC A side) - } else { - sign = -1; // (TPC C side) - } - - if (sign == 1 && z < AliTPCPoissonSolver::fgkZOffSet) { - z = AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if (sign == -1 && z > -AliTPCPoissonSolver::fgkZOffSet) { - z = -AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if ((sign == 1 && z < -1e-16) || (sign == -1 && z > -1e-16)) { // just a consistency check - Error("AliTPCSpaceCharge3DCalc::", "ROC number does not correspond to z coordinate! Calculation of distortions is most likely wrong!"); - } - - if (z > -1e-16) { - fLookupCorrA->GetValue(r, phi, z, dR, dRPhi, dZ); - } else { - fLookupCorrC->GetValue(r, phi, -z, dR, dRPhi, dZ); - dZ = -1 * dZ; - } - - dx[0] = fCorrectionFactor * dR; - dx[1] = fCorrectionFactor * dRPhi; - dx[2] = fCorrectionFactor * - dZ; // z distortion - (scaled with drift velocity dependency on the Ez field and the overall scaling factor) -} - -/// Get Electric field from look up table -/// \param x -/// \param roc -/// \param dx -void AliTPCSpaceCharge3DCalc::GetElectricFieldCyl(const Float_t x[], Short_t roc, Double_t dx[]) -{ - Double_t eR, ePhi, eZ; - Double_t r, phi, z; - Int_t sign; - - r = x[0]; - phi = x[1]; - if (phi < 0) { - phi += TMath::TwoPi(); // Table uses phi from 0 to 2*Pi - } - if (phi > TMath::TwoPi()) { - phi = phi - TMath::TwoPi(); // Table uses phi from 0 to 2*Pi - } - z = x[2]; // Create temporary copy of x[2] - - if ((roc % 36) < 18) { - sign = 1; // (TPC A side) - } else { - sign = -1; // (TPC C side) - } - - if (sign == 1 && z < AliTPCPoissonSolver::fgkZOffSet) { - z = AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if (sign == -1 && z > -AliTPCPoissonSolver::fgkZOffSet) { - z = -AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if ((sign == 1 && z < -1e-16) || (sign == -1 && z > -1e-16)) { // just a consistency check - Error("AliTPCSpaceCharge3DCalc::", "ROC number does not correspond to z coordinate! Calculation of distortions is most likely wrong!"); - } - - if (z > -1e-16) { - fLookupElectricFieldA->GetValue(r, phi, z, eR, ePhi, eZ); - } else { - fLookupElectricFieldC->GetValue(r, phi, -z, eR, ePhi, eZ); - eZ = -1 * eZ; - } - - dx[0] = eR; - dx[1] = ePhi; - dx[2] = eZ; -} - -/// -/// \param x -/// \param roc -/// \param dx -void AliTPCSpaceCharge3DCalc::GetInverseLocalDistortionCyl(const Float_t x[], Short_t roc, Float_t dx[]) -{ - if (!fInitLookUp) { - Info("AliTPCSpaceCharge3DCalc::", "Lookup table was not initialized! Performing the initialization now ..."); - InitSpaceCharge3DPoissonIntegralDz(129, 129, 144, 100, 1e-8); - } - - Float_t dR, dRPhi, dZ; - Double_t r, phi, z; - Int_t sign; - - r = x[0]; - phi = x[1]; - if (phi < 0) { - phi += TMath::TwoPi(); // Table uses phi from 0 to 2*Pi - } - if (phi > TMath::TwoPi()) { - phi = phi - TMath::TwoPi(); // Table uses phi from 0 to 2*Pi - } - z = x[2]; // Create temporary copy of x[2] - - if ((roc % 36) < 18) { - sign = 1; // (TPC A side) - } else { - sign = -1; // (TPC C side) - } - - if (sign == 1 && z < AliTPCPoissonSolver::fgkZOffSet) { - z = AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if (sign == -1 && z > -AliTPCPoissonSolver::fgkZOffSet) { - z = -AliTPCPoissonSolver::fgkZOffSet; // Protect against discontinuity at CE - } - if ((sign == 1 && z < -1e-16) || (sign == -1 && z > -1e-16)) { // just a consistency check - Error("AliTPCSpaceCharge3DCalc::", "ROC number does not correspond to z coordinate! Calculation of distortions is most likely wrong!"); - } - - if (z > -1e-16) { - fLookupInverseDistA->GetValue(r, phi, z, dR, dRPhi, dZ); - } else { - fLookupInverseDistC->GetValue(r, phi, -z, dR, dRPhi, dZ); - } - - dx[0] = fCorrectionFactor * dR; - dx[1] = fCorrectionFactor * dRPhi; - dx[2] = fCorrectionFactor * - dZ; // z distortion - (scaled with drift velocity dependency on the Ez field and the overall scaling factor) -} -/// Function for setting Potential Boundary Values and Charge distribution input TFormula -/// -/// \param vTestFunction -/// \param rhoTestFunction -/// -void AliTPCSpaceCharge3DCalc::SetPotentialBoundaryAndChargeFormula(TFormula* vTestFunction, TFormula* rhoTestFunction) -{ - /**** allocate memory for charge ***/ - // we allocate pointer to TMatrixD array to picture 3D (slices), this representation should be revised - // since it is easier for GPU implementation to run for 1D memory - const Float_t gridSizeR = (AliTPCPoissonSolver::fgkOFCRadius - AliTPCPoissonSolver::fgkIFCRadius) / (fNRRows - 1); - const Float_t gridSizeZ = AliTPCPoissonSolver::fgkTPCZ0 / (fNZColumns - 1); - const Float_t gridSizePhi = TMath::TwoPi() / fNPhiSlices; - - fFormulaPotentialV = vTestFunction; - fFormulaChargeRho = rhoTestFunction; - - // grid size for one side - TMatrixD* chargeA; - TMatrixD* chargeC; - Double_t radius0, z0, phi0, z0neg; - Int_t indexB = 0; - for (Int_t k = 0; k < fNPhiSlices; k++) { - chargeA = fMatrixChargeA[k]; - chargeC = fMatrixChargeC[k]; - - phi0 = k * gridSizePhi; - - /// Fill the non-boundary values - for (Int_t i = 0; i < fNRRows; i++) { - radius0 = AliTPCPoissonSolver::fgkIFCRadius + (i * gridSizeR); - for (Int_t j = 0; j < fNZColumns; j++) { - z0 = j * gridSizeZ; - z0neg = -z0; - - (*chargeA)(i, j) = -1.0 * rhoTestFunction->Eval(radius0, phi0, z0); - (*chargeC)(i, j) = -1.0 * rhoTestFunction->Eval(radius0, phi0, z0neg); - - /// TODO: fListPotentialBoundary arrays are never used in the code. Remove? - // if ((i == 0) || (i == fNRRows - 1) || (j == 0) || (j == fNZColumns - 1)) { - // fListPotentialBoundaryA[indexB] = vTestFunction->Eval(radius0, phi0, z0); - // fListPotentialBoundaryC[indexB] = vTestFunction->Eval(radius0, phi0, z0neg); - // indexB++; - // } - - } // end j - } // end i - } // end phi - - fInterpolatorChargeA->SetValue(fMatrixChargeA); - fInterpolatorChargeA->InitCubicSpline(); - fInterpolatorChargeC->SetValue(fMatrixChargeC); - fInterpolatorChargeC->InitCubicSpline(); -} - -/// Set interpolation -void AliTPCSpaceCharge3DCalc::SetInterpolationOrder(Int_t order) -{ - fInterpolationOrder = order; - - fInterpolatorChargeA->SetOrder(fInterpolationOrder); - fInterpolatorChargeC->SetOrder(fInterpolationOrder); - fInterpolatorPotentialA->SetOrder(fInterpolationOrder); - fInterpolatorPotentialC->SetOrder(fInterpolationOrder); - fInterpolatorInverseChargeA->SetOrder(fInterpolationOrder); - fInterpolatorInverseChargeC->SetOrder(fInterpolationOrder); - - fLookupDistA->SetOrder(fInterpolationOrder); - - fLookupDistC->SetOrder(fInterpolationOrder); - - fLookupInverseDistA->SetOrder(fInterpolationOrder); - - fLookupInverseDistC->SetOrder(fInterpolationOrder); - - fLookupElectricFieldA->SetOrder(fInterpolationOrder); - fLookupElectricFieldC->SetOrder(fInterpolationOrder); - fLookupIntDistA->SetOrder(fInterpolationOrder); - fLookupIntDistC->SetOrder(fInterpolationOrder); - fLookupIntCorrA->SetOrder(fInterpolationOrder); - fLookupIntCorrC->SetOrder(fInterpolationOrder); - fLookupIntENoDriftA->SetOrder(fInterpolationOrder); - fLookupIntENoDriftC->SetOrder(fInterpolationOrder); - fLookupIntCorrIrregularA->SetOrder(fInterpolationOrder); - - fLookupIntCorrIrregularC->SetOrder(fInterpolationOrder); -} - -void AliTPCSpaceCharge3DCalc::SetDistortionLookupTables(TMatrixD** matrixIntDistDrA, TMatrixD** matrixIntDistDrphiA, TMatrixD** matrixIntDistDzA, TMatrixD** matrixIntDistDrC, TMatrixD** matrixIntDistDrphiC, TMatrixD** matrixIntDistDzC) -{ - fMatrixIntDistDrEzA = matrixIntDistDrA; - fMatrixIntDistDPhiREzA = matrixIntDistDrphiA; - fMatrixIntDistDzA = matrixIntDistDzA; - fLookupIntDistA->SetLookUpR(fMatrixIntDistDrEzA); - fLookupIntDistA->SetLookUpPhi(fMatrixIntDistDPhiREzA); - fLookupIntDistA->SetLookUpZ(fMatrixIntDistDzA); - fLookupIntDistA->CopyFromMatricesToInterpolator(); - - fMatrixIntDistDrEzC = matrixIntDistDrC; - fMatrixIntDistDPhiREzC = matrixIntDistDrphiC; - fMatrixIntDistDzC = matrixIntDistDzC; - fLookupIntDistC->SetLookUpR(fMatrixIntDistDrEzC); - fLookupIntDistC->SetLookUpPhi(fMatrixIntDistDPhiREzC); - fLookupIntDistC->SetLookUpZ(fMatrixIntDistDzC); - fLookupIntDistC->CopyFromMatricesToInterpolator(); - - fInitLookUp = kTRUE; -} \ No newline at end of file diff --git a/GPU/TPCSpaceChargeBase/AliTPCSpaceCharge3DCalc.h b/GPU/TPCSpaceChargeBase/AliTPCSpaceCharge3DCalc.h deleted file mode 100644 index bf686deff2b0f..0000000000000 --- a/GPU/TPCSpaceChargeBase/AliTPCSpaceCharge3DCalc.h +++ /dev/null @@ -1,454 +0,0 @@ -// Copyright CERN and copyright holders of ALICE O2. This software is -// distributed under the terms of the GNU General Public License v3 (GPL -// Version 3), copied verbatim in the file "COPYING". -// -// See http://alice-o2.web.cern.ch/license for full licensing information. -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file AliTPCSpaceCharge3DCalc.h -/// \brief This class provides distortion and correction map calculation with integration following electron drift -/// TODO: validate distortion z by comparing with exisiting classes -/// -/// \author Rifki Sadikin , Indonesian Institute of Sciences -/// \date Nov 20, 2017 - -#ifndef ALI_TPC_SPACECHARGE3D_CALC_H -#define ALI_TPC_SPACECHARGE3D_CALC_H - -#include "TF1.h" -#include "TF2.h" -#include "TH3F.h" -#include "TMatrixD.h" - -#include "AliTPCPoissonSolver.h" -#include "AliTPCLookUpTable3DInterpolatorD.h" -#include "AliTPC3DCylindricalInterpolator.h" -#include "AliTPCLookUpTable3DInterpolatorIrregularD.h" -#include "AliTPC3DCylindricalInterpolatorIrregular.h" - -class TFormula; - -class AliTPCSpaceCharge3DCalc -{ - public: - AliTPCSpaceCharge3DCalc(); - AliTPCSpaceCharge3DCalc(Int_t nRRow, Int_t nZColumn, Int_t nPhiSlice); - AliTPCSpaceCharge3DCalc(Int_t nRRow, Int_t nZColumn, Int_t nPhiSlice, - Int_t interpolationOrder, Int_t irregularGridSize, Int_t rbfKernelType); - ~AliTPCSpaceCharge3DCalc(); - void InitSpaceCharge3DPoissonIntegralDz(Int_t nRRow, Int_t nZColumn, Int_t phiSlice, Int_t maxIteration, - Double_t stopConvergence); - - // outdated, to be removed after modifications in aliroot are pushed - void InitSpaceCharge3DPoissonIntegralDz( - Int_t nRRow, Int_t nZColumn, Int_t phiSlice, Int_t maxIteration, Double_t stopConvergence, - TMatrixD** matricesErA, TMatrixD** matricesEphiA, TMatrixD** matricesEzA, - TMatrixD** matricesErC, TMatrixD** matricesEphiC, TMatrixD** matricesEzC, - TMatrixD** matricesDistDrDzA, TMatrixD** matricesDistDPhiRDzA, TMatrixD** matricesDistDzA, - TMatrixD** matricesCorrDrDzA, TMatrixD** matricesCorrDPhiRDzA, TMatrixD** matricesCorrDzA, - TMatrixD** matricesDistDrDzC, TMatrixD** matricesDistDPhiRDzC, TMatrixD** matricesDistDzC, - TMatrixD** matricesCorrDrDzC, TMatrixD** matricesCorrDPhiRDzC, TMatrixD** matricesCorrDzC, - TFormula* intErDzTestFunction, TFormula* intEPhiRDzTestFunction, TFormula* intDzTestFunction); - - void InitSpaceCharge3DPoissonIntegralDz( - Int_t nRRow, Int_t nZColumn, Int_t phiSlice, Int_t maxIteration, Double_t stopConvergence, - TFormula* intErDzTestFunction, TFormula* intEPhiRDzTestFunction, TFormula* intDzTestFunction); - - void - InitSpaceCharge3DPoisson(Int_t nRRow, Int_t nZColumn, Int_t phiSlice, Int_t maxIteration, Double_t stopConvergence); - void ForceInitSpaceCharge3DPoissonIntegralDz(Int_t nRRow, Int_t nZColumn, Int_t phiSlice, Int_t maxIteration, - Double_t stopConvergence); - void GetDistortionCyl(const Float_t x[], Short_t roc, Float_t dx[]) const; - void GetDistortionCylAC(const Float_t x[], Short_t roc, Float_t dx[]) const; - void GetCorrectionCyl(const Float_t x[], Short_t roc, Float_t dx[]) const; - void GetCorrectionCylAC(const Float_t x[], Short_t roc, Float_t dx[]) const; - void GetCorrectionCylACIrregular(const Float_t x[], Short_t roc, Float_t dx[]) const; - void GetCorrectionCylACIrregular(const Float_t x[], Short_t roc, Float_t dx[], const Int_t side) const; - void GetDistortion(const Float_t x[], Short_t roc, Float_t dx[]) const; - - void GetCorrection(const Float_t x[], Short_t roc, Float_t dx[]) const; - void GetCorrection(const Float_t x[], Short_t roc, Float_t dx[], const Int_t side) const; - - Double_t GetChargeCylAC(const Float_t x[], Short_t roc) const; - Double_t GetPotentialCylAC(const Float_t x[], Short_t roc) const; - - Double_t GetInverseChargeCylAC(const Float_t x[], Short_t roc) const; - - void SetCorrectionType(Int_t correctionType) { fCorrectionType = correctionType; } - - enum { - kNumSector = 18 - }; - - enum CorrectionType { - kRegularInterpolator = 0, ///< use interpolation with regular interpolator for correction look up table - kIrregularInterpolator = 1, ///< use irregular interpolator for correction look up table - }; - - enum IntegrationStrategy { - kNaive = 0, ///< use interpolation with regular interpolator for correction look up table - kOpt = 1, ///< use irregular interpolator for correction look up table - }; - void SetInputSpaceCharge(TH3* hisSpaceCharge3D, Double_t norm); - void SetInputSpaceCharge(TH3* hisSpaceCharge3D) { SetInputSpaceCharge(hisSpaceCharge3D, 1); } - void SetInputSpaceCharge(TH3* hisSpaceCharge3D, Double_t norm, Int_t side); - void SetInputSpaceCharge(TH3* hisSpaceCharge3D, Int_t side) { SetInputSpaceCharge(hisSpaceCharge3D, 1, side); } - - void SetInputSpaceChargeA(TMatrixD** matricesLookUpCharge) - { - fInterpolatorChargeA->SetValue(matricesLookUpCharge); - fInterpolatorChargeA->InitCubicSpline(); - } - - void SetInputSpaceChargeC(TMatrixD** matricesLookUpCharge) - { - fInterpolatorChargeC->SetValue(matricesLookUpCharge); - fInterpolatorChargeC->InitCubicSpline(); - } - - void SetNRRows(Int_t nRRow) { fNRRows = nRRow; } - - void SetNPhiSlices(Int_t nPhiSlice) { fNPhiSlices = nPhiSlice; } - - void SetNZColumns(Int_t nZColumn) { fNZColumns = nZColumn; } - - Int_t GetNRRows() { return fNRRows; } - - Int_t GetNPhiSlices() { return fNPhiSlices; } - - Int_t GetNZColumns() { return fNZColumns; } - - void SetPoissonSolver(AliTPCPoissonSolver* poissonSolver) - { - if (fPoissonSolver != nullptr) { - delete fPoissonSolver; - } - fPoissonSolver = poissonSolver; - } - - AliTPCPoissonSolver* GetPoissonSolver() { return fPoissonSolver; } - - void SetInterpolationOrder(Int_t order); - - Int_t GetInterpolationOrder() { return fInterpolationOrder; } - - void SetOmegaTauT1T2(Float_t omegaTau, Float_t t1, Float_t t2) - { - const Double_t wt0 = t2 * omegaTau; - fC0 = 1. / (1. + wt0 * wt0); - const Double_t wt1 = t1 * omegaTau; - fC1 = wt1 / (1. + wt1 * wt1); - }; - - void SetC0C1(Float_t c0, Float_t c1) - { - fC0 = c0; - fC1 = c1; - } - - Float_t GetC0() const { return fC0; } - - Float_t GetC1() const { return fC1; } - - void SetCorrectionFactor(Float_t correctionFactor) { fCorrectionFactor = correctionFactor; } - - Float_t GetCorrectionFactor() const { return fCorrectionFactor; } - - void InverseDistortionMaps(TMatrixD** matricesCharge, TMatrixD** matricesEr, TMatrixD** matricesEPhi, - TMatrixD** matricesEz, TMatrixD** matricesInvLocalIntErDz, - TMatrixD**, TMatrixD** matricesInvLocalEz, - TMatrixD** matricesDistDrDz, TMatrixD** matricesDistDPhiRDz, TMatrixD** matricesDistDz, - const Int_t nRRow, const Int_t nZColumn, const Int_t phiSlice, const Int_t nStep, - const Bool_t useCylAC, Int_t stepR, Int_t stepZ, Int_t stepPhi, Int_t interpType); - - void InverseDistortionMapsNoDrift(TMatrixD** matricesCharge, TMatrixD** matricesEr, TMatrixD** matricesEPhi, - TMatrixD** matricesEz, TMatrixD** matricesInvLocalIntErDz, - TMatrixD** matricesInvLocalIntEPhiDz, TMatrixD** matricesInvLocalEz, - TMatrixD** matricesDistDrDz, TMatrixD** matricesDistDPhiRDz, - TMatrixD** matricesDistDz, const Int_t nRRow, const Int_t nZColumn, - const Int_t phiSlice); - - void GetCorrectionCylNoDrift(const Float_t x[], const Short_t roc, Float_t dx[]); - - void GetDistortionCylNoDrift(const Float_t x[], Short_t roc, Float_t dx[]); - - void InverseGlobalToLocalDistortionNoDrift(TMatrixD** matricesDistDrDz, TMatrixD** matricesDistDPhiRDz, - TMatrixD** matricesDistDz, Double_t* rList, Double_t* zList, - Double_t* phiList, const Int_t nRRow, const Int_t nZColumn, - const Int_t phiSlice); - - void GetChargeDensity(TMatrixD** matricesChargeA, TMatrixD** matricesChargeC, const TH3* spaceChargeHistogram3D, - const Int_t nRRow, const Int_t nZColumn, const Int_t phiSlice); - - void GetInverseLocalDistortionCyl(const Float_t x[], Short_t roc, Float_t dx[]); - - void GetLocalDistortionCyl(const Float_t x[], Short_t roc, Float_t dx[]); - - void GetLocalCorrectionCyl(const Float_t x[], Short_t roc, Float_t dx[]); - - void SetIrregularGridSize(Int_t size) { fIrregularGridSize = size; } - - Int_t GetIrregularGridSize() { return fIrregularGridSize; } - - Int_t GetRBFKernelType() { return fRBFKernelType; } - - void SetPotentialBoundaryAndChargeFormula(TFormula* vTestFunction, TFormula* rhoTestFunction); - TFormula* GetPotentialVFormula() const { return fFormulaPotentialV; } - TFormula* GetChargeRhoFormula() const { return fFormulaChargeRho; } - - void SetBoundaryIFCA(TF1* f1) { fFormulaBoundaryIFCA = f1; } - - void SetBoundaryIFCC(TF1* f1) { fFormulaBoundaryIFCC = f1; } - - void SetBoundaryOFCA(TF1* f1) { fFormulaBoundaryOFCA = f1; } - - void SetBoundaryOFCC(TF1* f1) { fFormulaBoundaryOFCC = f1; } - - void SetBoundaryROCA(TF1* f1) { fFormulaBoundaryROCA = f1; } - - void SetBoundaryROCC(TF1* f1) { fFormulaBoundaryROCC = f1; } - - void SetBoundaryCE(TF1* f1) { fFormulaBoundaryCE = f1; } - - void SetElectricFieldFormula(TFormula* formulaEr, TFormula* formulaEPhi, TFormula* formulaEz) - { - fFormulaEr = formulaEr; - fFormulaEPhi = formulaEPhi; - fFormulaEz = formulaEz; - } - TFormula* GetErFormula() const { return fFormulaEr; } - TFormula* GetEPhiFormula() const { return fFormulaEPhi; } - TFormula* GetEzFormula() const { return fFormulaEz; } - - Float_t GetSpaceChargeDensity(Float_t r, Float_t phi, Float_t z); - Float_t GetPotential(Float_t r, Float_t phi, Float_t z); - void GetElectricFieldCyl(const Float_t x[], Short_t roc, Double_t dx[]); - struct Profile { - Double_t poissonSolverTime; - Double_t electricFieldTime; - Double_t localDistortionTime; - Double_t globalDistortionTime; - Double_t interpolationInitTime; - Int_t iteration; - }; - - Profile GetProfile() { return myProfile; } - void SetIntegrationStrategy(Int_t integrationStrategy) { fIntegrationStrategy = integrationStrategy; } - - void SetDistortionLookupTables(TMatrixD** matrixIntDistDrA, TMatrixD** matrixIntDistDrphiA, TMatrixD** matrixIntDistDzA, TMatrixD** matrixIntDistDrC, TMatrixD** matrixIntDistDrphiC, TMatrixD** matrixIntDistDzC); - - private: - Profile myProfile; //! tri linear, 2->Lagrange interpolation order 2, 3> cubic spline) - Int_t fIrregularGridSize = 3; ///< Size of irregular grid cubes for interpolation (min 3) - Int_t fRBFKernelType = 0; ///< RBF kernel type - Int_t fIntegrationStrategy = 0; ///< Strategy for integration - - TMatrixD** fMatrixIntDistDrEzA; //! look-up table for global distortion side A - AliTPCLookUpTable3DInterpolatorD* fLookupIntCorrA = nullptr; //-> look-up table for global correction side A - AliTPCLookUpTable3DInterpolatorD* fLookupIntDistC = nullptr; //-> look-up table for global distortion side C - AliTPCLookUpTable3DInterpolatorD* fLookupIntCorrC = nullptr; //-> look-up table for global correction side C - AliTPCLookUpTable3DInterpolatorIrregularD* fLookupIntCorrIrregularA = nullptr; //! -#include "AliTPCSpaceCharge3DCalc.h" - -/// @brief Basic test if we can create the method class -BOOST_AUTO_TEST_CASE(TPCSpaceChargeBase_test1) -{ - auto spacecharge = new AliTPCSpaceCharge3DCalc; - delete spacecharge; -} diff --git a/Steer/DigitizerWorkflow/src/TPCDigitizerSpec.cxx b/Steer/DigitizerWorkflow/src/TPCDigitizerSpec.cxx index f35c3b30856d4..23bf4da2201d6 100644 --- a/Steer/DigitizerWorkflow/src/TPCDigitizerSpec.cxx +++ b/Steer/DigitizerWorkflow/src/TPCDigitizerSpec.cxx @@ -38,6 +38,7 @@ using namespace o2::framework; using SubSpecificationType = o2::framework::DataAllocator::SubSpecificationType; using DigiGroupRef = o2::dataformats::RangeReference; +using SC = o2::tpc::SpaceCharge; namespace o2 { @@ -89,29 +90,14 @@ class TPCDPLDigitizerTask : public BaseDPLDigitizer readSpaceCharge.push_back(substr); } if (readSpaceCharge[0].size() != 0) { // use pre-calculated space-charge object - std::unique_ptr spaceCharge; if (!gSystem->AccessPathName(readSpaceCharge[0].data())) { - auto fileSC = std::unique_ptr(TFile::Open(readSpaceCharge[0].data())); - if (fileSC->FindKey(readSpaceCharge[1].data())) { - spaceCharge.reset((o2::tpc::SpaceCharge*)fileSC->Get(readSpaceCharge[1].data())); - } - } - if (spaceCharge.get() != nullptr) { - LOG(INFO) << "Using pre-calculated space-charge object: " << readSpaceCharge[1].data(); - mDigitizer.setUseSCDistortions(spaceCharge.release()); + TFile fileSC(readSpaceCharge[0].data(), "READ"); + mDigitizer.setUseSCDistortions(fileSC); } else { LOG(ERROR) << "Space-charge object or file not found!"; } } else { // create new space-charge object either with empty TPC or an initial space-charge density provided by histogram - o2::tpc::SpaceCharge::SCDistortionType distortionType = useDistortions == 2 ? o2::tpc::SpaceCharge::SCDistortionType::SCDistortionsConstant : o2::tpc::SpaceCharge::SCDistortionType::SCDistortionsRealistic; - auto gridSizeString = ic.options().get("gridSize"); - std::vector gridSize; - std::stringstream ss(gridSizeString); - while (ss.good()) { - std::string substr; - getline(ss, substr, ','); - gridSize.push_back(std::stoi(substr)); - } + SC::SCDistortionType distortionType = useDistortions == 2 ? SC::SCDistortionType::SCDistortionsConstant : SC::SCDistortionType::SCDistortionsRealistic; auto inputHistoString = ic.options().get("initialSpaceChargeDensity"); std::vector inputHisto; std::stringstream ssHisto(inputHistoString); @@ -130,9 +116,9 @@ class TPCDPLDigitizerTask : public BaseDPLDigitizer } if (hisSCDensity.get() != nullptr) { LOG(INFO) << "TPC: Providing initial space-charge density histogram: " << hisSCDensity->GetName(); - mDigitizer.setUseSCDistortions(distortionType, hisSCDensity.get(), gridSize[0], gridSize[1], gridSize[2]); + mDigitizer.setUseSCDistortions(distortionType, hisSCDensity.get()); } else { - if (distortionType == SpaceCharge::SCDistortionType::SCDistortionsConstant) { + if (distortionType == SC::SCDistortionType::SCDistortionsConstant) { LOG(ERROR) << "Input space-charge density histogram or file not found!"; } } @@ -368,7 +354,6 @@ o2::framework::DataProcessorSpec getTPCDigitizerSpec(int channel, bool writeGRP, outputs, AlgorithmSpec{adaptFromTask()}, Options{{"distortionType", VariantType::Int, 0, {"Distortion type to be used. 0 = no distortions (default), 1 = realistic distortions (not implemented yet), 2 = constant distortions"}}, - {"gridSize", VariantType::String, "129,144,129", {"Comma separated list of number of bins in (r,phi,z) for distortion lookup tables (r and z can only be 2**N + 1, N=1,2,3,...)"}}, {"initialSpaceChargeDensity", VariantType::String, "", {"Path to root file containing TH3 with initial space-charge density and name of the TH3 (comma separated)"}}, {"readSpaceCharge", VariantType::String, "", {"Path to root file containing pre-calculated space-charge object and name of the object (comma separated)"}}, {"TPCtriggered", VariantType::Bool, false, {"Impose triggered RO mode (default: continuous)"}}}};