StFemtoHelix.cxx 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641
  1. //
  2. // Helix parametrization that uses TVector3
  3. //
  4. // C++ headers
  5. #if !defined(ST_NO_NUMERIC_LIMITS)
  6. # include <limits>
  7. # if !defined(ST_NO_NAMESPACES)
  8. using std::numeric_limits;
  9. # endif
  10. #endif
  11. #define FOR_PICO_HELIX
  12. // C++ headers
  13. #include <float.h>
  14. #include <ostream>
  15. // StFemtoEvent headers
  16. #include "StFemtoHelix.h"
  17. #include "PhysicalConstants.h"
  18. ClassImpT(StFemtoHelix,double);
  19. const Double_t StFemtoHelix::NoSolution = 3.e+33;
  20. //_________________
  21. StFemtoHelix::StFemtoHelix() : mSingularity(false), mOrigin(0., 0., 0.),
  22. mDipAngle(0.), mCurvature(0.), mPhase(0.),
  23. mH(0), mCosDipAngle(0.), mSinDipAngle(0.),
  24. mCosPhase(0.), mSinPhase(0.) {
  25. /*no-op*/
  26. }
  27. //_________________
  28. StFemtoHelix::StFemtoHelix(Double_t c, Double_t d, Double_t phase,
  29. const TVector3& o, Int_t h) {
  30. setParameters(c, d, phase, o, h);
  31. }
  32. //_________________
  33. StFemtoHelix::StFemtoHelix(const StFemtoHelix &h) {
  34. mSingularity = h.mSingularity;
  35. mOrigin = h.mOrigin;
  36. mDipAngle = h.mDipAngle;
  37. mCurvature = h.mCurvature;
  38. mPhase = h.mPhase;
  39. mH = h.mH;
  40. mCosDipAngle = h.mCosDipAngle;
  41. mSinDipAngle = h.mSinDipAngle;
  42. mCosPhase = h.mCosPhase;
  43. mSinPhase = h.mSinPhase;
  44. }
  45. //_________________
  46. StFemtoHelix::~StFemtoHelix() { /* noop */ };
  47. //_________________
  48. void StFemtoHelix::setParameters(Double_t c, Double_t dip, Double_t phase,
  49. const TVector3& o, Int_t h) {
  50. //
  51. // The order in which the parameters are set is important
  52. // since setCurvature might have to adjust the others.
  53. //
  54. mH = (h>=0) ? 1 : -1; // Default is: positive particle
  55. // positive field
  56. mOrigin = o;
  57. setDipAngle(dip);
  58. setPhase(phase);
  59. //
  60. // Check for singularity and correct for negative curvature.
  61. // May change mH and mPhase. Must therefore be set last.
  62. //
  63. setCurvature(c);
  64. //
  65. // For the case B=0, h is ill defined. In the following we
  66. // always assume h = +1. Since phase = psi - h * pi/2
  67. // we have to correct the phase in case h = -1.
  68. // This assumes that the user uses the same h for phase
  69. // as the one he passed to the constructor.
  70. //
  71. if (mSingularity && mH == -1) {
  72. mH = +1;
  73. setPhase(mPhase-M_PI);
  74. }
  75. }
  76. //_________________
  77. void StFemtoHelix::setCurvature(Double_t val) {
  78. if (val < 0) {
  79. mCurvature = -val;
  80. mH = -mH;
  81. setPhase( mPhase+M_PI );
  82. }
  83. else {
  84. mCurvature = val;
  85. }
  86. #ifndef ST_NO_NUMERIC_LIMITS
  87. if ( ::fabs(mCurvature) <= numeric_limits<Double_t>::epsilon() ) {
  88. #else
  89. if ( ::fabs(mCurvature) <= static_cast<Double_t>(0) ) {
  90. #endif
  91. mSingularity = true; // straight line
  92. }
  93. else {
  94. mSingularity = false; // curved
  95. }
  96. }
  97. //_________________
  98. void StFemtoHelix::setPhase(Double_t val) {
  99. mPhase = val;
  100. mCosPhase = cos(mPhase);
  101. mSinPhase = sin(mPhase);
  102. if ( ::fabs(mPhase) > M_PI) {
  103. mPhase = atan2(mSinPhase, mCosPhase); // force range [-pi,pi]
  104. }
  105. }
  106. //_________________
  107. void StFemtoHelix::setDipAngle(Double_t val) {
  108. mDipAngle = val;
  109. mCosDipAngle = cos(mDipAngle);
  110. mSinDipAngle = sin(mDipAngle);
  111. }
  112. //_________________
  113. Double_t StFemtoHelix::xcenter() const {
  114. if (mSingularity) {
  115. return 0;
  116. }
  117. else {
  118. return mOrigin.x()-mCosPhase/mCurvature;
  119. }
  120. }
  121. //_________________
  122. Double_t StFemtoHelix::ycenter() const {
  123. if (mSingularity) {
  124. return 0;
  125. }
  126. else {
  127. return mOrigin.y()-mSinPhase/mCurvature;
  128. }
  129. }
  130. //_________________
  131. Double_t StFemtoHelix::fudgePathLength(const TVector3& p) const {
  132. Double_t s;
  133. Double_t dx = p.x()-mOrigin.x();
  134. Double_t dy = p.y()-mOrigin.y();
  135. if (mSingularity) {
  136. s = (dy*mCosPhase - dx*mSinPhase)/mCosDipAngle;
  137. }
  138. else {
  139. s = atan2(dy*mCosPhase - dx*mSinPhase,
  140. 1/mCurvature + dx*mCosPhase+dy*mSinPhase)/
  141. (mH*mCurvature*mCosDipAngle);
  142. }
  143. return s;
  144. }
  145. //_________________
  146. Double_t StFemtoHelix::distance(const TVector3& p, Bool_t scanPeriods) const {
  147. return ( this->at( pathLength(p,scanPeriods) )-p ).Mag();
  148. }
  149. //_________________
  150. Double_t StFemtoHelix::pathLength(const TVector3& p, Bool_t scanPeriods) const {
  151. //
  152. // Returns the path length at the distance of closest
  153. // approach between the helix and point p.
  154. // For the case of B=0 (straight line) the path length
  155. // can be calculated analytically. For B>0 there is
  156. // unfortunately no easy solution to the problem.
  157. // Here we use the Newton method to find the root of the
  158. // referring equation. The 'fudgePathLength' serves
  159. // as a starting value.
  160. //
  161. Double_t s;
  162. Double_t dx = p.x()-mOrigin.x();
  163. Double_t dy = p.y()-mOrigin.y();
  164. Double_t dz = p.z()-mOrigin.z();
  165. if (mSingularity) {
  166. s = mCosDipAngle*(mCosPhase*dy-mSinPhase*dx) +
  167. mSinDipAngle*dz;
  168. }
  169. else {
  170. #ifndef ST_NO_NUMERIC_LIMITS
  171. {
  172. using namespace units;
  173. #endif
  174. const Double_t MaxPrecisionNeeded = micrometer;
  175. const Int_t MaxIterations = 100;
  176. //
  177. // The math is taken from Maple with C(expr,optimized) and
  178. // some hand-editing. It is not very nice but efficient.
  179. //
  180. Double_t t34 = mCurvature*mCosDipAngle*mCosDipAngle;
  181. Double_t t41 = mSinDipAngle*mSinDipAngle;
  182. Double_t t6, t7, t11, t12, t19;
  183. //
  184. // Get a first guess by using the dca in 2D. Since
  185. // in some extreme cases we might be off by n periods
  186. // we add (subtract) periods in case we get any closer.
  187. //
  188. s = fudgePathLength(p);
  189. if (scanPeriods) {
  190. Double_t ds = period();
  191. Int_t j;
  192. Int_t jmin = 0;
  193. Double_t d;
  194. Double_t dmin = ( at(s) - p).Mag() ;
  195. for(j=1; j<MaxIterations; j++) {
  196. d = ( at(s+j*ds) - p ).Mag();
  197. if ( d < dmin) {
  198. dmin = d;
  199. jmin = j;
  200. }
  201. else {
  202. break;
  203. }
  204. } //for(j=1; j<MaxIterations; j++)
  205. for(j=-1; -j<MaxIterations; j--) {
  206. d = ( at(s+j*ds ) - p ).Mag() ;
  207. if ( d < dmin) {
  208. dmin = d;
  209. jmin = j;
  210. }
  211. else {
  212. break;
  213. }
  214. } //for(j=-1; -j<MaxIterations; j--)
  215. if (jmin) {
  216. s += jmin*ds;
  217. }
  218. } //if (scanPeriods)
  219. //
  220. // Newtons method:
  221. // Stops after MaxIterations iterations or if the required
  222. // precision is obtained. Whatever comes first.
  223. //
  224. Double_t sOld = s;
  225. for (Int_t i=0; i<MaxIterations; i++) {
  226. t6 = mPhase+s*mH*mCurvature*mCosDipAngle;
  227. t7 = cos(t6);
  228. t11 = dx-(1/mCurvature)*(t7-mCosPhase);
  229. t12 = sin(t6);
  230. t19 = dy-(1/mCurvature)*(t12-mSinPhase);
  231. s -= (t11*t12*mH*mCosDipAngle-t19*t7*mH*mCosDipAngle -
  232. (dz-s*mSinDipAngle)*mSinDipAngle)/
  233. (t12*t12*mCosDipAngle*mCosDipAngle+t11*t7*t34 +
  234. t7*t7*mCosDipAngle*mCosDipAngle +
  235. t19*t12*t34+t41);
  236. if (fabs(sOld-s) < MaxPrecisionNeeded) break;
  237. sOld = s;
  238. } //for (Int_t i=0; i<MaxIterations; i++)
  239. #ifndef ST_NO_NUMERIC_LIMITS
  240. }
  241. #endif
  242. } //else
  243. return s;
  244. }
  245. //_________________
  246. Double_t StFemtoHelix::period() const {
  247. if (mSingularity) {
  248. #ifndef ST_NO_NUMERIC_LIMITS
  249. return numeric_limits<Double_t>::max();
  250. #else
  251. return DBL_MAX;
  252. #endif
  253. }
  254. else {
  255. return fabs(2*M_PI/(mH*mCurvature*mCosDipAngle));
  256. }
  257. }
  258. //_________________
  259. pair<Double_t, Double_t> StFemtoHelix::pathLength(Double_t r) const {
  260. pair<Double_t,Double_t> value;
  261. pair<Double_t,Double_t> VALUE(999999999.,999999999.);
  262. //
  263. // The math is taken from Maple with C(expr,optimized) and
  264. // some hand-editing. It is not very nice but efficient.
  265. // 'first' is the smallest of the two solutions (may be negative)
  266. // 'second' is the other.
  267. //
  268. if (mSingularity) {
  269. Double_t t1 = mCosDipAngle*(mOrigin.x()*mSinPhase-mOrigin.y()*mCosPhase);
  270. Double_t t12 = mOrigin.y()*mOrigin.y();
  271. Double_t t13 = mCosPhase*mCosPhase;
  272. Double_t t15 = r*r;
  273. Double_t t16 = mOrigin.x()*mOrigin.x();
  274. Double_t t20 = -mCosDipAngle*mCosDipAngle*(2.0*mOrigin.x()*mSinPhase*mOrigin.y()*mCosPhase +
  275. t12-t12*t13-t15+t13*t16);
  276. if (t20<0.) {
  277. return VALUE;
  278. }
  279. t20 = ::sqrt(t20);
  280. value.first = (t1-t20)/(mCosDipAngle*mCosDipAngle);
  281. value.second = (t1+t20)/(mCosDipAngle*mCosDipAngle);
  282. }
  283. else {
  284. Double_t t1 = mOrigin.y()*mCurvature;
  285. Double_t t2 = mSinPhase;
  286. Double_t t3 = mCurvature*mCurvature;
  287. Double_t t4 = mOrigin.y()*t2;
  288. Double_t t5 = mCosPhase;
  289. Double_t t6 = mOrigin.x()*t5;
  290. Double_t t8 = mOrigin.x()*mOrigin.x();
  291. Double_t t11 = mOrigin.y()*mOrigin.y();
  292. Double_t t14 = r*r;
  293. Double_t t15 = t14*mCurvature;
  294. Double_t t17 = t8*t8;
  295. Double_t t19 = t11*t11;
  296. Double_t t21 = t11*t3;
  297. Double_t t23 = t5*t5;
  298. Double_t t32 = t14*t14;
  299. Double_t t35 = t14*t3;
  300. Double_t t38 = 8.0*t4*t6 - 4.0*t1*t2*t8 - 4.0*t11*mCurvature*t6 +
  301. 4.0*t15*t6 + t17*t3 + t19*t3 + 2.0*t21*t8 + 4.0*t8*t23 -
  302. 4.0*t8*mOrigin.x()*mCurvature*t5 - 4.0*t11*t23 -
  303. 4.0*t11*mOrigin.y()*mCurvature*t2 + 4.0*t11 - 4.0*t14 +
  304. t32*t3 + 4.0*t15*t4 - 2.0*t35*t11 - 2.0*t35*t8;
  305. Double_t t40 = (-t3*t38);
  306. if (t40<0.) {
  307. return VALUE;
  308. }
  309. t40 = ::sqrt(t40);
  310. Double_t t43 = mOrigin.x()*mCurvature;
  311. Double_t t45 = 2.0*t5 - t35 + t21 + 2.0 - 2.0*t1*t2 -2.0*t43 - 2.0*t43*t5 + t8*t3;
  312. Double_t t46 = mH*mCosDipAngle*mCurvature;
  313. value.first = (-mPhase + 2.0*atan((-2.0*t1 + 2.0*t2 + t40)/t45))/t46;
  314. value.second = -(mPhase + 2.0*atan((2.0*t1 - 2.0*t2 + t40)/t45))/t46;
  315. //
  316. // Solution can be off by +/- one period, select smallest
  317. //
  318. Double_t p = period();
  319. if ( ! std::isnan(value.first) ) {
  320. if ( ::fabs(value.first-p) < ::fabs(value.first) ) {
  321. value.first = value.first-p;
  322. }
  323. else if ( ::fabs(value.first+p) < ::fabs(value.first) ) {
  324. value.first = value.first+p;
  325. }
  326. } //if ( ! std::isnan(value.first) )
  327. if (! std::isnan(value.second)) {
  328. if ( ::fabs(value.second-p) < ::fabs(value.second) ) {
  329. value.second = value.second-p;
  330. }
  331. else if ( ::fabs(value.second+p) < ::fabs(value.second) ) {
  332. value.second = value.second+p;
  333. }
  334. } //if (! std::isnan(value.second))
  335. } //else
  336. if (value.first > value.second) {
  337. swap(value.first,value.second);
  338. }
  339. return(value);
  340. }
  341. //_________________
  342. pair<Double_t, Double_t> StFemtoHelix::pathLength(Double_t r, Double_t x, Double_t y) {
  343. Double_t x0 = mOrigin.x();
  344. Double_t y0 = mOrigin.y();
  345. mOrigin.SetX(x0-x);
  346. mOrigin.SetY(y0-y);
  347. pair<Double_t, Double_t> result = this->pathLength(r);
  348. mOrigin.SetX(x0);
  349. mOrigin.SetY(y0);
  350. return result;
  351. }
  352. //________________
  353. Double_t StFemtoHelix::pathLength(const TVector3& r,
  354. const TVector3& n) const {
  355. //
  356. // Vector 'r' defines the position of the center and
  357. // vector 'n' the normal vector of the plane.
  358. // For a straight line there is a simple analytical
  359. // solution. For curvatures > 0 the root is determined
  360. // by Newton method. In case no valid s can be found
  361. // the max. largest value for s is returned.
  362. //
  363. Double_t s;
  364. if (mSingularity) {
  365. Double_t t = n.z()*mSinDipAngle +
  366. n.y()*mCosDipAngle*mCosPhase -
  367. n.x()*mCosDipAngle*mSinPhase;
  368. if (t == 0) {
  369. s = NoSolution;
  370. }
  371. else {
  372. s = ((r - mOrigin)*n)/t;
  373. }
  374. }
  375. else {
  376. const Double_t MaxPrecisionNeeded = micrometer;
  377. const Int_t MaxIterations = 20;
  378. Double_t A = mCurvature*((mOrigin - r)*n) -
  379. n.x()*mCosPhase -
  380. n.y()*mSinPhase;
  381. Double_t t = mH*mCurvature*mCosDipAngle;
  382. Double_t u = n.z()*mCurvature*mSinDipAngle;
  383. Double_t a, f, fp;
  384. Double_t sOld = s = 0;
  385. Double_t shiftOld = 0;
  386. Double_t shift;
  387. // (cos(angMax)-1)/angMax = 0.1
  388. const Double_t angMax = 0.21;
  389. Double_t deltas = fabs(angMax/(mCurvature*mCosDipAngle));
  390. // dampingFactor = exp(-0.5);
  391. // Double_t dampingFactor = 0.60653;
  392. Int_t i;
  393. for (i=0; i<MaxIterations; i++) {
  394. a = t*s+mPhase;
  395. Double_t sina = sin(a);
  396. Double_t cosa = cos(a);
  397. f = A + n.x()*cosa + n.y()*sina + u*s;
  398. fp = -n.x()*sina*t + n.y()*cosa*t + u;
  399. if ( fabs(fp)*deltas <= fabs(f) ) { //too big step
  400. Int_t sgn = 1;
  401. if (fp<0.) sgn = -sgn;
  402. if (f <0.) sgn = -sgn;
  403. shift = sgn*deltas;
  404. if (shift<0) shift*=0.9; // don't get stuck shifting +/-deltas
  405. }
  406. else {
  407. shift = f/fp;
  408. }
  409. s -= shift;
  410. shiftOld = shift;
  411. if ( ::fabs(sOld-s) < MaxPrecisionNeeded ) {
  412. break;
  413. }
  414. sOld = s;
  415. } //for (i=0; i<MaxIterations; i++)
  416. if (i == MaxIterations) {
  417. return NoSolution;
  418. }
  419. } //else
  420. return s;
  421. }
  422. //_________________
  423. pair<Double_t, Double_t> StFemtoHelix::pathLengths(const StFemtoHelix& h, Double_t minStepSize,
  424. Double_t minRange) const {
  425. //
  426. // Cannot handle case where one is a helix
  427. // and the other one is a straight line.
  428. //
  429. if (mSingularity != h.mSingularity) {
  430. return pair<Double_t, Double_t>(NoSolution, NoSolution);
  431. }
  432. Double_t s1, s2;
  433. if (mSingularity) {
  434. //
  435. // Analytic solution
  436. //
  437. TVector3 dv = h.mOrigin - mOrigin;
  438. TVector3 a(-mCosDipAngle*mSinPhase,
  439. mCosDipAngle*mCosPhase,
  440. mSinDipAngle);
  441. TVector3 b(-h.mCosDipAngle*h.mSinPhase,
  442. h.mCosDipAngle*h.mCosPhase,
  443. h.mSinDipAngle);
  444. Double_t ab = a*b;
  445. Double_t g = dv*a;
  446. Double_t k = dv*b;
  447. s2 = (k-ab*g)/(ab*ab-1.);
  448. s1 = g+s2*ab;
  449. return pair<Double_t, Double_t>(s1, s2);
  450. } //if (mSingularity)
  451. else {
  452. //
  453. // First step: get dca in the xy-plane as start value
  454. //
  455. Double_t dx = h.xcenter() - xcenter();
  456. Double_t dy = h.ycenter() - ycenter();
  457. Double_t dd = ::sqrt(dx*dx + dy*dy);
  458. Double_t r1 = 1/curvature();
  459. Double_t r2 = 1/h.curvature();
  460. Double_t cosAlpha = (r1*r1 + dd*dd - r2*r2)/(2*r1*dd);
  461. Double_t s;
  462. Double_t x, y;
  463. if ( ::fabs(cosAlpha) < 1) { // two solutions
  464. Double_t sinAlpha = sin(acos(cosAlpha));
  465. x = xcenter() + r1*(cosAlpha*dx - sinAlpha*dy)/dd;
  466. y = ycenter() + r1*(sinAlpha*dx + cosAlpha*dy)/dd;
  467. s = pathLength(x, y);
  468. x = xcenter() + r1*(cosAlpha*dx + sinAlpha*dy)/dd;
  469. y = ycenter() + r1*(cosAlpha*dy - sinAlpha*dx)/dd;
  470. Double_t a = pathLength(x, y);
  471. if ( h.distance(at(a)) < h.distance(at(s)) ) {
  472. s = a;
  473. }
  474. } //if ( ::fabs(cosAlpha) < 1)
  475. else { // no intersection (or exactly one)
  476. Int_t rsign = ((r2-r1) > dd ? -1 : 1); // set -1 when *this* helix is
  477. // completely contained in the other
  478. x = xcenter() + rsign*r1*dx/dd;
  479. y = ycenter() + rsign*r1*dy/dd;
  480. s = pathLength(x, y);
  481. } //else
  482. //
  483. // Second step: scan in decreasing intervals around seed 's'
  484. // minRange and minStepSize are passed as arguments to the method.
  485. // They have default values defined in the header file.
  486. //
  487. Double_t dmin = h.distance(at(s));
  488. Double_t range = max(2*dmin, minRange);
  489. Double_t ds = range/10;
  490. Double_t slast=-999999, ss, d;
  491. s1 = s - range/2.;
  492. s2 = s + range/2.;
  493. while (ds > minStepSize) {
  494. for ( ss=s1; ss<s2+ds; ss+=ds ) {
  495. d = h.distance(at(ss));
  496. if (d < dmin) {
  497. dmin = d;
  498. s = ss;
  499. }
  500. slast = ss;
  501. } //for ( ss=s1; ss<s2+ds; ss+=ds )
  502. //
  503. // In the rare cases where the minimum is at the
  504. // the border of the current range we shift the range
  505. // and start all over, i.e we do not decrease 'ds'.
  506. // Else we decrease the search intervall around the
  507. // current minimum and redo the scan in smaller steps.
  508. //
  509. if (s == s1) {
  510. d = 0.8*(s2-s1);
  511. s1 -= d;
  512. s2 -= d;
  513. }
  514. else if (s == slast) {
  515. d = 0.8*(s2-s1);
  516. s1 += d;
  517. s2 += d;
  518. }
  519. else {
  520. s1 = s-ds;
  521. s2 = s+ds;
  522. ds /= 10;
  523. }
  524. } //while (ds > minStepSize)
  525. return pair<Double_t, Double_t>(s, h.pathLength(at(s)));
  526. } //else
  527. }
  528. //_________________
  529. void StFemtoHelix::moveOrigin(Double_t s) {
  530. if (mSingularity) {
  531. mOrigin = at(s);
  532. }
  533. else {
  534. TVector3 newOrigin = at(s);
  535. Double_t newPhase = atan2(newOrigin.y() - ycenter(),
  536. newOrigin.x() - xcenter());
  537. mOrigin = newOrigin;
  538. setPhase(newPhase);
  539. }
  540. }
  541. //_________________
  542. Int_t operator== (const StFemtoHelix& a, const StFemtoHelix& b) {
  543. //
  544. // Checks for numerical identity only !
  545. //
  546. return ( a.origin() == b.origin() &&
  547. a.dipAngle() == b.dipAngle() &&
  548. a.curvature() == b.curvature() &&
  549. a.phase() == b.phase() &&
  550. a.h() == b.h() );
  551. }
  552. //_________________
  553. Int_t operator!= (const StFemtoHelix& a, const StFemtoHelix& b) {
  554. return !(a == b);
  555. }
  556. //_________________
  557. std::ostream& operator<<(std::ostream& os, const StFemtoHelix& h) {
  558. return os << '('
  559. << "curvature = " << h.curvature() << ", "
  560. << "dip angle = " << h.dipAngle() << ", "
  561. << "phase = " << h.phase() << ", "
  562. << "h = " << h.h() << ", "
  563. << "origin = " << h.origin().X()
  564. << " , " << h.origin().Y() << " , " << h.origin().Z() << " "
  565. << ')';
  566. }