10#include <analysis/variables/HelicityVariables.h>
12#include <analysis/variables/EventVariables.h>
14#include <analysis/dataobjects/Particle.h>
16#include <analysis/utility/ReferenceFrame.h>
17#include <analysis/VariableManager/Manager.h>
19#include <framework/gearbox/Const.h>
21#include <Math/Boost.h>
22#include <Math/Vector4D.h>
23#include <Math/VectorUtil.h>
24using namespace ROOT::Math;
34 double cosHelicityAngleMomentum(
const Particle* part)
38 XYZVector motherBoost = frame.getMomentum(part).BoostToCM();
39 PxPyPzEVector motherMomentum = frame.getMomentum(part);
40 const auto& daughters = part -> getDaughters() ;
42 if (daughters.size() == 2) {
45 bool isOneConversion =
false;
47 for (
const auto* idaughter : daughters) {
49 if (idaughter -> getPDGCode() !=
Const::photon.getPDGCode()) {
50 isOneConversion =
false;
54 if (idaughter -> getNDaughters() == 2) {
55 if (std::abs(idaughter -> getDaughters()[0]-> getPDGCode()) ==
Const::electron.getPDGCode()
56 && std::abs(idaughter -> getDaughters()[1]-> getPDGCode()) ==
Const::electron.getPDGCode()) {
57 isOneConversion =
true;
63 if (isOneConversion) {
64 B2WARNING(
"cosHelicityAngleMomentum: Special treatment for pi0->gamma gamma, gamma -> e+ e-, is called. "
65 "This treatment is going to be deprecated and we recommend using ``cosHelicityAngleMomentumPi0Dalitz`` "
66 "If you find this message in another case, it must be a bug. Please report it to the software mailing list.");
70 for (
const auto* idaughter : daughters) {
71 if (idaughter -> getNDaughters() == 2)
continue;
72 else pGamma = frame.getMomentum(idaughter);
75 pGamma = Boost(motherBoost) * pGamma;
77 return VectorUtil::CosTheta(motherMomentum, pGamma);
80 PxPyPzEVector pDaughter1 = frame.getMomentum(daughters[0]);
81 PxPyPzEVector pDaughter2 = frame.getMomentum(daughters[1]);
83 pDaughter1 = Boost(motherBoost) * pDaughter1;
84 pDaughter2 = Boost(motherBoost) * pDaughter2;
86 PxPyPzEVector p12 = pDaughter2 - pDaughter1;
88 return VectorUtil::CosTheta(motherMomentum, p12);
91 }
else if (daughters.size() == 3) {
93 PxPyPzEVector pDaughter1 = frame.getMomentum(daughters[0]);
94 PxPyPzEVector pDaughter2 = frame.getMomentum(daughters[1]);
95 PxPyPzEVector pDaughter3 = frame.getMomentum(daughters[2]);
97 pDaughter1 = Boost(motherBoost) * pDaughter1;
98 pDaughter2 = Boost(motherBoost) * pDaughter2;
99 pDaughter3 = Boost(motherBoost) * pDaughter3;
101 XYZVector p12 = (pDaughter2 - pDaughter1).Vect();
102 XYZVector p13 = (pDaughter3 - pDaughter1).Vect();
104 XYZVector n = p12.Cross(p13);
106 return VectorUtil::CosTheta(motherMomentum, n);
112 double cosHelicityAngleMomentumPi0Dalitz(
const Particle* part)
116 XYZVector motherBoost = frame.getMomentum(part).BoostToCM();
117 PxPyPzEVector motherMomentum = frame.getMomentum(part);
118 const auto& daughters = part -> getDaughters() ;
121 if (daughters.size() == 3) {
123 PxPyPzEVector pGamma;
125 for (
const auto* idaughter : daughters) {
126 if (std::abs(idaughter -> getPDGCode()) ==
Const::photon.getPDGCode()) {
127 pGamma = frame.getMomentum(idaughter);
131 pGamma = Boost(motherBoost) * pGamma;
133 return VectorUtil::CosTheta(motherMomentum, pGamma);
135 }
else if (daughters.size() == 2) {
137 PxPyPzEVector pGamma;
140 if (daughters[0] -> getPDGCode() !=
Const::photon.getPDGCode() or
144 if (daughters[0] -> getNDaughters() == 2 and daughters[1] -> getNDaughters() == 0) {
145 if (std::abs(daughters[0] -> getDaughters()[0]-> getPDGCode()) ==
Const::electron.getPDGCode()
146 && std::abs(daughters[0] -> getDaughters()[1]-> getPDGCode()) ==
Const::electron.getPDGCode()) {
147 pGamma = frame.getMomentum(daughters[1]);
151 }
else if (daughters[0] -> getNDaughters() == 0 and daughters[1] -> getNDaughters() == 2) {
152 if (std::abs(daughters[1] -> getDaughters()[0]-> getPDGCode()) ==
Const::electron.getPDGCode()
153 && std::abs(daughters[1] -> getDaughters()[1]-> getPDGCode()) ==
Const::electron.getPDGCode()) {
154 pGamma = frame.getMomentum(daughters[0]);
162 pGamma = Boost(motherBoost) * pGamma;
164 return VectorUtil::CosTheta(motherMomentum, pGamma);
171 double cosHelicityAngleBeamMomentum(
const Particle* mother,
const std::vector<double>& index)
173 if (index.size() != 1) {
174 B2FATAL(
"Wrong number of arguments for cosHelicityAngleIfCMSIsTheMother");
177 int idau = std::lround(index[0]);
179 const Particle* part = mother->getDaughter(idau);
181 B2FATAL(
"Couldn't find the " << idau <<
"th daughter");
184 PxPyPzEVector beam4Vector(getBeamPx(
nullptr), getBeamPy(
nullptr), getBeamPz(
nullptr), getBeamE(
nullptr));
185 PxPyPzEVector part4Vector = part->get4Vector();
186 PxPyPzEVector mother4Vector = mother->get4Vector();
188 XYZVector motherBoost = mother4Vector.BoostToCM();
190 beam4Vector = Boost(motherBoost) * beam4Vector;
191 part4Vector = Boost(motherBoost) * part4Vector;
193 return - VectorUtil::CosTheta(part4Vector, beam4Vector);
197 double cosHelicityAngle(
const Particle* mother,
const std::vector<double>& indices)
199 if (indices.size() != 2) {
200 B2FATAL(
"Wrong number of arguments for cosHelicityAngleIfRefFrameIsTheDaughter: two are needed.");
203 int iDau = std::lround(indices[0]);
204 int iGrandDau = std::lround(indices[1]);
206 const Particle* daughter = mother->getDaughter(iDau);
208 B2FATAL(
"Couldn't find the " << iDau <<
"th daughter.");
210 const Particle* grandDaughter = daughter->getDaughter(iGrandDau);
212 B2FATAL(
"Couldn't find the " << iGrandDau <<
"th daughter of the " << iDau <<
"th daughter.");
214 PxPyPzEVector mother4Vector = mother->get4Vector();
215 PxPyPzEVector daughter4Vector = daughter->get4Vector();
216 PxPyPzEVector grandDaughter4Vector = grandDaughter->get4Vector();
218 XYZVector daughterBoost = daughter4Vector.BoostToCM();
221 grandDaughter4Vector = Boost(daughterBoost) * grandDaughter4Vector;
222 mother4Vector = Boost(daughterBoost) * mother4Vector;
224 return - VectorUtil::CosTheta(grandDaughter4Vector, mother4Vector);
227 double cosAcoplanarityAngle(
const Particle* mother,
const std::vector<double>& granddaughters)
229 if (granddaughters.size() != 2) {
230 B2FATAL(
"Wrong number of arguments for cosAcoplanarityAngleIfRefFrameIsTheMother: two are needed.");
233 if (mother->getNDaughters() != 2)
234 B2FATAL(
"cosAcoplanarityAngleIfRefFrameIsTheMother: this variable works only for two-body decays.");
236 int iGrandDau1 = std::lround(granddaughters[0]);
237 int iGrandDau2 = std::lround(granddaughters[1]);
239 const Particle* daughter1 = mother->getDaughter(0);
240 const Particle* daughter2 = mother->getDaughter(1);
242 const Particle* grandDaughter1 = daughter1->getDaughter(iGrandDau1);
244 B2FATAL(
"Couldn't find the " << iGrandDau1 <<
"th daughter of the first daughter.");
246 const Particle* grandDaughter2 = daughter2->getDaughter(iGrandDau2);
248 B2FATAL(
"Couldn't find the " << iGrandDau2 <<
"th daughter of the second daughter.");
250 PxPyPzEVector mother4Vector = mother->get4Vector();
251 PxPyPzEVector daughter4Vector1 = daughter1->get4Vector();
252 PxPyPzEVector daughter4Vector2 = daughter2->get4Vector();
253 PxPyPzEVector grandDaughter4Vector1 = grandDaughter1->get4Vector();
254 PxPyPzEVector grandDaughter4Vector2 = grandDaughter2->get4Vector();
256 XYZVector motherBoost = mother4Vector.BoostToCM();
257 XYZVector daughter1Boost = daughter4Vector1.BoostToCM();
258 XYZVector daughter2Boost = daughter4Vector2.BoostToCM();
261 daughter4Vector1 = Boost(motherBoost) * daughter4Vector1;
262 daughter4Vector2 = Boost(motherBoost) * daughter4Vector2;
265 grandDaughter4Vector1 = Boost(daughter1Boost) * grandDaughter4Vector1;
266 grandDaughter4Vector2 = Boost(daughter2Boost) * grandDaughter4Vector2;
269 XYZVector normalVector1 = daughter4Vector1.Vect().Cross(grandDaughter4Vector1.Vect());
270 XYZVector normalVector2 = daughter4Vector2.Vect().Cross(grandDaughter4Vector2.Vect());
272 return VectorUtil::CosTheta(normalVector1, normalVector2);
275 double cosHelicityAnglePrimary(
const Particle* part)
277 return part->getCosHelicity();
280 double cosHelicityAngleDaughter(
const Particle* part,
const std::vector<double>& indices)
282 if ((indices.size() == 0) || (indices.size() > 2)) {
283 B2FATAL(
"Wrong number of arguments for cosHelicityAngleDaughter: one or two are needed.");
286 int iDaughter = std::lround(indices[0]);
287 int iGrandDaughter = 0;
288 if (indices.size() == 2) {
289 iGrandDaughter = std::lround(indices[1]);
292 return part->getCosHelicityDaughter(iDaughter, iGrandDaughter);
295 double acoplanarityAngle(
const Particle* part)
297 return part->getAcoplanarity();
301 double cosHelicityAngleForQuasiTwoBodyDecay(
const Particle* mother,
const std::vector<double>& indices)
303 if (indices.size() != 2) {
304 B2FATAL(
"Wrong number of arguments for cosHelicityAngleForQuasiTwoBodyDecay: two are needed.");
307 if (mother->getNDaughters() != 3)
310 int iDau = std::lround(indices[0]);
311 int jDau = std::lround(indices[1]);
313 const Particle* iDaughter = mother->getDaughter(iDau);
317 const Particle* jDaughter = mother->getDaughter(jDau);
321 PxPyPzEVector mother4Vector = mother->get4Vector();
322 PxPyPzEVector iDaughter4Vector = iDaughter->get4Vector();
323 PxPyPzEVector jDaughter4Vector = jDaughter->get4Vector();
325 PxPyPzEVector resonance4Vector = iDaughter4Vector + jDaughter4Vector;
326 XYZVector resonanceBoost = resonance4Vector.BoostToCM();
328 iDaughter4Vector = Boost(resonanceBoost) * iDaughter4Vector;
329 mother4Vector = Boost(resonanceBoost) * mother4Vector;
331 return - VectorUtil::CosTheta(iDaughter4Vector, mother4Vector);
336 if (arguments.size() != 3) {
337 B2FATAL(
"Wrong number of arguments for momentaTripleProduct: three (particles) are needed.");
341 auto func = [arguments](
const Particle * mother) ->
double {
342 auto iDau = arguments[0];
343 auto jDau = arguments[1];
344 auto kDau = arguments[2];
346 const Particle* iDaughter = mother->getParticleFromGeneralizedIndexString(iDau);
347 if (!iDaughter) B2FATAL(
"Couldn't find the " << iDau <<
"th daughter.");
348 const Particle* jDaughter = mother->getParticleFromGeneralizedIndexString(jDau);
349 if (!jDaughter) B2FATAL(
"Couldn't find the " << jDau <<
"th daughter.");
350 const Particle* kDaughter = mother->getParticleFromGeneralizedIndexString(kDau);
351 if (!kDaughter) B2FATAL(
"Couldn't find the " << kDau <<
"th daughter.");
353 PxPyPzEVector mother4Vector = mother->get4Vector();
354 PxPyPzEVector iDaughter4Vector = iDaughter->get4Vector();
355 PxPyPzEVector jDaughter4Vector = jDaughter->get4Vector();
356 PxPyPzEVector kDaughter4Vector = kDaughter->get4Vector();
358 XYZVector motherBoost = mother4Vector.BoostToCM();
361 iDaughter4Vector = Boost(motherBoost) * iDaughter4Vector;
362 jDaughter4Vector = Boost(motherBoost) * jDaughter4Vector;
363 kDaughter4Vector = Boost(motherBoost) * kDaughter4Vector;
366 XYZVector jkDaughterCrossProduct = jDaughter4Vector.Vect().Cross(kDaughter4Vector.Vect());
368 return iDaughter4Vector.Vect().Dot(jkDaughterCrossProduct) ;
374 VARIABLE_GROUP(
"Helicity variables");
376 REGISTER_VARIABLE(
"cosHelicityAngleMomentum", cosHelicityAngleMomentum, R
"DOC(
377Returns the cosine of an angle whose definition changes depending on how many daughters the particle has; otherwise it returns 0.0.
379.. topic:: For two daughters
381 The angle is between the vector defined by the momentum difference of the two daughters in the frame of the given particle
382 (the mother) and the momentum of the given particle in the lab frame.
384.. topic:: For three daughters
386 The angle is between the normal vector of the plane defined by the momenta of the daughters in the frame of the given particle
387 (the mother) and the momentum of the given particle in the lab frame.
390 REGISTER_VARIABLE("cosHelicityAngleMomentumPi0Dalitz", cosHelicityAngleMomentumPi0Dalitz, R
"DOC(
391Returns the cosine of the angle of the photon in the frame of the given particle (the mother) and the momentum
392of the given particle in the lab frame, otherwise it returns 0.0.
396 This variable should only be used for the decays :math:`\pi^0 \to e^+ e^- \gamma` and
397 :math:`\pi^0 \to \gamma \gamma, \gamma \to e^+ e^-`.
400 REGISTER_VARIABLE("cosHelicityAngleBeamMomentum(i)", cosHelicityAngleBeamMomentum, R
"DOC(
401Returns the cosine of the helicity angle of the :math:`i`-th daughter of the particle provided,
402assuming that the mother of the provided particle corresponds to the centre-of-mass system, whose parameters are
403automatically loaded by the function, given the accelerator's conditions.
406 REGISTER_VARIABLE("cosHelicityAngle(i, j)", cosHelicityAngle, R
"DOC(
407Returns the cosine of the helicity angle between the momentum of the selected granddaughter (index ``j``) and
408the direction opposite to the momentum of the provided particle, both calculated in the reference frame of the selected daughter (index ``i``).
409This variable is useful for angular analyses of :math:`B`-meson decays into two vector particles.
411For example, for the decay :math:`B^0 \to \left(J/\psi \to \mu^+ \mu^-\right) \left(K^{*0} \to K^+ \pi^-\right)`, if the provided particle
412is :math:`B^0` and the selected indices are (0, 0), the variable will return the angle between the momentum of the :math:`\mu^+` and the
413direction opposite to the momentum of the :math:`B^0`, with both momenta in the rest frame of the :math:`J/\psi`.
415.. seealso:: The polarisation of :math:`B` decays is reviewed in this `PDG review <https://pdg.lbl.gov/2026/web/viewer.html?file=../reviews/rpp2026-rev-b-decays-polarization.pdf>`_.
418 REGISTER_VARIABLE("cosAcoplanarityAngle(i, j)", cosAcoplanarityAngle, R
"DOC(
419Returns the cosine of the acoplanarity angle which, for a two-body decay, is defined as the angle between the two normal vectors
420of the decay planes in the reference frame of the mother. Each normal vector is defined as the cross product of the momentum of
421one daughter (in the frame of the mother) and the momentum of one of its granddaughters (in the frame of the daughter). The two integers
422``i`` and ``j`` index the granddaughters for the first and second daughters, respectively.
424For example, for the decay :math:`B^0 \to \left(J/\psi \to \mu^+ \mu^-\right) \left(K^{*0} \to K^+ \pi^-\right)`, if the provided particle
425is :math:`B^0` and the selected indices are (0, 0), the variable will return the acoplanarity using the :math:`\mu^+` and :math:`K^+` granddaughters.
427.. seealso:: The polarisation of :math:`B` decays is reviewed in this `PDG review <https://pdg.lbl.gov/2026/web/viewer.html?file=../reviews/rpp2026-rev-b-decays-polarization.pdf>`_.
430 REGISTER_VARIABLE("cosHelicityAnglePrimary", cosHelicityAnglePrimary, R
"DOC(
431Returns the cosine of the helicity angle (see ``cosHelicityAngle``) assuming the CM system as the mother rest frame.
433.. seealso:: The polarisation of :math:`B` decays is reviewed in this `PDG review <https://pdg.lbl.gov/2026/web/viewer.html?file=../reviews/rpp2026-rev-b-decays-polarization.pdf>`_.
436 REGISTER_VARIABLE("cosHelicityAngleDaughter(i[, j])", cosHelicityAngleDaughter, R
"DOC(
437Returns the cosine of the helicity angle of the daughter at index ``i``. The optional second argument is the index of the granddaughter that defines the angle
440For example, for 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`
441and the selected index is 0, the variable will return the helicity angle of the :math:`\mu^+`. If the selected index is 1 the variable will return the
442helicity angle of the :math:`K^+` (defined via the rest frame of the :math:`K^{*0}`). In rare cases, if one wanted the helicity angle of the second granddaughter,
443indices (1, 1) would return the helicity angle of the :math:`\pi^-`.
445.. seealso:: The polarisation of :math:`B` decays is reviewed in this `PDG review <https://pdg.lbl.gov/2026/web/viewer.html?file=../reviews/rpp2026-rev-b-decays-polarization.pdf>`_.
448 REGISTER_VARIABLE("acoplanarityAngle", acoplanarityAngle, R
"DOC(
449Returns the acoplanarity angle, as described in the definition for ``cosAcoplanarityAngle``, assuming a two body decay of the particle and
452.. seealso:: The polarisation of :math:`B` decays is reviewed in this `PDG review <https://pdg.lbl.gov/2026/web/viewer.html?file=../reviews/rpp2026-rev-b-decays-polarization.pdf>`_.
455 REGISTER_VARIABLE(
"cosHelicityAngleForQuasiTwoBodyDecay(i, j)", cosHelicityAngleForQuasiTwoBodyDecay, R
"DOC(
456Returns the cosine of the helicity angle between the momentum of the provided particle and the momentum of the daughter at
457index :math:`i` in the reference frame of the two selected daughters (at index :math:`i` and :math:`j`) combined.
459For example, for the decay :math:`\bar{B}^0 \to D^+ K^- K^{*0}`, if the provided particle is :math:`\bar{B}^0` and
460the selected indices are (1, 2), the variable will return the angle between the momentum of the :math:`\bar{B}^0` and
461the momentum of the :math:`K^-`, both in the rest frame of the :math:`K^- K^{*0}` combination.
464 The variable is supposed to be used for analyses of quasi-two-body decays. The number of daughters of the given particle
465 must be three, otherwise the variable returns ``NaN``.
468 REGISTER_METAVARIABLE("momentaTripleProduct(i,j,k)", momentaTripleProduct, R
"DOC(
469Returns a triple product of the three momenta as defined by
472 C_T=\vec{p}_i\cdot(\vec{p}_j\times\vec{p}_k)
474where :math:`i, j` and :math:`k` are indices of the daughters of the provided particle.
476For example, in a four body decay :math:`M \rightarrow d_1 d_2 d_3 d_4`, for the selected indices (0, 1, 2), the variable will return
477:math:`C_T` calculated using the momenta of the particles :math:`d_1, d_2` and :math:`d_3`. In instances of secondary decays such as
478:math:`M \rightarrow R (\rightarrow d_1 d_2) d_3 d_4`, the selected indices (0:0, 1, 2) returns :math:`C_T` calculated using the
479momenta of the particles :math:`d_1, d_3` and :math:`d_4`.
481)DOC", Manager::VariableDataType::c_double);
int getPDGCode() const
PDG code.
static const ParticleType pi0
neutral pion particle
static const double doubleNaN
quiet_NaN
static const ParticleType photon
photon particle
static const ChargedStable electron
electron particle
static const ReferenceFrame & GetCurrent()
Get current rest frame.
std::function< VarVariant(const Particle *)> FunctionPtr
functions stored take a const Particle* and return VarVariant.
Abstract base class for different kinds of events.