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