Belle II Software prerelease-10-00-00a
HelixUtils.cc
1/**************************************************************************
2 * basf2 (Belle II Analysis Software Framework) *
3 * Author: The Belle II Collaboration *
4 * External Contributor: Wouter Hulsbergen *
5 * *
6 * See git log for contributors and copyright holders. *
7 * This file is licensed under LGPL-3.0, see LICENSE.md. *
8 **************************************************************************/
9
10#include <TMath.h>
11
12#include <framework/gearbox/Const.h>
13#include <framework/logging/Logger.h>
14
15#include <analysis/VertexFitting/TreeFitter/HelixUtils.h>
16
17#include <algorithm>
18#include <initializer_list>
19#include <vector>
20
21namespace TreeFitter {
22
24 double L, double Bz,
25 ROOT::Math::XYZVector& position,
26 ROOT::Math::XYZVector& momentum, int& charge)
27 {
28 position = helix.getPositionAtArcLength2D(L);
29 momentum = helix.getMomentumAtArcLength2D(L, Bz);
30 charge = helix.getChargeSign();
31 }
32
33 void HelixUtils::helixFromVertex(const Eigen::Matrix<double, 1, 6>& positionAndMomentum,
34 int charge, double Bz,
35 Belle2::Helix& helix,
36 double& L,
37 Eigen::Matrix<double, 5, 6>& jacobian)
38 {
39
40 helix = Belle2::Helix(ROOT::Math::XYZVector(positionAndMomentum(0), positionAndMomentum(1), positionAndMomentum(2)),
41 ROOT::Math::XYZVector(positionAndMomentum(3), positionAndMomentum(4), positionAndMomentum(5)),
42 charge, Bz);
43
44 L = helix.getArcLength2DAtXY(positionAndMomentum(0),
45 positionAndMomentum(1));
46
47 const double alpha = helix.getAlpha(Bz);
48
49 //Copied from Belle2::UncertainHelix
50 // COMPLETELY WRONG SINCE IT ASSUMES IT'S IN THE.operator() PERIGEE,
51 // ONLY A PLACEHOLDER FOR NOW
52 // 1. Rotate to a system where phi0 = 0
53 Eigen::Matrix<double, 6, 6> jacobianRot = Eigen::Matrix<double, 6, 6>::Zero(6, 6);
54
55 const double px = positionAndMomentum(3);
56 const double py = positionAndMomentum(4);
57 const double pt = hypot(px, py);
58 const double cosPhi0 = px / pt;
59 const double sinPhi0 = py / pt;
60
61 // Passive rotation matrix by phi0:
62 jacobianRot(iX, iX) = cosPhi0;
63 jacobianRot(iX, iY) = sinPhi0;
64 jacobianRot(iY, iX) = -sinPhi0;
65 jacobianRot(iY, iY) = cosPhi0;
66 jacobianRot(iZ, iZ) = 1.0;
67
68 jacobianRot(iPx, iPx) = cosPhi0;
69 jacobianRot(iPx, iPy) = sinPhi0;
70 jacobianRot(iPy, iPx) = -sinPhi0;
71 jacobianRot(iPy, iPy) = cosPhi0;
72 jacobianRot(iPz, iPz) = 1.0;
73
74 // 2. Translate to perigee parameters on the position
75 const double pz = positionAndMomentum(5);
76 const double invPt = 1 / pt;
77 const double invPtSquared = invPt * invPt;
78 Eigen::Matrix<double, 5, 6> jacobianToHelixParameters = Eigen::Matrix<double, 5, 6>::Zero(5, 6);
79 jacobianToHelixParameters(iD0, iY) = -1;
80 jacobianToHelixParameters(iPhi0, iX) = charge * invPt / alpha;
81 jacobianToHelixParameters(iPhi0, iPy) = invPt;
82 jacobianToHelixParameters(iOmega, iPx) = -charge * invPtSquared / alpha;
83 jacobianToHelixParameters(iTanLambda, iPx) = - pz * invPtSquared;
84 jacobianToHelixParameters(iTanLambda, iPz) = invPt;
85 jacobianToHelixParameters(iZ0, iX) = - pz * invPt;
86 jacobianToHelixParameters(iZ0, iZ) = 1;
87 //
88 jacobian = jacobianToHelixParameters * jacobianRot;
89
90 }
91
92 std::string HelixUtils::helixParName(int i)
93 {
94 std::string rc ;
95 switch (i) {
96 case 1 : rc = "d0 : " ; break ;
97 case 2 : rc = "phi0 : " ; break ;
98 case 3 : rc = "omega : " ; break ;
99 case 4 : rc = "z0 : " ; break ;
100 case 5 : rc = "tandip: " ; break ;
101 case 6 : rc = "L : " ; break ;
102 }
103 return rc ;
104 }
105
106 std::string HelixUtils::vertexParName(int i)
107 {
108 std::string rc ;
109 switch (i) {
110 case 1 : rc = "x : " ; break ;
111 case 2 : rc = "y : " ; break ;
112 case 3 : rc = "z : " ; break ;
113 case 4 : rc = "px : " ; break ;
114 case 5 : rc = "py : " ; break ;
115 case 6 : rc = "pz : " ; break ;
116 }
117 return rc ;
118 }
119
120 void HelixUtils::printVertexPar(const ROOT::Math::XYZVector& position, const ROOT::Math::XYZVector& momentum, int charge)
121 {
122 B2INFO(vertexParName(1).c_str() << position.X());
123 B2INFO(vertexParName(2).c_str() << position.Y());
124 B2INFO(vertexParName(3).c_str() << position.Z());
125 B2INFO(vertexParName(4).c_str() << momentum.X());
126 B2INFO(vertexParName(5).c_str() << momentum.Y());
127 B2INFO(vertexParName(6).c_str() << momentum.Z());
128 B2INFO("charge: " << charge);
129
130 }
131
132 void HelixUtils::getHelixAndJacobianFromVertexNumerical(const Eigen::Matrix<double, 1, 6>& positionAndMom,
133 int charge, double Bz,
134 Belle2::Helix& helix,
135 Eigen::Matrix<double, 5, 6>& jacobian)
136 {
137
138 helix = Belle2::Helix(ROOT::Math::XYZVector(positionAndMom(0), positionAndMom(1), positionAndMom(2)),
139 ROOT::Math::XYZVector(positionAndMom(3), positionAndMom(4), positionAndMom(5)),
140 charge, Bz);
141
142 // numeric calculation of the jacobian
143 Belle2::Helix helixPlusDelta;
144
145 double delta = 1e-5;// this is arbitrary, only needs to be small
146
147 ROOT::Math::XYZVector postmp;
148 ROOT::Math::XYZVector momtmp;
149
150 for (int jin = 0; jin < 6; ++jin) {
151 postmp.SetCoordinates(positionAndMom(0), positionAndMom(1), positionAndMom(2));
152 momtmp.SetCoordinates(positionAndMom(3), positionAndMom(4), positionAndMom(5));
153 if (jin == 0) postmp.SetX(postmp.X() + delta);
154 if (jin == 1) postmp.SetY(postmp.Y() + delta);
155 if (jin == 2) postmp.SetZ(postmp.Z() + delta);
156 if (jin == 3) momtmp.SetX(momtmp.X() + delta);
157 if (jin == 4) momtmp.SetY(momtmp.Y() + delta);
158 if (jin == 5) momtmp.SetZ(momtmp.Z() + delta);
159
160 helixPlusDelta = Belle2::Helix(postmp, momtmp, charge, Bz);
161 jacobian(iD0, jin) = (helixPlusDelta.getD0() - helix.getD0()) / delta ;
162 jacobian(iPhi0, jin) = (helixPlusDelta.getPhi0() - helix.getPhi0()) / delta ;
163 jacobian(iOmega, jin) = (helixPlusDelta.getOmega() - helix.getOmega()) / delta ;
164 jacobian(iZ0, jin) = (helixPlusDelta.getZ0() - helix.getZ0()) / delta ;
165 jacobian(iTanLambda, jin) = (helixPlusDelta.getTanLambda() - helix.getTanLambda()) / delta ;
166
167 // jacobian[iArcLength2D][jin] = (LPlusDelta - L) / delta ;
168 }
169 }
170
172 const Eigen::Matrix<double, 1, 6>& positionAndMom,
173 int charge, double Bz,
174 const Belle2::Helix& helix,
175 Eigen::Matrix<double, 5, 6>& jacobian,
176 double delta
177 )
178 {
179 // numeric calculation of the jacobian
180 Belle2::Helix helixPlusDelta;
181
182 ROOT::Math::XYZVector postmp;
183 ROOT::Math::XYZVector momtmp;
184
185 for (int jin = 0; jin < 6; ++jin) {
186 postmp.SetCoordinates(positionAndMom(0), positionAndMom(1), positionAndMom(2));
187 momtmp.SetCoordinates(positionAndMom(3), positionAndMom(4), positionAndMom(5));
188 if (jin == 0) postmp.SetX(postmp.X() + delta);
189 if (jin == 1) postmp.SetY(postmp.Y() + delta);
190 if (jin == 2) postmp.SetZ(postmp.Z() + delta);
191 if (jin == 3) momtmp.SetX(momtmp.X() + delta);
192 if (jin == 4) momtmp.SetY(momtmp.Y() + delta);
193 if (jin == 5) momtmp.SetZ(momtmp.Z() + delta);
194
195 helixPlusDelta = Belle2::Helix(postmp, momtmp, charge, Bz);
196 jacobian(iD0, jin) = (helixPlusDelta.getD0() - helix.getD0()) / delta ;
197 jacobian(iPhi0, jin) = (helixPlusDelta.getPhi0() - helix.getPhi0()) / delta ;
198 jacobian(iOmega, jin) = (helixPlusDelta.getOmega() - helix.getOmega()) / delta ;
199 jacobian(iZ0, jin) = (helixPlusDelta.getZ0() - helix.getZ0()) / delta ;
200 jacobian(iTanLambda, jin) = (helixPlusDelta.getTanLambda() - helix.getTanLambda()) / delta ;
201 }
202
203 }
204
205 inline double sqr(double x) { return x * x ; }
206
207 double HelixUtils::phidomain(const double phi)
208 {
209 double rc = phi ;
210 if (phi < -TMath::Pi()) rc += TMath::TwoPi();
211 else if (phi > TMath::Pi()) rc -= TMath::TwoPi();
212 return rc ;
213 }
214
215 //POCA between two tracks
217 const Belle2::Helix& helix2,
218 double& flt1, double& flt2,
219 ROOT::Math::XYZVector& vertex, bool parallel)
220 {
221
222 const double d0_1 = helix1.getD0();
223 const double phi0_1 = helix1.getPhi0();
224 const double omega_1 = helix1.getOmega();
225
226 const double d0_2 = helix2.getD0();
227 const double phi0_2 = helix2.getPhi0();
228 const double omega_2 = helix2.getOmega();
229
230 // These radii have a sign, like omega (negative for negative charge)
231 const double r_1 = 1 / omega_1 ;
232 const double r_2 = 1 / omega_2 ;
233
234 // 1) First look at the transverse plane, where the helix projection is a circle
235 // Coordinates of the centers of the circles
236 const double x0_1 = (r_1 + d0_1) * sin(phi0_1) ;
237 const double y0_1 = -(r_1 + d0_1) * cos(phi0_1) ;
238
239 const double x0_2 = (r_2 + d0_2) * sin(phi0_2) ;
240 const double y0_2 = -(r_2 + d0_2) * cos(phi0_2) ;
241
242 // Vector that goes from center1 to center2
243 const double deltax = x0_2 - x0_1 ;
244 const double deltay = y0_2 - y0_1 ;
245
246 // Intersections of the circles, can be at most two
247 double phi1[2] ;
248 double phi2[2] ;
249 int nsolutions = 1;
250
251 // The phi of the delta vector.
252 const double phi = - atan2(deltax, deltay) ;
253 const double phinot = phi > 0 ? phi - TMath::Pi() : phi + TMath::Pi() ;
254 phi1[0] = r_1 < 0 ? phi : phinot ;
255 phi2[0] = r_2 > 0 ? phi : phinot ;
256
257 // These radii do NOT have a sign instead
258 const double R1 = fabs(r_1) ;
259 const double R2 = fabs(r_2) ;
260 const double Rmin = R1 < R2 ? R1 : R2 ;
261 const double Rmax = R1 > R2 ? R1 : R2 ;
262 const double dX = hypot(deltax, deltay) ;
263
264 if (!parallel && dX + Rmin > Rmax && dX < R1 + R2) {
265 // Circles intersect in two points
266 nsolutions = 2 ;
267
268 // This is just the law of cosines
269 const double ddphi1 = acos((dX * dX - R2 * R2 + R1 * R1) / (2.*dX * R1)) ;
270 phi1[1] = phidomain(phi1[0] + ddphi1) ;
271 phi1[0] = phidomain(phi1[0] - ddphi1) ;
272
273 const double ddphi2 = acos((dX * dX - R1 * R1 + R2 * R2) / (2.*dX * R2)) ;
274 phi2[1] = phidomain(phi2[0] - ddphi2) ;
275 phi2[0] = phidomain(phi2[0] + ddphi2) ;
276
277 } else if (dX < Rmax) {
278 // Tangent or non-intersecting circles, one inside the other (only one POCA)
279 if (R1 > R2) phi2[0] = r_2 < 0 ? phi : phinot ;
280 else phi1[0] = r_1 < 0 ? phi : phinot ;
281 }
282 // else: tangent or non-intersecting circles, outside of each other (only one POCA)
283 // what we saved in phi1 and phi2 gives already the correct solution
284
285 // Intersections of the circles (cartesian)
286 double x1[2], y1[2], x2[2], y2[2];
287 for (int i = 0; i < nsolutions; i++) {
288 x1[i] = r_1 * sin(phi1[i]) + x0_1 ;
289 y1[i] = -r_1 * cos(phi1[i]) + y0_1 ;
290 x2[i] = r_2 * sin(phi2[i]) + x0_2 ;
291 y2[i] = -r_2 * cos(phi2[i]) + y0_2 ;
292 }
293
294 // 2) Find the best solution for z by running multiples of 2pi from the xy intersection(s)
295 double z1(0), z2(0);
296 bool first = true;
297 int ibest = 0;
298 const int nturnsmax = 10; // Max number of turns we try backwards and forwards
299
300 // Loop on all xy-plane solutions
301 for (int i = 0; i < nsolutions; ++i) {
302 const double l1 = helix1.getArcLength2DAtXY(x1[i], y1[i]);
303 const double l2 = helix2.getArcLength2DAtXY(x2[i], y2[i]);
304
305 // Loop on helix1 turns, save corresponding z positions
306 std::vector<double> z1s;
307 for (int n1 = 0; n1 <= nturnsmax; ++n1) {
308 bool added = false;
309 // Try forwards and backwards
310 for (int sn1 : {n1, -n1}) {
311 const double tmpz1 = helix1.getPositionAtArcLength2D(l1 + sn1 * TMath::TwoPi() / omega_1).Z();
312 if (sn1 == 0 || (-82 <= tmpz1 && tmpz1 <= 158)) {
313 // Only keep the 0th turn and those inside CDC volume
314 z1s.push_back(tmpz1);
315 added = true;
316 }
317 if (sn1 == 0)
318 break; // Do not store 0th turn twice
319 }
320 // If we did not add any point we are already outside CDC volume both backwards and forwards
321 if (!added)
322 break;
323 }
324
325 // Loop on helix2 turns, find closest approach to one of helix1 points
326 for (int n2 = 0; n2 <= nturnsmax; ++n2) {
327 bool tried = false;
328 // Try forwards and backwards
329 for (int sn2 : {n2, -n2}) {
330 const double tmpz2 = helix2.getPositionAtArcLength2D(l2 + sn2 * TMath::TwoPi() / omega_2).Z();
331 if (sn2 == 0 || (-82 <= tmpz2 && tmpz2 <= 158)) {
332 // Only keep the 0th turn and those inside CDC volume
333 tried = true;
334 // Find the tmpz1 closest to tmpz2
335 const auto i1best = std::min_element(
336 z1s.cbegin(), z1s.cend(), [&tmpz2](const double & z1a, const double & z1b) {
337 return fabs(z1a - tmpz2) < fabs(z1b - tmpz2);
338 });
339 const double tmpz1 = *i1best;
340 // Keep the solution where the z distance of closest approach is minimum
341 if (first || fabs(tmpz1 - tmpz2) < fabs(z1 - z2)) {
342 ibest = i;
343 first = false;
344 z1 = tmpz1;
345 z2 = tmpz2;
346 flt1 = l1;
347 flt2 = l2;
348 }
349 }
350 if (n2 == 0)
351 break; // Do not try 0th turn twice
352 }
353 // If we did not try any point we are already outside CDC volume both backwards and forwards
354 if (!tried)
355 break;
356 }
357 }
358
359 vertex.SetX(0.5 * (x1[ibest] + x2[ibest]));
360 vertex.SetY(0.5 * (y1[ibest] + y2[ibest]));
361 vertex.SetZ(0.5 * (z1 + z2));
362
363 return std::hypot(x2[ibest] - x1[ibest], y2[ibest] - y1[ibest], z2 - z1);
364 }
365
366 //POCA between a track and a point
368 const ROOT::Math::XYZVector& point,
369 double& flt)
370 {
371 const double d0 = helix.getD0();
372 const double phi0 = helix.getPhi0();
373 const double omega = helix.getOmega();
374 const double z0 = helix.getZ0();
375 const double tandip = helix.getTanLambda();
376 const double cosdip = cos(atan(tandip)) ; // can do that faster
377
378 const double r = 1 / omega ;
379
380 const double x0 = - (r + d0) * sin(phi0) ;
381 const double y0 = (r + d0) * cos(phi0) ;
382
383 const double deltax = x0 - point.X() ;
384 const double deltay = y0 - point.Y() ;
385
386 const double pi = TMath::Pi();
387 double phi = - atan2(deltax, deltay) ;
388 if (r < 0) phi = phi > 0 ? phi - pi : phi + pi ;
389
390 // find the best solution for z by running multiples of 2_pi
391 const double x = r * sin(phi) + x0 ;
392 const double y = -r * cos(phi) + y0 ;
393 double z(0) ;
394 bool first(true) ;
395 const int ncirc(2) ;
396 const double dphi = phidomain(phi - phi0) ;
397 for (int n = 1 - ncirc; n <= 1 + ncirc ; ++n) {
398 const double l = (dphi + n * TMath::TwoPi()) / omega ;
399 const double tmpz = (z0 + l * tandip) ;
400 if (first || fabs(tmpz - point.Z()) < fabs(z - point.Z())) {
401 first = false ;
402 z = tmpz ;
403 flt = l / cosdip ;
404 }
405 }
406 return sqrt(sqr(x - point.X()) + sqr(y - point.Y()) + sqr(z - point.Z())) ;
407 }
408
409 void HelixUtils::getJacobianToCartesianFrameworkHelix(Eigen::Matrix<double, 5, 6>& jacobian,
410 const double x,
411 const double y,
412 const double z __attribute__((unused)),
413 const double px,
414 const double py,
415 const double pz,
416 const double bfield,
417 const double charge
418 )
419
420 {
421 const double alpha = 1.0 / (bfield * Belle2::Const::speedOfLight) * 1E4;
422 const double aq = charge / alpha;
423
424 const double pt = std::hypot(px, py);
425 const double pt2 = pt * pt;
426 const double pt3 = pt2 * pt;
427 const double aq2 = aq * aq;
428
429 const double x2 = x * x;
430 const double y2 = y * y;
431 const double r = x2 + y2;
432
433 const double px2 = px * px;
434 const double py2 = py * py;
435
436 const double px0 = px - aq * y;
437 const double py0 = py + aq * x;
438
439 const double pt02 = px0 * px0 + py0 * py0;
440 const double pt0 = std::sqrt(pt02);
441 double sqrt13 = pt0 / pt;
442
443 // D d0 / Dx_i
444 jacobian(0, 0) = py0 / pt0;
445 jacobian(0, 1) = -px0 / pt0;
446 jacobian(0, 2) = 0;
447 jacobian(0, 3) = (-(y * (aq2 * r + 2 * aq * py * x + 2 * py2 * (1 + sqrt13))) - px * (2 * py * x * (1 + sqrt13) + aq * (y2 *
448 (-1 + sqrt13) + x2 * (1 + sqrt13)))) /
449 (pt2 * pt0 * (1 + sqrt13) * (1 + sqrt13));
450
451 jacobian(0, 4) = (2 * px2 * x * (1 + sqrt13) + 2 * px * y * (py - aq * x + py * sqrt13) + aq * (aq * r * x - py * (x2 *
452 (-1 + sqrt13) + y2 * (1 + sqrt13)))) /
453 (pt2 * pt0 * (1 + sqrt13) * (1 + sqrt13));
454 jacobian(0, 5) = 0;
455
456 // D phi0 / Dx_i0;
457 jacobian(1, 0) = aq * px0 / pt02;
458 jacobian(1, 1) = aq * py0 / pt02;
459 jacobian(1, 2) = 0;
460 jacobian(1, 3) = -py0 / pt02;
461 jacobian(1, 4) = px0 / pt02;
462 jacobian(1, 5) = 0;
463
464 // D omega / Dx_i
465 jacobian(2, 0) = 0;
466 jacobian(2, 1) = 0;
467 jacobian(2, 2) = 0;
468 jacobian(2, 3) = - aq * px / pt3;
469 jacobian(2, 4) = - aq * py / pt3;
470 jacobian(2, 5) = 0;
471
472 // D z0 / Dx_i
473 jacobian(3, 0) = -pz * px0 / pt02;
474 jacobian(3, 1) = -pz * py0 / pt02;
475 jacobian(3, 2) = 1;
476 jacobian(3, 3) = (pz * (px2 * x - py * (aq * r + py * x) + 2 * px * py * y)) / (pt2 * pt02);
477 jacobian(3, 4) = (pz * (px * (aq * r + 2 * py * x) - px2 * y + py2 * y)) / (pt2 * pt02);
478 jacobian(3, 5) = std::atan2(-(aq * (px * x + py * y)), (px2 + py * py0 - aq * px * y)) / aq; //pt on num. and denom cancels.
479
480 // D tan lambda / Dx_i
481 jacobian(4, 0) = 0;
482 jacobian(4, 1) = 0;
483 jacobian(4, 2) = 0;
484 jacobian(4, 3) = -pz * px / pt3;
485 jacobian(4, 4) = -pz * py / pt3;
486 jacobian(4, 5) = 1. / pt;
487 }
488
489}
Helix parameter class.
Definition Helix.h:48
static const double speedOfLight
[cm/ns]
Definition Const.h:695
static std::string vertexParName(int i)
map of the vertex parameters by list index
static void printVertexPar(const ROOT::Math::XYZVector &position, const ROOT::Math::XYZVector &momentum, int charge)
Print the vertex parameters.
static void helixFromVertex(const Eigen::Matrix< double, 1, 6 > &positionAndMomentum, int charge, double Bz, Belle2::Helix &helix, double &L, Eigen::Matrix< double, 5, 6 > &jacobian)
vertex --> helix
Definition HelixUtils.cc:33
static void getHelixAndJacobianFromVertexNumerical(const Eigen::Matrix< double, 1, 6 > &positionAndMom, int charge, double Bz, Belle2::Helix &helix, Eigen::Matrix< double, 5, 6 > &jacobian)
get helix and jacobian from a vertex
static void vertexFromHelix(const Belle2::Helix &helix, double L, double Bz, ROOT::Math::XYZVector &position, ROOT::Math::XYZVector &momentum, int &charge)
helix --> vertex
Definition HelixUtils.cc:23
static void getJacobianFromVertexNumerical(const Eigen::Matrix< double, 1, 6 > &positionAndMom, int charge, double Bz, const Belle2::Helix &helix, Eigen::Matrix< double, 5, 6 > &jacobian, double delta=1e-5)
get jacobian from a vertex
static double helixPoca(const Belle2::Helix &helix1, const Belle2::Helix &helix2, double &flt1, double &flt2, ROOT::Math::XYZVector &vertex, bool parallel=false)
POCA between two tracks.
static std::string helixParName(int i)
map of the helix parameters by list index
Definition HelixUtils.cc:92
static double phidomain(const double phi)
the domain of phi
static void getJacobianToCartesianFrameworkHelix(Eigen::Matrix< double, 5, 6 > &jacobian, const double x, const double y, const double z, const double px, const double py, const double pz, const double bfield, const double charge)
get the jacobian dh={helix pars}/dx={x,y,z,px,py,pz} for the implementation of the framework helix.