Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-16 09:28:01

0001 // FIXME SPDX-License-Identifier: TBD
0002 // Copyright (C) 1997 - 2025 The STAR Collaboration, Xin Dong, Rongrong Ma
0003 
0004 #include <Evaluator/DD4hepUnits.h>
0005 #include <edm4eic/Track.h>
0006 #include <edm4eic/Trajectory.h>
0007 #include <edm4eic/unit_system.h>
0008 #include <edm4hep/Vector2f.h>
0009 #include <edm4hep/Vector3f.h>
0010 #include <edm4hep/utils/vector_utils.h>
0011 #include <podio/RelationRange.h>
0012 #include <algorithm>
0013 #include <cmath>
0014 #include <iostream>
0015 #include <limits>
0016 #include <utility>
0017 #include <vector>
0018 
0019 #include "algorithms/reco/Helix.h"
0020 
0021 namespace eicrecon {
0022 
0023 const double Helix::NoSolution = 3.e+33;
0024 
0025 // basic constructor
0026 Helix::Helix(double c, double d, double phase, const edm4hep::Vector3f& o, int h) {
0027   setParameters(c, d, phase, o, h);
0028 }
0029 
0030 // momentum in GeV, position in cm, B in Tesla
0031 Helix::Helix(const edm4hep::Vector3f& p, const edm4hep::Vector3f& o, const double B, const int q) {
0032   setParameters(p, o, B, q);
0033 }
0034 
0035 // construct using ReconstructParticle
0036 Helix::Helix(const edm4eic::ReconstructedParticle& p, const double b_field) {
0037   const auto& tracks = p.getTracks();
0038   for (const auto& trk : tracks) {
0039     const auto& traj    = trk.getTrajectory();
0040     const auto& trkPars = traj.getTrackParameters();
0041     for (const auto& par : trkPars) {
0042       setParameters(par, b_field);
0043     }
0044   }
0045 }
0046 
0047 // construct using TrackParameteters
0048 void Helix::setParameters(const edm4eic::TrackParameters& trk, const double b_field) {
0049   const auto mom    = edm4hep::utils::sphericalToVector(1.0 / std::abs(trk.getQOverP()),
0050                                                         trk.getTheta(), trk.getPhi());
0051   const auto charge = std::copysign(1., trk.getQOverP());
0052   const auto phi    = trk.getPhi();
0053   const auto loc    = trk.getLoc();
0054   edm4hep::Vector3f pos(-1. * loc.a * sin(phi), loc.a * cos(phi), loc.b); // PCA point
0055 
0056   setParameters(mom * edm4eic::unit::GeV / dd4hep::GeV, pos * edm4eic::unit::mm / edm4eic::unit::cm,
0057                 b_field, charge);
0058 }
0059 
0060 void Helix::setParameters(const edm4hep::Vector3f& p, const edm4hep::Vector3f& o, const double B,
0061                           const int q) {
0062   mH = (q * B <= 0) ? 1 : -1;
0063   if (p.y == 0 && p.x == 0)
0064     setPhase((M_PI / 4) * (1 - 2. * mH));
0065   else
0066     setPhase(atan2(p.y, p.x) - mH * M_PI / 2);
0067   setDipAngle(atan2(p.z, edm4hep::utils::magnitudeTransverse(p)));
0068   mOrigin = o;
0069 
0070   double curvature_val =
0071       fabs((dd4hep::c_light * dd4hep::nanosecond / dd4hep::meter * q * B / dd4hep::tesla) /
0072            (edm4hep::utils::magnitude(p) / dd4hep::GeV * mCosDipAngle) / dd4hep::meter);
0073   setCurvature(curvature_val);
0074 }
0075 
0076 void Helix::setParameters(double c, double dip, double phase, const edm4hep::Vector3f& o, int h) {
0077   //
0078   //  The order in which the parameters are set is important
0079   //  since setCurvature might have to adjust the others.
0080   //
0081   mH = (h >= 0) ? 1 : -1; // Default is: positive particle
0082                           //             positive field
0083   mOrigin = o;
0084   setDipAngle(dip);
0085   setPhase(phase);
0086 
0087   //
0088   // Check for singularity and correct for negative curvature.
0089   // May change mH and mPhase. Must therefore be set last.
0090   //
0091   setCurvature(c);
0092 
0093   //
0094   // For the case B=0, h is ill defined. In the following we
0095   // always assume h = +1. Since phase = psi - h * pi/2
0096   // we have to correct the phase in case h = -1.
0097   // This assumes that the user uses the same h for phase
0098   // as the one he passed to the constructor.
0099   //
0100   if (mSingularity && mH == -1) {
0101     mH = +1;
0102     setPhase(mPhase - M_PI);
0103   }
0104 }
0105 
0106 void Helix::setCurvature(double val) {
0107   if (val < 0) {
0108     mCurvature = -val;
0109     mH         = -mH;
0110     setPhase(mPhase + M_PI);
0111   } else
0112     mCurvature = val;
0113 
0114   if (fabs(mCurvature) <= std::numeric_limits<double>::epsilon())
0115     mSingularity = true; // straight line
0116   else
0117     mSingularity = false; // curved
0118 }
0119 
0120 void Helix::setPhase(double val) {
0121   mPhase    = val;
0122   mCosPhase = cos(mPhase);
0123   mSinPhase = sin(mPhase);
0124   if (fabs(mPhase) > M_PI)
0125     mPhase = atan2(mSinPhase, mCosPhase); // force range [-pi,pi]
0126 }
0127 
0128 void Helix::setDipAngle(double val) {
0129   mDipAngle    = val;
0130   mCosDipAngle = cos(mDipAngle);
0131   mSinDipAngle = sin(mDipAngle);
0132 }
0133 
0134 double Helix::xcenter() const {
0135   if (mSingularity)
0136     return 0;
0137   else
0138     return mOrigin.x - mCosPhase / mCurvature;
0139 }
0140 
0141 double Helix::ycenter() const {
0142   if (mSingularity)
0143     return 0;
0144   else
0145     return mOrigin.y - mSinPhase / mCurvature;
0146 }
0147 
0148 double Helix::fudgePathLength(const edm4hep::Vector3f& p) const {
0149   double s;
0150   double dx = p.x - mOrigin.x;
0151   double dy = p.y - mOrigin.y;
0152 
0153   if (mSingularity) {
0154     s = (dy * mCosPhase - dx * mSinPhase) / mCosDipAngle;
0155   } else {
0156     s = atan2(dy * mCosPhase - dx * mSinPhase, 1 / mCurvature + dx * mCosPhase + dy * mSinPhase) /
0157         (mH * mCurvature * mCosDipAngle);
0158   }
0159   return s;
0160 }
0161 
0162 double Helix::distance(const edm4hep::Vector3f& p, bool scanPeriods) const {
0163   return edm4hep::utils::magnitude(this->at(pathLength(p, scanPeriods)) - p);
0164 }
0165 
0166 double Helix::pathLength(const edm4hep::Vector3f& p, bool scanPeriods) const {
0167   //
0168   //  Returns the path length at the distance of closest
0169   //  approach between the helix and point p.
0170   //  For the case of B=0 (straight line) the path length
0171   //  can be calculated analytically. For B>0 there is
0172   //  unfortunately no easy solution to the problem.
0173   //  Here we use the Newton method to find the root of the
0174   //  referring equation. The 'fudgePathLength' serves
0175   //  as a starting value.
0176   //
0177   double s;
0178   double dx = p.x - mOrigin.x;
0179   double dy = p.y - mOrigin.y;
0180   double dz = p.z - mOrigin.z;
0181 
0182   if (mSingularity) {
0183     s = mCosDipAngle * (mCosPhase * dy - mSinPhase * dx) + mSinDipAngle * dz;
0184   } else { //
0185     const double MaxPrecisionNeeded = edm4eic::unit::um;
0186     const int MaxIterations         = 100;
0187 
0188     //
0189     // The math is taken from Maple with C(expr,optimized) and
0190     // some hand-editing. It is not very nice but efficient.
0191     //
0192     double t34 = mCurvature * mCosDipAngle * mCosDipAngle;
0193     double t41 = mSinDipAngle * mSinDipAngle;
0194     double t6, t7, t11, t12, t19;
0195 
0196     //
0197     // Get a first guess by using the dca in 2D. Since
0198     // in some extreme cases we might be off by n periods
0199     // we add (subtract) periods in case we get any closer.
0200     //
0201     s = fudgePathLength(p);
0202 
0203     if (scanPeriods) {
0204       double ds = period();
0205       int j, jmin    = 0;
0206       double d, dmin = edm4hep::utils::magnitude(at(s) - p);
0207       for (j = 1; j < MaxIterations; j++) {
0208         if ((d = edm4hep::utils::magnitude(at(s + j * ds) - p)) < dmin) {
0209           dmin = d;
0210           jmin = j;
0211         } else
0212           break;
0213       }
0214       for (j = -1; -j < MaxIterations; j--) {
0215         if ((d = edm4hep::utils::magnitude(at(s + j * ds) - p)) < dmin) {
0216           dmin = d;
0217           jmin = j;
0218         } else
0219           break;
0220       }
0221       if (jmin)
0222         s += jmin * ds;
0223     }
0224 
0225     //
0226     // Newtons method:
0227     // Stops after MaxIterations iterations or if the required
0228     // precision is obtained. Whatever comes first.
0229     //
0230     double sOld = s;
0231     for (int i = 0; i < MaxIterations; i++) {
0232       t6  = mPhase + s * mH * mCurvature * mCosDipAngle;
0233       t7  = cos(t6);
0234       t11 = dx - (1 / mCurvature) * (t7 - mCosPhase);
0235       t12 = sin(t6);
0236       t19 = dy - (1 / mCurvature) * (t12 - mSinPhase);
0237       s -= (t11 * t12 * mH * mCosDipAngle - t19 * t7 * mH * mCosDipAngle -
0238             (dz - s * mSinDipAngle) * mSinDipAngle) /
0239            (t12 * t12 * mCosDipAngle * mCosDipAngle + t11 * t7 * t34 +
0240             t7 * t7 * mCosDipAngle * mCosDipAngle + t19 * t12 * t34 + t41);
0241       if (fabs(sOld - s) < MaxPrecisionNeeded)
0242         break;
0243       sOld = s;
0244     }
0245   }
0246   return s;
0247 }
0248 
0249 double Helix::period() const {
0250   if (mSingularity)
0251     return std::numeric_limits<double>::max();
0252   else
0253     return fabs(2 * M_PI / (mH * mCurvature * mCosDipAngle));
0254 }
0255 
0256 std::pair<double, double> Helix::pathLength(double r) const {
0257   std::pair<double, double> value;
0258   std::pair<double, double> VALUE(999999999., 999999999.);
0259   //
0260   // The math is taken from Maple with C(expr,optimized) and
0261   // some hand-editing. It is not very nice but efficient.
0262   // 'first' is the smallest of the two solutions (may be negative)
0263   // 'second' is the other.
0264   //
0265   if (mSingularity) {
0266     double t1  = mCosDipAngle * (mOrigin.x * mSinPhase - mOrigin.y * mCosPhase);
0267     double t12 = mOrigin.y * mOrigin.y;
0268     double t13 = mCosPhase * mCosPhase;
0269     double t15 = r * r;
0270     double t16 = mOrigin.x * mOrigin.x;
0271     double t20 =
0272         -mCosDipAngle * mCosDipAngle *
0273         (2.0 * mOrigin.x * mSinPhase * mOrigin.y * mCosPhase + t12 - t12 * t13 - t15 + t13 * t16);
0274     if (t20 < 0.)
0275       return VALUE;
0276     t20          = ::sqrt(t20);
0277     value.first  = (t1 - t20) / (mCosDipAngle * mCosDipAngle);
0278     value.second = (t1 + t20) / (mCosDipAngle * mCosDipAngle);
0279   } else {
0280     double t1  = mOrigin.y * mCurvature;
0281     double t2  = mSinPhase;
0282     double t3  = mCurvature * mCurvature;
0283     double t4  = mOrigin.y * t2;
0284     double t5  = mCosPhase;
0285     double t6  = mOrigin.x * t5;
0286     double t8  = mOrigin.x * mOrigin.x;
0287     double t11 = mOrigin.y * mOrigin.y;
0288     double t14 = r * r;
0289     double t15 = t14 * mCurvature;
0290     double t17 = t8 * t8;
0291     double t19 = t11 * t11;
0292     double t21 = t11 * t3;
0293     double t23 = t5 * t5;
0294     double t32 = t14 * t14;
0295     double t35 = t14 * t3;
0296     double t38 = 8.0 * t4 * t6 - 4.0 * t1 * t2 * t8 - 4.0 * t11 * mCurvature * t6 + 4.0 * t15 * t6 +
0297                  t17 * t3 + t19 * t3 + 2.0 * t21 * t8 + 4.0 * t8 * t23 -
0298                  4.0 * t8 * mOrigin.x * mCurvature * t5 - 4.0 * t11 * t23 -
0299                  4.0 * t11 * mOrigin.y * mCurvature * t2 + 4.0 * t11 - 4.0 * t14 + t32 * t3 +
0300                  4.0 * t15 * t4 - 2.0 * t35 * t11 - 2.0 * t35 * t8;
0301     double t40 = (-t3 * t38);
0302     if (t40 < 0.)
0303       return VALUE;
0304     t40 = ::sqrt(t40);
0305 
0306     double t43 = mOrigin.x * mCurvature;
0307     double t45 = 2.0 * t5 - t35 + t21 + 2.0 - 2.0 * t1 * t2 - 2.0 * t43 - 2.0 * t43 * t5 + t8 * t3;
0308     double t46 = mH * mCosDipAngle * mCurvature;
0309 
0310     value.first  = (-mPhase + 2.0 * atan((-2.0 * t1 + 2.0 * t2 + t40) / t45)) / t46;
0311     value.second = -(mPhase + 2.0 * atan((2.0 * t1 - 2.0 * t2 + t40) / t45)) / t46;
0312 
0313     //
0314     //   Solution can be off by +/- one period, select smallest
0315     //
0316     double p = period();
0317     if (!std::isnan(value.first)) {
0318       if (fabs(value.first - p) < fabs(value.first))
0319         value.first = value.first - p;
0320       else if (fabs(value.first + p) < fabs(value.first))
0321         value.first = value.first + p;
0322     }
0323     if (!std::isnan(value.second)) {
0324       if (fabs(value.second - p) < fabs(value.second))
0325         value.second = value.second - p;
0326       else if (fabs(value.second + p) < fabs(value.second))
0327         value.second = value.second + p;
0328     }
0329   }
0330   if (value.first > value.second)
0331     std::swap(value.first, value.second);
0332   return (value);
0333 }
0334 
0335 std::pair<double, double> Helix::pathLength(double r, double x, double y) {
0336   double x0                        = mOrigin.x;
0337   double y0                        = mOrigin.y;
0338   mOrigin.x                        = x0 - x;
0339   mOrigin.y                        = y0 - y;
0340   std::pair<double, double> result = this->pathLength(r);
0341   mOrigin.x                        = x0;
0342   mOrigin.y                        = y0;
0343   return result;
0344 }
0345 
0346 double Helix::pathLength(const edm4hep::Vector3f& r, const edm4hep::Vector3f& n) const {
0347   //
0348   // Vector 'r' defines the position of the center and
0349   // vector 'n' the normal vector of the plane.
0350   // For a straight line there is a simple analytical
0351   // solution. For curvatures > 0 the root is determined
0352   // by Newton method. In case no valid s can be found
0353   // the max. largest value for s is returned.
0354   //
0355   double s;
0356 
0357   if (mSingularity) {
0358     double t = n.z * mSinDipAngle + n.y * mCosDipAngle * mCosPhase - n.x * mCosDipAngle * mSinPhase;
0359     if (t == 0)
0360       s = NoSolution;
0361     else
0362       s = ((r - mOrigin) * n) / t;
0363   } else {
0364     const double MaxPrecisionNeeded = edm4eic::unit::um;
0365     const int MaxIterations         = 20;
0366 
0367     double A = mCurvature * ((mOrigin - r) * n) - n.x * mCosPhase - n.y * mSinPhase;
0368     double t = mH * mCurvature * mCosDipAngle;
0369     double u = n.z * mCurvature * mSinDipAngle;
0370 
0371     double a, f, fp;
0372     double sOld = s = 0;
0373     //    double shiftOld = 0;
0374     double shift;
0375     //          (cos(angMax)-1)/angMax = 0.1
0376     const double angMax = 0.21;
0377     double deltas       = fabs(angMax / (mCurvature * mCosDipAngle));
0378     //              dampingFactor = exp(-0.5);
0379     //  double dampingFactor = 0.60653;
0380     int i;
0381 
0382     for (i = 0; i < MaxIterations; i++) {
0383       a           = t * s + mPhase;
0384       double sina = sin(a);
0385       double cosa = cos(a);
0386       f           = A + n.x * cosa + n.y * sina + u * s;
0387       fp          = -n.x * sina * t + n.y * cosa * t + u;
0388       if (fabs(fp) * deltas <= fabs(f)) { //too big step
0389         int sgn = 1;
0390         if (fp < 0.)
0391           sgn = -sgn;
0392         if (f < 0.)
0393           sgn = -sgn;
0394         shift = sgn * deltas;
0395         if (shift < 0)
0396           shift *= 0.9; // don't get stuck shifting +/-deltas
0397       } else {
0398         shift = f / fp;
0399       }
0400       s -= shift;
0401       //      shiftOld = shift;
0402       if (fabs(sOld - s) < MaxPrecisionNeeded)
0403         break;
0404       sOld = s;
0405     }
0406     if (i == MaxIterations)
0407       return NoSolution;
0408   }
0409   return s;
0410 }
0411 
0412 std::pair<double, double> Helix::pathLengths(const Helix& h, double minStepSize,
0413                                              double minRange) const {
0414   //
0415   //    Cannot handle case where one is a helix
0416   //  and the other one is a straight line.
0417   //
0418   if (mSingularity != h.mSingularity)
0419     return std::pair<double, double>(NoSolution, NoSolution);
0420 
0421   double s1, s2;
0422 
0423   if (mSingularity) {
0424     //
0425     //  Analytic solution
0426     //
0427     edm4hep::Vector3f dv = h.mOrigin - mOrigin;
0428     edm4hep::Vector3f a(-mCosDipAngle * mSinPhase, mCosDipAngle * mCosPhase, mSinDipAngle);
0429     edm4hep::Vector3f b(-h.mCosDipAngle * h.mSinPhase, h.mCosDipAngle * h.mCosPhase,
0430                         h.mSinDipAngle);
0431     double ab = a * b;
0432     double g  = dv * a;
0433     double k  = dv * b;
0434     s2        = (k - ab * g) / (ab * ab - 1.);
0435     s1        = g + s2 * ab;
0436     return std::pair<double, double>(s1, s2);
0437   } else {
0438     //
0439     //  First step: get dca in the xy-plane as start value
0440     //
0441     double dx = h.xcenter() - xcenter();
0442     double dy = h.ycenter() - ycenter();
0443     double dd = ::sqrt(dx * dx + dy * dy);
0444     double r1 = 1 / curvature();
0445     double r2 = 1 / h.curvature();
0446 
0447     double cosAlpha = (r1 * r1 + dd * dd - r2 * r2) / (2 * r1 * dd);
0448 
0449     double s;
0450     double x, y;
0451     if (fabs(cosAlpha) < 1) { // two solutions
0452       double sinAlpha = sin(acos(cosAlpha));
0453       x               = xcenter() + r1 * (cosAlpha * dx - sinAlpha * dy) / dd;
0454       y               = ycenter() + r1 * (sinAlpha * dx + cosAlpha * dy) / dd;
0455       s               = pathLength(x, y);
0456       x               = xcenter() + r1 * (cosAlpha * dx + sinAlpha * dy) / dd;
0457       y               = ycenter() + r1 * (cosAlpha * dy - sinAlpha * dx) / dd;
0458       double a        = pathLength(x, y);
0459       if (h.distance(at(a)) < h.distance(at(s)))
0460         s = a;
0461     } else {                                 // no intersection (or exactly one)
0462       int rsign = ((r2 - r1) > dd ? -1 : 1); // set -1 when *this* helix is
0463       // completely contained in the other
0464       x = xcenter() + rsign * r1 * dx / dd;
0465       y = ycenter() + rsign * r1 * dy / dd;
0466       s = pathLength(x, y);
0467     }
0468 
0469     //
0470     //   Second step: scan in decreasing intervals around seed 's'
0471     //   minRange and minStepSize are passed as arguments to the method.
0472     //   They have default values defined in the header file.
0473     //
0474     double dmin  = h.distance(at(s));
0475     double range = std::max(2 * dmin, minRange);
0476     double ds    = range / 10;
0477     double slast = -999999, ss, d;
0478     s1           = s - range / 2.;
0479     s2           = s + range / 2.;
0480 
0481     while (ds > minStepSize) {
0482       for (ss = s1; ss < s2 + ds; ss += ds) {
0483         d = h.distance(at(ss));
0484         if (d < dmin) {
0485           dmin = d;
0486           s    = ss;
0487         }
0488         slast = ss;
0489       }
0490       //
0491       //  In the rare cases where the minimum is at the
0492       //  the border of the current range we shift the range
0493       //  and start all over, i.e we do not decrease 'ds'.
0494       //  Else we decrease the search interval around the
0495       //  current minimum and redo the scan in smaller steps.
0496       //
0497       if (s == s1) {
0498         d = 0.8 * (s2 - s1);
0499         s1 -= d;
0500         s2 -= d;
0501       } else if (s == slast) {
0502         d = 0.8 * (s2 - s1);
0503         s1 += d;
0504         s2 += d;
0505       } else {
0506         s1 = s - ds;
0507         s2 = s + ds;
0508         ds /= 10;
0509       }
0510     }
0511     return std::pair<double, double>(s, h.pathLength(at(s)));
0512   }
0513 }
0514 
0515 void Helix::moveOrigin(double s) {
0516   if (mSingularity)
0517     mOrigin = at(s);
0518   else {
0519     edm4hep::Vector3f newOrigin = at(s);
0520     double newPhase             = atan2(newOrigin.y - ycenter(), newOrigin.x - xcenter());
0521     mOrigin                     = newOrigin;
0522     setPhase(newPhase);
0523   }
0524 }
0525 void Helix::Print() const {
0526   std::cout << "("
0527             << "curvature = " << mCurvature << ", "
0528             << "dip angle = " << mDipAngle << ", "
0529             << "phase = " << mPhase << ", "
0530             << "h = " << mH << ", "
0531             << "origin = " << mOrigin.x << " " << mOrigin.y << " " << mOrigin.z << ")" << std::endl;
0532 }
0533 
0534 edm4hep::Vector3f Helix::momentum(double B) const {
0535   if (mSingularity)
0536     return (edm4hep::Vector3f(0, 0, 0));
0537   else {
0538     double pt = edm4eic::unit::GeV *
0539                 fabs(dd4hep::c_light * dd4hep::nanosecond / dd4hep::meter * B / dd4hep::tesla) /
0540                 (fabs(mCurvature) * dd4hep::meter);
0541 
0542     return (edm4hep::Vector3f(pt * cos(mPhase + mH * M_PI / 2), // pos part pos field
0543                               pt * sin(mPhase + mH * M_PI / 2), pt * tan(mDipAngle)));
0544   }
0545 }
0546 
0547 edm4hep::Vector3f Helix::momentumAt(double S, double B) const {
0548   // Obtain phase-shifted momentum from phase-shift of origin
0549   Helix tmp(*this);
0550   tmp.moveOrigin(S);
0551   return tmp.momentum(B);
0552 }
0553 
0554 int Helix::charge(double B) const { return (B > 0 ? -mH : mH); }
0555 
0556 double Helix::geometricSignedDistance(double x, double y) {
0557   // Geometric signed distance
0558   double thePath                  = this->pathLength(x, y);
0559   edm4hep::Vector3f DCA2dPosition = this->at(thePath);
0560   DCA2dPosition.z                 = 0;
0561   edm4hep::Vector3f position(x, y, 0);
0562   edm4hep::Vector3f DCAVec = (DCA2dPosition - position);
0563   edm4hep::Vector3f momVec;
0564   // Deal with straight tracks
0565   if (this->mSingularity) {
0566     momVec   = this->at(1) - this->at(0);
0567     momVec.z = 0;
0568   } else {
0569     momVec = this->momentumAt(
0570         thePath, 1. / dd4hep::tesla); // Don't care about Bmag.  Helicity is what matters.
0571     momVec.z = 0;
0572   }
0573 
0574   double cross   = DCAVec.x * momVec.y - DCAVec.y * momVec.x;
0575   double theSign = (cross >= 0) ? 1. : -1.;
0576   return theSign * edm4hep::utils::magnitudeTransverse(DCAVec);
0577 }
0578 
0579 double Helix::curvatureSignedDistance(double x, double y) {
0580   // Protect against mH = 0 or zero field
0581   if (this->mSingularity || abs(this->mH) <= 0) {
0582     return (this->geometricSignedDistance(x, y));
0583   } else {
0584     return (this->geometricSignedDistance(x, y)) / (this->mH);
0585   }
0586 }
0587 
0588 double Helix::geometricSignedDistance(const edm4hep::Vector3f& pos) {
0589   double sdca2d  = this->geometricSignedDistance(pos.x, pos.y);
0590   double theSign = (sdca2d >= 0) ? 1. : -1.;
0591   return (this->distance(pos)) * theSign;
0592 }
0593 
0594 double Helix::curvatureSignedDistance(const edm4hep::Vector3f& pos) {
0595   double sdca2d  = this->curvatureSignedDistance(pos.x, pos.y);
0596   double theSign = (sdca2d >= 0) ? 1. : -1.;
0597   return (this->distance(pos)) * theSign;
0598 }
0599 
0600 } // namespace eicrecon