1 /***************************************************************************
5 * Author: Thomas Ullrich, Sep 1997
6 ***************************************************************************
8 * Description: Parametrization of a helix
10 ***************************************************************************
13 * Revision 1.3 2007/04/27 07:24:34 akisiel
14 * Make revisions needed for compilation from the main AliRoot tree
16 * Revision 1.1.1.1 2007/04/25 15:38:41 panos
17 * Importing the HBT code dir
19 * Revision 1.1.1.1 2007/03/07 10:14:49 mchojnacki
20 * First version on CVS
22 * Revision 1.26 2005/10/13 23:15:13 genevb
23 * Save a few calculations
25 * Revision 1.25 2005/10/13 22:25:35 genevb
26 * pathLength to plane now finds nearest approach to intersection regardless of # of loops
28 * Revision 1.24 2005/07/06 18:49:56 fisyak
29 * Replace AliFmHelixD, AliFmLorentzVectorD,AliFmLorentzVectorF,AliFmMatrixD,AliFmMatrixF,AliFmPhysicalHelixD,AliFmThreeVectorD,AliFmThreeVectorF by templated version
31 * Revision 1.23 2004/12/02 02:51:16 ullrich
32 * Added option to pathLenghth() and distance() to search for
33 * DCA only within one period. Default stays as it was.
35 * Revision 1.22 2004/05/03 23:35:31 perev
36 * Possible non init WarnOff
38 * Revision 1.21 2004/01/27 02:49:48 perev
39 * Big value appropriate for float
41 * Revision 1.20 2003/12/22 18:59:36 perev
42 * remove test for only +ve pathLeng added before
44 * Revision 1.19 2003/12/18 17:27:02 perev
45 * Small bug fix in number of iters. i++ ==> i--
47 * Revision 1.18 2003/10/30 20:06:46 perev
48 * Check of quality added
50 * Revision 1.17 2003/10/19 20:17:00 perev
51 * Protection agains overfloat added into pathLength(AliFmThreeVector,AliFmThreeVector)
53 * Revision 1.16 2003/10/06 23:39:21 perev
54 * sqrt(-ve) == no solution. infinity returns
56 * Revision 1.15 2003/09/02 17:59:34 perev
57 * gcc 3.2 updates + WarnOff
59 * Revision 1.14 2003/06/26 17:15:56 ullrich
60 * Changed local variable name in pathLenght.
62 * Revision 1.13 2002/06/21 17:49:25 genevb
63 * Some minor speed improvements
65 * Revision 1.12 2002/04/24 02:40:25 ullrich
66 * Restored old format and lost CVS statements.
68 * Revision 1.11 2002/02/12 19:37:51 jeromel
69 * fix for Linux 7.2 (float.h). Took oportunity to doxygenize.
71 * Revision 1.10 2000/07/17 21:44:19 ullrich
72 * Fixed problem in pathLength(cyl_rad).
74 * Revision 1.9 2000/05/22 21:38:28 ullrich
75 * Add parenthesis to make Linux compiler happy.
77 * Revision 1.8 2000/05/22 21:11:21 ullrich
78 * In pathLength(AliFmThreeVector&): Increased number of max iteration
79 * in Newton method from 10 to 100. Improved initial guess in case
80 * it is off by n period.
82 * Revision 1.7 2000/03/06 20:24:25 ullrich
83 * Parameter h for case B=0 correctly handled now.
85 * Revision 1.6 1999/12/22 15:14:39 ullrich
86 * Added analytical solution for dca between two helices
87 * in the case for B=0.
89 * Revision 1.5 1999/12/21 15:14:08 ullrich
90 * Modified to cope with new compiler version on Sun (CC5.0).
92 * Revision 1.4 1999/11/29 21:45:38 fisyak
95 * Revision 1.3 1999/03/07 14:55:41 wenaus
98 * Revision 1.2 1999/03/02 19:47:35 ullrich
99 * Added method to find dca between two helices
101 * Revision 1.1 1999/01/30 03:59:02 fisyak
102 * Root Version of AliFmarClassLibrary
104 * Revision 1.1 1999/01/23 00:29:15 ullrich
107 **************************************************************************/
108 #if !defined(ST_NO_NUMERIC_LIMITS)
110 # if !defined(ST_NO_NAMESPACES)
111 using std::numeric_limits;
118 #include "AliFmHelix.h"
119 #include "Base/PhysicalConstants.h"
120 #include "Base/SystemOfUnits.h"
122 ClassImpT(AliFmHelix,double);
133 const double AliFmHelix::NoSolution = 3.e+33;
135 AliFmHelix::AliFmHelix() :
148 AliFmHelix::AliFmHelix(double c, double d, double phase,
149 const AliFmThreeVector<double>& o, int h) :
161 setParameters(c, d, phase, o, h);
164 AliFmHelix::~AliFmHelix() { /* noop */ };
166 void AliFmHelix::setParameters(double c, double dip, double phase,
167 const AliFmThreeVector<double>& o, int h)
170 // The order in which the parameters are set is important
171 // since setCurvature might have to adjust the others.
173 mH = (h>=0) ? 1 : -1; // Default is: positive particle
180 // Check for singularity and correct for negative curvature.
181 // May change mH and mPhase. Must therefore be set last.
186 // For the case B=0, h is ill defined. In the following we
187 // always assume h = +1. Since phase = psi - h * pi/2
188 // we have to correct the phase in case h = -1.
189 // This assumes that the user uses the same h for phase
190 // as the one he passed to the constructor.
192 if (mSingularity && mH == -1) {
194 setPhase(mPhase-M_PI);
198 void AliFmHelix::setCurvature(double val)
203 setPhase(mPhase+M_PI);
208 #ifndef ST_NO_NUMERIC_LIMITS
209 if (fabs(mCurvature) <= numeric_limits<double>::epsilon())
211 if (fabs(mCurvature) <= static_cast<double>(0))
213 mSingularity = true; // straight line
215 mSingularity = false; // curved
218 void AliFmHelix::setPhase(double val)
221 mCosPhase = cos(mPhase);
222 mSinPhase = sin(mPhase);
223 if (fabs(mPhase) > M_PI)
224 mPhase = atan2(mSinPhase, mCosPhase); // force range [-pi,pi]
227 void AliFmHelix::setDipAngle(double val)
230 mCosDipAngle = cos(mDipAngle);
231 mSinDipAngle = sin(mDipAngle);
234 double AliFmHelix::xcenter() const
239 return mOrigin.x()-mCosPhase/mCurvature;
242 double AliFmHelix::ycenter() const
247 return mOrigin.y()-mSinPhase/mCurvature;
250 double AliFmHelix::fudgePathLength(const AliFmThreeVector<double>& p) const
253 double dx = p.x()-mOrigin.x();
254 double dy = p.y()-mOrigin.y();
257 s = (dy*mCosPhase - dx*mSinPhase)/mCosDipAngle;
260 s = atan2(dy*mCosPhase - dx*mSinPhase,
261 1/mCurvature + dx*mCosPhase+dy*mSinPhase)/
262 (mH*mCurvature*mCosDipAngle);
267 double AliFmHelix::distance(const AliFmThreeVector<double>& p, bool scanPeriods) const
269 return abs(this->at(pathLength(p,scanPeriods))-p);
272 double AliFmHelix::pathLength(const AliFmThreeVector<double>& p, bool scanPeriods) const
275 // Returns the path length at the distance of closest
276 // approach between the helix and point p.
277 // For the case of B=0 (straight line) the path length
278 // can be calculated analytically. For B>0 there is
279 // unfortunately no easy solution to the problem.
280 // Here we use the Newton method to find the root of the
281 // referring equation. The 'fudgePathLength' serves
282 // as a starting value.
285 double dx = p.x()-mOrigin.x();
286 double dy = p.y()-mOrigin.y();
287 double dz = p.z()-mOrigin.z();
290 s = mCosDipAngle*(mCosPhase*dy-mSinPhase*dx) +
294 #ifndef ST_NO_NAMESPACES
296 using namespace units;
298 const double MaxPrecisionNeeded = micrometer;
299 const int MaxIterations = 100;
302 // The math is taken from Maple with C(expr,optimized) and
303 // some hand-editing. It is not very nice but efficient.
305 double t34 = mCurvature*mCosDipAngle*mCosDipAngle;
306 double t41 = mSinDipAngle*mSinDipAngle;
307 double t6, t7, t11, t12, t19;
310 // Get a first guess by using the dca in 2D. Since
311 // in some extreme cases we might be off by n periods
312 // we add (subtract) periods in case we get any closer.
314 s = fudgePathLength(p);
317 double ds = period();
319 double d, dmin = abs(at(s) - p);
320 for(j=1; j<MaxIterations; j++) {
321 if ((d = abs(at(s+j*ds) - p)) < dmin) {
328 for(j=-1; -j<MaxIterations; j--) {
329 if ((d = abs(at(s+j*ds) - p)) < dmin) {
336 if (jmin) s += jmin*ds;
341 // Stops after MaxIterations iterations or if the required
342 // precision is obtained. Whatever comes first.
345 for (int i=0; i<MaxIterations; i++) {
346 t6 = mPhase+s*mH*mCurvature*mCosDipAngle;
348 t11 = dx-(1/mCurvature)*(t7-mCosPhase);
350 t19 = dy-(1/mCurvature)*(t12-mSinPhase);
351 s -= (t11*t12*mH*mCosDipAngle-t19*t7*mH*mCosDipAngle -
352 (dz-s*mSinDipAngle)*mSinDipAngle)/
353 (t12*t12*mCosDipAngle*mCosDipAngle+t11*t7*t34 +
354 t7*t7*mCosDipAngle*mCosDipAngle +
356 if (fabs(sOld-s) < MaxPrecisionNeeded) break;
359 #ifndef ST_NO_NAMESPACES
366 double AliFmHelix::period() const
369 #ifndef ST_NO_NUMERIC_LIMITS
370 return numeric_limits<double>::max();
375 return fabs(2*M_PI/(mH*mCurvature*mCosDipAngle));
378 pair<double, double> AliFmHelix::pathLength(double r) const
380 pair<double,double> value;
381 pair<double,double> VALUE(999999999.,999999999.);
383 // The math is taken from Maple with C(expr,optimized) and
384 // some hand-editing. It is not very nice but efficient.
385 // 'first' is the smallest of the two solutions (may be negative)
386 // 'second' is the other.
389 double t1 = mCosDipAngle*(mOrigin.x()*mSinPhase-mOrigin.y()*mCosPhase);
390 double t12 = mOrigin.y()*mOrigin.y();
391 double t13 = mCosPhase*mCosPhase;
393 double t16 = mOrigin.x()*mOrigin.x();
394 double t20 = -mCosDipAngle*mCosDipAngle*(2.0*mOrigin.x()*mSinPhase*mOrigin.y()*mCosPhase +
395 t12-t12*t13-t15+t13*t16);
396 if (t20<0.) return VALUE;
398 value.first = (t1-t20)/(mCosDipAngle*mCosDipAngle);
399 value.second = (t1+t20)/(mCosDipAngle*mCosDipAngle);
402 double t1 = mOrigin.y()*mCurvature;
403 double t2 = mSinPhase;
404 double t3 = mCurvature*mCurvature;
405 double t4 = mOrigin.y()*t2;
406 double t5 = mCosPhase;
407 double t6 = mOrigin.x()*t5;
408 double t8 = mOrigin.x()*mOrigin.x();
409 double t11 = mOrigin.y()*mOrigin.y();
411 double t15 = t14*mCurvature;
413 double t19 = t11*t11;
416 double t32 = t14*t14;
418 double t38 = 8.0*t4*t6 - 4.0*t1*t2*t8 - 4.0*t11*mCurvature*t6 +
419 4.0*t15*t6 + t17*t3 + t19*t3 + 2.0*t21*t8 + 4.0*t8*t23 -
420 4.0*t8*mOrigin.x()*mCurvature*t5 - 4.0*t11*t23 -
421 4.0*t11*mOrigin.y()*mCurvature*t2 + 4.0*t11 - 4.0*t14 +
422 t32*t3 + 4.0*t15*t4 - 2.0*t35*t11 - 2.0*t35*t8;
423 double t40 = (-t3*t38);
424 if (t40<0.) return VALUE;
427 double t43 = mOrigin.x()*mCurvature;
428 double t45 = 2.0*t5 - t35 + t21 + 2.0 - 2.0*t1*t2 -2.0*t43 - 2.0*t43*t5 + t8*t3;
429 double t46 = mH*mCosDipAngle*mCurvature;
431 value.first = (-mPhase + 2.0*atan((-2.0*t1 + 2.0*t2 + t40)/t45))/t46;
432 value.second = -(mPhase + 2.0*atan((2.0*t1 - 2.0*t2 + t40)/t45))/t46;
435 // Solution can be off by +/- one period, select smallest
438 // malisa deletes "isnan" check 22apr2006 if (!isnan(value.first)) {
439 if (fabs(value.first-p) < fabs(value.first)) value.first = value.first-p;
440 else if (fabs(value.first+p) < fabs(value.first)) value.first = value.first+p;
442 // malisa deletes "isnan" check 22apr2006 if (!isnan(value.second)) {
443 if (fabs(value.second-p) < fabs(value.second)) value.second = value.second-p;
444 else if (fabs(value.second+p) < fabs(value.second)) value.second = value.second+p;
447 if (value.first > value.second)
448 swap(value.first,value.second);
452 pair<double, double> AliFmHelix::pathLength(double r, double x, double y, bool scanPeriods)
454 double x0 = mOrigin.x();
455 double y0 = mOrigin.y();
458 pair<double, double> result = this->pathLength(r);
464 double AliFmHelix::pathLength(const AliFmThreeVector<double>& r,
465 const AliFmThreeVector<double>& n) const
468 // Vector 'r' defines the position of the center and
469 // vector 'n' the normal vector of the plane.
470 // For a straight line there is a simple analytical
471 // solution. For curvatures > 0 the root is determined
472 // by Newton method. In case no valid s can be found
473 // the max. largest value for s is returned.
478 double t = n.z()*mSinDipAngle +
479 n.y()*mCosDipAngle*mCosPhase -
480 n.x()*mCosDipAngle*mSinPhase;
484 s = ((r - mOrigin)*n)/t;
487 const double MaxPrecisionNeeded = micrometer;
488 const int MaxIterations = 20;
490 double A = mCurvature*((mOrigin - r)*n) -
493 double t = mH*mCurvature*mCosDipAngle;
494 double u = n.z()*mCurvature*mSinDipAngle;
500 // (cos(angMax)-1)/angMax = 0.1
501 const double angMax = 0.21;
502 double deltas = fabs(angMax/(mCurvature*mCosDipAngle));
503 // dampingFactor = exp(-0.5);
504 double dampingFactor = 0.60653;
507 for (i=0; i<MaxIterations; i++) {
509 double sina = sin(a);
510 double cosa = cos(a);
518 if ( fabs(fp)*deltas <= fabs(f) ) { //too big step
520 if (fp<0.) sgn = -sgn;
521 if (f <0.) sgn = -sgn;
523 if (shift == -shiftOld) { // don't get stuck shifting +/-deltas
524 deltas *= dampingFactor; // dampen magnitude of shift
526 // allow iterations to run out
528 i--; // don't count against iterations
535 if (fabs(sOld-s) < MaxPrecisionNeeded) break;
538 if (i == MaxIterations) return NoSolution;
544 AliFmHelix::pathLengths(const AliFmHelix& h, bool scanPeriods) const
548 // Cannot handle case where one is a helix
549 // and the other one is a straight line.
551 if (mSingularity != h.mSingularity)
552 return pair<double, double>(NoSolution, NoSolution);
560 AliFmThreeVector<double> dv = h.mOrigin - mOrigin;
561 AliFmThreeVector<double> a(-mCosDipAngle*mSinPhase,
562 mCosDipAngle*mCosPhase,
564 AliFmThreeVector<double> b(-h.mCosDipAngle*h.mSinPhase,
565 h.mCosDipAngle*h.mCosPhase,
570 s2 = (k-ab*g)/(ab*ab-1.);
572 return pair<double, double>(s1, s2);
576 // First step: get dca in the xy-plane as start value
578 double dx = h.xcenter() - xcenter();
579 double dy = h.ycenter() - ycenter();
580 double dd = ::sqrt(dx*dx + dy*dy);
581 double r1 = 1/curvature();
582 double r2 = 1/h.curvature();
584 /* malisa 22apr2006 is commenting out the "isnan" check
585 * if ( !finite(r1) || isnan(r1) || !finite(r2) || isnan(r2) ) {
586 * cerr << __FUNCTION__ << " *** error *** ";
587 * cerr << " r1=" << r1;
588 * cerr << " r2=" << r2 << endl;
589 * return pair<double, double>(NoSolution, NoSolution);
593 double cosAlpha = (r1*r1 + dd*dd - r2*r2)/(2*r1*dd);
597 if (fabs(cosAlpha) < 1) { // two solutions
598 double sinAlpha = sin(acos(cosAlpha));
599 x = xcenter() + r1*(cosAlpha*dx - sinAlpha*dy)/dd;
600 y = ycenter() + r1*(sinAlpha*dx + cosAlpha*dy)/dd;
601 s = pathLength(x, y);
602 x = xcenter() + r1*(cosAlpha*dx + sinAlpha*dy)/dd;
603 y = ycenter() + r1*(cosAlpha*dy - sinAlpha*dx)/dd;
604 double a = pathLength(x, y);
605 if (h.distance(at(a),scanPeriods) < h.distance(at(s),scanPeriods)) s = a;
607 else { // no intersection (or exactly one)
608 x = xcenter() + r1*dx/dd;
609 y = ycenter() + r1*dy/dd;
610 s = pathLength(x, y);
614 // Second step: scan in decreasing intervals around seed 's'
616 const double MinStepSize = 10*micrometer;
617 const double MinRange = 10*centimeter;
618 double dmin = h.distance(at(s),scanPeriods);
619 double range = max(2*dmin, MinRange);
621 /* malisa comments out the "isnan" check 22apr2006
622 * if ( !finite(range) || isnan(range)) {
623 * cerr << __FUNCTION__ << " *** error *** ";
624 * cerr << " range=" << range << endl;
625 * return pair<double, double>(NoSolution, NoSolution);
629 double ds = range/10.;
630 double slast=-999999, d;
637 while (ds > MinStepSize) {
639 cerr << __FUNCTION__ << " *** error *** s1 = ";
640 cerr << " s2 = " << s2;
641 cerr << " range = " << range;
642 cerr << " ds = " << ds << endl;
643 return pair<double, double>(NoSolution, NoSolution);
649 cerr << __FUNCTION__ << " *** error *** ds = " << ds << endl;
650 return pair<double, double>(NoSolution, NoSolution);
653 for (double ss=s1; ss<(s2+ds); ss+=ds) {
655 if ( iterations > 100 ) {
656 cerr << __FUNCTION__ << " *** error *** iterations = " << iterations << endl;
657 return pair<double, double>(NoSolution, NoSolution);
659 d = h.distance(at(ss),scanPeriods);
667 // In the rare cases where the minimum is at the
668 // the border of the current range we shift the range
669 // and start all over, i.e we do not decrease 'ds'.
670 // Else we decrease the search intervall around the
671 // current minimum and redo the scan in smaller steps.
678 else if (s == slast) {
689 return pair<double, double>(s, h.pathLength(at(s),scanPeriods));
694 void AliFmHelix::moveOrigin(double s)
699 AliFmThreeVector<double> newOrigin = at(s);
700 double newPhase = atan2(newOrigin.y() - ycenter(),
701 newOrigin.x() - xcenter());
707 int operator== (const AliFmHelix& a, const AliFmHelix& b)
710 // Checks for numerical identity only !
712 return (a.origin() == b.origin() &&
713 a.dipAngle() == b.dipAngle() &&
714 a.curvature() == b.curvature() &&
715 a.phase() == b.phase() &&
719 int operator!= (const AliFmHelix& a, const AliFmHelix& b) {return !(a == b);}
721 ostream& operator<<(ostream& os, const AliFmHelix& h)
724 << "curvature = " << h.curvature() << ", "
725 << "dip angle = " << h.dipAngle() << ", "
726 << "phase = " << h.phase() << ", "
727 << "h = " << h.h() << ", "
728 << "origin = " << h.origin() << ')';