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