Belle II Software  release-06-01-15
Helix.cc
1 /**************************************************************************
2  * basf2 (Belle II Analysis Software Framework) *
3  * Author: The Belle II Collaboration *
4  * *
5  * See git log for contributors and copyright holders. *
6  * This file is licensed under LGPL-3.0, see LICENSE.md. *
7  **************************************************************************/
8 #include <framework/dataobjects/Helix.h>
9 
10 #include <TVector3.h>
11 #include <TRandom3.h>
12 
13 #include <framework/utilities/TestHelpers.h>
14 
15 #include <gtest/gtest.h>
16 
17 using namespace std;
18 using namespace Belle2;
19 using namespace HelixParameterIndex;
20 
22 std::ostream& operator<<(::std::ostream& output, const TVector3& tVector3)
23 {
24  return output
25  << "TVector3("
26  << tVector3.X() << ", "
27  << tVector3.Y() << ", "
28  << tVector3.Z() << ")";
29 }
30 
32 
37  template<>
38  bool allNear<Belle2::Helix>(const Belle2::Helix& expected,
39  const Belle2::Helix& actual,
40  double tolerance)
41  {
42  bool d0Near = fabs(expected.getD0() - actual.getD0()) < tolerance;
43  bool phi0Near = angleNear(expected.getPhi0(), actual.getPhi0(), tolerance);
44  bool omegaNear = fabs(expected.getOmega() - actual.getOmega()) < tolerance;
45  bool z0Near = fabs(expected.getZ0() - actual.getZ0()) < tolerance;
46  bool tanLambdaNear = fabs(expected.getTanLambda() - actual.getTanLambda()) < tolerance;
47 
48  return d0Near and phi0Near and omegaNear and z0Near and tanLambdaNear;
49  }
50 }
51 
52 namespace {
53  Belle2::Helix helixFromCenter(const TVector3& center, const float& radius, const float& tanLambda)
54  {
55  double omega = 1 / radius;
56  double phi0 = center.Phi() + copysign(M_PI / 2.0, radius);
57  double d0 = copysign(center.Perp(), radius) - radius;
58  double z0 = center.Z();
59 
60  return Belle2::Helix(d0, phi0, omega, z0, tanLambda);
61  }
62 
64  vector<float> linspace(const float& start, const float& end, const int n)
65  {
66  std::vector<float> result(n);
67  result[0] = start;
68  result[n - 1] = end;
69 
70  for (int i = 1; i < n - 1; ++i) {
71  float start_weight = (float)(n - 1 - i) / (n - 1);
72  float end_weight = 1 - start_weight;
73  result[i] = start * start_weight + end * end_weight;
74  }
75 
76  return result;
77  }
78 
80  class HelixTest : public ::testing::Test {
81 
82  protected:
83  // Common level precision for all tests.
84  double absError = 1e-6;
85  double nominalBz = 1.5;
86 
87  std::vector<float> omegas { -1, 0, 1};
88  //std::vector<float> omegas {1};
89  std::vector<float> phi0s = linspace(-M_PI, M_PI, 11);
90  std::vector<float> d0s { -0.5, -0.2, 0, 0.2, 0.5};
91  //std::vector<float> d0s {0.5};
92  std::vector<float> chis = linspace(-5 * M_PI / 6, 5 * M_PI / 6, 11);
93 
94  std::vector<TVector3> bys = {
95  TVector3(-2.0, 0.5, 0.0),
96  TVector3(-1.0, 0.5, 0.0),
97  TVector3(0.0, 0.5, 0.0),
98  TVector3(1.0, 0.5, 0.0),
99  TVector3(2.0, 0.5, 0.0),
100 
101  TVector3(-2.0, 0.0, 0.0),
102  TVector3(-1.0, 0.0, 0.0),
103  TVector3(0.0, 0.0, 0.0),
104  TVector3(1.0, 0.0, 0.0),
105  TVector3(2.0, 0.0, 0.0),
106 
107  TVector3(-2.0, -1.0, 0.0),
108  TVector3(-1.0, -1.0, 0.0),
109  TVector3(0.0, -1.0, 0.0),
110  TVector3(1.0, -1.0, 0.0),
111  TVector3(2.0, -1.0, 0.0),
112  };
113 
114  };
115 
117  TEST_F(HelixTest, Getters)
118  {
119  TRandom3 generator;
120  unsigned int nCases = 1;
121  double bField = nominalBz;
122 
123  for (unsigned int i = 0; i < nCases; ++i) {
124 
125  short int charge = generator.Uniform(-1, 1) > 0 ? 1 : -1;
126 
127  // Generate a random put orthogonal pair of vectors in the r-phi plane
128  TVector2 d(generator.Uniform(-1, 1), generator.Uniform(-1, 1));
129  TVector2 pt(generator.Uniform(-1, 1), generator.Uniform(-1, 1));
130  d.Set(d.X(), -d.X() * pt.Px() / pt.Py());
131 
132  // Add a random z component
133  TVector3 position(d.X(), d.Y(), generator.Uniform(-1, 1));
134  TVector3 momentum(pt.Px(), pt.Py(), generator.Uniform(-1, 1));
135 
136  // Set up class for testing
137  Helix helix(position, momentum, charge, bField);
138 
139  // Test all vector elements
140  EXPECT_ALL_NEAR(position, helix.getPerigee(), absError);
141  EXPECT_ALL_NEAR(momentum, helix.getMomentum(bField), absError);
142 
143  // Test getter for transverse momentum
144  EXPECT_NEAR(momentum.Perp(), helix.getTransverseMomentum(bField), absError);
145 
146  // Test getter of kappa
147  EXPECT_NEAR(charge / momentum.Perp(), helix.getKappa(bField), absError);
148 
149  // Test getter of d0
150  // Check absolute value of d0
151  EXPECT_NEAR(position.Perp(), fabs(helix.getD0()), absError);
152 
153  // Check sign of d0
154  EXPECT_SAME_SIGN(position.Cross(momentum).Z(), helix.getD0());
155 
156  // Test getter of phi0
157  EXPECT_NEAR(momentum.Phi(), helix.getPhi0(), absError);
158 
159  // Test getter of d0
160  EXPECT_NEAR(position.Z(), helix.getZ0(), absError);
161 
162  // Test getter of tan lambda
163  EXPECT_NEAR(1 / tan(momentum.Theta()), helix.getTanLambda(), absError);
164 
165  // Test getter of cot theta
166  EXPECT_NEAR(1 / tan(momentum.Theta()), helix.getCotTheta(), absError);
167 
168  // Test other variables
169  EXPECT_EQ(charge, helix.getChargeSign());
170 
171  }
172  } // Testcases for getters
173 
174  TEST_F(HelixTest, SignOfD0)
175  {
176  // This tests the assumption that the sign of d0 is given by the sign of position x momentum as a two dimensional cross product.
177 
178  const TVector3 position(1, 0, 0);
179  const TVector3 momentum(0, 1, 0);
180  const TVector3 oppositeMomentum(0, -1, 0);
181  const float charge = 1;
182  const float bField = nominalBz;
183 
184  Helix helix(position, momentum, charge, bField);
185  EXPECT_NEAR(1, helix.getD0(), absError);
186 
187  // D0 does not change with the charge
188  Helix helix2(position, momentum, -charge, bField);
189  EXPECT_NEAR(1, helix2.getD0(), absError);
190 
191  // But with reversal of momentum
192  Helix oppositeMomentumHelix(position, oppositeMomentum, charge, bField);
193  EXPECT_NEAR(-1, oppositeMomentumHelix.getD0(), absError);
194 
195  Helix oppositeMomentumHelix2(position, oppositeMomentum, -charge, bField);
196  EXPECT_NEAR(-1, oppositeMomentumHelix2.getD0(), absError);
197 
198  }
199 
200  TEST_F(HelixTest, Center)
201  {
208  TVector3 center(0.0, -2.0, 0.0);
209  float radius = -1;
210  // Keep it flat
211  float tanLambda = 0;
212 
213  Helix helix = helixFromCenter(center, radius, tanLambda);
214 
215  TVector3 actualCenter = helix.getPerigee() * ((1 / helix.getOmega() + helix.getD0()) / helix.getD0());
216  EXPECT_NEAR(center.X(), actualCenter.X(), absError);
217  EXPECT_NEAR(center.Y(), actualCenter.Y(), absError);
218  }
219 
220  TEST_F(HelixTest, Explicit)
221  {
228  TVector3 center(0.0, -2.0, 0.0);
229  float radius = -1;
230  // Keep it flat
231  float tanLambda = 0;
232 
233  Helix helix = helixFromCenter(center, radius, tanLambda);
234  EXPECT_NEAR(-1, helix.getD0(), absError);
235  EXPECT_ANGLE_NEAR(-M_PI, helix.getPhi0(), absError);
236  EXPECT_NEAR(-1, helix.getOmega(), absError);
237 
238  // Positions on the helix
239  {
240  // Start point
241  float arcLength2D = 0;
242  TVector3 position = helix.getPositionAtArcLength2D(arcLength2D);
243  TVector3 tangential = helix.getUnitTangentialAtArcLength2D(arcLength2D);
244 
245  EXPECT_ALL_NEAR(TVector3(0.0, -1.0, 0.0), position, absError);
246  EXPECT_ANGLE_NEAR(-M_PI, tangential.Phi(), absError);
247  }
248 
249  {
250  float arcLength2D = M_PI / 2;
251  TVector3 position = helix.getPositionAtArcLength2D(arcLength2D);
252  TVector3 tangential = helix.getUnitTangentialAtArcLength2D(arcLength2D);
253  EXPECT_ALL_NEAR(TVector3(-1.0, -2.0, 0.0), position, absError);
254  EXPECT_ANGLE_NEAR(-M_PI / 2, tangential.Phi(), absError);
255  }
256 
257  {
258  float arcLength2D = M_PI;
259  TVector3 position = helix.getPositionAtArcLength2D(arcLength2D);
260  TVector3 tangential = helix.getUnitTangentialAtArcLength2D(arcLength2D);
261  EXPECT_ALL_NEAR(TVector3(0.0, -3.0, 0.0), position, absError);
262  EXPECT_ANGLE_NEAR(0, tangential.Phi(), absError);
263  }
264 
265  {
266  float arcLength2D = 3 * M_PI / 2 ;
267  TVector3 position = helix.getPositionAtArcLength2D(arcLength2D);
268  TVector3 tangential = helix.getUnitTangentialAtArcLength2D(arcLength2D);
269  EXPECT_ALL_NEAR(TVector3(1.0, -2.0, 0.0), position, absError);
270  EXPECT_ANGLE_NEAR(M_PI / 2, tangential.Phi(), absError);
271  }
272  }
273 
274  TEST_F(HelixTest, Tangential)
275  {
276  float z0 = 0;
277  float tanLambda = 2;
278 
279  for (const float d0 : d0s) {
280  for (const float phi0 : phi0s) {
281  for (const float omega : omegas) {
282 
283  Helix helix(d0, phi0, omega, z0, tanLambda);
284  TEST_CONTEXT("Failed for " << helix);
285 
286  TVector3 tangentialAtPerigee = helix.getUnitTangentialAtArcLength2D(0.0);
287 
288  EXPECT_ANGLE_NEAR(phi0, tangentialAtPerigee.Phi(), absError);
289  EXPECT_FLOAT_EQ(1.0, tangentialAtPerigee.Mag());
290  EXPECT_FLOAT_EQ(tanLambda, 1 / tan(tangentialAtPerigee.Theta()));
291 
292  for (const float chi : chis) {
293 
294  if (omega == 0) {
295  // Use chi as the arc length in the straight line case
296  float arcLength2D = chi;
297 
298  // Tangential vector shall not change along the line
299  TVector3 tangential = helix.getUnitTangentialAtArcLength2D(arcLength2D);
300  EXPECT_ALL_NEAR(tangentialAtPerigee, tangential, absError);
301 
302  } else {
303  float arcLength2D = -chi / omega;
304  TVector3 tangential = helix.getUnitTangentialAtArcLength2D(arcLength2D);
305 
306  float actualChi = tangential.DeltaPhi(tangentialAtPerigee);
307  EXPECT_ANGLE_NEAR(chi, actualChi, absError);
308  EXPECT_FLOAT_EQ(tangentialAtPerigee.Theta(), tangential.Theta());
309  EXPECT_FLOAT_EQ(1, tangential.Mag());
310 
311  }
312 
313  }
314  }
315  }
316  }
317  }
318 
319 
320  TEST_F(HelixTest, MomentumExtrapolation)
321  {
322  float z0 = 0;
323  float tanLambda = -2;
324 
325  for (const float d0 : d0s) {
326  for (const float phi0 : phi0s) {
327  for (const float omega : omegas) {
328  if (omega != 0) {
329 
330  Helix helix(d0, phi0, omega, z0, tanLambda);
331  TVector3 momentumAtPerigee = helix.getMomentum(nominalBz);
332  for (const float chi : chis) {
333 
334  float arcLength2D = -chi / omega;
335  TVector3 extrapolatedMomentum = helix.getMomentumAtArcLength2D(arcLength2D, nominalBz);
336 
337  float actualChi = extrapolatedMomentum.DeltaPhi(momentumAtPerigee);
338  EXPECT_ANGLE_NEAR(chi, actualChi, absError);
339  EXPECT_FLOAT_EQ(momentumAtPerigee.Theta(), extrapolatedMomentum.Theta());
340  EXPECT_FLOAT_EQ(momentumAtPerigee.Mag(), extrapolatedMomentum.Mag());
341  }
342  }
343  }
344  }
345  }
346  }
347 
348 
349 
350  TEST_F(HelixTest, Extrapolation)
351  {
352 
353  // z coordinates do not matter for this test.
354  float z0 = 0;
355  float tanLambda = 2;
356 
357  for (const float d0 : d0s) {
358  for (const float phi0 : phi0s) {
359  for (const float omega : omegas) {
360 
361  Helix helix(d0, phi0, omega, z0, tanLambda);
362  TVector3 perigee = helix.getPerigee();
363 
364  TVector3 tangentialAtPerigee = helix.getUnitTangentialAtArcLength2D(0.0);
365  TEST_CONTEXT("Failed for " << helix);
366 
367  //continue;
368 
369  for (const float chi : chis) {
370  TEST_CONTEXT("Failed for chi = " << chi);
371 
372  // In the cases where omega is 0 (straight line case) chi become undefined.
373  // Use chi sample as transverse travel distance instead.
374  float expectedArcLength2D = omega != 0 ? -chi / omega : chi;
375  TVector3 pointOnHelix = helix.getPositionAtArcLength2D(expectedArcLength2D);
376 
377  float cylindricalR = pointOnHelix.Perp();
378  float arcLength2D = helix.getArcLength2DAtCylindricalR(cylindricalR);
379 
380  // Only the absolute value is returned.
381  EXPECT_NEAR(fabs(expectedArcLength2D), arcLength2D, absError);
382 
383  // Also check it the extrapolation lies in the forward direction.
384  TVector3 secantVector = pointOnHelix - perigee;
385 
386  if (expectedArcLength2D == 0) {
387  EXPECT_NEAR(0, secantVector.Mag(), absError);
388  } else {
389  TVector2 secantVectorXY = secantVector.XYvector();
390 
391  TVector2 tangentialXY = tangentialAtPerigee.XYvector();
392  float coalignment = secantVectorXY * tangentialXY ;
393 
394  bool extrapolationIsForward = coalignment > 0;
395  bool expectedIsForward = expectedArcLength2D > 0;
396  EXPECT_EQ(expectedIsForward, extrapolationIsForward);
397  }
398  }
399  }
400  }
401  }
402  } // end TEST_F
403 
404 
405  TEST_F(HelixTest, ExtrapolationToNormalPlane)
406  {
407  {
408  TVector3 center(0.0, 2.0, 0.0);
409  float radius = -1;
410  // Keep it flat
411  float tanLambda = 0;
412  Helix helix = helixFromCenter(center, radius, tanLambda);
413 
414  // Plane coordinate
415  double x = 0.0;
416  double y = 3.0;
417  double nx = -1.0;
418  double ny = 1.0;
419  double arcLength2D = helix.getArcLength2DAtNormalPlane(x, y, nx, ny);
420  EXPECT_NEAR(-M_PI / 2, arcLength2D, absError);
421  }
422 
423  {
424  TVector3 center(0.0, 2.0, 0.0);
425  float radius = -1;
426  // Keep it flat
427  float tanLambda = 0;
428  Helix helix = helixFromCenter(center, radius, tanLambda);
429 
430  // Plane coordinate
431  double x = 0.0;
432  double y = 3.0;
433  double nx = 1.0;
434  double ny = 1.0;
435  double arcLength2D = helix.getArcLength2DAtNormalPlane(x, y, nx, ny);
436  EXPECT_NEAR(M_PI / 2, arcLength2D, absError);
437  }
438 
439  {
440  TVector3 center(0.0, 2.0, 0.0);
441  float radius = 1;
442  // Keep it flat
443  float tanLambda = 0;
444  Helix helix = helixFromCenter(center, radius, tanLambda);
445 
446  // Plane coordinate
447  double x = 0.0;
448  double y = 3.0;
449  double nx = -1.0;
450  double ny = 1.0;
451  double arcLength2D = helix.getArcLength2DAtNormalPlane(x, y, nx, ny);
452  EXPECT_NEAR(M_PI / 2, arcLength2D, absError);
453  }
454 
455  {
456  TVector3 center(0.0, 2.0, 0.0);
457  float radius = 1;
458  // Keep it flat
459  float tanLambda = 0;
460  Helix helix = helixFromCenter(center, radius, tanLambda);
461 
462  // Plane coordinate
463  double x = 0.0;
464  double y = 3.0;
465  double nx = 1.0;
466  double ny = 1.0;
467  double arcLength2D = helix.getArcLength2DAtNormalPlane(x, y, nx, ny);
468  EXPECT_NEAR(-M_PI / 2, arcLength2D, absError);
469  }
470 
471 
472 
473  }
474 
475  TEST_F(HelixTest, PerigeeExtrapolateRoundTrip)
476  {
477  float z0 = 0;
478  float tanLambda = -2;
479 
480  for (const float d0 : d0s) {
481  for (const float phi0 : phi0s) {
482  for (const float omega : omegas) {
483 
484  // Extrapolations involving the momentum only makes sense with finit momenta
485  if (omega != 0) {
486  Helix expectedHelix(d0, phi0, omega, z0, tanLambda);
487 
488  for (const float chi : chis) {
489  float arcLength2D = -chi / omega;
490  TVector3 position = expectedHelix.getPositionAtArcLength2D(arcLength2D);
491  TVector3 momentum = expectedHelix.getMomentumAtArcLength2D(arcLength2D, nominalBz);
492  int chargeSign = expectedHelix.getChargeSign();
493 
494  EXPECT_NEAR(tanLambda, 1 / tan(momentum.Theta()), absError);
495  EXPECT_ANGLE_NEAR(phi0 + chi, momentum.Phi(), absError);
496  EXPECT_NEAR(z0 + tanLambda * arcLength2D, position.Z(), absError);
497 
498  //B2INFO("chi out " << chi);
499  Helix helix(position, momentum, chargeSign, nominalBz);
500 
501  EXPECT_NEAR(expectedHelix.getOmega(), helix.getOmega(), absError);
502  EXPECT_ANGLE_NEAR(expectedHelix.getPhi0(), helix.getPhi0(), absError);
503  EXPECT_NEAR(expectedHelix.getD0(), helix.getD0(), absError);
504  EXPECT_NEAR(expectedHelix.getTanLambda(), helix.getTanLambda(), absError);
505  EXPECT_NEAR(expectedHelix.getZ0(), helix.getZ0(), absError);
506  }
507  }
508 
509  }
510  }
511  }
512  }
513 
514 
515 
516 
517  /*
518  TEST_F(HelixTest, CalculateDrExplicit)
519  {
520  float tanLambda = 3;
521 
522  TVector3 center(0.0, -2.0, 0.0);
523  float radius = -1;
524 
525  Helix helix = helixFromCenter(center, radius, tanLambda);
526  EXPECT_NEAR(-1, helix.getD0(), absError);
527  EXPECT_ANGLE_NEAR(-M_PI, helix.getPhi0(), absError);
528  EXPECT_NEAR(-1, helix.getOmega(), absError);
529  {
530  TVector3 position(0.0, 0.0, 0.0);
531  float newD0 = helix.getDr(position);
532  EXPECT_NEAR(-1, newD0, absError);
533  }
534 
535  {
536  TVector3 position(2.0, -2.0, 0.0);
537  float newD0 = helix.getDr(position);
538  EXPECT_NEAR(-1, newD0, absError);
539  }
540  {
541  TVector3 position(-2.0, -2.0, 0.0);
542  float newD0 = helix.getDr(position);
543  EXPECT_NEAR(-1, newD0, absError);
544  }
545 
546  {
547  TVector3 position(1.0, -1.0, 0.0);
548  float newD0 = helix.getDr(position);
549  EXPECT_NEAR(-(sqrt(2) - 1) , newD0, absError);
550  }
551  }
552 
553  TEST_F(HelixTest, CalculateDr)
554  {
555  float z0 = 2;
556  float tanLambda = 3;
557 
558  for (const float phi0 : phi0s) {
559  for (const float omega : omegas) {
560  for (const float d0 : d0s) {
561  Helix helix(d0, phi0, omega, z0, tanLambda);
562  TEST_CONTEXT("Failed for " << helix);
563 
564  EXPECT_NEAR(d0, helix.getDr(TVector3(0.0, 0.0, 0.0)), absError);
565 
566  for (const float chi : chis) {
567  for (const float newD0 : d0s) {
568  // In the line case use the chi value directly as the arc length
569 
570  float arcLength2D = omega == 0 ? chi : -chi / omega;
571  TVector3 positionOnHelix = helix.getPositionAtArcLength2D(arcLength2D);
572 
573  TVector3 tangentialToHelix = helix.getUnitTangentialAtArcLength2D(arcLength2D);
574 
575  TVector3 perpendicularToHelix = tangentialToHelix;
576  perpendicularToHelix.RotateZ(M_PI / 2.0);
577  // Normalize the xy part
578  perpendicularToHelix *= 1 / perpendicularToHelix.Perp();
579 
580  TVector3 displacementFromHelix = perpendicularToHelix * newD0;
581  TVector3 testPosition = positionOnHelix + displacementFromHelix;
582 
583  TEST_CONTEXT("Failed for chi " << chi);
584  TEST_CONTEXT("Failed for position on helix " << positionOnHelix);
585  TEST_CONTEXT("Failed for tangential to helix " << tangentialToHelix);
586  TEST_CONTEXT("Failed for perpendicular to helix " << perpendicularToHelix);
587  TEST_CONTEXT("Failed for test position " << testPosition);
588 
589  float testDr = helix.getDr(testPosition);
590  EXPECT_NEAR(newD0, testDr, absError);
591  }
592  }
593  }
594  }
595  }
596  }
597  */
598 
599  TEST_F(HelixTest, PassiveMoveExplicit)
600  {
601  TVector3 center(0.0, 1.0, 0.0);
602  float radius = -1;
603  float tanLambda = 3;
604 
605  Helix helix = helixFromCenter(center, radius, tanLambda);
606 
607  ASSERT_NEAR(0, helix.getD0(), absError);
608  ASSERT_ANGLE_NEAR(0, helix.getPhi0(), absError);
609  ASSERT_NEAR(-1, helix.getOmega(), absError);
610 
611  // Save the untransformed Helix
612  Helix expectedHelix(helix);
613 
614  // Vector by which the coordinate system should move.
615  // (To the right of the circle)
616  TVector3 by(1.0, 1.0, 0.0);
617 
618  float arcLength2D = helix.passiveMoveBy(by);
619 
620  // The right of the circle lies in the counterclockwise direction
621  // The forward direction is counterclockwise, so we expect to move forward.
622  ASSERT_NEAR(M_PI / 2, arcLength2D, absError);
623 
624  ASSERT_NEAR(0, helix.getD0(), absError);
625  ASSERT_ANGLE_NEAR(M_PI / 2, helix.getPhi0(), absError);
626  ASSERT_NEAR(-1, helix.getOmega(), absError);
627 
628  ASSERT_NEAR(3 * M_PI / 2, helix.getZ0(), absError);
629  ASSERT_NEAR(3, helix.getTanLambda(), absError);
630 
631  // Now transform back to the original point
632  float arcLength2DBackward = helix.passiveMoveBy(-by);
633 
634  ASSERT_NEAR(arcLength2D, -arcLength2DBackward, absError);
635  ASSERT_ALL_NEAR(expectedHelix, helix, absError);
636  }
637 
638 
639  TEST_F(HelixTest, PassiveMove)
640  {
641  float z0 = 2;
642  float tanLambda = 2;
643 
644  for (const float phi0 : phi0s) {
645  for (const float omega : omegas) {
646  for (const float d0 : d0s) {
647  for (const float chi : chis) {
648  for (const float newD0 : d0s) {
649  Helix helix(d0, phi0, omega, z0, tanLambda);
650  TEST_CONTEXT("Failed for " << helix);
651 
652  // In the line case use the chi value directly as the arc length
653 
654  float expectedArcLength2D = omega == 0 ? chi : -chi / omega;
655  TVector3 positionOnHelix = helix.getPositionAtArcLength2D(expectedArcLength2D);
656  TVector3 tangentialToHelix = helix.getUnitTangentialAtArcLength2D(expectedArcLength2D);
657 
658  TVector3 perpendicularToHelix = tangentialToHelix;
659  perpendicularToHelix.RotateZ(M_PI / 2.0);
660  // Normalize the xy part
661  perpendicularToHelix *= 1 / perpendicularToHelix.Perp();
662 
663  TVector3 displacementFromHelix = -perpendicularToHelix * newD0;
664  TVector3 testPosition = positionOnHelix + displacementFromHelix;
665 
666  TVector3 expectedPerigee = -displacementFromHelix;
667 
668  TEST_CONTEXT("Failed for chi " << chi);
669  TEST_CONTEXT("Failed for position on helix " << positionOnHelix);
670  TEST_CONTEXT("Failed for tangential to helix " << tangentialToHelix);
671  TEST_CONTEXT("Failed for perpendicular to helix " << perpendicularToHelix);
672  TEST_CONTEXT("Failed for test position " << testPosition);
673 
674  float arcLength2D = helix.passiveMoveBy(testPosition);
675 
676  ASSERT_NEAR(expectedArcLength2D, arcLength2D, absError);
677 
678  ASSERT_ALL_NEAR(expectedPerigee, helix.getPerigee(), absError);
679 
680  }
681  }
682  }
683  }
684  }
685  }
686 
687  TEST_F(HelixTest, calcPassiveMoveByJacobian_identity)
688  {
689  TVector3 center(0.0, 1.0, 0.0);
690  float radius = -1;
691  float tanLambda = 3;
692 
693  Helix helix = helixFromCenter(center, radius, tanLambda);
694 
695  TMatrixD noMoveJacobian = helix.calcPassiveMoveByJacobian(TVector3(0.0, 0.0, 0.0));
696 
697  EXPECT_NEAR(1.0, noMoveJacobian(0, 0), 1e-7);
698  EXPECT_NEAR(0.0, noMoveJacobian(0, 1), 1e-7);
699  EXPECT_NEAR(0.0, noMoveJacobian(0, 2), 1e-7);
700 
701  EXPECT_NEAR(0.0, noMoveJacobian(1, 0), 1e-7);
702  EXPECT_NEAR(1.0, noMoveJacobian(1, 1), 1e-7);
703  EXPECT_NEAR(0.0, noMoveJacobian(1, 2), 1e-7);
704 
705  EXPECT_NEAR(0.0, noMoveJacobian(2, 0), 1e-7);
706  EXPECT_NEAR(0.0, noMoveJacobian(2, 1), 1e-7);
707  EXPECT_NEAR(1.0, noMoveJacobian(2, 2), 1e-7);
708  }
709 
710  TEST_F(HelixTest, calcPassiveMoveByJacobian_orthogonalToPhi0)
711  {
712  TVector3 center(0.0, 1.0, 0.0);
713  float radius = -1;
714  float tanLambda = 3;
715 
716  Helix helix = helixFromCenter(center, radius, tanLambda);
717 
718  TMatrixD moveByOneJacobian = helix.calcPassiveMoveByJacobian(TVector3(0.0, -1.0, 0.0));
719 
720  EXPECT_NEAR(1.0, moveByOneJacobian(iD0, iD0), 1e-7);
721  EXPECT_NEAR(0.0, moveByOneJacobian(iD0, iPhi0), 1e-7);
722  EXPECT_NEAR(0.0, moveByOneJacobian(iD0, iOmega), 1e-7);
723 
724  EXPECT_NEAR(0.0, moveByOneJacobian(iPhi0, iD0), 1e-7);
725  EXPECT_NEAR(1.0 / 2.0, moveByOneJacobian(iPhi0, iPhi0), 1e-7);
726  EXPECT_NEAR(0.0, moveByOneJacobian(iPhi0, iOmega), 1e-7);
727 
728  EXPECT_NEAR(0.0, moveByOneJacobian(iOmega, iD0), 1e-7);
729  EXPECT_NEAR(0.0, moveByOneJacobian(iOmega, iPhi0), 1e-7);
730  EXPECT_NEAR(1.0, moveByOneJacobian(iOmega, iOmega), 1e-7);
731  }
732 
733 
734  TEST_F(HelixTest, calcPassiveMoveByJacobian_parallelToPhi0_compare_opposite_transformation)
735  {
736  TVector3 center(0.0, 1.0, 0.0);
737  float radius = -1;
738  float tanLambda = 3;
739 
740  Helix helix = helixFromCenter(center, radius, tanLambda);
741 
742  TMatrixD moveByPlusTwoXJacobian = helix.calcPassiveMoveByJacobian(TVector3(2.0, 0.0, 0.0));
743  TMatrixD moveByMinusTwoXJacobian = helix.calcPassiveMoveByJacobian(TVector3(-2.0, 0.0, 0.0));
744 
745  EXPECT_NEAR(1.0, moveByPlusTwoXJacobian(iOmega, iOmega), 1e-7);
746  EXPECT_NEAR(0.0, moveByPlusTwoXJacobian(iOmega, iPhi0), 1e-7);
747  EXPECT_NEAR(0.0, moveByPlusTwoXJacobian(iOmega, iD0), 1e-7);
748 
749  EXPECT_NEAR(1.0, moveByMinusTwoXJacobian(iOmega, iOmega), 1e-7);
750  EXPECT_NEAR(0.0, moveByMinusTwoXJacobian(iOmega, iPhi0), 1e-7);
751  EXPECT_NEAR(0.0, moveByMinusTwoXJacobian(iOmega, iD0), 1e-7);
752 
753  // Symmetric effects
754  EXPECT_NEAR(moveByMinusTwoXJacobian(iD0, iOmega), moveByPlusTwoXJacobian(iD0, iOmega), 1e-7);
755  EXPECT_NEAR(moveByMinusTwoXJacobian(iPhi0, iPhi0), moveByPlusTwoXJacobian(iPhi0, iPhi0), 1e-7);
756  EXPECT_NEAR(moveByMinusTwoXJacobian(iD0, iD0), moveByPlusTwoXJacobian(iD0, iD0), 1e-7);
757 
758  // Asymmetric effects
759  EXPECT_NEAR(moveByMinusTwoXJacobian(iD0, iPhi0), -moveByPlusTwoXJacobian(iD0, iPhi0), 1e-7);
760  EXPECT_NEAR(moveByMinusTwoXJacobian(iPhi0, iD0), -moveByPlusTwoXJacobian(iPhi0, iD0), 1e-7);
761  EXPECT_NEAR(moveByMinusTwoXJacobian(iPhi0, iOmega), -moveByPlusTwoXJacobian(iPhi0, iOmega), 1e-7);
762 
763  // Signs
764  EXPECT_NEGATIVE(moveByPlusTwoXJacobian(iD0, iPhi0));
765  EXPECT_POSITIVE(moveByPlusTwoXJacobian(iD0, iOmega));
766  EXPECT_POSITIVE(moveByPlusTwoXJacobian(iPhi0, iD0));
767  EXPECT_NEGATIVE(moveByPlusTwoXJacobian(iPhi0, iOmega));
768 
769  EXPECT_NEGATIVE(moveByPlusTwoXJacobian(iD0, iD0) - 1);
770  EXPECT_NEGATIVE(moveByPlusTwoXJacobian(iPhi0, iPhi0) - 1);
771  }
772 
773  TEST_F(HelixTest, calcPassiveMoveByJacobian_roundtrip)
774  {
775  for (const TVector3& by : bys) {
776  TVector3 center(0.0, 1.0, 0.0);
777  float radius = -1;
778  float tanLambda = 3;
779 
780  Helix helix = helixFromCenter(center, radius, tanLambda);
781 
782  TMatrixD jacobian = helix.calcPassiveMoveByJacobian(by);
783  helix.passiveMoveBy(by);
784 
785  TMatrixD reverseJacobian = helix.calcPassiveMoveByJacobian(-by);
786 
787  TMatrixD unitMatrix = reverseJacobian * jacobian;
788 
789  for (int i = 0; i < 5; ++i) {
790  for (int j = 0; j < 5; ++j) {
791  if (i == j) {
792  // Diagonal is one.
793  EXPECT_NEAR(1, unitMatrix(i, j), 1e-7);
794  } else {
795  // Off diagonal is zero.
796  EXPECT_NEAR(0, unitMatrix(i, j), 1e-7);
797  }
798  }
799  }
800  }
801 
802  }
803 
804 
805  TEST_F(HelixTest, calcPassiveMoveByJacobian_parallelToPhi0)
806  {
807  TVector3 center(1.0, 0.0, 0.0);
808  float radius = -1;
809  float tanLambda = 3;
810 
811  Helix helix = helixFromCenter(center, radius, tanLambda);
812 
813  TMatrixD moveByTwoYJacobian = helix.calcPassiveMoveByJacobian(TVector3(0.0, 2.0, 0.0));
814 
815  // Hand caluclated intermediate quantities;
816  double deltaParallel = 2;
817  double A = 4;
818  double u = 1;
819 
820  double nu = 1;
821  //double xi = 1.0 / 5.0;
822  double lambda = 1.0 / (5.0 + 3.0 * sqrt(5.0));
823  double mu = sqrt(5.0) / 10.0;
824  double zeta = 4;
825 
826  EXPECT_NEAR(1.0, moveByTwoYJacobian(iOmega, iOmega), 1e-7);
827  EXPECT_NEAR(0.0, moveByTwoYJacobian(iOmega, iPhi0), 1e-7);
828  EXPECT_NEAR(0.0, moveByTwoYJacobian(iOmega, iD0), 1e-7);
829 
830  EXPECT_NEAR(2.0 / 5.0, moveByTwoYJacobian(iPhi0, iOmega), 1e-7);
831  EXPECT_NEAR(1.0 / 5.0, moveByTwoYJacobian(iPhi0, iPhi0), 1e-7);
832  EXPECT_NEAR(-2.0 / 5.0, moveByTwoYJacobian(iPhi0, iD0), 1e-7);
833 
834  EXPECT_NEAR(mu * zeta - A * lambda, moveByTwoYJacobian(iD0, iOmega), 1e-7);
835  EXPECT_NEAR(2.0 * mu * u * deltaParallel, moveByTwoYJacobian(iD0, iPhi0), 1e-7);
836  EXPECT_NEAR(2.0 * mu * nu, moveByTwoYJacobian(iD0, iD0), 1e-7);
837 
838  }
839 
840 
841  TEST_F(HelixTest, calcPassiveMoveByJacobian_consistency_of_expansion)
842  {
843  TVector3 center(0.0, 1.0, 0.0);
844  float radius = -1;
845  float tanLambda = 3;
846 
847  Helix helix = helixFromCenter(center, radius, tanLambda);
848 
849  for (const TVector3& by : bys) {
850  TMatrixD jacobian = helix.calcPassiveMoveByJacobian(by);
851  TMatrixD jacobianWithExpansion = helix.calcPassiveMoveByJacobian(by, 10000);
852 
853  EXPECT_NEAR(1.0, jacobian(iOmega, iOmega), 1e-7);
854  EXPECT_NEAR(0.0, jacobian(iOmega, iPhi0), 1e-7);
855  EXPECT_NEAR(0.0, jacobian(iOmega, iD0), 1e-7);
856 
857  EXPECT_NEAR(1.0, jacobianWithExpansion(iOmega, iOmega), 1e-7);
858  EXPECT_NEAR(0.0, jacobianWithExpansion(iOmega, iPhi0), 1e-7);
859  EXPECT_NEAR(0.0, jacobianWithExpansion(iOmega, iD0), 1e-7);
860 
861  EXPECT_NEAR(jacobianWithExpansion(iD0, iD0), jacobian(iD0, iD0), 1e-7);
862  EXPECT_NEAR(jacobianWithExpansion(iD0, iPhi0), jacobian(iD0, iPhi0), 1e-7);
863  EXPECT_NEAR(jacobianWithExpansion(iD0, iOmega), jacobian(iD0, iOmega), 1e-7);
864 
865  EXPECT_NEAR(jacobianWithExpansion(iPhi0, iD0), jacobian(iPhi0, iD0), 1e-7);
866  EXPECT_NEAR(jacobianWithExpansion(iPhi0, iPhi0), jacobian(iPhi0, iPhi0), 1e-7);
867  EXPECT_NEAR(jacobianWithExpansion(iPhi0, iOmega), jacobian(iPhi0, iOmega), 1e-7);
868  }
869  }
870 
871  TEST_F(HelixTest, realExample)
872  {
873  // Example from Belle I data.
874  std::vector<double> helixParams(5);
875  helixParams[0] = 3.82384;
876  helixParams[1] = std::remainder(3.575595, 2 * M_PI);
877  helixParams[2] = 0.00530726;
878  helixParams[3] = -0.000317335;
879  helixParams[4] = 1.34536;
880 
881  TVector3 momentum(-0.768755, -0.356297, 1.13994);
882  TVector3 position(-1.60794, 3.46933, -0.000317335);
883 
884  // Note: The helix parameters already have small mismatches that can be fixed as follows
885  // helixParams[1] = std::atan2(static_cast<double>(momentum.Y()), static_cast<double>(momentum.X()));
886  // helixParams[0] = static_cast<double>(position.Perp());
887 
888  const double bZ = 1.5;
889 
890  // Test if the cartesian coordinates are at the perigee
891  EXPECT_NEAR(0.0, momentum.XYvector() * position.XYvector(), 1e-6);
892 
893  Helix helix(helixParams[0], helixParams[1], helixParams[2], helixParams[3], helixParams[4]);
894 
895  EXPECT_ALL_NEAR(momentum, helix.getMomentum(bZ), 1e-5);
896  EXPECT_ALL_NEAR(position, helix.getPerigee(), 1e-5);
897  }
898 
899 
900  TEST_F(HelixTest, omegaForUnitMomentum)
901  {
902  TVector3 expectedMomentum(1.0, 0.0, 0.0);
903  TVector3 expectedPosition(0.0, 1.0, 0.0);
904  const double expectedCharge = 1;
905  const double bZ = 1.5;
906 
907  Helix helix(expectedPosition, expectedMomentum, expectedCharge, bZ);
908 
909  double expectedOmega = 0.0044968868700000003; // Omega for one GeV
910  EXPECT_ALL_NEAR(expectedOmega, helix.getOmega(), 1e-7);
911  }
912 }
Helix parameter class.
Definition: Helix.h:48
TEST_F(GlobalLabelTest, LargeNumberOfTimeDependentParameters)
Test large number of time-dep params for registration and retrieval.
Definition: globalLabel.cc:72
std::ostream & operator<<(std::ostream &output, const Helix &helix)
Output operator for debugging and the generation of unittest error messages.
Definition: Helix.cc:630
double charge(int pdgCode)
Returns electric charge of a particle with given pdg code.
Definition: EvtPDLUtil.cc:44
Some utilities to help with writing unit tests.
Definition: Helix.cc:31
bool angleNear(double expected, double actual, double tolerance)
Predicate checking that two angular values are close to each other modulus a 2 * PI difference.
Definition: TestHelpers.cc:65
Abstract base class for different kinds of events.