]> git.uio.no Git - u/mrichter/AliRoot.git/blame - PWGCF/FEMTOSCOPY/AliFemto/AliFmHelix.cxx
Merge branch 'master_patch'
[u/mrichter/AliRoot.git] / PWGCF / FEMTOSCOPY / AliFemto / AliFmHelix.cxx
CommitLineData
76ce4b5b 1///////////////////////////////////////////////////////////////////////////
2// //
3// AliFmHelix: a helper helix class //
4// Includes all the operations and specifications of the helix. Can be //
5// used to determine path lengths, distance of closest approach etc. //
6// //
7///////////////////////////////////////////////////////////////////////////
8#if !defined(ST_NO_NUMERIC_LIMITS)
9# include <limits>
10# if !defined(ST_NO_NAMESPACES)
11using std::numeric_limits;
12# endif
13#endif
14#define FOR_HELIX
15#include <float.h>
16#include <assert.h>
17
18#include "AliFmHelix.h"
19#include "PhysicalConstants.h"
20#include "SystemOfUnits.h"
21#ifdef __ROOT__
22ClassImpT(AliFmHelix,double);
23#endif
24
25#ifdef WIN32
26#include "gcc2vs.h"
27#endif
28
29#include <iostream>
30#include <fstream>
31using namespace std;
32
33const double AliFmHelix::fgkNoSolution = 3.e+33;
34
35AliFmHelix::AliFmHelix() :
36 fSingularity(0),
37 fOrigin(0,0,0),
38 fDipAngle(0),
39 fCurvature(0),
40 fPhase(0),
41 fH(0),
42 fCosDipAngle(0),
43 fSinDipAngle(0),
44 fCosPhase(0),
45 fSinPhase(0)
46{
47 //Default constructor
48/*noop*/
49}
50
51AliFmHelix::AliFmHelix(double c, double d, double phase,
52 const AliFmThreeVector<double>& o, int h) :
53 fSingularity(0),
54 fOrigin(0,0,0),
55 fDipAngle(0),
56 fCurvature(0),
57 fPhase(0),
58 fH(0),
59 fCosDipAngle(0),
60 fSinDipAngle(0),
61 fCosPhase(0),
62 fSinPhase(0)
63{
64 // Constructor with helix parameters
65 SetParameters(c, d, phase, o, h);
66}
67
68AliFmHelix::~AliFmHelix() {
69 // Default destructor
70/* noop */
71}
72
73void AliFmHelix::SetParameters(double c, double dip, double phase,
74 const AliFmThreeVector<double>& o, int h)
75{
76 //
77 // The order in which the parameters are set is important
78 // since setCurvature might have to adjust the others.
79 //
80 fH = (h>=0) ? 1 : -1; // Default is: positive particle
81 // positive field
82 fOrigin = o;
83 SetDipAngle(dip);
84 SetPhase(phase);
85
86 //
87 // Check for singularity and correct for negative curvature.
88 // May change mH and mPhase. Must therefore be set last.
89 //
90 SetCurvature(c);
91
92 //
93 // For the case B=0, h is ill defined. In the following we
94 // always assume h = +1. Since phase = psi - h * pi/2
95 // we have to correct the phase in case h = -1.
96 // This assumes that the user uses the same h for phase
97 // as the one he passed to the constructor.
98 //
99 if (fSingularity && fH == -1) {
100 fH = +1;
101 SetPhase(fPhase-M_PI);
102 }
103}
104
105void AliFmHelix::SetCurvature(double val)
106{
107 // Set helix curvature
108 if (val < 0) {
109 fCurvature = -val;
110 fH = -fH;
111 SetPhase(fPhase+M_PI);
112 }
113 else
114 fCurvature = val;
115
116#ifndef ST_NO_NUMERIC_LIMITS
117 if (fabs(fCurvature) <= numeric_limits<double>::epsilon())
118#else
119 if (fabs(fCurvature) <= static_cast<double>(0))
120#endif
121 fSingularity = true; // straight line
122 else
123 fSingularity = false; // curved
124}
125
126void AliFmHelix::SetPhase(double val)
127{
128 // Set helix phase
129 fPhase = val;
130 fCosPhase = cos(fPhase);
131 fSinPhase = sin(fPhase);
132 if (fabs(fPhase) > M_PI)
133 fPhase = atan2(fSinPhase, fCosPhase); // force range [-pi,pi]
134}
135
136void AliFmHelix::SetDipAngle(double val)
137{
138 // Set helix dip angle
139 fDipAngle = val;
140 fCosDipAngle = cos(fDipAngle);
141 fSinDipAngle = sin(fDipAngle);
142}
143
144double AliFmHelix::XCenter() const
145{
146 // Set helix center in X
147 if (fSingularity)
148 return 0;
149 else
150 return fOrigin.x()-fCosPhase/fCurvature;
151}
152
153double AliFmHelix::YCenter() const
154{
155 // Set helix center in Y
156 if (fSingularity)
157 return 0;
158 else
159 return fOrigin.y()-fSinPhase/fCurvature;
160}
161
162double AliFmHelix::FudgePathLength(const AliFmThreeVector<double>& p) const
163{
164 // Path length
165 double s;
166 double dx = p.x()-fOrigin.x();
167 double dy = p.y()-fOrigin.y();
168
169 if (fSingularity) {
170 s = (dy*fCosPhase - dx*fSinPhase)/fCosDipAngle;
171 }
172 else {
173 s = atan2(dy*fCosPhase - dx*fSinPhase,
174 1/fCurvature + dx*fCosPhase+dy*fSinPhase)/
175 (fH*fCurvature*fCosDipAngle);
176 }
177 return s;
178}
179
180double AliFmHelix::Distance(const AliFmThreeVector<double>& p, bool scanPeriods) const
181{
182 // calculate distance between origin an p along the helix
183 return abs(this->At(PathLength(p,scanPeriods))-p);
184}
185
186double AliFmHelix::PathLength(const AliFmThreeVector<double>& p, bool scanPeriods) const
187{
188 //
189 // Returns the path length at the distance of closest
190 // approach between the helix and point p.
191 // For the case of B=0 (straight line) the path length
192 // can be calculated analytically. For B>0 there is
193 // unfortunately no easy solution to the problem.
194 // Here we use the Newton method to find the root of the
195 // referring equation. The 'fudgePathLength' serves
196 // as a starting value.
197 //
198 double s;
199 double dx = p.x()-fOrigin.x();
200 double dy = p.y()-fOrigin.y();
201 double dz = p.z()-fOrigin.z();
202
203 if (fSingularity) {
204 s = fCosDipAngle*(fCosPhase*dy-fSinPhase*dx) +
205 fSinDipAngle*dz;
206 }
207 else { //
208#ifndef ST_NO_NAMESPACES
209 {
210 using namespace units;
211#endif
212 const double ktMaxPrecisionNeeded = micrometer;
213 const int ktMaxIterations = 100;
214
215 //
216 // The math is taken from Maple with C(expr,optimized) and
217 // some hand-editing. It is not very nice but efficient.
218 //
219 double t34 = fCurvature*fCosDipAngle*fCosDipAngle;
220 double t41 = fSinDipAngle*fSinDipAngle;
221 double t6, t7, t11, t12, t19;
222
223 //
224 // Get a first guess by using the dca in 2D. Since
225 // in some extreme cases we might be off by n periods
226 // we add (subtract) periods in case we get any closer.
227 //
228 s = FudgePathLength(p);
229
230 if (scanPeriods) {
231 double ds = Period();
232 int j, jmin = 0;
233 double d, dmin = abs(At(s) - p);
234 for(j=1; j<ktMaxIterations; j++) {
235 if ((d = abs(At(s+j*ds) - p)) < dmin) {
236 dmin = d;
237 jmin = j;
238 }
239 else
240 break;
241 }
242 for(j=-1; -j<ktMaxIterations; j--) {
243 if ((d = abs(At(s+j*ds) - p)) < dmin) {
244 dmin = d;
245 jmin = j;
246 }
247 else
248 break;
249 }
250 if (jmin) s += jmin*ds;
251 }
252
253 //
254 // Newtons method:
255 // Stops after ktMaxIterations iterations or if the required
256 // precision is obtained. Whatever comes first.
257 //
258 double sOld = s;
259 for (int i=0; i<ktMaxIterations; i++) {
260 t6 = fPhase+s*fH*fCurvature*fCosDipAngle;
261 t7 = cos(t6);
262 t11 = dx-(1/fCurvature)*(t7-fCosPhase);
263 t12 = sin(t6);
264 t19 = dy-(1/fCurvature)*(t12-fSinPhase);
265 s -= (t11*t12*fH*fCosDipAngle-t19*t7*fH*fCosDipAngle -
266 (dz-s*fSinDipAngle)*fSinDipAngle)/
267 (t12*t12*fCosDipAngle*fCosDipAngle+t11*t7*t34 +
268 t7*t7*fCosDipAngle*fCosDipAngle +
269 t19*t12*t34+t41);
270 if (fabs(sOld-s) < ktMaxPrecisionNeeded) break;
271 sOld = s;
272 }
273#ifndef ST_NO_NAMESPACES
274 }
275#endif
276 }
277 return s;
278}
279
280double AliFmHelix::Period() const
281{
282 // period
283 if (fSingularity)
284#ifndef ST_NO_NUMERIC_LIMITS
285 return numeric_limits<double>::max();
286#else
287 return DBL_MAX;
288#endif
289 else
290 return fabs(2*M_PI/(fH*fCurvature*fCosDipAngle));
291}
292
293pair<double, double> AliFmHelix::PathLength(double r) const
294{
295 //
296 // The math is taken from Maple with C(expr,optimized) and
297 // some hand-editing. It is not very nice but efficient.
298 // 'first' is the smallest of the two solutions (may be negative)
299 // 'second' is the other.
300 //
301 pair<double,double> tvalue;
302 pair<double,double> tVALUE(999999999.,999999999.);
303 if (fSingularity) {
304 double t1 = fCosDipAngle*(fOrigin.x()*fSinPhase-fOrigin.y()*fCosPhase);
305 double t12 = fOrigin.y()*fOrigin.y();
306 double t13 = fCosPhase*fCosPhase;
307 double t15 = r*r;
308 double t16 = fOrigin.x()*fOrigin.x();
309 double t20 = -fCosDipAngle*fCosDipAngle*(2.0*fOrigin.x()*fSinPhase*fOrigin.y()*fCosPhase +
310 t12-t12*t13-t15+t13*t16);
311 if (t20<0.) return tVALUE;
312 t20 = ::sqrt(t20);
313 tvalue.first = (t1-t20)/(fCosDipAngle*fCosDipAngle);
314 tvalue.second = (t1+t20)/(fCosDipAngle*fCosDipAngle);
315 }
316 else {
317 double t1 = fOrigin.y()*fCurvature;
318 double t2 = fSinPhase;
319 double t3 = fCurvature*fCurvature;
320 double t4 = fOrigin.y()*t2;
321 double t5 = fCosPhase;
322 double t6 = fOrigin.x()*t5;
323 double t8 = fOrigin.x()*fOrigin.x();
324 double t11 = fOrigin.y()*fOrigin.y();
325 double t14 = r*r;
326 double t15 = t14*fCurvature;
327 double t17 = t8*t8;
328 double t19 = t11*t11;
329 double t21 = t11*t3;
330 double t23 = t5*t5;
331 double t32 = t14*t14;
332 double t35 = t14*t3;
333 double t38 = 8.0*t4*t6 - 4.0*t1*t2*t8 - 4.0*t11*fCurvature*t6 +
334 4.0*t15*t6 + t17*t3 + t19*t3 + 2.0*t21*t8 + 4.0*t8*t23 -
335 4.0*t8*fOrigin.x()*fCurvature*t5 - 4.0*t11*t23 -
336 4.0*t11*fOrigin.y()*fCurvature*t2 + 4.0*t11 - 4.0*t14 +
337 t32*t3 + 4.0*t15*t4 - 2.0*t35*t11 - 2.0*t35*t8;
338 double t40 = (-t3*t38);
339 if (t40<0.) return tVALUE;
340 t40 = ::sqrt(t40);
341
342 double t43 = fOrigin.x()*fCurvature;
343 double t45 = 2.0*t5 - t35 + t21 + 2.0 - 2.0*t1*t2 -2.0*t43 - 2.0*t43*t5 + t8*t3;
344 double t46 = fH*fCosDipAngle*fCurvature;
345
346 tvalue.first = (-fPhase + 2.0*atan((-2.0*t1 + 2.0*t2 + t40)/t45))/t46;
347 tvalue.second = -(fPhase + 2.0*atan((2.0*t1 - 2.0*t2 + t40)/t45))/t46;
348
349 //
350 // Solution can be off by +/- one Period, select smallest
351 //
352 double p = Period();
353 // malisa deletes "isnan" check 22apr2006 if (!isnan(value.first)) {
354 if (fabs(tvalue.first-p) < fabs(tvalue.first)) tvalue.first = tvalue.first-p;
355 else if (fabs(tvalue.first+p) < fabs(tvalue.first)) tvalue.first = tvalue.first+p;
356 // malisa }
357 // malisa deletes "isnan" check 22apr2006 if (!isnan(tvalue.second)) {
358 if (fabs(tvalue.second-p) < fabs(tvalue.second)) tvalue.second = tvalue.second-p;
359 else if (fabs(tvalue.second+p) < fabs(tvalue.second)) tvalue.second = tvalue.second+p;
360 // malisa }
361 }
362 if (tvalue.first > tvalue.second)
363 swap(tvalue.first,tvalue.second);
364 return(tvalue);
365}
366
367pair<double, double> AliFmHelix::PathLength(double r, double x, double y, bool /* scanPeriods */)
368{
369 // path length
370 double x0 = fOrigin.x();
371 double y0 = fOrigin.y();
372 fOrigin.SetX(x0-x);
373 fOrigin.SetY(y0-y);
374 pair<double, double> result = this->PathLength(r);
375 fOrigin.SetX(x0);
376 fOrigin.SetY(y0);
377 return result;
378}
379
380double AliFmHelix::PathLength(const AliFmThreeVector<double>& r,
381 const AliFmThreeVector<double>& n) const
382{
383 //
384 // Vector 'r' defines the position of the center and
385 // vector 'n' the normal vector of the plane.
386 // For a straight line there is a simple analytical
387 // solution. For curvatures > 0 the root is determined
388 // by Newton method. In case no valid s can be found
389 // the max. largest value for s is returned.
390 //
391 double s;
392
393 if (fSingularity) {
394 double t = n.z()*fSinDipAngle +
395 n.y()*fCosDipAngle*fCosPhase -
396 n.x()*fCosDipAngle*fSinPhase;
397 if (t == 0)
398 s = fgkNoSolution;
399 else
400 s = ((r - fOrigin)*n)/t;
401 }
402 else {
403 const double ktMaxPrecisionNeeded = micrometer;
404 const int ktMaxIterations = 20;
405
406 double tA = fCurvature*((fOrigin - r)*n) -
407 n.x()*fCosPhase -
408 n.y()*fSinPhase;
409 double t = fH*fCurvature*fCosDipAngle;
410 double u = n.z()*fCurvature*fSinDipAngle;
411
412 double a, f, fp;
413 double sOld = s = 0;
414 double shiftOld = 0;
415 double shift;
416 // (cos(kangMax)-1)/kangMax = 0.1
417 const double kangMax = 0.21;
418 double deltas = fabs(kangMax/(fCurvature*fCosDipAngle));
419 // dampingFactor = exp(-0.5);
420 double dampingFactor = 0.60653;
421 int i;
422
423 for (i=0; i<ktMaxIterations; i++) {
424 a = t*s+fPhase;
425 double sina = sin(a);
426 double cosa = cos(a);
427 f = tA +
428 n.x()*cosa +
429 n.y()*sina +
430 u*s;
431 fp = -n.x()*sina*t +
432 n.y()*cosa*t +
433 u;
434 if ( fabs(fp)*deltas <= fabs(f) ) { //too big step
435 int sgn = 1;
436 if (fp<0.) sgn = -sgn;
437 if (f <0.) sgn = -sgn;
438 shift = sgn*deltas;
439 if (shift == -shiftOld) { // don't get stuck shifting +/-deltas
440 deltas *= dampingFactor; // dampen magnitude of shift
441 shift = sgn*deltas;
442 // allow iterations to run out
443 } else {
444 i--; // don't count against iterations
445 }
446 } else {
447 shift = f/fp;
448 }
449 s -= shift;
450 shiftOld = shift;
451 if (fabs(sOld-s) < ktMaxPrecisionNeeded) break;
452 sOld = s;
453 }
454 if (i == ktMaxIterations) return fgkNoSolution;
455 }
456 return s;
457}
458
459pair<double, double>
460AliFmHelix::PathLengths(const AliFmHelix& h, bool scanPeriods) const
461{
462 //
463 // Cannot handle case where one is a helix
464 // and the other one is a straight line.
465 //
466 if (fSingularity != h.fSingularity)
467 return pair<double, double>(fgkNoSolution, fgkNoSolution);
468
469 double s1, s2;
470
471 if (fSingularity) {
472 //
473 // Analytic solution
474 //
475 AliFmThreeVector<double> dv = h.fOrigin - fOrigin;
476 AliFmThreeVector<double> a(-fCosDipAngle*fSinPhase,
477 fCosDipAngle*fCosPhase,
478 fSinDipAngle);
479 AliFmThreeVector<double> b(-h.fCosDipAngle*h.fSinPhase,
480 h.fCosDipAngle*h.fCosPhase,
481 h.fSinDipAngle);
482 double ab = a*b;
483 double g = dv*a;
484 double k = dv*b;
485 s2 = (k-ab*g)/(ab*ab-1.);
486 s1 = g+s2*ab;
487 return pair<double, double>(s1, s2);
488 }
489 else {
490 //
491 // First step: get dca in the xy-plane as start value
492 //
493 double dx = h.XCenter() - XCenter();
494 double dy = h.YCenter() - YCenter();
495 double dd = ::sqrt(dx*dx + dy*dy);
496 double r1 = 1/Curvature();
497 double r2 = 1/h.Curvature();
498
499 /* malisa 22apr2006 is commenting out the "isnan" check
500 * if ( !finite(r1) || isnan(r1) || !finite(r2) || isnan(r2) ) {
501 * cerr << __FUNCTION__ << " *** error *** ";
502 * cerr << " r1=" << r1;
503 * cerr << " r2=" << r2 << endl;
504 * return pair<double, double>(fgkNoSolution, fgkNoSolution);
505 * }
506 */
507
508 double cosAlpha = (r1*r1 + dd*dd - r2*r2)/(2*r1*dd);
509
510 double s;
511 double x, y;
512 if (fabs(cosAlpha) < 1) { // two solutions
513 double sinAlpha = sin(acos(cosAlpha));
514 x = XCenter() + r1*(cosAlpha*dx - sinAlpha*dy)/dd;
515 y = YCenter() + r1*(sinAlpha*dx + cosAlpha*dy)/dd;
516 s = PathLength(x, y);
517 x = XCenter() + r1*(cosAlpha*dx + sinAlpha*dy)/dd;
518 y = YCenter() + r1*(cosAlpha*dy - sinAlpha*dx)/dd;
519 double a = PathLength(x, y);
520 if (h.Distance(At(a),scanPeriods) < h.Distance(At(s),scanPeriods)) s = a;
521 }
522 else { // no intersection (or exactly one)
523 x = XCenter() + r1*dx/dd;
524 y = YCenter() + r1*dy/dd;
525 s = PathLength(x, y);
526 }
527
528 //
529 // Second step: scan in decreasing intervals around seed 's'
530 //
531 const double ktMinStepSize = 10*micrometer;
532 const double ktMinRange = 10*centimeter;
533 double dmin = h.Distance(At(s),scanPeriods);
534 double range = max(2*dmin, ktMinRange);
535
536 /* malisa comments out the "isnan" check 22apr2006
537 * if ( !finite(range) || isnan(range)) {
538 * cerr << __FUNCTION__ << " *** error *** ";
539 * cerr << " range=" << range << endl;
540 * return pair<double, double>(fgkNoSolution, fgkNoSolution);
541 * }
542 */
543
544 double ds = range/10.;
545 double slast=-999999, d;
546 s1 = s - range/2.;
547 s2 = s + range/2.;
548
549 double tmp1;
550 double tmp2;
551
552 while (ds > ktMinStepSize) {
553 if ( !(s1<s2) ) {
554 cerr << __FUNCTION__ << " *** error *** s1 = ";
555 cerr << " s2 = " << s2;
556 cerr << " range = " << range;
557 cerr << " ds = " << ds << endl;
558 return pair<double, double>(fgkNoSolution, fgkNoSolution);
559 }
560 tmp1 = (s2-s1);
561 tmp2 = tmp1/10.0;
562 ds = tmp2;
563 if ( ds<0) {
564 cerr << __FUNCTION__ << " *** error *** ds = " << ds << endl;
565 return pair<double, double>(fgkNoSolution, fgkNoSolution);
566 }
567 int iterations = 0;
568 for (double ss=s1; ss<(s2+ds); ss+=ds) {
569 iterations++;
570 if ( iterations > 100 ) {
571 cerr << __FUNCTION__ << " *** error *** iterations = " << iterations << endl;
572 return pair<double, double>(fgkNoSolution, fgkNoSolution);
573 }
574 d = h.Distance(At(ss),scanPeriods);
575 if (d < dmin) {
576 dmin = d;
577 s = ss;
578 }
579 slast = ss;
580 }
581 //
582 // In the rare cases where the minimum is at the
583 // the border of the current range we shift the range
584 // and start all over, i.e we do not decrease 'ds'.
585 // Else we decrease the search intervall around the
586 // current minimum and redo the scan in smaller steps.
587 //
588 if (s == s1) {
589 d = 0.8*(s2-s1);
590 s1 -= d;
591 s2 -= d;
592 }
593 else if (s == slast) {
594 d = 0.8*(s2-s1);
595 s1 += d;
596 s2 += d;
597 }
598 else {
599 s1 = s-ds;
600 s2 = s+ds;
601 // ds /= 10;
602 }
603 }
604 return pair<double, double>(s, h.PathLength(At(s),scanPeriods));
605 }
606}
607
608
609void AliFmHelix::MoveOrigin(double s)
610{
611 // Move the helix origin
612 if (fSingularity)
613 fOrigin = At(s);
614 else {
615 AliFmThreeVector<double> newOrigin = At(s);
616 double newPhase = atan2(newOrigin.y() - YCenter(),
617 newOrigin.x() - XCenter());
618 fOrigin = newOrigin;
619 SetPhase(newPhase);
620 }
621}
622
623int operator== (const AliFmHelix& a, const AliFmHelix& b)
624{
625 //
626 // Checks for numerical identity only !
627 //
628 return (a.Origin() == b.Origin() &&
629 a.DipAngle() == b.DipAngle() &&
630 a.Curvature() == b.Curvature() &&
631 a.Phase() == b.Phase() &&
632 a.H() == b.H());
633}
634
635int operator!= (const AliFmHelix& a, const AliFmHelix& b) {return !(a == b);}
636
637ostream& operator<<(ostream& os, const AliFmHelix& h)
638{
639 return os << '('
640 << "curvature = " << h.Curvature() << ", "
641 << "dip angle = " << h.DipAngle() << ", "
642 << "phase = " << h.Phase() << ", "
643 << "h = " << h.H() << ", "
644 << "origin = " << h.Origin() << ')';
645}
646
647
648
649
650