Belle II Software  release-08-01-10
HelicityVariables.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 
9 // Own header.
10 #include <analysis/variables/HelicityVariables.h>
11 
12 #include <analysis/VariableManager/Utility.h>
13 
14 #include <analysis/variables/EventVariables.h>
15 
16 #include <analysis/dataobjects/Particle.h>
17 
18 #include <analysis/utility/ReferenceFrame.h>
19 #include <analysis/VariableManager/Manager.h>
20 
21 #include <framework/utilities/Conversion.h>
22 #include <framework/gearbox/Const.h>
23 
24 #include <boost/algorithm/string.hpp>
25 
26 #include <Math/Boost.h>
27 #include <Math/Vector4D.h>
28 using namespace ROOT::Math;
29 #include <cmath>
30 
31 namespace Belle2 {
36  namespace Variable {
37 
38  double cosHelicityAngleMomentum(const Particle* part)
39  {
40 
41  const auto& frame = ReferenceFrame::GetCurrent();
42  B2Vector3D motherBoost = frame.getMomentum(part).BoostToCM();
43  B2Vector3D motherMomentum = frame.getMomentum(part).Vect();
44  const auto& daughters = part -> getDaughters() ;
45 
46  if (daughters.size() == 2) {
47 
48  bool isOneConversion = false;
49 
50  for (auto& idaughter : daughters) {
51  if (idaughter -> getNDaughters() == 2) {
52  if (std::abs(idaughter -> getDaughters()[0]-> getPDGCode()) == Const::electron.getPDGCode()
53  && std::abs(idaughter -> getDaughters()[1]-> getPDGCode()) == Const::electron.getPDGCode()) {
54  isOneConversion = true;
55  }
56  }
57  }
58 
59  if (isOneConversion) {
60  //only for pi0 decay where one gamma converts
61 
62  PxPyPzEVector pGamma;
63 
64  for (auto& idaughter : daughters) {
65  if (idaughter -> getNDaughters() == 2) continue;
66  else pGamma = frame.getMomentum(idaughter);
67  }
68 
69  pGamma = Boost(motherBoost) * pGamma;
70 
71  return std::cos(motherMomentum.Angle(pGamma.Vect()));
72 
73  } else {
74  PxPyPzEVector pDaughter1 = frame.getMomentum(daughters[0]);
75  PxPyPzEVector pDaughter2 = frame.getMomentum(daughters[1]);
76 
77  pDaughter1 = Boost(motherBoost) * pDaughter1;
78  pDaughter2 = Boost(motherBoost) * pDaughter2;
79 
80  B2Vector3D p12 = (pDaughter2 - pDaughter1).Vect();
81 
82  return std::cos(motherMomentum.Angle(p12));
83  }
84 
85  } else if (daughters.size() == 3) {
86 
87  PxPyPzEVector pDaughter1 = frame.getMomentum(daughters[0]);
88  PxPyPzEVector pDaughter2 = frame.getMomentum(daughters[1]);
89  PxPyPzEVector pDaughter3 = frame.getMomentum(daughters[2]);
90 
91  pDaughter1 = Boost(motherBoost) * pDaughter1;
92  pDaughter2 = Boost(motherBoost) * pDaughter2;
93  pDaughter3 = Boost(motherBoost) * pDaughter3;
94 
95  B2Vector3D p12 = (pDaughter2 - pDaughter1).Vect();
96  B2Vector3D p13 = (pDaughter3 - pDaughter1).Vect();
97 
98  B2Vector3D n = p12.Cross(p13);
99 
100  return std::cos(motherMomentum.Angle(n));
101 
102  } else return Const::doubleNaN;
103 
104  }
105 
106  double cosHelicityAngleMomentumPi0Dalitz(const Particle* part)
107  {
108 
109  const auto& frame = ReferenceFrame::GetCurrent();
110  B2Vector3D motherBoost = frame.getMomentum(part).BoostToCM();
111  B2Vector3D motherMomentum = frame.getMomentum(part).Vect();
112  const auto& daughters = part -> getDaughters() ;
113 
114 
115  if (daughters.size() == 3) {
116 
117  PxPyPzEVector pGamma;
118 
119  for (auto& idaughter : daughters) {
120  if (std::abs(idaughter -> getPDGCode()) == Const::photon.getPDGCode()) pGamma = frame.getMomentum(idaughter);
121  }
122 
123  pGamma = Boost(motherBoost) * pGamma;
124 
125  return std::cos(motherMomentum.Angle(pGamma.Vect()));
126 
127  } else return Const::doubleNaN;
128 
129  }
130 
131 
132  double cosHelicityAngleBeamMomentum(const Particle* mother, const std::vector<double>& index)
133  {
134  if (index.size() != 1) {
135  B2FATAL("Wrong number of arguments for cosHelicityAngleIfCMSIsTheMother");
136  }
137 
138  int idau = std::lround(index[0]);
139 
140  const Particle* part = mother->getDaughter(idau);
141  if (!part) {
142  B2FATAL("Couldn't find the " << idau << "th daughter");
143  }
144 
145  PxPyPzEVector beam4Vector(getBeamPx(nullptr), getBeamPy(nullptr), getBeamPz(nullptr), getBeamE(nullptr));
146  PxPyPzEVector part4Vector = part->get4Vector();
147  PxPyPzEVector mother4Vector = mother->get4Vector();
148 
149  B2Vector3D motherBoost = mother4Vector.BoostToCM();
150 
151  beam4Vector = Boost(motherBoost) * beam4Vector;
152  part4Vector = Boost(motherBoost) * part4Vector;
153 
154  return - part4Vector.Vect().Dot(beam4Vector.Vect()) / part4Vector.P() / beam4Vector.P();
155  }
156 
157 
158  double cosHelicityAngle(const Particle* mother, const std::vector<double>& indices)
159  {
160  if (indices.size() != 2) {
161  B2FATAL("Wrong number of arguments for cosHelicityAngleIfRefFrameIsTheDaughter: two are needed.");
162  }
163 
164  int iDau = std::lround(indices[0]);
165  int iGrandDau = std::lround(indices[1]);
166 
167  const Particle* daughter = mother->getDaughter(iDau);
168  if (!daughter)
169  B2FATAL("Couldn't find the " << iDau << "th daughter.");
170 
171  const Particle* grandDaughter = daughter->getDaughter(iGrandDau);
172  if (!grandDaughter)
173  B2FATAL("Couldn't find the " << iGrandDau << "th daughter of the " << iDau << "th daughter.");
174 
175  PxPyPzEVector mother4Vector = mother->get4Vector();
176  PxPyPzEVector daughter4Vector = daughter->get4Vector();
177  PxPyPzEVector grandDaughter4Vector = grandDaughter->get4Vector();
178 
179  B2Vector3D daughterBoost = daughter4Vector.BoostToCM();
180 
181  // We boost the momentum of the mother and of the granddaughter to the reference frame of the daughter.
182  grandDaughter4Vector = Boost(daughterBoost) * grandDaughter4Vector;
183  mother4Vector = Boost(daughterBoost) * mother4Vector;
184 
185  return - grandDaughter4Vector.Vect().Dot(mother4Vector.Vect()) / grandDaughter4Vector.P() / mother4Vector.P();
186  }
187 
188  double cosAcoplanarityAngle(const Particle* mother, const std::vector<double>& granddaughters)
189  {
190  if (granddaughters.size() != 2) {
191  B2FATAL("Wrong number of arguments for cosAcoplanarityAngleIfRefFrameIsTheMother: two are needed.");
192  }
193 
194  if (mother->getNDaughters() != 2)
195  B2FATAL("cosAcoplanarityAngleIfRefFrameIsTheMother: this variable works only for two-body decays.");
196 
197  int iGrandDau1 = std::lround(granddaughters[0]);
198  int iGrandDau2 = std::lround(granddaughters[1]);
199 
200  const Particle* daughter1 = mother->getDaughter(0);
201  const Particle* daughter2 = mother->getDaughter(1);
202 
203  const Particle* grandDaughter1 = daughter1->getDaughter(iGrandDau1);
204  if (!grandDaughter1)
205  B2FATAL("Couldn't find the " << iGrandDau1 << "th daughter of the first daughter.");
206 
207  const Particle* grandDaughter2 = daughter2->getDaughter(iGrandDau2);
208  if (!grandDaughter2)
209  B2FATAL("Couldn't find the " << iGrandDau2 << "th daughter of the second daughter.");
210 
211  PxPyPzEVector mother4Vector = mother->get4Vector();
212  PxPyPzEVector daughter4Vector1 = daughter1->get4Vector();
213  PxPyPzEVector daughter4Vector2 = daughter2->get4Vector();
214  PxPyPzEVector grandDaughter4Vector1 = grandDaughter1->get4Vector();
215  PxPyPzEVector grandDaughter4Vector2 = grandDaughter2->get4Vector();
216 
217  B2Vector3D motherBoost = mother4Vector.BoostToCM();
218  B2Vector3D daughter1Boost = daughter4Vector1.BoostToCM();
219  B2Vector3D daughter2Boost = daughter4Vector2.BoostToCM();
220 
221  // Boosting daughters to reference frame of the mother
222  daughter4Vector1 = Boost(motherBoost) * daughter4Vector1;
223  daughter4Vector2 = Boost(motherBoost) * daughter4Vector2;
224 
225  // Boosting each granddaughter to reference frame of its mother
226  grandDaughter4Vector1 = Boost(daughter1Boost) * grandDaughter4Vector1;
227  grandDaughter4Vector2 = Boost(daughter2Boost) * grandDaughter4Vector2;
228 
229  // We calculate the normal vectors of the decay two planes
230  B2Vector3D normalVector1 = daughter4Vector1.Vect().Cross(grandDaughter4Vector1.Vect());
231  B2Vector3D normalVector2 = daughter4Vector2.Vect().Cross(grandDaughter4Vector2.Vect());
232 
233  return std::cos(normalVector1.Angle(normalVector2));
234  }
235 
236  double cosHelicityAnglePrimary(const Particle* part)
237  {
238  return part->getCosHelicity();
239  }
240 
241  double cosHelicityAngleDaughter(const Particle* part, const std::vector<double>& indices)
242  {
243  if ((indices.size() == 0) || (indices.size() > 2)) {
244  B2FATAL("Wrong number of arguments for cosHelicityAngleDaughter: one or two are needed.");
245  }
246 
247  int iDaughter = std::lround(indices[0]);
248  int iGrandDaughter = 0;
249  if (indices.size() == 2) {
250  iGrandDaughter = std::lround(indices[1]);
251  }
252 
253  return part->getCosHelicityDaughter(iDaughter, iGrandDaughter);
254  }
255 
256  double acoplanarityAngle(const Particle* part)
257  {
258  return part->getAcoplanarity();
259  }
260 
261 
262  double cosHelicityAngleForQuasiTwoBodyDecay(const Particle* mother, const std::vector<double>& indices)
263  {
264  if (indices.size() != 2) {
265  B2FATAL("Wrong number of arguments for cosHelicityAngleForQuasiTwoBodyDecay: two are needed.");
266  }
267 
268  if (mother->getNDaughters() != 3)
269  return Const::doubleNaN;
270 
271  int iDau = std::lround(indices[0]);
272  int jDau = std::lround(indices[1]);
273 
274  const Particle* iDaughter = mother->getDaughter(iDau);
275  if (!iDaughter)
276  return Const::doubleNaN;
277 
278  const Particle* jDaughter = mother->getDaughter(jDau);
279  if (!jDaughter)
280  return Const::doubleNaN;
281 
282  PxPyPzEVector mother4Vector = mother->get4Vector();
283  PxPyPzEVector iDaughter4Vector = iDaughter->get4Vector();
284  PxPyPzEVector jDaughter4Vector = jDaughter->get4Vector();
285 
286  PxPyPzEVector resonance4Vector = iDaughter4Vector + jDaughter4Vector;
287  B2Vector3D resonanceBoost = resonance4Vector.BoostToCM();
288 
289  iDaughter4Vector = Boost(resonanceBoost) * iDaughter4Vector;
290  mother4Vector = Boost(resonanceBoost) * mother4Vector;
291 
292  return - iDaughter4Vector.Vect().Dot(mother4Vector.Vect()) / iDaughter4Vector.P() / mother4Vector.P();
293  }
294 
295  Manager::FunctionPtr momentaTripleProduct(const std::vector<std::string>& arguments)
296  {
297  if (arguments.size() != 3) {
298  B2FATAL("Wrong number of arguments for momentaTripleProduct: three (particles) are needed.");
299  }
300 
301  // wrap with func and return it
302  auto func = [arguments](const Particle * mother) -> double {
303  auto iDau = arguments[0];
304  auto jDau = arguments[1];
305  auto kDau = arguments[2];
306 
307  const Particle* iDaughter = mother->getParticleFromGeneralizedIndexString(iDau);
308  if (!iDaughter) B2FATAL("Couldn't find the " << iDau << "th daughter.");
309  const Particle* jDaughter = mother->getParticleFromGeneralizedIndexString(jDau);
310  if (!jDaughter) B2FATAL("Couldn't find the " << jDau << "th daughter.");
311  const Particle* kDaughter = mother->getParticleFromGeneralizedIndexString(kDau);
312  if (!kDaughter) B2FATAL("Couldn't find the " << kDau << "th daughter.");
313 
314  PxPyPzEVector mother4Vector = mother->get4Vector();
315  PxPyPzEVector iDaughter4Vector = iDaughter->get4Vector();
316  PxPyPzEVector jDaughter4Vector = jDaughter->get4Vector();
317  PxPyPzEVector kDaughter4Vector = kDaughter->get4Vector();
318 
319  B2Vector3D motherBoost = mother4Vector.BoostToCM();
320 
321  // We boost the momenta of offspring to the reference frame of the mother.
322  iDaughter4Vector = Boost(motherBoost) * iDaughter4Vector;
323  jDaughter4Vector = Boost(motherBoost) * jDaughter4Vector;
324  kDaughter4Vector = Boost(motherBoost) * kDaughter4Vector;
325 
326  // cross product: p_j x p_k
327  B2Vector3D jkDaughterCrossProduct = jDaughter4Vector.Vect().Cross(kDaughter4Vector.Vect());
328  // triple product: p_i * (p_j x p_k)
329  return iDaughter4Vector.Vect().Dot(jkDaughterCrossProduct) ;
330  };
331  return func;
332  }
333 
334 
335  VARIABLE_GROUP("Helicity variables");
336 
337  REGISTER_VARIABLE("cosHelicityAngleMomentum", cosHelicityAngleMomentum, R"DOC(
338  If the given particle has two daughters: cosine of the angle between the line defined by the momentum difference of the two daughters
339  in the frame of the given particle (mother)
340  and the momentum of the given particle in the lab frame.
341 
342  If the given particle has three daughters: cosine of the angle between the normal vector of the plane defined by
343  the momenta of the three daughters in the frame of the given particle (mother)
344  and the momentum of the given particle in the lab frame.
345 
346  Otherwise, it returns 0.)DOC");
347 
348  REGISTER_VARIABLE("cosHelicityAngleMomentumPi0Dalitz", cosHelicityAngleMomentumPi0Dalitz, R"DOC(
349  To be used for the decay :math:`\pi^0 \to e^+ e^- \gamma`:
350  cosine of the angle between the momentum of the gamma in the frame of the given particle (mother)
351  and the momentum of the given particle in the lab frame.
352 
353  Otherwise, it returns 0.)DOC");
354 
355  REGISTER_VARIABLE("cosHelicityAngleBeamMomentum(i)", cosHelicityAngleBeamMomentum, R"DOC(
356  Cosine of the helicity angle of the :math:`i`-th daughter of the particle provided,
357  assuming that the mother of the provided particle corresponds to the centre-of-mass system, whose parameters are
358  automatically loaded by the function, given the accelerator's conditions.)DOC");
359 
360  REGISTER_VARIABLE("cosHelicityAngle(i, j)", cosHelicityAngle, R"DOC(
361  Cosine of the helicity angle between the momentum of the selected granddaughter and the direction opposite to the momentum of the provided particle
362  in the reference frame of the selected daughter (:math:`\theta_1` and :math:`\theta_2` in the
363  `PDG <https://journals.aps.org/prd/abstract/10.1103/PhysRevD.98.030001>`_ 2018, p. 722).
364 
365  This variable needs two integer arguments: the first one, ``i``, is the index of the daughter and the second one, ``j`` is the index of the granddaughter.
366 
367  For example, in the decay :math:`B^0 \to \left(J/\psi \to \mu^+ \mu^-\right) \left(K^{*0} \to K^+ \pi^-\right)`,
368  if the provided particle is :math:`B^0` and the selected indices are (0, 0),
369  the variable will return the angle between the momentum of the :math:`\mu^+` and the direction opposite to the momentum of
370  the :math:`B^0`, both momenta in the rest frame of the :math:`J/\psi`.
371 
372  This variable is needed for angular analyses of :math:`B`-meson decays into two vector particles.)DOC");
373 
374  REGISTER_VARIABLE("cosAcoplanarityAngle(i, j)", cosAcoplanarityAngle, R"DOC(
375  Cosine of the acoplanarity angle (:math:`\Phi` in the `PDG Polarization Review <http://pdg.lbl.gov/2019/reviews/rpp2018-rev-b-decays-polarization.pdf>`_).
376  Given a two-body decay, the acoplanarity angle is defined as
377  the angle between the two decay planes in the reference frame of the mother.
378 
379  We calculate the acoplanarity angle as the angle between the two
380  normal vectors of the decay planes. Each normal vector is the cross product of the momentum of one daughter (in the frame of the mother) and the
381  momentum of one of the granddaughters (in the reference frame of the daughter).
382 
383  This variable needs two integer arguments: the first one, ``i`` is the index of the first granddaughter, and the second one, ``j`` the index of the
384  second granddaughter.
385 
386  For example, in the decay :math:`B^0 \to \left(J/\psi \to \mu^+ \mu^-\right) \left(K^{*0} \to K^+ \pi^-\right)`, if the provided particle is :math:`B^0` and the selected indices are (0, 0),
387  the variable will return the acoplanarity using the :math:`\mu^+` and the :math:`K^+` granddaughters.)DOC");
388 
389  REGISTER_VARIABLE("cosHelicityAnglePrimary", cosHelicityAnglePrimary, R"DOC(
390  Cosine of the helicity angle (see``Particle::getCosHelicity``) assuming the center of mass system as mother rest frame.
391  See `PDG Polarization Review <http://pdg.lbl.gov/2019/reviews/rpp2018-rev-b-decays-polarization.pdf>`_ for the definition of the helicity angle.)DOC");
392 
393  REGISTER_VARIABLE("cosHelicityAngleDaughter(i [, j] )", cosHelicityAngleDaughter, R"DOC(
394  Cosine of the helicity angle of the i-th daughter (see ``Particle::getCosHelicityDaughter``).
395  The optional second argument is the index of the granddaughter that defines the angle, default is 0.
396 
397  For example, in the decay: :math:`B^0 \to \left(J/\psi \to \mu^+ \mu^-\right) \left(K^{*0} \to K^+ \pi^-\right)`, if the provided particle is :math:`B^0` and the selected index is 0,
398  the variable will return the helicity angle of the :math:`\mu^+`.
399  If the selected index is 1 the variable will return the helicity angle of the :math:`K^+` (defined via the rest frame of the :math:`K^{*0}`).
400  In rare cases if one wanted the helicity angle of the second granddaughter, indices 1,1 would return the helicity angle of the :math:`\pi^-`).
401 
402  See `PDG Polarization Review <http://pdg.lbl.gov/2019/reviews/rpp2018-rev-b-decays-polarization.pdf>`_ for the definition of the helicity angle.)DOC");
403 
404  REGISTER_VARIABLE("acoplanarityAngle", acoplanarityAngle, R"DOC(
405 Acoplanarity angle (see ``Particle::getAcoplanarity``) assuming a two body decay of the particle and its daughters.
406 See `PDG Polarization Review <http://pdg.lbl.gov/2019/reviews/rpp2018-rev-b-decays-polarization.pdf>`_ for the definition of the acoplanarity angle.
407 
408 )DOC", "rad");
409 
410  REGISTER_VARIABLE("cosHelicityAngleForQuasiTwoBodyDecay(i, j)", cosHelicityAngleForQuasiTwoBodyDecay, R"DOC(
411  Cosine of the helicity angle between the momentum of the provided particle and the momentum of the first selected
412  daughter (i-th) in the reference frame of the sum of two selected daughters (i-th + j-th).
413 
414  The variable is supposed to be used for the analysis of a quasi-two-body decay. The number of daughters of the given
415  particle must be three. Otherwise, the variable returns NaN.
416 
417  For example, in the decay :math:`\bar{B}^0 \to D^+ K^- K^{*0}`, if the provided particle is :math:`\bar{B}^0` and
418  the selected indices are (1, 2), the variable will return the angle between the momentum of the :math:`\bar{B}^0`
419  and the momentum of the :math:`K^-`, both momenta in the rest frame of the :math:`K^- K^{*0}`.)DOC");
420 
421  REGISTER_METAVARIABLE("momentaTripleProduct(i,j,k)", momentaTripleProduct, R"DOC(
422 a triple-product of three momenta of offspring in the mother rest frame: :math:`C_T=\vec{p}_i\cdot(\vec{p}_j\times\vec{p}_k)`. For examples,
423 In a four-body decay M->D1D2D3D4, momentaTripleProduct(0,1,2) returns CT using the momenta of D1D2D3 particles.
424 In other decays involving secondary decay, e.g. for M->(R->D1D2)D3D4, momentaTripleProduct(0:0,1,2) returns C_T using momenta of D1D3D4 particles.
425 )DOC", Manager::VariableDataType::c_double);
426 
427  }
429 }
B2Vector3< DataType > Cross(const B2Vector3< DataType > &p) const
Cross product.
Definition: B2Vector3.h:296
DataType Dot(const B2Vector3< DataType > &p) const
Scalar product.
Definition: B2Vector3.h:290
B2Vector3< double > B2Vector3D
typedef for common usage with double
Definition: B2Vector3.h:516
Abstract base class for different kinds of events.