File indexing completed on 2025-12-16 09:28:01
0001
0002
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
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
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
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
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);
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
0079
0080
0081 mH = (h >= 0) ? 1 : -1;
0082
0083 mOrigin = o;
0084 setDipAngle(dip);
0085 setPhase(phase);
0086
0087
0088
0089
0090
0091 setCurvature(c);
0092
0093
0094
0095
0096
0097
0098
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;
0116 else
0117 mSingularity = false;
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);
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
0169
0170
0171
0172
0173
0174
0175
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
0190
0191
0192 double t34 = mCurvature * mCosDipAngle * mCosDipAngle;
0193 double t41 = mSinDipAngle * mSinDipAngle;
0194 double t6, t7, t11, t12, t19;
0195
0196
0197
0198
0199
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
0227
0228
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
0261
0262
0263
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
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
0349
0350
0351
0352
0353
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
0374 double shift;
0375
0376 const double angMax = 0.21;
0377 double deltas = fabs(angMax / (mCurvature * mCosDipAngle));
0378
0379
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)) {
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;
0397 } else {
0398 shift = f / fp;
0399 }
0400 s -= shift;
0401
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
0416
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
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
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) {
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 {
0462 int rsign = ((r2 - r1) > dd ? -1 : 1);
0463
0464 x = xcenter() + rsign * r1 * dx / dd;
0465 y = ycenter() + rsign * r1 * dy / dd;
0466 s = pathLength(x, y);
0467 }
0468
0469
0470
0471
0472
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
0492
0493
0494
0495
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),
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
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
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
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);
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
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 }