Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-12-17 09:19:56

0001 #include "BEmcRecCEMC.h"
0002 
0003 #include "BEmcCluster.h"
0004 #include "BEmcProfile.h"
0005 
0006 #include <cmath>
0007 #include <iostream>
0008 #include <Math/Vector3D.h>   // for TVector3 used in calculateIncidence
0009 #include <algorithm>    // for std::max
0010 using ROOT::Math::XYZVector;
0011 
0012 BEmcRecCEMC::BEmcRecCEMC()
0013 {
0014   Name("BEmcRecCEMC");
0015   SetCylindricalGeometry();
0016 }
0017 
0018 void BEmcRecCEMC::LoadProfile(const std::string& fname)
0019 {
0020   //  std::cout << "Infor from BEmcRecCEMC::LoadProfile(): no external file used for shower profile evaluation in CEMC" << std::endl;
0021   _emcprof = new BEmcProfile(fname);
0022 }
0023 
0024 void BEmcRecCEMC::GetImpactThetaPhi(float xg, float yg, float zg, float& theta, float& phi)
0025 {
0026   theta = 0;
0027   phi = 0;
0028 
0029   //  float theta = atan(sqrt(xg*xg + yg*yg)/fabs(zg-fVz));
0030   float rg = std::sqrt((xg * xg) + (yg * yg));
0031   float theta_twr;
0032   if (std::fabs(zg) <= 15)
0033   {
0034     theta_twr = 0;
0035   }
0036   else if (zg > 15)
0037   {
0038     theta_twr = std::atan2(zg - 15, rg);
0039   }
0040   else
0041   {
0042     theta_twr = std::atan2(zg + 15, rg);
0043   }
0044   float theta_tr = std::atan2(zg - fVz, rg);
0045   theta = std::fabs(theta_tr - theta_twr);
0046   //  phi = atan2(yg,xg);
0047 }
0048 
0049 /*
0050 float BEmcRecCEMC::GetProb(vector<EmcModule> HitList, float ecl, float xg, float yg, float zg, float& chi2, int& ndf)
0051 {
0052   chi2 = 0;
0053   ndf = 0;
0054   float prob = -1;
0055 
0056   //  float theta = atan(sqrt(xg*xg + yg*yg)/fabs(zg-fVz));
0057   float rg = sqrt(xg * xg + yg * yg);
0058   float theta_twr;
0059   if (fabs(zg) <= 15)
0060     theta_twr = 0;
0061   else if (zg > 15)
0062     theta_twr = atan2(zg - 15, rg);
0063   else
0064     theta_twr = atan2(zg + 15, rg);
0065   float theta_tr = atan2(zg - fVz, rg);
0066   float theta = fabs(theta_tr - theta_twr);
0067 
0068   float phi = atan2(yg, xg);
0069   if (_emcprof != nullptr) prob = _emcprof->GetProb(&HitList, fNx, ecl, theta, phi);
0070 
0071   return prob;
0072 }
0073 */
0074 /*
0075 float BEmcRecCEMC::GetProb(vector<EmcModule> HitList, float et, float xg, float yg, float zg, float& chi2, int& ndf)
0076 // et, xg, yg, zg not used here
0077 {
0078   const float thresh = 0.01;
0079   const int DXY = 3;  // 2 is for 5x5 matrix; 3 for 7x7 matrix
0080   const int Nmax = 1000;
0081   float ee[Nmax];
0082   int iyy[Nmax];
0083   int izz[Nmax];
0084 
0085   int ich;
0086   vector<EmcModule>::iterator ph = HitList.begin();
0087 
0088   chi2 = 0;
0089   ndf = 0;
0090 
0091   int nn = 0;
0092 
0093   while (ph != HitList.end())
0094   {
0095     ee[nn] = ph->amp;
0096     if (ee[nn] > thresh)
0097     {
0098       ich = ph->ich;
0099       izz[nn] = ich % fNx;
0100       iyy[nn] = ich / fNx;
0101       nn++;
0102       if (nn >= Nmax)
0103       {
0104       std::cout << "BEmcRec::GetProb: Cluster size is too big. Skipping the rest of the towers" << std::endl;
0105         break;
0106       }
0107     }  // if( ee[nn]
0108     ++ph;
0109   }  // while( ph
0110 
0111   if (nn <= 0) return -1;
0112 
0113   int iy0 = -1, iz0 = -1;
0114   float emax = 0;
0115 
0116   for (int i = 0; i < nn; i++)
0117   {
0118     if (ee[i] > emax)
0119     {
0120       emax = ee[i];
0121       iy0 = iyy[i];
0122       iz0 = izz[i];
0123     }
0124   }
0125 
0126   if (emax <= 0) return -1;
0127 
0128   int id;
0129   float etot = 0;
0130   float sz = 0;
0131   float sy = 0;
0132 
0133   for (int idz = -DXY; idz <= DXY; idz++)
0134   {
0135     for (int idy = -DXY; idy <= DXY; idy++)
0136     {
0137       id = GetTowerID(iy0 + idy, iz0 + idz, nn, iyy, izz, ee);
0138       if (id >= 0)
0139       {
0140         etot += ee[id];
0141         sz += ee[id] * (iz0 + idz);
0142         sy += ee[id] * (iy0 + idy);
0143       }
0144     }
0145   }
0146   float zcg = sz / etot;  // Here cg allowed to be out of range
0147   float ycg = sy / etot;
0148   int iz0cg = int(zcg + 0.5);
0149   int iy0cg = int(ycg + 0.5);
0150   float ddz = fabs(zcg - iz0cg);
0151   float ddy = fabs(ycg - iy0cg);
0152 
0153   int isz = 1;
0154   if (zcg - iz0cg < 0) isz = -1;
0155   int isy = 1;
0156   if (ycg - iy0cg < 0) isy = -1;
0157 
0158   // 4 central towers: 43
0159   //                   12
0160   // Tower 1 - central one
0161   float e1, e2, e3, e4;
0162   e1 = e2 = e3 = e4 = 0;
0163   id = GetTowerID(iy0cg, iz0cg, nn, iyy, izz, ee);
0164   if (id >= 0) e1 = ee[id];
0165   id = GetTowerID(iy0cg, iz0cg + isz, nn, iyy, izz, ee);
0166   if (id >= 0) e2 = ee[id];
0167   id = GetTowerID(iy0cg + isy, iz0cg + isz, nn, iyy, izz, ee);
0168   if (id >= 0) e3 = ee[id];
0169   id = GetTowerID(iy0cg + isy, iz0cg, nn, iyy, izz, ee);
0170   if (id >= 0) e4 = ee[id];
0171 
0172   float e1t = (e1 + e2 + e3 + e4) / etot;
0173   float e2t = (e1 + e2 - e3 - e4) / etot;
0174   float e3t = (e1 - e2 - e3 + e4) / etot;
0175   float e4t = (e3) / etot;
0176   //  float e5t = (e2+e4)/etot;
0177 
0178   float rr = sqrt((0.5 - ddz) * (0.5 - ddz) + (0.5 - ddy) * (0.5 - ddy));
0179 
0180   float c1, c2, c11;
0181 
0182   float logE = log(etot);
0183 
0184   // e1 energy is the most effective for PID if properly tuned !
0185   // Discrimination power is very sensitive to paramter c1: the bigger it is
0186   // the better discrimination;
0187   c1 = 0.95;
0188   c2 = 0.0066364 * logE + 0.00466667;
0189   if (c2 < 0) c2 = 0;
0190   float e1p = c1 - c2 * rr * rr;
0191   c1 = 0.034 - 0.01523 * logE + 0.0029 * logE * logE;
0192   float err1 = c1;
0193 
0194   // For e2
0195   c1 = 0.00844086 + 0.00645359 * logE - 0.00119381 * logE * logE;
0196   if (etot > 15) c1 = 0.00844086 + 0.00645359 * log(15.) - 0.00119381 * log(15.) * log(15.);  // Const at etot>15GeV
0197   if (c1 < 0) c1 = 0;
0198   c2 = 3.9;                                                      // Fixed
0199   float e2p = sqrt(c1 + 0.25 * c2) - sqrt(c1 + c2 * ddy * ddy);  // =0 at ddy=0.5
0200 
0201   c1 = 0.0212333 + 0.0420473 / etot;
0202   c2 = 0.090;  // Fixed
0203   float err2 = c1 + c2 * ddy;
0204   if (ddy > 0.3) err2 = c1 + c2 * 0.3;  // Const at ddy>0.3
0205 
0206   // For e3
0207   c1 = 0.0107857 + 0.0056801 * logE - 0.000892016 * logE * logE;
0208   if (etot > 15) c1 = 0.0107857 + 0.0056801 * log(15.) - 0.000892016 * log(15.) * log(15.);  // Const at etot>15GeV
0209   if (c1 < 0) c1 = 0;
0210   c2 = 3.9;                                                      // Fixed
0211   float e3p = sqrt(c1 + 0.25 * c2) - sqrt(c1 + c2 * ddz * ddz);  // =0 at ddz=0.5
0212 
0213   //  c1 = 0.0200 + 0.042/etot;
0214   c1 = 0.0167 + 0.058 / etot;
0215   c2 = 0.090;  // Fixed
0216   float err3 = c1 + c2 * ddz;
0217   if (ddz > 0.3) err3 = c1 + c2 * 0.3;  // Const at ddz>0.3
0218 
0219   // For e4
0220   float e4p = 0.25 - 0.668 * rr + 0.460 * rr * rr;
0221   c11 = 0.171958 + 0.0142421 * logE - 0.00214827 * logE * logE;
0222   //  c11 = 0.171085 + 0.0156215*logE - -0.0025809*logE*logE;
0223   float err4 = 0.102 - 1.43 * c11 * rr + c11 * rr * rr;  // Min is set to x=1.43/2.
0224   err4 *= 1.1;
0225 
0226   chi2 = 0.;
0227   chi2 += (e1p - e1t) * (e1p - e1t) / err1 / err1;
0228   chi2 += (e2p - e2t) * (e2p - e2t) / err2 / err2;
0229   chi2 += (e3p - e3t) * (e3p - e3t) / err3 / err3;
0230   chi2 += (e4p - e4t) * (e4p - e4t) / err4 / err4;
0231   ndf = 4;
0232 
0233   //  chi2 /= 1.1;
0234   float prob = TMath::Prob(chi2, ndf);
0235 
0236   return prob;
0237 }
0238 */
0239 
0240 bool BEmcRecCEMC::calculateIncidence(float x, float y,
0241                                      float& a_phi_sgn, float& a_eta_sgn)
0242 {
0243   // ────────────────────────────────────────────────────────────────────────────
0244   // Step 1) Owner tower in "tower units"
0245   // ────────────────────────────────────────────────────────────────────────────
0246   const int ix = EmcCluster::lowint(x + 0.5F);
0247   const int iy = EmcCluster::lowint(y + 0.5F);
0248 
0249   TowerGeom g{};
0250   if (!GetTowerGeometry(ix, iy, g))
0251   {
0252     return false;  // need detailed geometry
0253   }
0254 
0255   // Sub-cell offsets δ ∈ (−0.5, +0.5]
0256   const float dx = x - ix;
0257   const float dy = y - iy;
0258 
0259   // ────────────────────────────────────────────────────────────────────────────
0260   // Step 2) Front-surface point F in cm:
0261   //         F = C + δx * eφ_geom + δy * eη_geom
0262   // ────────────────────────────────────────────────────────────────────────────
0263   const XYZVector C(g.Xcenter, g.Ycenter, g.Zcenter);
0264   const XYZVector ephi_geom(g.dX[0], g.dY[0], g.dZ[0]);
0265   const XYZVector eeta_geom(g.dX[1], g.dY[1], g.dZ[1]);
0266 
0267   if (std::sqrt(ephi_geom.Mag2()) < 1e-9 || std::sqrt(eeta_geom.Mag2()) < 1e-9)
0268   {
0269     return false;
0270   }
0271 
0272   const XYZVector F = C + dx * ephi_geom + dy * eeta_geom;
0273 
0274   // ────────────────────────────────────────────────────────────────────────────
0275   // Step 3) Unit ray from vertex to the front surface
0276   // ────────────────────────────────────────────────────────────────────────────
0277   const XYZVector V(0., 0., fVz);
0278   XYZVector pF = F - V;
0279   const double pFmag = std::sqrt(pF.Mag2());
0280   if (!(pFmag > 0.0))
0281   {
0282     return false;
0283   }
0284   pF *= (1.0 / pFmag);
0285 
0286   // Helper: from a basis {un,uphi,ueta} and ray pF, compute projections
0287   // and signed incidence angles (no cosines returned).
0288   auto basis_to_incidence =
0289     [&](const XYZVector& un_b, const XYZVector& uphi_b, const XYZVector& ueta_b,
0290         double& pn_b, double& pph_b, double& pet_b,
0291         double& aphi_b, double& aeta_b) -> bool
0292   {
0293     pn_b  = pF.Dot(un_b);
0294     pph_b = pF.Dot(uphi_b);
0295     pet_b = pF.Dot(ueta_b);
0296 
0297     const double apn_b      = std::max(1e-12, std::fabs(pn_b));
0298     const double aphi_mag_b = std::atan2(std::fabs(pph_b), apn_b);
0299     const double aeta_mag_b = std::atan2(std::fabs(pet_b), apn_b);
0300 
0301     const double sgn_phi_b = (pph_b >= 0.0) ? +1.0 : -1.0;
0302     const double sgn_eta_b = (pet_b >= 0.0) ? +1.0 : -1.0;
0303 
0304     aphi_b = sgn_phi_b * aphi_mag_b;
0305     aeta_b = sgn_eta_b * aeta_mag_b;
0306 
0307     return (std::isfinite(aphi_b) && std::isfinite(aeta_b));
0308   };
0309 
0310   // ────────────────────────────────────────────────────────────────────────────
0311   // Step 4) MECHANICAL frame from RawTowerGeomv5 rotations
0312   // ────────────────────────────────────────────────────────────────────────────
0313   XYZVector un_mech;
0314   XYZVector uphi_mech;
0315   XYZVector ueta_mech;
0316   {
0317     const double rx = g.rotX;
0318     const double ry = g.rotY;
0319     const double rz = g.rotZ;
0320 
0321     const double cx = std::cos(rx);
0322     const double sx = std::sin(rx);
0323     const double cy = std::cos(ry);
0324     const double sy = std::sin(ry);
0325     const double cz = std::cos(rz);
0326     const double sz = std::sin(rz);
0327 
0328     auto applyRot = [&](const XYZVector& v) -> XYZVector
0329     {
0330       // Apply Rz * Ry * Rx to local vector v (column-vector convention).
0331       double vx = v.X();
0332       double vy = v.Y();
0333       double vz = v.Z();
0334 
0335       // Rx
0336       const double vx1 = vx;
0337       const double vy1 =  cx * vy - sx * vz;
0338       const double vz1 =  sx * vy + cx * vz;
0339 
0340       // Ry
0341       const double vx2 =  cy * vx1 + sy * vz1;
0342       const double vy2 =  vy1;
0343       const double vz2 = -sy * vx1 + cy * vz1;
0344 
0345       // Rz
0346       const double vx3 =  cz * vx2 - sz * vy2;
0347       const double vy3 =  sz * vx2 + cz * vy2;
0348       const double vz3 =  vz2;
0349 
0350       return XYZVector(vx3, vy3, vz3);
0351     };
0352 
0353     // Local axes: ẑ = bar axis, x̂/ŷ = transverse directions
0354     XYZVector u_axis = applyRot(XYZVector(0., 0., 1.)); // tower "z" axis
0355     const XYZVector u_x    = applyRot(XYZVector(1., 0., 0.)); // tower "x" axis
0356     const XYZVector u_y    = applyRot(XYZVector(0., 1., 0.)); // tower "y" axis
0357 
0358     // Normalize axis and enforce outward direction
0359     const double amag = std::sqrt(u_axis.Mag2());
0360     if (!(amag > 0.0))
0361     {
0362       return false;
0363     }
0364 
0365     u_axis *= (1.0 / amag);
0366     if (u_axis.Dot(C) < 0.0)
0367     {
0368       u_axis = -u_axis;
0369     }
0370 
0371     un_mech = u_axis;
0372 
0373     // φ̂: project rotated local x̂ into plane ⟂ axis; normalize
0374     uphi_mech = u_x - un_mech * u_x.Dot(un_mech);
0375     const double uphim = std::sqrt(uphi_mech.Mag2());
0376     if (!(uphim > 0.0))
0377     {
0378       return false;
0379     }
0380     uphi_mech *= (1.0 / uphim);
0381 
0382     // η̂: start from rotated local ŷ, orthogonalize to {un,uphi}; normalize
0383     ueta_mech = u_y
0384               - un_mech   * u_y.Dot(un_mech)
0385               - uphi_mech * u_y.Dot(uphi_mech);
0386     const double uetam = std::sqrt(ueta_mech.Mag2());
0387     if (!(uetam > 0.0))
0388     {
0389       return false;
0390     }
0391     ueta_mech *= (1.0 / uetam);
0392 
0393     // Ensure right-handed triad
0394     if (un_mech.Cross(uphi_mech).Dot(ueta_mech) < 0.0)
0395     {
0396       ueta_mech = -ueta_mech;
0397     }
0398 
0399     // Align mechanical φ-axis sign with geometric φ tangent if available
0400     XYZVector uphi_ref = ephi_geom;
0401     const double refMag = std::sqrt(uphi_ref.Mag2());
0402     if (refMag > 0.0)
0403     {
0404       uphi_ref *= (1.0 / refMag);
0405       if (uphi_mech.Dot(uphi_ref) < 0.0)
0406       {
0407         uphi_mech = -uphi_mech;
0408         ueta_mech = -ueta_mech;
0409       }
0410     }
0411   }
0412 
0413   // ────────────────────────────────────────────────────────────────────────────
0414   // Step 5) Compute incidence in the mechanical frame
0415   // ────────────────────────────────────────────────────────────────────────────
0416   double pn   = 0.0;
0417   double pph  = 0.0;
0418   double pet  = 0.0;
0419   double aphi = 0.0;
0420   double aeta = 0.0;
0421 
0422   if (!basis_to_incidence(un_mech, uphi_mech, ueta_mech,
0423                           pn, pph, pet,
0424                           aphi, aeta))
0425   {
0426     return false;
0427   }
0428 
0429   a_phi_sgn = static_cast<float>(aphi);
0430   a_eta_sgn = static_cast<float>(aeta);
0431 
0432   m_lastAlphaPhiSigned = a_phi_sgn;
0433   m_lastAlphaEtaSigned = a_eta_sgn;
0434 
0435   const bool ok =
0436       (std::isfinite(a_phi_sgn) && std::isfinite(a_eta_sgn));
0437 
0438   return ok;
0439 }
0440 
0441 void BEmcRecCEMC::CorrectShowerDepth(int ix, int iy, float E, float xA, float yA, float zA, float& xC, float& yC, float& zC)
0442 {
0443   if (!m_UseCorrectShowerDepth)
0444   {
0445     xC = xA;
0446     yC = yA;
0447     zC = zA;
0448     return;
0449   }
0450 
0451   float logE = log(0.1);
0452   if (E > 0.1)
0453   {
0454     logE = std::log(E);
0455   }
0456 
0457   float phi = 0;
0458   // Rotate by phi (towers are tilted by a fixed angle in phi by ~9 deg?)
0459   // Just tuned from sim data
0460   if (m_UseDetailedGeometry)
0461   {
0462     phi = 0.0033 - 0.0010 * logE;
0463   }
0464   else
0465   {
0466     phi = 0.0016 - 0.0010 * logE;
0467   }
0468   xC = xA * std::cos(phi) - yA * std::sin(phi);
0469   yC = xA * std::sin(phi) + yA * std::cos(phi);
0470 
0471   // Correction in z
0472   float rA = std::sqrt((xA * xA) + (yA * yA));
0473   float theta_twr;
0474   if (m_UseDetailedGeometry)
0475   {
0476     // Read the angle right from the detailed RawTowerGeom objects
0477     TowerGeom geom0;
0478     GetTowerGeometry(ix, iy, geom0);
0479     theta_twr = M_PI / 2 + geom0.rotX;
0480   }
0481   else
0482   {
0483     // Use the approximate default tower angles array.
0484     theta_twr = M_PI / 2 - angles[iy];
0485   }
0486 
0487   float theta_tr = std::atan2(zA - fVz, rA);
0488 
0489   // Shower CG in long. direction
0490   // Different tuning for the approximate and detailed geometry
0491   float L = 0;
0492   if (m_UseDetailedGeometry)
0493   {
0494     L = -2.67787 + 0.924138 * logE;
0495   }
0496   else
0497   {
0498     L = -1.79968 + 0.837322 * logE;
0499   }
0500   float dz = L * std::sin(theta_tr - theta_twr) / std::cos(theta_twr);
0501 
0502   if (!m_UseDetailedGeometry)
0503   {
0504     // Not strictly speaking a "shower depth correction" but rather a baseline correction
0505     // The factor 0.10 accounts for the fact that the approximate geometry
0506     // is projected at 93.5cm, which is roughly 90 % of the actual average EMCal radius (~ 105 cm)
0507     dz -= fVz * 0.10;
0508   }
0509 
0510   zC = zA - dz;
0511 
0512   // zvtx-dependent pseudorapidity-correction
0513   // At large zvtx (> 20 cm), the sawtooth shape of the EMCal is no longer projective wrt collision point,
0514   // it introduces a bias which can be approximately corrected with the linear formula below
0515   if (m_UseDetailedGeometry && std::abs(fVz) > 20)
0516   {
0517     float eta = std::asinh((zC - fVz) / rA);
0518     if (fVz > 0)
0519     {
0520       eta -= slopes_[iy] * (fVz - 20);
0521     }
0522     else
0523     {
0524       eta -= slopes_[fNy - iy - 1] * (fVz + 20);
0525     }
0526     zC = fVz + rA * std::sinh(eta);
0527   }
0528 
0529   return;
0530 }
0531 
0532 void BEmcRecCEMC::CorrectEnergy(float Energy, float /*x*/, float /*y*/,
0533                                 float& Ecorr)
0534 {
0535   // Corrects the EM Shower Energy for attenuation in fibers and
0536   // long energy leakage
0537   //
0538   // (x,y) - impact position (cm) in Sector frame
0539   /*
0540   float sinT;
0541   float att, leak, corr;
0542   const float leakPar = 0.0033; // parameter from fit
0543   const float attPar = 120; // Attenuation in module (cm)
0544   const float X0 = 2; // radiation length (cm)
0545 
0546   *Ecorr = Energy;
0547   if( Energy < 0.01 ) return;
0548 
0549   GetImpactAngle(x, y, &sinT); // sinT not used so far
0550   leak = 2-sqrt(1+leakPar*log(1+Energy)*log(1+Energy));
0551   att = exp(log(Energy)*X0/attPar);
0552   corr = leak*att;
0553   *Ecorr = Energy/corr;
0554   */
0555   Ecorr = Energy;
0556 }
0557 
0558 void BEmcRecCEMC::CorrectECore(float Ecore, float /*x*/, float /*y*/, float& Ecorr)
0559 {
0560   // Corrects the EM Shower Core Energy for attenuation in fibers,
0561   // long energy leakage and angle dependance
0562   //
0563   // (x,y) - impact position (cm) in Sector frame
0564 
0565   const float c0 = 0.950;  // For no threshold
0566   Ecorr = Ecore / c0;
0567 }
0568 
0569 void BEmcRecCEMC::CorrectPosition(float Energy, float x, float y,
0570                                   float& xc, float& yc)
0571 {
0572   // Corrects the Shower Center of Gravity for the systematic shift due to
0573   // limited tower size
0574   //
0575   // Everything here is in tower units.
0576   // (x,y) - CG position, (xc,yc) - corrected position
0577 
0578   float bx;
0579   float by;
0580   float x0;
0581   float y0;
0582   int ix0;
0583   int iy0;
0584 
0585   if (!m_UseCorrectPosition)
0586   {
0587     xc = x;
0588     yc = y;
0589     return;
0590   }
0591 
0592   if (Energy < 0.01)
0593   {
0594     return;
0595   }
0596 
0597   bx = 0.15;
0598   by = 0.15;
0599 
0600   x0 = x;
0601   ix0 = EmcCluster::lowint(x0 + 0.5);
0602 
0603   if (std::abs(x0 - ix0) <= 0.5)
0604   {
0605     x0 = ix0 + bx * asinh(2. * (x0 - ix0) * sinh(0.5 / bx));
0606   }
0607   else
0608   {
0609     x0 = x;
0610     std::cout << "????? Something wrong in BEmcRecCEMC::CorrectPosition: x = "
0611               << x << " dx = " << x0 - ix0 << std::endl;
0612   }
0613 
0614   // Correct for phi bias within module of 8 towers
0615 // NOLINTNEXTLINE(bugprone-incorrect-roundings)
0616   int ix8 = int(x + 0.5) / 8; // that is hokey - suggest lroundf(x)
0617   float x8 = x + 0.5 - (ix8 * 8) - 4;  // from -4 to +4
0618   float dx = 0;
0619   if (m_UseDetailedGeometry)
0620   {
0621     // Don't know why there is a different factor for each tower of the sector
0622     // Just tuned from MC
0623 // NOLINTNEXTLINE(bugprone-incorrect-roundings)
0624     int local_ix8 = int(x+0.5) - ix8 * 8; // that is hokey - suggest lroundf(x)
0625     dx = factor_[local_ix8] * x8 / 4.;
0626   }
0627   else
0628   {
0629     dx = 0.10 * x8 / 4.;
0630     if (std::fabs(x8) > 3.3)
0631     {
0632       dx = 0;  // Don't correct near the module edge
0633     }
0634   }
0635 
0636   xc = x0 - dx;
0637   while (xc < -0.5)
0638   {
0639     xc += float(fNx);
0640   }
0641   while (xc >= fNx - 0.5)
0642   {
0643     xc -= float(fNx);
0644   }
0645 
0646   y0 = y;
0647   iy0 = EmcCluster::lowint(y0 + 0.5);
0648 
0649   if (std::abs(y0 - iy0) <= 0.5)
0650   {
0651     y0 = iy0 + by * std::asinh(2. * (y0 - iy0) * sinh(0.5 / by));
0652   }
0653   else
0654   {
0655     y0 = y;
0656     std::cout << "????? Something wrong in BEmcRecCEMC::CorrectPosition: y = "
0657               << y << "dy = " << y0 - iy0 << std::endl;
0658   }
0659   yc = y0;
0660 }