Belle II Software  light-2303-iriomote
MetaVariables.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/MetaVariables.h>
11 
12 #include <analysis/VariableManager/Utility.h>
13 #include <analysis/dataobjects/Particle.h>
14 #include <analysis/dataobjects/ParticleList.h>
15 #include <analysis/dataobjects/RestOfEvent.h>
16 #include <analysis/utility/PCmsLabTransform.h>
17 #include <analysis/utility/ReferenceFrame.h>
18 #include <analysis/utility/EvtPDLUtil.h>
19 #include <analysis/utility/ParticleCopy.h>
20 #include <analysis/utility/ValueIndexPairSorting.h>
21 #include <analysis/ClusterUtility/ClusterUtils.h>
22 #include <analysis/variables/VariableFormulaConstructor.h>
23 
24 #include <framework/logging/Logger.h>
25 #include <framework/datastore/StoreArray.h>
26 #include <framework/datastore/StoreObjPtr.h>
27 #include <framework/dataobjects/EventExtraInfo.h>
28 #include <framework/utilities/Conversion.h>
29 #include <framework/utilities/MakeROOTCompatible.h>
30 #include <framework/gearbox/Const.h>
31 
32 #include <mdst/dataobjects/Track.h>
33 #include <mdst/dataobjects/MCParticle.h>
34 #include <mdst/dataobjects/ECLCluster.h>
35 #include <mdst/dataobjects/TrackFitResult.h>
36 
37 #include <boost/algorithm/string.hpp>
38 #include <limits>
39 
40 #include <cmath>
41 #include <stdexcept>
42 #include <memory>
43 #include <regex>
44 
45 #include <TDatabasePDG.h>
46 #include <Math/Vector4D.h>
47 
48 namespace Belle2 {
53  namespace Variable {
54 
55  Manager::FunctionPtr useRestFrame(const std::vector<std::string>& arguments)
56  {
57  if (arguments.size() == 1) {
58  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
59  auto func = [var](const Particle * particle) -> double {
60  UseReferenceFrame<RestFrame> frame(particle);
61  double result = std::get<double>(var->function(particle));
62  return result;
63  };
64  return func;
65  } else {
66  B2FATAL("Wrong number of arguments for meta function useRestFrame");
67  }
68  }
69 
70  Manager::FunctionPtr useCMSFrame(const std::vector<std::string>& arguments)
71  {
72  if (arguments.size() == 1) {
73  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
74  auto func = [var](const Particle * particle) -> double {
75  UseReferenceFrame<CMSFrame> frame;
76  double result = std::get<double>(var->function(particle));
77  return result;
78  };
79  return func;
80  } else {
81  B2FATAL("Wrong number of arguments for meta function useCMSFrame");
82  }
83  }
84 
85  Manager::FunctionPtr useLabFrame(const std::vector<std::string>& arguments)
86  {
87  if (arguments.size() == 1) {
88  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
89  auto func = [var](const Particle * particle) -> double {
90  UseReferenceFrame<LabFrame> frame;
91  double result = std::get<double>(var->function(particle));
92  return result;
93  };
94  return func;
95  } else {
96  B2FATAL("Wrong number of arguments for meta function useLabFrame");
97  }
98  }
99 
100  Manager::FunctionPtr useTagSideRecoilRestFrame(const std::vector<std::string>& arguments)
101  {
102  if (arguments.size() == 2) {
103  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
104 
105  int daughterIndexTagB = 0;
106  try {
107  daughterIndexTagB = Belle2::convertString<int>(arguments[1]);
108  } catch (std::invalid_argument&) {
109  B2FATAL("Second argument of useTagSideRecoilRestFrame meta function must be integer!");
110  }
111 
112  auto func = [var, daughterIndexTagB](const Particle * particle) -> double {
113  if (particle->getPDGCode() != 300553)
114  {
115  B2ERROR("Variable should only be used on a Upsilon(4S) Particle List!");
116  return std::numeric_limits<float>::quiet_NaN();
117  }
118 
119  PCmsLabTransform T;
120  ROOT::Math::PxPyPzEVector pSigB = T.getBeamFourMomentum() - particle->getDaughter(daughterIndexTagB)->get4Vector();
121  Particle tmp(pSigB, -particle->getDaughter(daughterIndexTagB)->getPDGCode());
122 
123  UseReferenceFrame<RestFrame> frame(&tmp);
124  double result = std::get<double>(var->function(particle));
125  return result;
126  };
127 
128  return func;
129  } else {
130  B2FATAL("Wrong number of arguments for meta function useTagSideRecoilRestFrame");
131  }
132  }
133 
134  Manager::FunctionPtr useParticleRestFrame(const std::vector<std::string>& arguments)
135  {
136  if (arguments.size() == 2) {
137  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
138  std::string listName = arguments[1];
139  auto func = [var, listName](const Particle * particle) -> double {
140  StoreObjPtr<ParticleList> list(listName);
141  unsigned listSize = list->getListSize();
142  if (listSize == 0)
143  return std::numeric_limits<float>::quiet_NaN();
144  if (listSize > 1)
145  B2WARNING("The selected ParticleList contains more than 1 Particles in this event. The variable useParticleRestFrame will use only the first candidate, and the result may not be the expected one."
146  << LogVar("ParticleList", listName)
147  << LogVar("Number of candidates in the list", listSize));
148  const Particle* p = list->getParticle(0);
149  UseReferenceFrame<RestFrame> frame(p);
150  double result = std::get<double>(var->function(particle));
151  return result;
152  };
153  return func;
154  } else {
155  B2FATAL("Wrong number of arguments for meta function useParticleRestFrame.");
156  }
157  }
158 
159  Manager::FunctionPtr useRecoilParticleRestFrame(const std::vector<std::string>& arguments)
160  {
161  if (arguments.size() == 2) {
162  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
163  std::string listName = arguments[1];
164  auto func = [var, listName](const Particle * particle) -> double {
165  StoreObjPtr<ParticleList> list(listName);
166  unsigned listSize = list->getListSize();
167  if (listSize == 0)
168  return std::numeric_limits<float>::quiet_NaN();
169  if (listSize > 1)
170  B2WARNING("The selected ParticleList contains more than 1 Particles in this event. The variable useParticleRestFrame will use only the first candidate, and the result may not be the expected one."
171  << LogVar("ParticleList", listName)
172  << LogVar("Number of candidates in the list", listSize));
173  const Particle* p = list->getParticle(0);
174  PCmsLabTransform T;
175  ROOT::Math::PxPyPzEVector recoil = T.getBeamFourMomentum() - p->get4Vector();
176  /* Let's use 0 as PDG code to avoid wrong assumptions. */
177  Particle pRecoil(recoil, 0);
178  pRecoil.setVertex(particle->getVertex());
179  UseReferenceFrame<RestFrame> frame(&pRecoil);
180  double result = std::get<double>(var->function(particle));
181  return result;
182  };
183  return func;
184  } else {
185  B2FATAL("Wrong number of arguments for meta function useParticleRestFrame.");
186  }
187  }
188 
189  Manager::FunctionPtr useDaughterRestFrame(const std::vector<std::string>& arguments)
190  {
191  if (arguments.size() >= 2) {
192  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
193  auto func = [var, arguments](const Particle * particle) -> double {
194 
195  // Sum of the 4-momenta of all the selected daughters
196  ROOT::Math::PxPyPzEVector pSum(0, 0, 0, 0);
197 
198  for (unsigned int i = 1; i < arguments.size(); i++)
199  {
200  auto generalizedIndex = arguments[i];
201  const Particle* dauPart = particle->getParticleFromGeneralizedIndexString(generalizedIndex);
202  if (dauPart)
203  pSum += dauPart->get4Vector();
204  else
205  return std::numeric_limits<float>::quiet_NaN();
206  }
207  Particle tmp(pSum, 0);
208  UseReferenceFrame<RestFrame> frame(&tmp);
209  double result = std::get<double>(var->function(particle));
210  return result;
211  };
212  return func;
213  } else {
214  B2FATAL("Wrong number of arguments for meta function useDaughterRestFrame.");
215  }
216  }
217 
218  Manager::FunctionPtr extraInfo(const std::vector<std::string>& arguments)
219  {
220  if (arguments.size() == 1) {
221  auto extraInfoName = arguments[0];
222  auto func = [extraInfoName](const Particle * particle) -> double {
223  if (particle == nullptr)
224  {
225  B2WARNING("Returns NaN because the particle is nullptr! If you want EventExtraInfo variables, please use eventExtraInfo() instead");
226  return std::numeric_limits<float>::quiet_NaN();
227  }
228  if (particle->hasExtraInfo(extraInfoName))
229  {
230  return particle->getExtraInfo(extraInfoName);
231  } else
232  {
233  return std::numeric_limits<float>::quiet_NaN();
234  }
235  };
236  return func;
237  } else {
238  B2FATAL("Wrong number of arguments for meta function extraInfo");
239  }
240  }
241 
242  Manager::FunctionPtr eventExtraInfo(const std::vector<std::string>& arguments)
243  {
244  if (arguments.size() == 1) {
245  auto extraInfoName = arguments[0];
246  auto func = [extraInfoName](const Particle*) -> double {
247  StoreObjPtr<EventExtraInfo> eventExtraInfo;
248  if (not eventExtraInfo.isValid())
249  return std::numeric_limits<float>::quiet_NaN();
250  if (eventExtraInfo->hasExtraInfo(extraInfoName))
251  {
252  return eventExtraInfo->getExtraInfo(extraInfoName);
253  } else
254  {
255  return std::numeric_limits<float>::quiet_NaN();
256  }
257  };
258  return func;
259  } else {
260  B2FATAL("Wrong number of arguments for meta function extraInfo");
261  }
262  }
263 
264  Manager::FunctionPtr eventCached(const std::vector<std::string>& arguments)
265  {
266  if (arguments.size() == 1) {
267  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
268  std::string key = std::string("__") + MakeROOTCompatible::makeROOTCompatible(var->name);
269  auto func = [var, key](const Particle*) -> double {
270 
271  StoreObjPtr<EventExtraInfo> eventExtraInfo;
272  if (not eventExtraInfo.isValid())
273  eventExtraInfo.create();
274  if (eventExtraInfo->hasExtraInfo(key))
275  {
276  return eventExtraInfo->getExtraInfo(key);
277  } else
278  {
279  double value = std::numeric_limits<double>::quiet_NaN();
280  auto var_result = var->function(nullptr);
281  if (std::holds_alternative<double>(var_result)) {
282  value = std::get<double>(var_result);
283  } else if (std::holds_alternative<int>(var_result)) {
284  return std::get<int>(var_result);
285  } else if (std::holds_alternative<bool>(var_result)) {
286  return std::get<bool>(var_result);
287  }
288  eventExtraInfo->addExtraInfo(key, value);
289  return value;
290  }
291  };
292  return func;
293  } else {
294  B2FATAL("Wrong number of arguments for meta function eventCached");
295  }
296  }
297 
298  Manager::FunctionPtr particleCached(const std::vector<std::string>& arguments)
299  {
300  if (arguments.size() == 1) {
301  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
302  std::string key = std::string("__") + MakeROOTCompatible::makeROOTCompatible(var->name);
303  auto func = [var, key](const Particle * particle) -> double {
304 
305  if (particle->hasExtraInfo(key))
306  {
307  return particle->getExtraInfo(key);
308  } else
309  {
310  double value = std::get<double>(var->function(particle));
311  // Remove constness from Particle pointer.
312  // The extra-info is used as a cache in our case,
313  // indicated by the double-underscore in front of the key.
314  // One could implement the cache as a separate property of the particle object
315  // and mark it as mutable, however, this would only lead to code duplication
316  // and an increased size of the particle object.
317  // Thus, we decided to use the extra-info field and cast away the const in this case.
318  const_cast<Particle*>(particle)->addExtraInfo(key, value);
319  return value;
320  }
321  };
322  return func;
323  } else {
324  B2FATAL("Wrong number of arguments for meta function particleCached");
325  }
326  }
327 
328  // Formula of other variables, going to require a space between all operators and operations.
329  // Later can add some check for : (colon) trailing + or - to distinguish between particle lists
330  // and operations, but for now cbf.
331  Manager::FunctionPtr formula(const std::vector<std::string>& arguments)
332  {
333  if (arguments.size() != 1) B2FATAL("Wrong number of arguments for meta function formula");
334  FormulaParser<VariableFormulaConstructor> parser;
335  try {
336  return parser.parse(arguments[0]);
337  } catch (std::runtime_error& e) {
338  B2FATAL(e.what());
339  }
340  }
341 
342  Manager::FunctionPtr nCleanedTracks(const std::vector<std::string>& arguments)
343  {
344  if (arguments.size() <= 1) {
345 
346  std::string cutString;
347  if (arguments.size() == 1)
348  cutString = arguments[0];
349  std::shared_ptr<Variable::Cut> cut = std::shared_ptr<Variable::Cut>(Variable::Cut::compile(cutString));
350  auto func = [cut](const Particle*) -> int {
351 
352  int number_of_tracks = 0;
353  StoreArray<Track> tracks;
354  for (const auto& track : tracks)
355  {
356  const TrackFitResult* trackFit = track.getTrackFitResultWithClosestMass(Const::pion);
357  if (trackFit->getChargeSign() == 0) {
358  // Ignore track
359  } else {
360  Particle particle(&track, Const::pion);
361  if (cut->check(&particle))
362  number_of_tracks++;
363  }
364  }
365 
366  return number_of_tracks;
367 
368  };
369  return func;
370  } else {
371  B2FATAL("Wrong number of arguments for meta function nCleanedTracks");
372  }
373  }
374 
375  Manager::FunctionPtr nCleanedECLClusters(const std::vector<std::string>& arguments)
376  {
377  if (arguments.size() <= 1) {
378 
379  std::string cutString;
380  if (arguments.size() == 1)
381  cutString = arguments[0];
382  std::shared_ptr<Variable::Cut> cut = std::shared_ptr<Variable::Cut>(Variable::Cut::compile(cutString));
383  auto func = [cut](const Particle*) -> int {
384 
385  int number_of_clusters = 0;
386  StoreArray<ECLCluster> clusters;
387  for (const auto& cluster : clusters)
388  {
389  // look only at momentum of N1 (n photons) ECLClusters
390  if (!cluster.hasHypothesis(ECLCluster::EHypothesisBit::c_nPhotons))
391  continue;
392 
393  Particle particle(&cluster);
394  if (cut->check(&particle))
395  number_of_clusters++;
396  }
397 
398  return number_of_clusters;
399 
400  };
401  return func;
402  } else {
403  B2FATAL("Wrong number of arguments for meta function nCleanedECLClusters");
404  }
405  }
406 
407  Manager::FunctionPtr passesCut(const std::vector<std::string>& arguments)
408  {
409  if (arguments.size() == 1) {
410  std::string cutString = arguments[0];
411  std::shared_ptr<Variable::Cut> cut = std::shared_ptr<Variable::Cut>(Variable::Cut::compile(cutString));
412  auto func = [cut](const Particle * particle) -> bool {
413  if (cut->check(particle))
414  return 1;
415  else
416  return 0;
417  };
418  return func;
419  } else {
420  B2FATAL("Wrong number of arguments for meta function passesCut");
421  }
422  }
423 
424  Manager::FunctionPtr passesEventCut(const std::vector<std::string>& arguments)
425  {
426  if (arguments.size() == 1) {
427  std::string cutString = arguments[0];
428  std::shared_ptr<Variable::Cut> cut = std::shared_ptr<Variable::Cut>(Variable::Cut::compile(cutString));
429  auto func = [cut](const Particle*) -> bool {
430  if (cut->check(nullptr))
431  return 1;
432  else
433  return 0;
434  };
435  return func;
436  } else {
437  B2FATAL("Wrong number of arguments for meta function passesEventCut");
438  }
439  }
440 
441  Manager::FunctionPtr varFor(const std::vector<std::string>& arguments)
442  {
443  if (arguments.size() == 2) {
444  int pdgCode = 0;
445  try {
446  pdgCode = Belle2::convertString<int>(arguments[0]);
447  } catch (std::invalid_argument&) {
448  B2FATAL("The first argument of varFor meta function must be a positive integer!");
449  }
450  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[1]);
451  auto func = [pdgCode, var](const Particle * particle) -> double {
452  if (std::abs(particle->getPDGCode()) == std::abs(pdgCode))
453  {
454  auto var_result = var->function(particle);
455  if (std::holds_alternative<double>(var_result)) {
456  return std::get<double>(var_result);
457  } else if (std::holds_alternative<int>(var_result)) {
458  return std::get<int>(var_result);
459  } else if (std::holds_alternative<bool>(var_result)) {
460  return std::get<bool>(var_result);
461  } else return std::numeric_limits<double>::quiet_NaN();
462  } else return std::numeric_limits<double>::quiet_NaN();
463  };
464  return func;
465  } else {
466  B2FATAL("Wrong number of arguments for meta function varFor");
467  }
468  }
469 
470  Manager::FunctionPtr varForMCGen(const std::vector<std::string>& arguments)
471  {
472  if (arguments.size() == 1) {
473  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
474  auto func = [var](const Particle * particle) -> double {
475  if (particle->getMCParticle())
476  {
477  if (particle->getMCParticle()->getStatus(MCParticle::c_PrimaryParticle)
478  && (! particle->getMCParticle()->getStatus(MCParticle::c_IsVirtual))
479  && (! particle->getMCParticle()->getStatus(MCParticle::c_Initial))) {
480  auto var_result = var->function(particle);
481  if (std::holds_alternative<double>(var_result)) {
482  return std::get<double>(var_result);
483  } else if (std::holds_alternative<int>(var_result)) {
484  return std::get<int>(var_result);
485  } else if (std::holds_alternative<bool>(var_result)) {
486  return std::get<bool>(var_result);
487  } else return std::numeric_limits<double>::quiet_NaN();
488  } else return std::numeric_limits<double>::quiet_NaN();
489  } else return std::numeric_limits<double>::quiet_NaN();
490  };
491  return func;
492  } else {
493  B2FATAL("Wrong number of arguments for meta function varForMCGen");
494  }
495  }
496 
497  Manager::FunctionPtr nParticlesInList(const std::vector<std::string>& arguments)
498  {
499  if (arguments.size() == 1) {
500  std::string listName = arguments[0];
501  auto func = [listName](const Particle * particle) -> int {
502 
503  (void) particle;
504  StoreObjPtr<ParticleList> listOfParticles(listName);
505 
506  if (!(listOfParticles.isValid())) B2FATAL("Invalid Listname " << listName << " given to nParticlesInList");
507 
508  return listOfParticles->getListSize();
509 
510  };
511  return func;
512  } else {
513  B2FATAL("Wrong number of arguments for meta function nParticlesInList");
514  }
515  }
516 
517  Manager::FunctionPtr isInList(const std::vector<std::string>& arguments)
518  {
519  // unpack arguments, there should be only one: the name of the list we're checking
520  if (arguments.size() != 1) {
521  B2FATAL("Wrong number of arguments for isInList");
522  }
523  auto listName = arguments[0];
524 
525  auto func = [listName](const Particle * particle) -> bool {
526 
527  // check the list exists
528  StoreObjPtr<ParticleList> list(listName);
529  if (!(list.isValid()))
530  {
531  B2FATAL("Invalid Listname " << listName << " given to isInList");
532  }
533 
534  // is the particle in the list?
535  return list->contains(particle);
536 
537  };
538  return func;
539  }
540 
541  Manager::FunctionPtr sourceObjectIsInList(const std::vector<std::string>& arguments)
542  {
543  // unpack arguments, there should be only one: the name of the list we're checking
544  if (arguments.size() != 1) {
545  B2FATAL("Wrong number of arguments for sourceObjectIsInList");
546  }
547  auto listName = arguments[0];
548 
549  auto func = [listName](const Particle * particle) -> int {
550 
551  // check the list exists
552  StoreObjPtr<ParticleList> list(listName);
553  if (!(list.isValid()))
554  {
555  B2FATAL("Invalid Listname " << listName << " given to sourceObjectIsInList");
556  }
557 
558  // this only makes sense for particles that are *not* composite and come
559  // from some mdst object (tracks, clusters..)
560  Particle::EParticleSourceObject particlesource = particle->getParticleSource();
561  if (particlesource == Particle::EParticleSourceObject::c_Composite
562  or particlesource == Particle::EParticleSourceObject::c_Undefined)
563  return -1;
564 
565  // it *is* possible to have a particle list from different sources (like
566  // hadrons from the ECL and KLM) so we have to check each particle in
567  // the list individually
568  for (unsigned i = 0; i < list->getListSize(); ++i)
569  {
570  Particle* iparticle = list->getParticle(i);
571  if (particlesource == iparticle->getParticleSource())
572  if (particle->getMdstArrayIndex() == iparticle->getMdstArrayIndex())
573  return 1;
574  }
575  return 0;
576 
577  };
578  return func;
579  }
580 
581  Manager::FunctionPtr mcParticleIsInMCList(const std::vector<std::string>& arguments)
582  {
583  // unpack arguments, there should be only one: the name of the list we're checking
584  if (arguments.size() != 1) {
585  B2FATAL("Wrong number of arguments for mcParticleIsInMCList");
586  }
587  auto listName = arguments[0];
588 
589  auto func = [listName](const Particle * particle) -> bool {
590 
591  // check the list exists
592  StoreObjPtr<ParticleList> list(listName);
593  if (!(list.isValid()))
594  B2FATAL("Invalid Listname " << listName << " given to mcParticleIsInMCList");
595 
596  // this can only be true for mc-matched particles or particles are created from MCParticles
597  const MCParticle* mcp = particle->getMCParticle();
598  if (mcp == nullptr) return false;
599 
600  // check every particle in the input list is not matched to (or created from) the same MCParticle
601  for (unsigned i = 0; i < list->getListSize(); ++i)
602  {
603  const MCParticle* imcp = list->getParticle(i)->getMCParticle();
604  if ((imcp != nullptr) and (mcp->getArrayIndex() == imcp->getArrayIndex()))
605  return true;
606  }
607  return false;
608  };
609  return func;
610  }
611 
612  Manager::FunctionPtr isDaughterOfList(const std::vector<std::string>& arguments)
613  {
614  B2WARNING("isDaughterOfList is outdated and replaced by isDescendantOfList.");
615  std::vector<std::string> new_arguments = arguments;
616  new_arguments.push_back(std::string("1"));
617  return isDescendantOfList(new_arguments);
618  }
619 
620  Manager::FunctionPtr isGrandDaughterOfList(const std::vector<std::string>& arguments)
621  {
622  B2WARNING("isGrandDaughterOfList is outdated and replaced by isDescendantOfList.");
623  std::vector<std::string> new_arguments = arguments;
624  new_arguments.push_back(std::string("2"));
625  return isDescendantOfList(new_arguments);
626  }
627 
628  Manager::FunctionPtr isDescendantOfList(const std::vector<std::string>& arguments)
629  {
630  if (arguments.size() > 0) {
631  auto listNames = arguments;
632  auto func = [listNames](const Particle * particle) -> bool {
633  bool output = false;
634  int generation_flag = -1;
635  try
636  {
637  generation_flag = Belle2::convertString<int>(listNames.back());
638  } catch (std::exception& e) {}
639 
640  for (auto& iListName : listNames)
641  {
642  try {
643  Belle2::convertString<int>(iListName);
644  continue;
645  } catch (std::exception& e) {}
646 
647  // Creating recursive lambda
648  auto list_comparison = [](auto&& self, const Particle * m, const Particle * p, int flag)-> bool {
649  bool result = false;
650  for (unsigned i = 0; i < m->getNDaughters(); ++i)
651  {
652  const Particle* daughter = m->getDaughter(i);
653  if ((flag == 1.) or (flag < 0)) {
654  if (p->isCopyOf(daughter)) {
655  return true;
656  }
657  }
658 
659  if (flag != 1.) {
660  if (daughter->getNDaughters() > 0) {
661  result = self(self, daughter, p, flag - 1);
662  if (result == 1) {
663  return true;
664  }
665  }
666  }
667  }
668  return result;
669  };
670 
671  StoreObjPtr<ParticleList> listOfParticles(iListName);
672 
673  if (!(listOfParticles.isValid())) B2FATAL("Invalid Listname " << iListName << " given to isDescendantOfList");
674 
675  for (unsigned i = 0; i < listOfParticles->getListSize(); ++i) {
676  Particle* iParticle = listOfParticles->getParticle(i);
677  output = list_comparison(list_comparison, iParticle, particle, generation_flag);
678  if (output) {
679  return output;
680  }
681  }
682  }
683  return output;
684  };
685  return func;
686  } else {
687  B2FATAL("Wrong number of arguments for meta function isDescendantOfList");
688  }
689  }
690 
691  Manager::FunctionPtr isMCDescendantOfList(const std::vector<std::string>& arguments)
692  {
693  if (arguments.size() > 0) {
694  auto listNames = arguments;
695  auto func = [listNames](const Particle * particle) -> bool {
696  bool output = false;
697  int generation_flag = -1;
698  try
699  {
700  generation_flag = Belle2::convertString<int>(listNames.back());
701  } catch (std::exception& e) {}
702 
703  if (particle->getMCParticle() == nullptr)
704  {
705  return false;
706  }
707 
708  for (auto& iListName : listNames)
709  {
710  try {
711  std::stod(iListName);
712  continue;
713  } catch (std::exception& e) {}
714  // Creating recursive lambda
715  auto list_comparison = [](auto&& self, const Particle * m, const Particle * p, int flag)-> bool {
716  bool result = false;
717  for (unsigned i = 0; i < m->getNDaughters(); ++i)
718  {
719  const Particle* daughter = m->getDaughter(i);
720  if ((flag == 1.) or (flag < 0)) {
721  if (daughter->getMCParticle() != nullptr) {
722  if (p->getMCParticle()->getArrayIndex() == daughter->getMCParticle()->getArrayIndex()) {
723  return true;
724  }
725  }
726  }
727  if (flag != 1.) {
728  if (daughter->getNDaughters() > 0) {
729  result = self(self, daughter, p, flag - 1);
730  if (result) {
731  return true;
732  }
733  }
734  }
735  }
736  return result;
737  };
738 
739  StoreObjPtr<ParticleList> listOfParticles(iListName);
740 
741  if (!(listOfParticles.isValid())) B2FATAL("Invalid Listname " << iListName << " given to isMCDescendantOfList");
742 
743  for (unsigned i = 0; i < listOfParticles->getListSize(); ++i) {
744  Particle* iParticle = listOfParticles->getParticle(i);
745  output = list_comparison(list_comparison, iParticle, particle, generation_flag);
746  if (output) {
747  return output;
748  }
749  }
750  }
751  return output;
752  };
753  return func;
754  } else {
755  B2FATAL("Wrong number of arguments for meta function isMCDescendantOfList");
756  }
757  }
758 
759  Manager::FunctionPtr daughterProductOf(const std::vector<std::string>& arguments)
760  {
761  if (arguments.size() == 1) {
762  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
763  auto func = [var](const Particle * particle) -> double {
764  double product = 1.0;
765  if (particle->getNDaughters() == 0)
766  {
767  return std::numeric_limits<double>::quiet_NaN();
768  }
769  if (std::holds_alternative<double>(var->function(particle->getDaughter(0))))
770  {
771  for (unsigned j = 0; j < particle->getNDaughters(); ++j) {
772  product *= std::get<double>(var->function(particle->getDaughter(j)));
773  }
774  } else if (std::holds_alternative<int>(var->function(particle->getDaughter(0))))
775  {
776  for (unsigned j = 0; j < particle->getNDaughters(); ++j) {
777  product *= std::get<int>(var->function(particle->getDaughter(j)));
778  }
779  } else return std::numeric_limits<double>::quiet_NaN();
780  return product;
781  };
782  return func;
783  } else {
784  B2FATAL("Wrong number of arguments for meta function daughterProductOf");
785  }
786  }
787 
788  Manager::FunctionPtr daughterSumOf(const std::vector<std::string>& arguments)
789  {
790  if (arguments.size() == 1) {
791  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
792  auto func = [var](const Particle * particle) -> double {
793  double sum = 0.0;
794  if (particle->getNDaughters() == 0)
795  {
796  return std::numeric_limits<double>::quiet_NaN();
797  }
798  if (std::holds_alternative<double>(var->function(particle->getDaughter(0))))
799  {
800  for (unsigned j = 0; j < particle->getNDaughters(); ++j) {
801  sum += std::get<double>(var->function(particle->getDaughter(j)));
802  }
803  } else if (std::holds_alternative<int>(var->function(particle->getDaughter(0))))
804  {
805  for (unsigned j = 0; j < particle->getNDaughters(); ++j) {
806  sum += std::get<int>(var->function(particle->getDaughter(j)));
807  }
808  } else return std::numeric_limits<double>::quiet_NaN();
809  return sum;
810  };
811  return func;
812  } else {
813  B2FATAL("Wrong number of arguments for meta function daughterSumOf");
814  }
815  }
816 
817  Manager::FunctionPtr daughterLowest(const std::vector<std::string>& arguments)
818  {
819  if (arguments.size() == 1) {
820  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
821  auto func = [var](const Particle * particle) -> double {
822  double min = std::numeric_limits<double>::quiet_NaN();
823  if (particle->getNDaughters() == 0)
824  {
825  return std::numeric_limits<double>::quiet_NaN();
826  }
827  if (std::holds_alternative<double>(var->function(particle->getDaughter(0))))
828  {
829  for (unsigned j = 0; j < particle->getNDaughters(); ++j) {
830  double iValue = std::get<double>(var->function(particle->getDaughter(j)));
831  if (std::isnan(iValue)) continue;
832  if (std::isnan(min)) min = iValue;
833  if (iValue < min) min = iValue;
834  }
835  } else if (std::holds_alternative<int>(var->function(particle->getDaughter(0))))
836  {
837  for (unsigned j = 0; j < particle->getNDaughters(); ++j) {
838  int iValue = std::get<int>(var->function(particle->getDaughter(j)));
839  if (std::isnan(min)) min = iValue;
840  if (iValue < min) min = iValue;
841  }
842  }
843  return min;
844  };
845  return func;
846  } else {
847  B2FATAL("Wrong number of arguments for meta function daughterLowest");
848  }
849  }
850 
851  Manager::FunctionPtr daughterHighest(const std::vector<std::string>& arguments)
852  {
853  if (arguments.size() == 1) {
854  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
855  auto func = [var](const Particle * particle) -> double {
856  double max = std::numeric_limits<double>::quiet_NaN();
857  if (particle->getNDaughters() == 0)
858  {
859  return std::numeric_limits<double>::quiet_NaN();
860  }
861  if (std::holds_alternative<double>(var->function(particle->getDaughter(0))))
862  {
863  for (unsigned j = 0; j < particle->getNDaughters(); ++j) {
864  double iValue = std::get<double>(var->function(particle->getDaughter(j)));
865  if (std::isnan(iValue)) continue;
866  if (std::isnan(max)) max = iValue;
867  if (iValue > max) max = iValue;
868  }
869  } else if (std::holds_alternative<int>(var->function(particle->getDaughter(0))))
870  {
871  for (unsigned j = 0; j < particle->getNDaughters(); ++j) {
872  int iValue = std::get<int>(var->function(particle->getDaughter(j)));
873  if (std::isnan(max)) max = iValue;
874  if (iValue > max) max = iValue;
875  }
876  }
877  return max;
878  };
879  return func;
880  } else {
881  B2FATAL("Wrong number of arguments for meta function daughterHighest");
882  }
883  }
884 
885  Manager::FunctionPtr daughterDiffOf(const std::vector<std::string>& arguments)
886  {
887  if (arguments.size() == 3) {
888  int iDaughterNumber = 0;
889  int jDaughterNumber = 0;
890  try {
891  iDaughterNumber = Belle2::convertString<int>(arguments[0]);
892  jDaughterNumber = Belle2::convertString<int>(arguments[1]);
893  } catch (std::invalid_argument&) {
894  B2FATAL("First two arguments of daughterDiffOf meta function must be integers!");
895  }
896  auto variablename = arguments[2];
897  auto func = [variablename, iDaughterNumber, jDaughterNumber](const Particle * particle) -> double {
898  if (particle == nullptr)
899  return std::numeric_limits<double>::quiet_NaN();
900  if (iDaughterNumber >= int(particle->getNDaughters()) || jDaughterNumber >= int(particle->getNDaughters()))
901  return std::numeric_limits<double>::quiet_NaN();
902  else
903  {
904  const Variable::Manager::Var* var = Manager::Instance().getVariable(variablename);
905  auto result_j = var->function(particle->getDaughter(jDaughterNumber));
906  auto result_i = var->function(particle->getDaughter(iDaughterNumber));
907  double diff = std::numeric_limits<double>::quiet_NaN();
908  if (std::holds_alternative<double>(result_j) && std::holds_alternative<double>(result_i)) {
909  diff = std::get<double>(result_j) - std::get<double>(result_i);
910  } else if (std::holds_alternative<int>(result_j) && std::holds_alternative<int>(result_i)) {
911  diff = std::get<int>(result_j) - std::get<int>(result_i);
912  } else {
913  throw std::runtime_error("Bad variant access");
914  }
915  if (variablename == "phi" or variablename == "clusterPhi" or std::regex_match(variablename, std::regex("use.*Frame\\(phi\\)"))
916  or std::regex_match(variablename, std::regex("use.*Frame\\(clusterPhi\\)"))) {
917  if (fabs(diff) > M_PI) {
918  if (diff > M_PI) {
919  diff = diff - 2 * M_PI;
920  } else {
921  diff = 2 * M_PI + diff;
922  }
923  }
924  }
925  return diff;
926  }
927  };
928  return func;
929  } else {
930  B2FATAL("Wrong number of arguments for meta function daughterDiffOf");
931  }
932  }
933 
934  Manager::FunctionPtr mcDaughterDiffOf(const std::vector<std::string>& arguments)
935  {
936  if (arguments.size() == 3) {
937  int iDaughterNumber = 0;
938  int jDaughterNumber = 0;
939  try {
940  iDaughterNumber = Belle2::convertString<int>(arguments[0]);
941  jDaughterNumber = Belle2::convertString<int>(arguments[1]);
942  } catch (std::invalid_argument&) {
943  B2FATAL("First two arguments of mcDaughterDiffOf meta function must be integers!");
944  }
945  auto variablename = arguments[2];
946  auto func = [variablename, iDaughterNumber, jDaughterNumber](const Particle * particle) -> double {
947  if (particle == nullptr)
948  return std::numeric_limits<double>::quiet_NaN();
949  if (iDaughterNumber >= int(particle->getNDaughters()) || jDaughterNumber >= int(particle->getNDaughters()))
950  return std::numeric_limits<double>::quiet_NaN();
951  if (particle->getDaughter(jDaughterNumber)->getMCParticle() == nullptr || particle->getDaughter(iDaughterNumber)->getMCParticle() == nullptr)
952  return std::numeric_limits<double>::quiet_NaN();
953  else
954  {
955  const MCParticle* iMcDaughter = particle->getDaughter(iDaughterNumber)->getMCParticle();
956  const MCParticle* jMcDaughter = particle->getDaughter(jDaughterNumber)->getMCParticle();
957  Particle iTmpPart(iMcDaughter);
958  Particle jTmpPart(jMcDaughter);
959  const Variable::Manager::Var* var = Manager::Instance().getVariable(variablename);
960  auto result_j = var->function(&jTmpPart);
961  auto result_i = var->function(&iTmpPart);
962  double diff = std::numeric_limits<double>::quiet_NaN();
963  if (std::holds_alternative<double>(result_j) && std::holds_alternative<double>(result_i)) {
964  diff = std::get<double>(result_j) - std::get<double>(result_i);
965  } else if (std::holds_alternative<int>(result_j) && std::holds_alternative<int>(result_i)) {
966  diff = std::get<int>(result_j) - std::get<int>(result_i);
967  } else {
968  throw std::runtime_error("Bad variant access");
969  }
970  if (variablename == "phi" or std::regex_match(variablename, std::regex("use.*Frame\\(phi\\)"))) {
971  if (fabs(diff) > M_PI) {
972  if (diff > M_PI) {
973  diff = diff - 2 * M_PI;
974  } else {
975  diff = 2 * M_PI + diff;
976  }
977  }
978  }
979  return diff;
980  }
981  };
982  return func;
983  } else {
984  B2FATAL("Wrong number of arguments for meta function mcDaughterDiffOf");
985  }
986  }
987 
988  Manager::FunctionPtr grandDaughterDiffOf(const std::vector<std::string>& arguments)
989  {
990  if (arguments.size() == 5) {
991  int iDaughterNumber = 0, jDaughterNumber = 0, agrandDaughterNumber = 0, bgrandDaughterNumber = 0;
992  try {
993  iDaughterNumber = Belle2::convertString<int>(arguments[0]);
994  jDaughterNumber = Belle2::convertString<int>(arguments[1]);
995  agrandDaughterNumber = Belle2::convertString<int>(arguments[2]);
996  bgrandDaughterNumber = Belle2::convertString<int>(arguments[3]);
997  } catch (std::invalid_argument&) {
998  B2FATAL("First four arguments of grandDaughterDiffOf meta function must be integers!");
999  }
1000  auto variablename = arguments[4];
1001  auto func = [variablename, iDaughterNumber, jDaughterNumber, agrandDaughterNumber,
1002  bgrandDaughterNumber](const Particle * particle) -> double {
1003  if (particle == nullptr)
1004  return std::numeric_limits<double>::quiet_NaN();
1005  if (iDaughterNumber >= int(particle->getNDaughters()) || jDaughterNumber >= int(particle->getNDaughters()))
1006  return std::numeric_limits<double>::quiet_NaN();
1007  if (agrandDaughterNumber >= int((particle->getDaughter(iDaughterNumber))->getNDaughters()) || bgrandDaughterNumber >= int((particle->getDaughter(jDaughterNumber))->getNDaughters()))
1008  return std::numeric_limits<double>::quiet_NaN();
1009  else
1010  {
1011  const Variable::Manager::Var* var = Manager::Instance().getVariable(variablename);
1012  double diff = std::get<double>(var->function((particle->getDaughter(jDaughterNumber))->getDaughter(
1013  bgrandDaughterNumber))) - std::get<double>(var->function((particle->getDaughter(iDaughterNumber))->getDaughter(
1014  agrandDaughterNumber)));
1015  if (variablename == "phi" or variablename == "clusterPhi" or std::regex_match(variablename, std::regex("use.*Frame\\(phi\\)"))
1016  or std::regex_match(variablename, std::regex("use.*Frame\\(clusterPhi\\)"))) {
1017  if (fabs(diff) > M_PI) {
1018  if (diff > M_PI) {
1019  diff = diff - 2 * M_PI;
1020  } else {
1021  diff = 2 * M_PI + diff;
1022  }
1023  }
1024  }
1025  return diff;
1026  }
1027  };
1028  return func;
1029  } else {
1030  B2FATAL("Wrong number of arguments for meta function grandDaughterDiffOf");
1031  }
1032  }
1033 
1034  Manager::FunctionPtr daughterDiffOfPhi(const std::vector<std::string>& arguments)
1035  {
1036  std::vector<std::string> new_arguments = arguments;
1037  new_arguments.push_back(std::string("phi"));
1038  return daughterDiffOf(new_arguments);
1039  }
1040 
1041  Manager::FunctionPtr mcDaughterDiffOfPhi(const std::vector<std::string>& arguments)
1042  {
1043  std::vector<std::string> new_arguments = arguments;
1044  new_arguments.push_back(std::string("phi"));
1045  return mcDaughterDiffOf(new_arguments);
1046  }
1047 
1048  Manager::FunctionPtr grandDaughterDiffOfPhi(const std::vector<std::string>& arguments)
1049  {
1050  std::vector<std::string> new_arguments = arguments;
1051  new_arguments.push_back(std::string("phi"));
1052  return grandDaughterDiffOf(new_arguments);
1053  }
1054 
1055  Manager::FunctionPtr daughterDiffOfClusterPhi(const std::vector<std::string>& arguments)
1056  {
1057  std::vector<std::string> new_arguments = arguments;
1058  new_arguments.push_back(std::string("clusterPhi"));
1059  return daughterDiffOf(new_arguments);
1060  }
1061 
1062  Manager::FunctionPtr grandDaughterDiffOfClusterPhi(const std::vector<std::string>& arguments)
1063  {
1064  std::vector<std::string> new_arguments = arguments;
1065  new_arguments.push_back(std::string("clusterPhi"));
1066  return grandDaughterDiffOf(new_arguments);
1067  }
1068 
1069  Manager::FunctionPtr daughterDiffOfPhiCMS(const std::vector<std::string>& arguments)
1070  {
1071  std::vector<std::string> new_arguments = arguments;
1072  new_arguments.push_back(std::string("useCMSFrame(phi)"));
1073  return daughterDiffOf(new_arguments);
1074  }
1075 
1076  Manager::FunctionPtr mcDaughterDiffOfPhiCMS(const std::vector<std::string>& arguments)
1077  {
1078  std::vector<std::string> new_arguments = arguments;
1079  new_arguments.push_back(std::string("useCMSFrame(phi)"));
1080  return mcDaughterDiffOf(new_arguments);
1081  }
1082 
1083  Manager::FunctionPtr daughterDiffOfClusterPhiCMS(const std::vector<std::string>& arguments)
1084  {
1085  std::vector<std::string> new_arguments = arguments;
1086  new_arguments.push_back(std::string("useCMSFrame(clusterPhi)"));
1087  return daughterDiffOf(new_arguments);
1088  }
1089 
1090  Manager::FunctionPtr daughterNormDiffOf(const std::vector<std::string>& arguments)
1091  {
1092  if (arguments.size() == 3) {
1093  int iDaughterNumber = 0;
1094  int jDaughterNumber = 0;
1095  try {
1096  iDaughterNumber = Belle2::convertString<int>(arguments[0]);
1097  jDaughterNumber = Belle2::convertString<int>(arguments[1]);
1098  } catch (std::invalid_argument&) {
1099  B2FATAL("First two arguments of daughterDiffOf meta function must be integers!");
1100  }
1101  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[2]);
1102  auto func = [var, iDaughterNumber, jDaughterNumber](const Particle * particle) -> double {
1103  if (particle == nullptr)
1104  return std::numeric_limits<double>::quiet_NaN();
1105  if (iDaughterNumber >= int(particle->getNDaughters()) || jDaughterNumber >= int(particle->getNDaughters()))
1106  return std::numeric_limits<double>::quiet_NaN();
1107  else
1108  {
1109  double iValue, jValue;
1110  if (std::holds_alternative<double>(var->function(particle->getDaughter(jDaughterNumber)))) {
1111  iValue = std::get<double>(var->function(particle->getDaughter(iDaughterNumber)));
1112  jValue = std::get<double>(var->function(particle->getDaughter(jDaughterNumber)));
1113  } else if (std::holds_alternative<int>(var->function(particle->getDaughter(jDaughterNumber)))) {
1114  iValue = std::get<int>(var->function(particle->getDaughter(iDaughterNumber)));
1115  jValue = std::get<int>(var->function(particle->getDaughter(jDaughterNumber)));
1116  } else return std::numeric_limits<double>::quiet_NaN();
1117  return (jValue - iValue) / (jValue + iValue);
1118  }
1119  };
1120  return func;
1121  } else {
1122  B2FATAL("Wrong number of arguments for meta function daughterNormDiffOf");
1123  }
1124  }
1125 
1126  Manager::FunctionPtr daughterMotherDiffOf(const std::vector<std::string>& arguments)
1127  {
1128  if (arguments.size() == 2) {
1129  int daughterNumber = 0;
1130  try {
1131  daughterNumber = Belle2::convertString<int>(arguments[0]);
1132  } catch (std::invalid_argument&) {
1133  B2FATAL("First argument of daughterMotherDiffOf meta function must be integer!");
1134  }
1135  auto variablename = arguments[1];
1136  auto func = [variablename, daughterNumber](const Particle * particle) -> double {
1137  if (particle == nullptr)
1138  return std::numeric_limits<double>::quiet_NaN();
1139  if (daughterNumber >= int(particle->getNDaughters()))
1140  return std::numeric_limits<double>::quiet_NaN();
1141  else
1142  {
1143  const Variable::Manager::Var* var = Manager::Instance().getVariable(variablename);
1144  auto result_mother = var->function(particle);
1145  auto result_daughter = var->function(particle->getDaughter(daughterNumber));
1146  double diff = std::numeric_limits<double>::quiet_NaN();
1147  if (std::holds_alternative<double>(result_mother) && std::holds_alternative<double>(result_daughter)) {
1148  diff = std::get<double>(result_mother) - std::get<double>(result_daughter);
1149  } else if (std::holds_alternative<int>(result_mother) && std::holds_alternative<int>(result_daughter)) {
1150  diff = std::get<int>(result_mother) - std::get<int>(result_daughter);
1151  } else {
1152  throw std::runtime_error("Bad variant access");
1153  }
1154 
1155  if (variablename == "phi" or variablename == "useCMSFrame(phi)") {
1156  if (fabs(diff) > M_PI) {
1157  if (diff > M_PI) {
1158  diff = diff - 2 * M_PI;
1159  } else {
1160  diff = 2 * M_PI + diff;
1161  }
1162  }
1163  }
1164  return diff;
1165  }
1166  };
1167  return func;
1168  } else {
1169  B2FATAL("Wrong number of arguments for meta function daughterMotherDiffOf");
1170  }
1171  }
1172 
1173  Manager::FunctionPtr daughterMotherNormDiffOf(const std::vector<std::string>& arguments)
1174  {
1175  if (arguments.size() == 2) {
1176  int daughterNumber = 0;
1177  try {
1178  daughterNumber = Belle2::convertString<int>(arguments[0]);
1179  } catch (std::invalid_argument&) {
1180  B2FATAL("First argument of daughterMotherDiffOf meta function must be integer!");
1181  }
1182  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[1]);
1183  auto func = [var, daughterNumber](const Particle * particle) -> double {
1184  if (particle == nullptr)
1185  return std::numeric_limits<double>::quiet_NaN();
1186  if (daughterNumber >= int(particle->getNDaughters()))
1187  return std::numeric_limits<double>::quiet_NaN();
1188  else
1189  {
1190  double daughterValue = 0.0, motherValue = 0.0;
1191  if (std::holds_alternative<double>(var->function(particle))) {
1192  daughterValue = std::get<double>(var->function(particle->getDaughter(daughterNumber)));
1193  motherValue = std::get<double>(var->function(particle));
1194  } else if (std::holds_alternative<int>(var->function(particle))) {
1195  daughterValue = std::get<int>(var->function(particle->getDaughter(daughterNumber)));
1196  motherValue = std::get<int>(var->function(particle));
1197  }
1198  return (motherValue - daughterValue) / (motherValue + daughterValue);
1199  }
1200  };
1201  return func;
1202  } else {
1203  B2FATAL("Wrong number of arguments for meta function daughterMotherNormDiffOf");
1204  }
1205  }
1206 
1207  Manager::FunctionPtr daughterAngle(const std::vector<std::string>& arguments)
1208  {
1209  if (arguments.size() == 2 || arguments.size() == 3) {
1210 
1211  auto func = [arguments](const Particle * particle) -> double {
1212  if (particle == nullptr)
1213  return std::numeric_limits<double>::quiet_NaN();
1214 
1215  std::vector<ROOT::Math::PxPyPzEVector> pDaus;
1216  const auto& frame = ReferenceFrame::GetCurrent();
1217 
1218  // Parses the generalized indexes and fetches the 4-momenta of the particles of interest
1219  for (auto& generalizedIndex : arguments)
1220  {
1221  const Particle* dauPart = particle->getParticleFromGeneralizedIndexString(generalizedIndex);
1222  if (dauPart)
1223  pDaus.push_back(frame.getMomentum(dauPart));
1224  else {
1225  B2WARNING("Trying to access a daughter that does not exist. Index = " << generalizedIndex);
1226  return std::numeric_limits<double>::quiet_NaN();
1227  }
1228  }
1229 
1230  // Calculates the angle between the selected particles
1231  if (pDaus.size() == 2)
1232  return B2Vector3D(pDaus[0].Vect()).Angle(B2Vector3D(pDaus[1].Vect()));
1233  else
1234  return B2Vector3D(pDaus[2].Vect()).Angle(B2Vector3D(pDaus[0].Vect() + pDaus[1].Vect()));
1235  };
1236  return func;
1237  } else {
1238  B2FATAL("Wrong number of arguments for meta function daughterAngle");
1239  }
1240  }
1241 
1242  double grandDaughterDecayAngle(const Particle* particle, const std::vector<double>& arguments)
1243  {
1244  if (arguments.size() == 2) {
1245 
1246  if (!particle)
1247  return std::numeric_limits<double>::quiet_NaN();
1248 
1249  int daughterIndex = std::lround(arguments[0]);
1250  if (daughterIndex >= int(particle->getNDaughters()))
1251  return std::numeric_limits<float>::quiet_NaN();
1252  const Particle* dau = particle->getDaughter(daughterIndex);
1253 
1254  int grandDaughterIndex = std::lround(arguments[1]);
1255  if (grandDaughterIndex >= int(dau->getNDaughters()))
1256  return std::numeric_limits<float>::quiet_NaN();
1257 
1258  B2Vector3D boost = dau->get4Vector().BoostToCM();
1259 
1260  ROOT::Math::PxPyPzEVector motherMomentum = - particle->get4Vector();
1261  motherMomentum = ROOT::Math::Boost(boost) * motherMomentum;
1262 
1263  ROOT::Math::PxPyPzEVector grandDaughterMomentum = dau->getDaughter(grandDaughterIndex)->get4Vector();
1264  grandDaughterMomentum = ROOT::Math::Boost(boost) * grandDaughterMomentum;
1265 
1266  return B2Vector3D(motherMomentum.Vect()).Angle(B2Vector3D(grandDaughterMomentum.Vect()));
1267 
1268  } else {
1269  B2FATAL("The variable grandDaughterDecayAngle needs exactly two integers as arguments!");
1270  }
1271  }
1272 
1273  Manager::FunctionPtr mcDaughterAngle(const std::vector<std::string>& arguments)
1274  {
1275  if (arguments.size() == 2 || arguments.size() == 3) {
1276 
1277  auto func = [arguments](const Particle * particle) -> double {
1278  if (particle == nullptr)
1279  return std::numeric_limits<double>::quiet_NaN();
1280 
1281  std::vector<ROOT::Math::PxPyPzEVector> pDaus;
1282  const auto& frame = ReferenceFrame::GetCurrent();
1283 
1284  // Parses the generalized indexes and fetches the 4-momenta of the particles of interest
1285  if (particle->getParticleSource() == Particle::EParticleSourceObject::c_MCParticle) // Check if MCParticle
1286  {
1287  for (auto& generalizedIndex : arguments) {
1288  const MCParticle* mcPart = particle->getMCParticle();
1289  if (mcPart == nullptr)
1290  return std::numeric_limits<double>::quiet_NaN();
1291  const MCParticle* dauMcPart = mcPart->getParticleFromGeneralizedIndexString(generalizedIndex);
1292  if (dauMcPart == nullptr)
1293  return std::numeric_limits<double>::quiet_NaN();
1294 
1295  pDaus.push_back(frame.getMomentum(dauMcPart->get4Vector()));
1296  }
1297  } else
1298  {
1299  for (auto& generalizedIndex : arguments) {
1300  const Particle* dauPart = particle->getParticleFromGeneralizedIndexString(generalizedIndex);
1301  if (dauPart == nullptr)
1302  return std::numeric_limits<double>::quiet_NaN();
1303 
1304  const MCParticle* dauMcPart = dauPart->getMCParticle();
1305  if (dauMcPart == nullptr)
1306  return std::numeric_limits<double>::quiet_NaN();
1307 
1308  pDaus.push_back(frame.getMomentum(dauMcPart->get4Vector()));
1309  }
1310  }
1311 
1312  // Calculates the angle between the selected particles
1313  if (pDaus.size() == 2)
1314  return B2Vector3D(pDaus[0].Vect()).Angle(B2Vector3D(pDaus[1].Vect()));
1315  else
1316  return B2Vector3D(pDaus[2].Vect()).Angle(B2Vector3D(pDaus[0].Vect() + pDaus[1].Vect()));
1317  };
1318  return func;
1319  } else {
1320  B2FATAL("Wrong number of arguments for meta function mcDaughterAngle");
1321  }
1322  }
1323 
1324  double daughterClusterAngleInBetween(const Particle* particle, const std::vector<double>& daughterIndices)
1325  {
1326  if (daughterIndices.size() == 2) {
1327  int daughterIndexi = std::lround(daughterIndices[0]);
1328  int daughterIndexj = std::lround(daughterIndices[1]);
1329  if (std::max(daughterIndexi, daughterIndexj) >= int(particle->getNDaughters())) {
1330  return std::numeric_limits<double>::quiet_NaN();
1331  } else {
1332  const ECLCluster* clusteri = particle->getDaughter(daughterIndexi)->getECLCluster();
1333  const ECLCluster* clusterj = particle->getDaughter(daughterIndexj)->getECLCluster();
1334  if (clusteri and clusterj) {
1335  const auto& frame = ReferenceFrame::GetCurrent();
1336  const ECLCluster::EHypothesisBit clusteriBit = (particle->getDaughter(daughterIndexi))->getECLClusterEHypothesisBit();
1337  const ECLCluster::EHypothesisBit clusterjBit = (particle->getDaughter(daughterIndexj))->getECLClusterEHypothesisBit();
1338  ClusterUtils clusutils;
1339  B2Vector3D pi = frame.getMomentum(clusutils.Get4MomentumFromCluster(clusteri, clusteriBit)).Vect();
1340  B2Vector3D pj = frame.getMomentum(clusutils.Get4MomentumFromCluster(clusterj, clusterjBit)).Vect();
1341  return pi.Angle(pj);
1342  }
1343  return std::numeric_limits<float>::quiet_NaN();
1344  }
1345  } else if (daughterIndices.size() == 3) {
1346  int daughterIndexi = std::lround(daughterIndices[0]);
1347  int daughterIndexj = std::lround(daughterIndices[1]);
1348  int daughterIndexk = std::lround(daughterIndices[2]);
1349  if (std::max(std::max(daughterIndexi, daughterIndexj), daughterIndexk) >= int(particle->getNDaughters())) {
1350  return std::numeric_limits<double>::quiet_NaN();
1351  } else {
1352  const ECLCluster* clusteri = (particle->getDaughter(daughterIndices[0]))->getECLCluster();
1353  const ECLCluster* clusterj = (particle->getDaughter(daughterIndices[1]))->getECLCluster();
1354  const ECLCluster* clusterk = (particle->getDaughter(daughterIndices[2]))->getECLCluster();
1355  if (clusteri and clusterj and clusterk) {
1356  const auto& frame = ReferenceFrame::GetCurrent();
1357  const ECLCluster::EHypothesisBit clusteriBit = (particle->getDaughter(daughterIndices[0]))->getECLClusterEHypothesisBit();
1358  const ECLCluster::EHypothesisBit clusterjBit = (particle->getDaughter(daughterIndices[1]))->getECLClusterEHypothesisBit();
1359  const ECLCluster::EHypothesisBit clusterkBit = (particle->getDaughter(daughterIndices[2]))->getECLClusterEHypothesisBit();
1360  ClusterUtils clusutils;
1361  B2Vector3D pi = frame.getMomentum(clusutils.Get4MomentumFromCluster(clusteri, clusteriBit)).Vect();
1362  B2Vector3D pj = frame.getMomentum(clusutils.Get4MomentumFromCluster(clusterj, clusterjBit)).Vect();
1363  B2Vector3D pk = frame.getMomentum(clusutils.Get4MomentumFromCluster(clusterk, clusterkBit)).Vect();
1364  return pk.Angle(pi + pj);
1365  }
1366  return std::numeric_limits<float>::quiet_NaN();
1367  }
1368  } else {
1369  B2FATAL("Wrong number of arguments for daughterClusterAngleInBetween!");
1370  }
1371  }
1372 
1373  Manager::FunctionPtr daughterInvM(const std::vector<std::string>& arguments)
1374  {
1375  if (arguments.size() > 1) {
1376  auto func = [arguments](const Particle * particle) -> double {
1377  const auto& frame = ReferenceFrame::GetCurrent();
1378  ROOT::Math::PxPyPzEVector pSum;
1379 
1380  for (auto& generalizedIndex : arguments)
1381  {
1382  const Particle* dauPart = particle->getParticleFromGeneralizedIndexString(generalizedIndex);
1383  if (dauPart)
1384  pSum += frame.getMomentum(dauPart);
1385  else {
1386  return std::numeric_limits<float>::quiet_NaN();
1387  }
1388  }
1389  return pSum.M();
1390  };
1391  return func;
1392  } else {
1393  B2FATAL("Wrong number of arguments for meta function daughterInvM. At least two integers are needed.");
1394  }
1395  }
1396 
1397  Manager::FunctionPtr modulo(const std::vector<std::string>& arguments)
1398  {
1399  if (arguments.size() == 2) {
1400  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
1401  int divideBy = 1;
1402  try {
1403  divideBy = Belle2::convertString<int>(arguments[1]);
1404  } catch (std::invalid_argument&) {
1405  B2FATAL("Second argument of modulo meta function must be integer!");
1406  }
1407  auto func = [var, divideBy](const Particle * particle) -> int {
1408  auto var_result = var->function(particle);
1409  if (std::holds_alternative<double>(var_result))
1410  {
1411  return int(std::get<double>(var_result)) % divideBy;
1412  } else if (std::holds_alternative<int>(var_result))
1413  {
1414  return std::get<int>(var_result) % divideBy;
1415  } else if (std::holds_alternative<bool>(var_result))
1416  {
1417  return int(std::get<bool>(var_result)) % divideBy;
1418  } else return 0;
1419  };
1420  return func;
1421  } else {
1422  B2FATAL("Wrong number of arguments for meta function modulo");
1423  }
1424  }
1425 
1426  Manager::FunctionPtr isNAN(const std::vector<std::string>& arguments)
1427  {
1428  if (arguments.size() == 1) {
1429  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
1430 
1431  auto func = [var](const Particle * particle) -> bool { return std::isnan(std::get<double>(var->function(particle))); };
1432  return func;
1433  } else {
1434  B2FATAL("Wrong number of arguments for meta function isNAN");
1435  }
1436  }
1437 
1438  Manager::FunctionPtr ifNANgiveX(const std::vector<std::string>& arguments)
1439  {
1440  if (arguments.size() == 2) {
1441  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
1442  double defaultOutput;
1443  try {
1444  defaultOutput = Belle2::convertString<double>(arguments[1]);
1445  } catch (std::invalid_argument&) {
1446  B2FATAL("The second argument of ifNANgiveX meta function must be a number!");
1447  }
1448  auto func = [var, defaultOutput](const Particle * particle) -> double {
1449  double output = std::get<double>(var->function(particle));
1450  if (std::isnan(output)) return defaultOutput;
1451  else return output;
1452  };
1453  return func;
1454  } else {
1455  B2FATAL("Wrong number of arguments for meta function ifNANgiveX");
1456  }
1457  }
1458 
1459  Manager::FunctionPtr isInfinity(const std::vector<std::string>& arguments)
1460  {
1461  if (arguments.size() == 1) {
1462  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
1463 
1464  auto func = [var](const Particle * particle) -> bool { return std::isinf(std::get<double>(var->function(particle))); };
1465  return func;
1466  } else {
1467  B2FATAL("Wrong number of arguments for meta function isInfinity");
1468  }
1469  }
1470 
1471  Manager::FunctionPtr unmask(const std::vector<std::string>& arguments)
1472  {
1473  if (arguments.size() >= 2) {
1474  // get the function pointer of variable to be unmasked
1475  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
1476 
1477  // get the final mask which summarize all the input masks
1478  int finalMask = 0;
1479  for (size_t i = 1; i < arguments.size(); ++i) {
1480  try {
1481  finalMask |= Belle2::convertString<int>(arguments[i]);
1482  } catch (std::invalid_argument&) {
1483  B2FATAL("The input flags to meta function unmask() should be integer!");
1484  return nullptr;
1485  }
1486  }
1487 
1488  // unmask the variable
1489  auto func = [var, finalMask](const Particle * particle) -> double {
1490  int value = 0;
1491  auto var_result = var->function(particle);
1492  if (std::holds_alternative<double>(var_result))
1493  {
1494  // judge if the value is nan before unmasking
1495  if (std::isnan(std::get<double>(var_result))) {
1496  return std::numeric_limits<double>::quiet_NaN();
1497  }
1498  value = int(std::get<double>(var_result));
1499  } else if (std::holds_alternative<int>(var_result))
1500  {
1501  value = std::get<int>(var_result);
1502  }
1503 
1504  // apply the final mask
1505  value &= (~finalMask);
1506 
1507  return value;
1508  };
1509  return func;
1510 
1511  } else {
1512  B2FATAL("Meta function unmask needs at least two arguments!");
1513  }
1514  }
1515 
1516  Manager::FunctionPtr conditionalVariableSelector(const std::vector<std::string>& arguments)
1517  {
1518  if (arguments.size() == 3) {
1519 
1520  std::string cutString = arguments[0];
1521  std::shared_ptr<Variable::Cut> cut = std::shared_ptr<Variable::Cut>(Variable::Cut::compile(cutString));
1522 
1523  const Variable::Manager::Var* variableIfTrue = Manager::Instance().getVariable(arguments[1]);
1524  const Variable::Manager::Var* variableIfFalse = Manager::Instance().getVariable(arguments[2]);
1525 
1526  auto func = [cut, variableIfTrue, variableIfFalse](const Particle * particle) -> double {
1527  if (particle == nullptr)
1528  return std::numeric_limits<float>::quiet_NaN();
1529  if (cut->check(particle))
1530  {
1531  auto var_result = variableIfTrue->function(particle);
1532  if (std::holds_alternative<double>(var_result)) {
1533  return std::get<double>(var_result);
1534  } else if (std::holds_alternative<int>(var_result)) {
1535  return std::get<int>(var_result);
1536  } else if (std::holds_alternative<bool>(var_result)) {
1537  return std::get<bool>(var_result);
1538  } else return std::numeric_limits<double>::quiet_NaN();
1539  } else
1540  {
1541  auto var_result = variableIfFalse->function(particle);
1542  if (std::holds_alternative<double>(var_result)) {
1543  return std::get<double>(var_result);
1544  } else if (std::holds_alternative<int>(var_result)) {
1545  return std::get<int>(var_result);
1546  } else if (std::holds_alternative<bool>(var_result)) {
1547  return std::get<bool>(var_result);
1548  } else return std::numeric_limits<double>::quiet_NaN();
1549  }
1550  };
1551  return func;
1552 
1553  } else {
1554  B2FATAL("Wrong number of arguments for meta function conditionalVariableSelector");
1555  }
1556  }
1557 
1558  Manager::FunctionPtr pValueCombination(const std::vector<std::string>& arguments)
1559  {
1560  if (arguments.size() > 0) {
1561  std::vector<const Variable::Manager::Var*> variables;
1562  for (auto& argument : arguments)
1563  variables.push_back(Manager::Instance().getVariable(argument));
1564 
1565  auto func = [variables, arguments](const Particle * particle) -> double {
1566  double pValueProduct = 1.;
1567  for (auto variable : variables)
1568  {
1569  double pValue = std::get<double>(variable->function(particle));
1570  if (pValue < 0)
1571  return -1;
1572  else
1573  pValueProduct *= pValue;
1574  }
1575  double pValueSum = 1.;
1576  double factorial = 1.;
1577  for (unsigned int i = 1; i < arguments.size(); ++i)
1578  {
1579  factorial *= i;
1580  pValueSum += pow(-std::log(pValueProduct), i) / factorial;
1581  }
1582  return pValueProduct * pValueSum;
1583  };
1584  return func;
1585  } else {
1586  B2FATAL("Wrong number of arguments for meta function pValueCombination");
1587  }
1588  }
1589 
1590  Manager::FunctionPtr abs(const std::vector<std::string>& arguments)
1591  {
1592  if (arguments.size() == 1) {
1593  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
1594  auto func = [var](const Particle * particle) -> double {
1595  auto var_result = var->function(particle);
1596  if (std::holds_alternative<double>(var_result))
1597  {
1598  return std::abs(std::get<double>(var_result));
1599  } else if (std::holds_alternative<int>(var_result))
1600  {
1601  return std::abs(std::get<int>(var_result));
1602  } else return std::numeric_limits<double>::quiet_NaN();
1603  };
1604  return func;
1605  } else {
1606  B2FATAL("Wrong number of arguments for meta function abs");
1607  }
1608  }
1609 
1610  Manager::FunctionPtr max(const std::vector<std::string>& arguments)
1611  {
1612  if (arguments.size() == 2) {
1613  const Variable::Manager::Var* var1 = Manager::Instance().getVariable(arguments[0]);
1614  const Variable::Manager::Var* var2 = Manager::Instance().getVariable(arguments[1]);
1615 
1616  if (!var1 or !var2)
1617  B2FATAL("One or both of the used variables doesn't exist!");
1618 
1619  auto func = [var1, var2](const Particle * particle) -> double {
1620  double val1, val2;
1621  auto var_result1 = var1->function(particle);
1622  auto var_result2 = var2->function(particle);
1623  if (std::holds_alternative<double>(var_result1))
1624  {
1625  val1 = std::get<double>(var_result1);
1626  } else if (std::holds_alternative<int>(var_result1))
1627  {
1628  val1 = std::get<int>(var_result1);
1629  }
1630  if (std::holds_alternative<double>(var_result2))
1631  {
1632  val2 = std::get<double>(var_result2);
1633  } else if (std::holds_alternative<int>(var_result2))
1634  {
1635  val2 = std::get<int>(var_result2);
1636  }
1637  return std::max(val1, val2);
1638  };
1639  return func;
1640  } else {
1641  B2FATAL("Wrong number of arguments for meta function max");
1642  }
1643  }
1644 
1645  Manager::FunctionPtr min(const std::vector<std::string>& arguments)
1646  {
1647  if (arguments.size() == 2) {
1648  const Variable::Manager::Var* var1 = Manager::Instance().getVariable(arguments[0]);
1649  const Variable::Manager::Var* var2 = Manager::Instance().getVariable(arguments[1]);
1650 
1651  if (!var1 or !var2)
1652  B2FATAL("One or both of the used variables doesn't exist!");
1653 
1654  auto func = [var1, var2](const Particle * particle) -> double {
1655  double val1, val2;
1656  auto var_result1 = var1->function(particle);
1657  auto var_result2 = var2->function(particle);
1658  if (std::holds_alternative<double>(var_result1))
1659  {
1660  val1 = std::get<double>(var_result1);
1661  } else if (std::holds_alternative<int>(var_result1))
1662  {
1663  val1 = std::get<int>(var_result1);
1664  }
1665  if (std::holds_alternative<double>(var_result2))
1666  {
1667  val2 = std::get<double>(var_result2);
1668  } else if (std::holds_alternative<int>(var_result2))
1669  {
1670  val2 = std::get<int>(var_result2);
1671  }
1672  return std::min(val1, val2);
1673  };
1674  return func;
1675  } else {
1676  B2FATAL("Wrong number of arguments for meta function min");
1677  }
1678  }
1679 
1680  Manager::FunctionPtr sin(const std::vector<std::string>& arguments)
1681  {
1682  if (arguments.size() == 1) {
1683  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
1684  auto func = [var](const Particle * particle) -> double {
1685  auto var_result = var->function(particle);
1686  if (std::holds_alternative<double>(var_result))
1687  return std::sin(std::get<double>(var_result));
1688  else if (std::holds_alternative<int>(var_result))
1689  return std::sin(std::get<int>(var_result));
1690  else return std::numeric_limits<double>::quiet_NaN();
1691  };
1692  return func;
1693  } else {
1694  B2FATAL("Wrong number of arguments for meta function sin");
1695  }
1696  }
1697 
1698  Manager::FunctionPtr asin(const std::vector<std::string>& arguments)
1699  {
1700  if (arguments.size() == 1) {
1701  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
1702  auto func = [var](const Particle * particle) -> double {
1703  auto var_result = var->function(particle);
1704  if (std::holds_alternative<double>(var_result))
1705  return std::asin(std::get<double>(var_result));
1706  else if (std::holds_alternative<int>(var_result))
1707  return std::asin(std::get<int>(var_result));
1708  else return std::numeric_limits<double>::quiet_NaN();
1709  };
1710  return func;
1711  } else {
1712  B2FATAL("Wrong number of arguments for meta function asin");
1713  }
1714  }
1715 
1716  Manager::FunctionPtr cos(const std::vector<std::string>& arguments)
1717  {
1718  if (arguments.size() == 1) {
1719  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
1720  auto func = [var](const Particle * particle) -> double {
1721  auto var_result = var->function(particle);
1722  if (std::holds_alternative<double>(var_result))
1723  return std::cos(std::get<double>(var_result));
1724  else if (std::holds_alternative<int>(var_result))
1725  return std::cos(std::get<int>(var_result));
1726  else return std::numeric_limits<double>::quiet_NaN();
1727  };
1728  return func;
1729  } else {
1730  B2FATAL("Wrong number of arguments for meta function cos");
1731  }
1732  }
1733 
1734  Manager::FunctionPtr acos(const std::vector<std::string>& arguments)
1735  {
1736  if (arguments.size() == 1) {
1737  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
1738  auto func = [var](const Particle * particle) -> double {
1739  auto var_result = var->function(particle);
1740  if (std::holds_alternative<double>(var_result))
1741  return std::acos(std::get<double>(var_result));
1742  else if (std::holds_alternative<int>(var_result))
1743  return std::acos(std::get<int>(var_result));
1744  else return std::numeric_limits<double>::quiet_NaN();
1745  };
1746  return func;
1747  } else {
1748  B2FATAL("Wrong number of arguments for meta function acos");
1749  }
1750  }
1751 
1752  Manager::FunctionPtr tan(const std::vector<std::string>& arguments)
1753  {
1754  if (arguments.size() == 1) {
1755  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
1756  auto func = [var](const Particle * particle) -> double { return std::tan(std::get<double>(var->function(particle))); };
1757  return func;
1758  } else {
1759  B2FATAL("Wrong number of arguments for meta function tan");
1760  }
1761  }
1762 
1763  Manager::FunctionPtr atan(const std::vector<std::string>& arguments)
1764  {
1765  if (arguments.size() == 1) {
1766  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
1767  auto func = [var](const Particle * particle) -> double { return std::atan(std::get<double>(var->function(particle))); };
1768  return func;
1769  } else {
1770  B2FATAL("Wrong number of arguments for meta function atan");
1771  }
1772  }
1773 
1774  Manager::FunctionPtr exp(const std::vector<std::string>& arguments)
1775  {
1776  if (arguments.size() == 1) {
1777  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
1778  auto func = [var](const Particle * particle) -> double {
1779  auto var_result = var->function(particle);
1780  if (std::holds_alternative<double>(var_result))
1781  return std::exp(std::get<double>(var_result));
1782  else if (std::holds_alternative<int>(var_result))
1783  return std::exp(std::get<int>(var_result));
1784  else return std::numeric_limits<double>::quiet_NaN();
1785  };
1786  return func;
1787  } else {
1788  B2FATAL("Wrong number of arguments for meta function exp");
1789  }
1790  }
1791 
1792  Manager::FunctionPtr log(const std::vector<std::string>& arguments)
1793  {
1794  if (arguments.size() == 1) {
1795  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
1796  auto func = [var](const Particle * particle) -> double {
1797  auto var_result = var->function(particle);
1798  if (std::holds_alternative<double>(var_result))
1799  return std::log(std::get<double>(var_result));
1800  else if (std::holds_alternative<int>(var_result))
1801  return std::log(std::get<int>(var_result));
1802  else return std::numeric_limits<double>::quiet_NaN();
1803  };
1804  return func;
1805  } else {
1806  B2FATAL("Wrong number of arguments for meta function log");
1807  }
1808  }
1809 
1810  Manager::FunctionPtr log10(const std::vector<std::string>& arguments)
1811  {
1812  if (arguments.size() == 1) {
1813  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
1814  auto func = [var](const Particle * particle) -> double {
1815  auto var_result = var->function(particle);
1816  if (std::holds_alternative<double>(var_result))
1817  return std::log10(std::get<double>(var_result));
1818  else if (std::holds_alternative<int>(var_result))
1819  return std::log10(std::get<int>(var_result));
1820  else return std::numeric_limits<double>::quiet_NaN();
1821  };
1822  return func;
1823  } else {
1824  B2FATAL("Wrong number of arguments for meta function log10");
1825  }
1826  }
1827 
1828  Manager::FunctionPtr originalParticle(const std::vector<std::string>& arguments)
1829  {
1830  if (arguments.size() == 1) {
1831  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
1832  auto func = [var](const Particle * particle) -> double {
1833  if (particle == nullptr)
1834  return std::numeric_limits<float>::quiet_NaN();
1835 
1836  StoreArray<Particle> particles;
1837  if (!particle->hasExtraInfo("original_index"))
1838  return std::numeric_limits<float>::quiet_NaN();
1839 
1840  auto originalParticle = particles[particle->getExtraInfo("original_index")];
1841  if (!originalParticle)
1842  return std::numeric_limits<float>::quiet_NaN();
1843  auto var_result = var->function(originalParticle);
1844  if (std::holds_alternative<double>(var_result))
1845  {
1846  return std::get<double>(var_result);
1847  } else if (std::holds_alternative<int>(var_result))
1848  {
1849  return std::get<int>(var_result);
1850  } else if (std::holds_alternative<bool>(var_result))
1851  {
1852  return std::get<bool>(var_result);
1853  } else return std::numeric_limits<double>::quiet_NaN();
1854  };
1855  return func;
1856  } else {
1857  B2FATAL("Wrong number of arguments for meta function originalParticle");
1858  }
1859  }
1860 
1861  Manager::FunctionPtr daughter(const std::vector<std::string>& arguments)
1862  {
1863  if (arguments.size() == 2) {
1864  int daughterNumber = 0;
1865  try {
1866  daughterNumber = Belle2::convertString<int>(arguments[0]);
1867  } catch (std::invalid_argument&) {
1868  B2FATAL("First argument of daughter meta function must be integer!");
1869  }
1870  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[1]);
1871  auto func = [var, daughterNumber](const Particle * particle) -> double {
1872  if (particle == nullptr)
1873  return std::numeric_limits<float>::quiet_NaN();
1874  if (daughterNumber >= int(particle->getNDaughters()))
1875  return std::numeric_limits<float>::quiet_NaN();
1876  else
1877  {
1878  auto var_result = var->function(particle->getDaughter(daughterNumber));
1879  if (std::holds_alternative<double>(var_result)) {
1880  return std::get<double>(var_result);
1881  } else if (std::holds_alternative<int>(var_result)) {
1882  return std::get<int>(var_result);
1883  } else if (std::holds_alternative<bool>(var_result)) {
1884  return std::get<bool>(var_result);
1885  } else return std::numeric_limits<double>::quiet_NaN();
1886  }
1887  };
1888  return func;
1889  } else {
1890  B2FATAL("Wrong number of arguments for meta function daughter");
1891  }
1892  }
1893 
1894  Manager::FunctionPtr originalDaughter(const std::vector<std::string>& arguments)
1895  {
1896  if (arguments.size() == 2) {
1897  int daughterNumber = 0;
1898  try {
1899  daughterNumber = Belle2::convertString<int>(arguments[0]);
1900  } catch (std::invalid_argument&) {
1901  B2FATAL("First argument of daughter meta function must be integer!");
1902  }
1903  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[1]);
1904  auto func = [var, daughterNumber](const Particle * particle) -> double {
1905  if (particle == nullptr)
1906  return std::numeric_limits<float>::quiet_NaN();
1907  if (daughterNumber >= int(particle->getNDaughters()))
1908  return std::numeric_limits<float>::quiet_NaN();
1909  else
1910  {
1911  StoreArray<Particle> particles;
1912  if (!particle->getDaughter(daughterNumber)->hasExtraInfo("original_index"))
1913  return std::numeric_limits<float>::quiet_NaN();
1914  auto originalDaughter = particles[particle->getDaughter(daughterNumber)->getExtraInfo("original_index")];
1915  if (!originalDaughter)
1916  return std::numeric_limits<float>::quiet_NaN();
1917 
1918  auto var_result = var->function(originalDaughter);
1919  if (std::holds_alternative<double>(var_result)) {
1920  return std::get<double>(var_result);
1921  } else if (std::holds_alternative<int>(var_result)) {
1922  return std::get<int>(var_result);
1923  } else if (std::holds_alternative<bool>(var_result)) {
1924  return std::get<bool>(var_result);
1925  } else return std::numeric_limits<double>::quiet_NaN();
1926  }
1927  };
1928  return func;
1929  } else {
1930  B2FATAL("Wrong number of arguments for meta function daughter");
1931  }
1932  }
1933 
1934  Manager::FunctionPtr mcDaughter(const std::vector<std::string>& arguments)
1935  {
1936  if (arguments.size() == 2) {
1937  int daughterNumber = 0;
1938  try {
1939  daughterNumber = Belle2::convertString<int>(arguments[0]);
1940  } catch (std::invalid_argument&) {
1941  B2FATAL("First argument of mcDaughter meta function must be integer!");
1942  }
1943  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[1]);
1944  auto func = [var, daughterNumber](const Particle * particle) -> double {
1945  if (particle == nullptr)
1946  return std::numeric_limits<float>::quiet_NaN();
1947  if (particle->getMCParticle()) // has MC match or is MCParticle
1948  {
1949  if (daughterNumber >= int(particle->getMCParticle()->getNDaughters())) {
1950  return std::numeric_limits<float>::quiet_NaN();
1951  }
1952  Particle tempParticle = Particle(particle->getMCParticle()->getDaughters().at(daughterNumber));
1953  auto var_result = var->function(&tempParticle);
1954  if (std::holds_alternative<double>(var_result)) {
1955  return std::get<double>(var_result);
1956  } else if (std::holds_alternative<int>(var_result)) {
1957  return std::get<int>(var_result);
1958  } else if (std::holds_alternative<bool>(var_result)) {
1959  return std::get<bool>(var_result);
1960  } else return std::numeric_limits<double>::quiet_NaN();
1961  } else
1962  {
1963  return std::numeric_limits<float>::quiet_NaN();
1964  }
1965  };
1966  return func;
1967  } else {
1968  B2FATAL("Wrong number of arguments for meta function mcDaughter");
1969  }
1970  }
1971 
1972  Manager::FunctionPtr mcMother(const std::vector<std::string>& arguments)
1973  {
1974  if (arguments.size() == 1) {
1975  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
1976  auto func = [var](const Particle * particle) -> double {
1977  if (particle == nullptr)
1978  return std::numeric_limits<float>::quiet_NaN();
1979  if (particle->getMCParticle()) // has MC match or is MCParticle
1980  {
1981  if (particle->getMCParticle()->getMother() == nullptr) {
1982  return std::numeric_limits<float>::quiet_NaN();
1983  }
1984  Particle tempParticle = Particle(particle->getMCParticle()->getMother());
1985  auto var_result = var->function(&tempParticle);
1986  if (std::holds_alternative<double>(var_result)) {
1987  return std::get<double>(var_result);
1988  } else if (std::holds_alternative<int>(var_result)) {
1989  return std::get<int>(var_result);
1990  } else if (std::holds_alternative<bool>(var_result)) {
1991  return std::get<bool>(var_result);
1992  } else return std::numeric_limits<double>::quiet_NaN();
1993  } else
1994  {
1995  return std::numeric_limits<float>::quiet_NaN();
1996  }
1997  };
1998  return func;
1999  } else {
2000  B2FATAL("Wrong number of arguments for meta function mcMother");
2001  }
2002  }
2003 
2004  Manager::FunctionPtr genParticle(const std::vector<std::string>& arguments)
2005  {
2006  if (arguments.size() == 2) {
2007  int particleNumber = 0;
2008  try {
2009  particleNumber = Belle2::convertString<int>(arguments[0]);
2010  } catch (std::invalid_argument&) {
2011  B2FATAL("First argument of genParticle meta function must be integer!");
2012  }
2013  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[1]);
2014 
2015  auto func = [var, particleNumber](const Particle*) -> double {
2016  StoreArray<MCParticle> mcParticles("MCParticles");
2017  if (particleNumber >= mcParticles.getEntries())
2018  {
2019  return std::numeric_limits<float>::quiet_NaN();
2020  }
2021 
2022  MCParticle* mcParticle = mcParticles[particleNumber];
2023  Particle part = Particle(mcParticle);
2024  auto var_result = var->function(&part);
2025  if (std::holds_alternative<double>(var_result))
2026  {
2027  return std::get<double>(var_result);
2028  } else if (std::holds_alternative<int>(var_result))
2029  {
2030  return std::get<int>(var_result);
2031  } else if (std::holds_alternative<bool>(var_result))
2032  {
2033  return std::get<bool>(var_result);
2034  } else return std::numeric_limits<double>::quiet_NaN();
2035  };
2036  return func;
2037  } else {
2038  B2FATAL("Wrong number of arguments for meta function genParticle");
2039  }
2040  }
2041 
2042  Manager::FunctionPtr genUpsilon4S(const std::vector<std::string>& arguments)
2043  {
2044  if (arguments.size() == 1) {
2045  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
2046 
2047  auto func = [var](const Particle*) -> double {
2048  StoreArray<MCParticle> mcParticles("MCParticles");
2049  if (mcParticles.getEntries() == 0)
2050  {
2051  return std::numeric_limits<float>::quiet_NaN();
2052  }
2053 
2054  MCParticle* mcUpsilon4S = mcParticles[0];
2055  if (mcUpsilon4S->getPDG() != 300553)
2056  {
2057  return std::numeric_limits<float>::quiet_NaN();
2058  }
2059 
2060  Particle upsilon4S = Particle(mcUpsilon4S);
2061  auto var_result = var->function(&upsilon4S);
2062  if (std::holds_alternative<double>(var_result))
2063  {
2064  return std::get<double>(var_result);
2065  } else if (std::holds_alternative<int>(var_result))
2066  {
2067  return std::get<int>(var_result);
2068  } else if (std::holds_alternative<bool>(var_result))
2069  {
2070  return std::get<bool>(var_result);
2071  } else return std::numeric_limits<double>::quiet_NaN();
2072  };
2073  return func;
2074  } else {
2075  B2FATAL("Wrong number of arguments for meta function genUpsilon4S");
2076  }
2077  }
2078 
2079  Manager::FunctionPtr getVariableByRank(const std::vector<std::string>& arguments)
2080  {
2081  if (arguments.size() == 4) {
2082  std::string listName = arguments[0];
2083  std::string rankedVariableName = arguments[1];
2084  std::string returnVariableName = arguments[2];
2085  std::string extraInfoName = rankedVariableName + "_rank";
2086  int rank = 1;
2087  try {
2088  rank = Belle2::convertString<int>(arguments[3]);
2089  } catch (std::invalid_argument&) {
2090  B2ERROR("3rd argument of getVariableByRank meta function (Rank) must be an integer!");
2091  return nullptr;
2092  }
2093 
2094  const Variable::Manager::Var* var = Manager::Instance().getVariable(returnVariableName);
2095  auto func = [var, rank, extraInfoName, listName](const Particle*)-> double {
2096  StoreObjPtr<ParticleList> list(listName);
2097 
2098  const unsigned int numParticles = list->getListSize();
2099  for (unsigned int i = 0; i < numParticles; i++)
2100  {
2101  const Particle* p = list->getParticle(i);
2102  if (p->getExtraInfo(extraInfoName) == rank) {
2103  auto var_result = var->function(p);
2104  if (std::holds_alternative<double>(var_result)) {
2105  return std::get<double>(var_result);
2106  } else if (std::holds_alternative<int>(var_result)) {
2107  return std::get<int>(var_result);
2108  } else if (std::holds_alternative<bool>(var_result)) {
2109  return std::get<bool>(var_result);
2110  } else return std::numeric_limits<double>::quiet_NaN();
2111  }
2112  }
2113  // return 0;
2114  return std::numeric_limits<double>::signaling_NaN();
2115  };
2116  return func;
2117  } else {
2118  B2FATAL("Wrong number of arguments for meta function getVariableByRank");
2119  }
2120  }
2121 
2122  Manager::FunctionPtr countInList(const std::vector<std::string>& arguments)
2123  {
2124  if (arguments.size() == 1 or arguments.size() == 2) {
2125 
2126  std::string listName = arguments[0];
2127  std::string cutString = "";
2128 
2129  if (arguments.size() == 2) {
2130  cutString = arguments[1];
2131  }
2132 
2133  std::shared_ptr<Variable::Cut> cut = std::shared_ptr<Variable::Cut>(Variable::Cut::compile(cutString));
2134 
2135  auto func = [listName, cut](const Particle*) -> int {
2136 
2137  StoreObjPtr<ParticleList> list(listName);
2138  int sum = 0;
2139  for (unsigned int i = 0; i < list->getListSize(); i++)
2140  {
2141  const Particle* particle = list->getParticle(i);
2142  if (cut->check(particle)) {
2143  sum++;
2144  }
2145  }
2146  return sum;
2147  };
2148  return func;
2149  } else {
2150  B2FATAL("Wrong number of arguments for meta function countInList");
2151  }
2152  }
2153 
2154  Manager::FunctionPtr veto(const std::vector<std::string>& arguments)
2155  {
2156  if (arguments.size() == 2 or arguments.size() == 3) {
2157 
2158  std::string roeListName = arguments[0];
2159  std::string cutString = arguments[1];
2160  int pdgCode = Const::electron.getPDGCode();
2161  if (arguments.size() == 2) {
2162  B2INFO("Use pdgCode of electron as default in meta variable veto, other arguments: " << roeListName << ", " << cutString);
2163  } else {
2164  try {
2165  pdgCode = Belle2::convertString<int>(arguments[2]);;
2166  } catch (std::invalid_argument&) {
2167  B2FATAL("Third argument of veto meta function must be integer!");
2168  }
2169  }
2170 
2172  std::shared_ptr<Variable::Cut> cut = std::shared_ptr<Variable::Cut>(Variable::Cut::compile(cutString));
2173 
2174  auto func = [roeListName, cut, pdgCode, flavourType](const Particle * particle) -> bool {
2175  StoreObjPtr<ParticleList> roeList(roeListName);
2176  ROOT::Math::PxPyPzEVector vec = particle->get4Vector();
2177  for (unsigned int i = 0; i < roeList->getListSize(); i++)
2178  {
2179  const Particle* roeParticle = roeList->getParticle(i);
2180  if (not particle->overlapsWith(roeParticle)) {
2181  ROOT::Math::PxPyPzEVector tempCombination = roeParticle->get4Vector() + vec;
2182  std::vector<int> indices = { particle->getArrayIndex(), roeParticle->getArrayIndex() };
2183  Particle tempParticle = Particle(tempCombination, pdgCode, flavourType, indices, particle->getArrayPointer());
2184  if (cut->check(&tempParticle)) {
2185  return 1;
2186  }
2187  }
2188  }
2189  return 0;
2190  };
2191  return func;
2192  } else {
2193  B2FATAL("Wrong number of arguments for meta function veto");
2194  }
2195  }
2196 
2197  Manager::FunctionPtr countDaughters(const std::vector<std::string>& arguments)
2198  {
2199  if (arguments.size() == 1) {
2200  std::string cutString = arguments[0];
2201  std::shared_ptr<Variable::Cut> cut = std::shared_ptr<Variable::Cut>(Variable::Cut::compile(cutString));
2202  auto func = [cut](const Particle * particle) -> int {
2203  int n = 0;
2204  for (auto& daughter : particle->getDaughters())
2205  {
2206  if (cut->check(daughter))
2207  ++n;
2208  }
2209  return n;
2210  };
2211  return func;
2212  } else {
2213  B2FATAL("Wrong number of arguments for meta function countDaughters");
2214  }
2215  }
2216 
2217  Manager::FunctionPtr countFSPDaughters(const std::vector<std::string>& arguments)
2218  {
2219  if (arguments.size() == 1) {
2220  std::string cutString = arguments[0];
2221  std::shared_ptr<Variable::Cut> cut = std::shared_ptr<Variable::Cut>(Variable::Cut::compile(cutString));
2222  auto func = [cut](const Particle * particle) -> int {
2223 
2224  std::vector<const Particle*> fspDaughters;
2225  particle->fillFSPDaughters(fspDaughters);
2226 
2227  int n = 0;
2228  for (auto& daughter : fspDaughters)
2229  {
2230  if (cut->check(daughter))
2231  ++n;
2232  }
2233  return n;
2234  };
2235  return func;
2236  } else {
2237  B2FATAL("Wrong number of arguments for meta function countFSPDaughters");
2238  }
2239  }
2240 
2241  Manager::FunctionPtr countDescendants(const std::vector<std::string>& arguments)
2242  {
2243  if (arguments.size() == 1) {
2244  std::string cutString = arguments[0];
2245  std::shared_ptr<Variable::Cut> cut = std::shared_ptr<Variable::Cut>(Variable::Cut::compile(cutString));
2246  auto func = [cut](const Particle * particle) -> int {
2247 
2248  std::vector<const Particle*> allDaughters;
2249  particle->fillAllDaughters(allDaughters);
2250 
2251  int n = 0;
2252  for (auto& daughter : allDaughters)
2253  {
2254  if (cut->check(daughter))
2255  ++n;
2256  }
2257  return n;
2258  };
2259  return func;
2260  } else {
2261  B2FATAL("Wrong number of arguments for meta function countDescendants");
2262  }
2263  }
2264 
2265  Manager::FunctionPtr numberOfNonOverlappingParticles(const std::vector<std::string>& arguments)
2266  {
2267 
2268  auto func = [arguments](const Particle * particle) -> int {
2269 
2270  int _numberOfNonOverlappingParticles = 0;
2271  for (const auto& listName : arguments)
2272  {
2273  StoreObjPtr<ParticleList> list(listName);
2274  if (not list.isValid()) {
2275  B2FATAL("Invalid list named " << listName << " encountered in numberOfNonOverlappingParticles.");
2276  }
2277  for (unsigned int i = 0; i < list->getListSize(); i++) {
2278  const Particle* p = list->getParticle(i);
2279  if (not particle->overlapsWith(p)) {
2280  _numberOfNonOverlappingParticles++;
2281  }
2282  }
2283  }
2284  return _numberOfNonOverlappingParticles;
2285  };
2286 
2287  return func;
2288 
2289  }
2290 
2291  Manager::FunctionPtr matchedMC(const std::vector<std::string>& arguments)
2292  {
2293  if (arguments.size() == 1) {
2294  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
2295  auto func = [var](const Particle * particle) -> double {
2296  const MCParticle* mcp = particle->getMCParticle();
2297  if (!mcp) // Has no MC match and is no MCParticle
2298  {
2299  return std::numeric_limits<float>::quiet_NaN();
2300  }
2301  Particle tmpPart(mcp);
2302  auto var_result = var->function(&tmpPart);
2303  if (std::holds_alternative<double>(var_result))
2304  {
2305  return std::get<double>(var_result);
2306  } else if (std::holds_alternative<int>(var_result))
2307  {
2308  return std::get<int>(var_result);
2309  } else if (std::holds_alternative<bool>(var_result))
2310  {
2311  return std::get<bool>(var_result);
2312  } else return std::numeric_limits<double>::quiet_NaN();
2313  };
2314  return func;
2315  } else {
2316  B2FATAL("Wrong number of arguments for meta function matchedMC");
2317  }
2318  }
2319 
2320  Manager::FunctionPtr clusterBestMatchedMCParticle(const std::vector<std::string>& arguments)
2321  {
2322  if (arguments.size() == 1) {
2323  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
2324 
2325  auto func = [var](const Particle * particle) -> double {
2326 
2327  const ECLCluster* cluster = particle->getECLCluster();
2328  if (!cluster) return std::numeric_limits<double>::quiet_NaN();
2329 
2330  auto mcps = cluster->getRelationsTo<MCParticle>();
2331  if (mcps.size() == 0) return std::numeric_limits<double>::quiet_NaN();
2332 
2333  std::vector<std::pair<double, int>> weightsAndIndices;
2334  for (unsigned int i = 0; i < mcps.size(); ++i)
2335  weightsAndIndices.emplace_back(mcps.weight(i), i);
2336 
2337  // sort descending by weight
2338  std::sort(weightsAndIndices.begin(), weightsAndIndices.end(),
2339  ValueIndexPairSorting::higherPair<decltype(weightsAndIndices)::value_type>);
2340 
2341  // cppcheck-suppress containerOutOfBounds
2342  const MCParticle* mcp = mcps.object(weightsAndIndices[0].second);
2343 
2344  Particle tmpPart(mcp);
2345  auto var_result = var->function(&tmpPart);
2346  if (std::holds_alternative<double>(var_result))
2347  {
2348  return std::get<double>(var_result);
2349  } else if (std::holds_alternative<int>(var_result))
2350  {
2351  return std::get<int>(var_result);
2352  } else if (std::holds_alternative<bool>(var_result))
2353  {
2354  return std::get<bool>(var_result);
2355  } else
2356  {
2357  return std::numeric_limits<double>::quiet_NaN();
2358  }
2359  };
2360 
2361  return func;
2362  } else {
2363  B2FATAL("Wrong number of arguments for meta function clusterBestMatchedMCParticle");
2364  }
2365  }
2366 
2367  double matchedMCHasPDG(const Particle* particle, const std::vector<double>& pdgCode)
2368  {
2369  if (pdgCode.size() != 1) {
2370  B2FATAL("Too many arguments provided to matchedMCHasPDG!");
2371  }
2372  int inputPDG = std::lround(pdgCode[0]);
2373 
2374  const MCParticle* mcp = particle->getMCParticle();
2375  if (!mcp)
2376  return std::numeric_limits<double>::quiet_NaN();
2377 
2378  return std::abs(mcp->getPDG()) == inputPDG;
2379  }
2380 
2381  Manager::FunctionPtr totalEnergyOfParticlesInList(const std::vector<std::string>& arguments)
2382  {
2383  if (arguments.size() == 1) {
2384  std::string listName = arguments[0];
2385  auto func = [listName](const Particle * particle) -> double {
2386 
2387  (void) particle;
2388  StoreObjPtr<ParticleList> listOfParticles(listName);
2389 
2390  if (!(listOfParticles.isValid())) B2FATAL("Invalid Listname " << listName << " given to totalEnergyOfParticlesInList");
2391  double totalEnergy = 0;
2392  int nParticles = listOfParticles->getListSize();
2393  for (int i = 0; i < nParticles; i++)
2394  {
2395  const Particle* part = listOfParticles->getParticle(i);
2396  const auto& frame = ReferenceFrame::GetCurrent();
2397  totalEnergy += frame.getMomentum(part).E();
2398  }
2399  return totalEnergy;
2400 
2401  };
2402  return func;
2403  } else {
2404  B2FATAL("Wrong number of arguments for meta function totalEnergyOfParticlesInList");
2405  }
2406  }
2407 
2408  Manager::FunctionPtr totalPxOfParticlesInList(const std::vector<std::string>& arguments)
2409  {
2410  if (arguments.size() == 1) {
2411  std::string listName = arguments[0];
2412  auto func = [listName](const Particle*) -> double {
2413  StoreObjPtr<ParticleList> listOfParticles(listName);
2414 
2415  if (!(listOfParticles.isValid())) B2FATAL("Invalid Listname " << listName << " given to totalPxOfParticlesInList");
2416  double totalPx = 0;
2417  int nParticles = listOfParticles->getListSize();
2418  const auto& frame = ReferenceFrame::GetCurrent();
2419  for (int i = 0; i < nParticles; i++)
2420  {
2421  const Particle* part = listOfParticles->getParticle(i);
2422  totalPx += frame.getMomentum(part).Px();
2423  }
2424  return totalPx;
2425  };
2426  return func;
2427  } else {
2428  B2FATAL("Wrong number of arguments for meta function totalPxOfParticlesInList");
2429  }
2430  }
2431 
2432  Manager::FunctionPtr totalPyOfParticlesInList(const std::vector<std::string>& arguments)
2433  {
2434  if (arguments.size() == 1) {
2435  std::string listName = arguments[0];
2436  auto func = [listName](const Particle*) -> double {
2437  StoreObjPtr<ParticleList> listOfParticles(listName);
2438 
2439  if (!(listOfParticles.isValid())) B2FATAL("Invalid Listname " << listName << " given to totalPyOfParticlesInList");
2440  double totalPy = 0;
2441  int nParticles = listOfParticles->getListSize();
2442  const auto& frame = ReferenceFrame::GetCurrent();
2443  for (int i = 0; i < nParticles; i++)
2444  {
2445  const Particle* part = listOfParticles->getParticle(i);
2446  totalPy += frame.getMomentum(part).Py();
2447  }
2448  return totalPy;
2449  };
2450  return func;
2451  } else {
2452  B2FATAL("Wrong number of arguments for meta function totalPyOfParticlesInList");
2453  }
2454  }
2455 
2456  Manager::FunctionPtr totalPzOfParticlesInList(const std::vector<std::string>& arguments)
2457  {
2458  if (arguments.size() == 1) {
2459  std::string listName = arguments[0];
2460  auto func = [listName](const Particle*) -> double {
2461  StoreObjPtr<ParticleList> listOfParticles(listName);
2462 
2463  if (!(listOfParticles.isValid())) B2FATAL("Invalid Listname " << listName << " given to totalPzOfParticlesInList");
2464  double totalPz = 0;
2465  int nParticles = listOfParticles->getListSize();
2466  const auto& frame = ReferenceFrame::GetCurrent();
2467  for (int i = 0; i < nParticles; i++)
2468  {
2469  const Particle* part = listOfParticles->getParticle(i);
2470  totalPz += frame.getMomentum(part).Pz();
2471  }
2472  return totalPz;
2473  };
2474  return func;
2475  } else {
2476  B2FATAL("Wrong number of arguments for meta function totalPzOfParticlesInList");
2477  }
2478  }
2479 
2480  Manager::FunctionPtr invMassInLists(const std::vector<std::string>& arguments)
2481  {
2482  if (arguments.size() > 0) {
2483 
2484  auto func = [arguments](const Particle * particle) -> double {
2485 
2486  ROOT::Math::PxPyPzEVector total4Vector;
2487  // To make sure particles in particlesList don't overlap.
2488  std::vector<Particle*> particlePool;
2489 
2490  (void) particle;
2491  for (const auto& argument : arguments)
2492  {
2493  StoreObjPtr <ParticleList> listOfParticles(argument);
2494 
2495  if (!(listOfParticles.isValid())) B2FATAL("Invalid Listname " << argument << " given to invMassInLists");
2496  int nParticles = listOfParticles->getListSize();
2497  for (int i = 0; i < nParticles; i++) {
2498  bool overlaps = false;
2499  Particle* part = listOfParticles->getParticle(i);
2500  for (auto poolPart : particlePool) {
2501  if (part->overlapsWith(poolPart)) {
2502  overlaps = true;
2503  break;
2504  }
2505  }
2506  if (!overlaps) {
2507  total4Vector += part->get4Vector();
2508  particlePool.push_back(part);
2509  }
2510  }
2511  }
2512  double invariantMass = total4Vector.M();
2513  return invariantMass;
2514 
2515  };
2516  return func;
2517  } else {
2518  B2FATAL("Wrong number of arguments for meta function invMassInLists");
2519  }
2520  }
2521 
2522  Manager::FunctionPtr totalECLEnergyOfParticlesInList(const std::vector<std::string>& arguments)
2523  {
2524  if (arguments.size() == 1) {
2525  std::string listName = arguments[0];
2526  auto func = [listName](const Particle * particle) -> double {
2527 
2528  (void) particle;
2529  StoreObjPtr<ParticleList> listOfParticles(listName);
2530 
2531  if (!(listOfParticles.isValid())) B2FATAL("Invalid Listname " << listName << " given to totalEnergyOfParticlesInList");
2532  double totalEnergy = 0;
2533  int nParticles = listOfParticles->getListSize();
2534  for (int i = 0; i < nParticles; i++)
2535  {
2536  const Particle* part = listOfParticles->getParticle(i);
2537  const ECLCluster* cluster = part->getECLCluster();
2538  const ECLCluster::EHypothesisBit clusterHypothesis = part->getECLClusterEHypothesisBit();
2539  if (cluster != nullptr) {
2540  totalEnergy += cluster->getEnergy(clusterHypothesis);
2541  }
2542  }
2543  return totalEnergy;
2544 
2545  };
2546  return func;
2547  } else {
2548  B2FATAL("Wrong number of arguments for meta function totalECLEnergyOfParticlesInList");
2549  }
2550  }
2551 
2552  Manager::FunctionPtr maxPtInList(const std::vector<std::string>& arguments)
2553  {
2554  if (arguments.size() == 1) {
2555  std::string listName = arguments[0];
2556  auto func = [listName](const Particle*) -> double {
2557  StoreObjPtr<ParticleList> listOfParticles(listName);
2558 
2559  if (!(listOfParticles.isValid())) B2FATAL("Invalid Listname " << listName << " given to maxPtInList");
2560  int nParticles = listOfParticles->getListSize();
2561  const auto& frame = ReferenceFrame::GetCurrent();
2562  double maxPt = 0;
2563  for (int i = 0; i < nParticles; i++)
2564  {
2565  const Particle* part = listOfParticles->getParticle(i);
2566  const double Pt = frame.getMomentum(part).Pt();
2567  if (Pt > maxPt) maxPt = Pt;
2568  }
2569  return maxPt;
2570  };
2571  return func;
2572  } else {
2573  B2FATAL("Wrong number of arguments for meta function maxPtInList");
2574  }
2575  }
2576 
2577  Manager::FunctionPtr eclClusterTrackMatchedWithCondition(const std::vector<std::string>& arguments)
2578  {
2579  if (arguments.size() <= 1) {
2580 
2581  std::string cutString;
2582  if (arguments.size() == 1)
2583  cutString = arguments[0];
2584  std::shared_ptr<Variable::Cut> cut = std::shared_ptr<Variable::Cut>(Variable::Cut::compile(cutString));
2585  auto func = [cut](const Particle * particle) -> double {
2586 
2587  if (particle == nullptr)
2588  return std::numeric_limits<double>::quiet_NaN();
2589 
2590  const ECLCluster* cluster = particle->getECLCluster();
2591 
2592  if (cluster)
2593  {
2594  auto tracks = cluster->getRelationsFrom<Track>();
2595 
2596  for (const auto& track : tracks) {
2597  Particle trackParticle(&track, Belle2::Const::pion);
2598 
2599  if (cut->check(&trackParticle))
2600  return 1;
2601  }
2602  return 0;
2603  }
2604  return std::numeric_limits<double>::quiet_NaN();
2605  };
2606  return func;
2607  } else {
2608  B2FATAL("Wrong number of arguments for meta function eclClusterSpecialTrackMatched");
2609  }
2610  }
2611 
2612  Manager::FunctionPtr averageValueInList(const std::vector<std::string>& arguments)
2613  {
2614  if (arguments.size() == 2) {
2615  std::string listName = arguments[0];
2616  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[1]);
2617 
2618  auto func = [listName, var](const Particle*) -> double {
2619  StoreObjPtr<ParticleList> listOfParticles(listName);
2620 
2621  if (!(listOfParticles.isValid())) B2FATAL("Invalid list name " << listName << " given to averageValueInList");
2622  int nParticles = listOfParticles->getListSize();
2623  if (nParticles == 0)
2624  {
2625  return std::numeric_limits<double>::quiet_NaN();
2626  }
2627  double average = 0;
2628  if (std::holds_alternative<double>(var->function(listOfParticles->getParticle(0))))
2629  {
2630  for (int i = 0; i < nParticles; i++) {
2631  average += std::get<double>(var->function(listOfParticles->getParticle(i))) / nParticles;
2632  }
2633  } else if (std::holds_alternative<int>(var->function(listOfParticles->getParticle(0))))
2634  {
2635  for (int i = 0; i < nParticles; i++) {
2636  average += std::get<int>(var->function(listOfParticles->getParticle(i))) / nParticles;
2637  }
2638  } else return std::numeric_limits<double>::quiet_NaN();
2639  return average;
2640  };
2641  return func;
2642  } else {
2643  B2FATAL("Wrong number of arguments for meta function averageValueInList");
2644  }
2645  }
2646 
2647  Manager::FunctionPtr medianValueInList(const std::vector<std::string>& arguments)
2648  {
2649  if (arguments.size() == 2) {
2650  std::string listName = arguments[0];
2651  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[1]);
2652 
2653  auto func = [listName, var](const Particle*) -> double {
2654  StoreObjPtr<ParticleList> listOfParticles(listName);
2655 
2656  if (!(listOfParticles.isValid())) B2FATAL("Invalid list name " << listName << " given to medianValueInList");
2657  int nParticles = listOfParticles->getListSize();
2658  if (nParticles == 0)
2659  {
2660  return std::numeric_limits<double>::quiet_NaN();
2661  }
2662  std::vector<double> valuesInList;
2663  if (std::holds_alternative<double>(var->function(listOfParticles->getParticle(0))))
2664  {
2665  for (int i = 0; i < nParticles; i++) {
2666  valuesInList.push_back(std::get<double>(var->function(listOfParticles->getParticle(i))));
2667  }
2668  } else if (std::holds_alternative<int>(var->function(listOfParticles->getParticle(0))))
2669  {
2670  for (int i = 0; i < nParticles; i++) {
2671  valuesInList.push_back(std::get<int>(var->function(listOfParticles->getParticle(i))));
2672  }
2673  } else return std::numeric_limits<double>::quiet_NaN();
2674  std::sort(valuesInList.begin(), valuesInList.end());
2675  if (nParticles % 2 != 0)
2676  {
2677  return valuesInList[nParticles / 2];
2678  } else
2679  {
2680  return 0.5 * (valuesInList[nParticles / 2] + valuesInList[nParticles / 2 - 1]);
2681  }
2682  };
2683  return func;
2684  } else {
2685  B2FATAL("Wrong number of arguments for meta function medianValueInList");
2686  }
2687  }
2688 
2689  Manager::FunctionPtr angleToClosestInList(const std::vector<std::string>& arguments)
2690  {
2691  // expecting the list name
2692  if (arguments.size() != 1)
2693  B2FATAL("Wrong number of arguments for meta function angleToClosestInList");
2694 
2695  std::string listname = arguments[0];
2696 
2697  auto func = [listname](const Particle * particle) -> double {
2698  // get the list and check it's valid
2699  StoreObjPtr<ParticleList> list(listname);
2700  if (not list.isValid())
2701  B2FATAL("Invalid particle list name " << listname << " given to angleToClosestInList");
2702 
2703  // check the list isn't empty
2704  if (list->getListSize() == 0)
2705  return std::numeric_limits<double>::quiet_NaN();
2706 
2707  // respect the current frame and get the momentum of our input
2708  const auto& frame = ReferenceFrame::GetCurrent();
2709  const auto p_this = B2Vector3D(frame.getMomentum(particle).Vect());
2710 
2711  // find the particle index with the smallest opening angle
2712  double minAngle = 2 * M_PI;
2713  for (unsigned int i = 0; i < list->getListSize(); ++i)
2714  {
2715  const Particle* compareme = list->getParticle(i);
2716  const auto p_compare = B2Vector3D(frame.getMomentum(compareme).Vect());
2717  double angle = p_compare.Angle(p_this);
2718  if (minAngle > angle) minAngle = angle;
2719  }
2720  return minAngle;
2721  };
2722  return func;
2723  }
2724 
2725  Manager::FunctionPtr closestInList(const std::vector<std::string>& arguments)
2726  {
2727  // expecting the list name and a variable name
2728  if (arguments.size() != 2)
2729  B2FATAL("Wrong number of arguments for meta function closestInList");
2730 
2731  std::string listname = arguments[0];
2732 
2733  // the requested variable and check it exists
2734  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[1]);
2735 
2736  auto func = [listname, var](const Particle * particle) -> double {
2737  // get the list and check it's valid
2738  StoreObjPtr<ParticleList> list(listname);
2739  if (not list.isValid())
2740  B2FATAL("Invalid particle list name " << listname << " given to closestInList");
2741 
2742  // respect the current frame and get the momentum of our input
2743  const auto& frame = ReferenceFrame::GetCurrent();
2744  const auto p_this = B2Vector3D(frame.getMomentum(particle).Vect());
2745 
2746  // find the particle index with the smallest opening angle
2747  double minAngle = 2 * M_PI;
2748  int iClosest = -1;
2749  for (unsigned int i = 0; i < list->getListSize(); ++i)
2750  {
2751  const Particle* compareme = list->getParticle(i);
2752  const auto p_compare = B2Vector3D(frame.getMomentum(compareme).Vect());
2753  double angle = p_compare.Angle(p_this);
2754  if (minAngle > angle) {
2755  minAngle = angle;
2756  iClosest = i;
2757  }
2758  }
2759 
2760  // final check that the list wasn't empty (or some other problem)
2761  if (iClosest == -1) return std::numeric_limits<double>::quiet_NaN();
2762  auto var_result = var->function(list->getParticle(iClosest));
2763  if (std::holds_alternative<double>(var_result))
2764  {
2765  return std::get<double>(var_result);
2766  } else if (std::holds_alternative<int>(var_result))
2767  {
2768  return std::get<int>(var_result);
2769  } else if (std::holds_alternative<bool>(var_result))
2770  {
2771  return std::get<bool>(var_result);
2772  } else return std::numeric_limits<double>::quiet_NaN();
2773  };
2774  return func;
2775  }
2776 
2777  Manager::FunctionPtr angleToMostB2BInList(const std::vector<std::string>& arguments)
2778  {
2779  // expecting the list name
2780  if (arguments.size() != 1)
2781  B2FATAL("Wrong number of arguments for meta function angleToMostB2BInList");
2782 
2783  std::string listname = arguments[0];
2784 
2785  auto func = [listname](const Particle * particle) -> double {
2786  // get the list and check it's valid
2787  StoreObjPtr<ParticleList> list(listname);
2788  if (not list.isValid())
2789  B2FATAL("Invalid particle list name " << listname << " given to angleToMostB2BInList");
2790 
2791  // check the list isn't empty
2792  if (list->getListSize() == 0)
2793  return std::numeric_limits<double>::quiet_NaN();
2794 
2795  // respect the current frame and get the momentum of our input
2796  const auto& frame = ReferenceFrame::GetCurrent();
2797  const auto p_this = B2Vector3D(frame.getMomentum(particle).Vect());
2798 
2799  // find the most back-to-back (the largest opening angle before they
2800  // start getting smaller again!)
2801  double maxAngle = 0;
2802  for (unsigned int i = 0; i < list->getListSize(); ++i)
2803  {
2804  const Particle* compareme = list->getParticle(i);
2805  const auto p_compare = B2Vector3D(frame.getMomentum(compareme).Vect());
2806  double angle = p_compare.Angle(p_this);
2807  if (maxAngle < angle) maxAngle = angle;
2808  }
2809  return maxAngle;
2810  };
2811  return func;
2812  }
2813 
2814  Manager::FunctionPtr mostB2BInList(const std::vector<std::string>& arguments)
2815  {
2816  // expecting the list name and a variable name
2817  if (arguments.size() != 2)
2818  B2FATAL("Wrong number of arguments for meta function mostB2BInList");
2819 
2820  std::string listname = arguments[0];
2821 
2822  // the requested variable and check it exists
2823  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[1]);
2824 
2825  auto func = [listname, var](const Particle * particle) -> double {
2826  // get the list and check it's valid
2827  StoreObjPtr<ParticleList> list(listname);
2828  if (not list.isValid())
2829  B2FATAL("Invalid particle list name " << listname << " given to mostB2BInList");
2830 
2831  // respect the current frame and get the momentum of our input
2832  const auto& frame = ReferenceFrame::GetCurrent();
2833  const auto p_this = B2Vector3D(frame.getMomentum(particle).Vect());
2834 
2835  // find the most back-to-back (the largest opening angle before they
2836  // start getting smaller again!)
2837  double maxAngle = -1.0;
2838  int iMostB2B = -1;
2839  for (unsigned int i = 0; i < list->getListSize(); ++i)
2840  {
2841  const Particle* compareme = list->getParticle(i);
2842  const auto p_compare = B2Vector3D(frame.getMomentum(compareme).Vect());
2843  double angle = p_compare.Angle(p_this);
2844  if (maxAngle < angle) {
2845  maxAngle = angle;
2846  iMostB2B = i;
2847  }
2848  }
2849 
2850  // final check that the list wasn't empty (or some other problem)
2851  if (iMostB2B == -1) return std::numeric_limits<double>::quiet_NaN();
2852  auto var_result = var->function(list->getParticle(iMostB2B));
2853  if (std::holds_alternative<double>(var_result))
2854  {
2855  return std::get<double>(var_result);
2856  } else if (std::holds_alternative<int>(var_result))
2857  {
2858  return std::get<int>(var_result);
2859  } else if (std::holds_alternative<bool>(var_result))
2860  {
2861  return std::get<bool>(var_result);
2862  } else return std::numeric_limits<double>::quiet_NaN();
2863  };
2864  return func;
2865  }
2866 
2867  Manager::FunctionPtr maxOpeningAngleInList(const std::vector<std::string>& arguments)
2868  {
2869  if (arguments.size() == 1) {
2870  std::string listName = arguments[0];
2871  auto func = [listName](const Particle*) -> double {
2872  StoreObjPtr<ParticleList> listOfParticles(listName);
2873 
2874  if (!(listOfParticles.isValid())) B2FATAL("Invalid Listname " << listName << " given to maxOpeningAngleInList");
2875  int nParticles = listOfParticles->getListSize();
2876  // return NaN if number of particles is less than 2
2877  if (nParticles < 2) return std::numeric_limits<double>::quiet_NaN();
2878 
2879  const auto& frame = ReferenceFrame::GetCurrent();
2880  double maxOpeningAngle = -1;
2881  for (int i = 0; i < nParticles; i++)
2882  {
2883  B2Vector3D v1 = frame.getMomentum(listOfParticles->getParticle(i)).Vect();
2884  for (int j = i + 1; j < nParticles; j++) {
2885  B2Vector3D v2 = frame.getMomentum(listOfParticles->getParticle(j)).Vect();
2886  const double angle = v1.Angle(v2);
2887  if (angle > maxOpeningAngle) maxOpeningAngle = angle;
2888  }
2889  }
2890  return maxOpeningAngle;
2891  };
2892  return func;
2893  } else {
2894  B2FATAL("Wrong number of arguments for meta function maxOpeningAngleInList");
2895  }
2896  }
2897 
2898  Manager::FunctionPtr daughterCombination(const std::vector<std::string>& arguments)
2899  {
2900  // Expect 2 or more arguments.
2901  if (arguments.size() >= 2) {
2902  // First argument is the variable name
2903  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
2904 
2905  // Core function: calculates a variable combining an arbitrary number of particles
2906  auto func = [var, arguments](const Particle * particle) -> double {
2907  if (particle == nullptr)
2908  {
2909  B2WARNING("Trying to access a daughter that does not exist. Skipping");
2910  return std::numeric_limits<float>::quiet_NaN();
2911  }
2912  const auto& frame = ReferenceFrame::GetCurrent();
2913 
2914  // Sum of the 4-momenta of all the selected daughters
2915  ROOT::Math::PxPyPzEVector pSum(0, 0, 0, 0);
2916 
2917  // Loop over the arguments. Each one of them is a generalizedIndex,
2918  // pointing to a particle in the decay tree.
2919  for (unsigned int iCoord = 1; iCoord < arguments.size(); iCoord++)
2920  {
2921  auto generalizedIndex = arguments[iCoord];
2922  const Particle* dauPart = particle->getParticleFromGeneralizedIndexString(generalizedIndex);
2923  if (dauPart)
2924  pSum += frame.getMomentum(dauPart);
2925  else {
2926  B2WARNING("Trying to access a daughter that does not exist. Index = " << generalizedIndex);
2927  return std::numeric_limits<float>::quiet_NaN();
2928  }
2929  }
2930 
2931  // Make a dummy particle out of the sum of the 4-momenta of the selected daughters
2932  Particle sumOfDaughters(pSum, 100); // 100 is one of the special numbers
2933 
2934  auto var_result = var->function(&sumOfDaughters);
2935  // Calculate the variable on the dummy particle
2936  if (std::holds_alternative<double>(var_result))
2937  {
2938  return std::get<double>(var_result);
2939  } else if (std::holds_alternative<int>(var_result))
2940  {
2941  return std::get<int>(var_result);
2942  } else if (std::holds_alternative<bool>(var_result))
2943  {
2944  return std::get<bool>(var_result);
2945  } else return std::numeric_limits<double>::quiet_NaN();
2946  };
2947  return func;
2948  } else
2949  B2FATAL("Wrong number of arguments for meta function daughterCombination");
2950  }
2951 
2952  Manager::FunctionPtr useAlternativeDaughterHypothesis(const std::vector<std::string>& arguments)
2953  {
2954  /*
2955  `arguments` contains the variable to calculate and a list of colon-separated index-particle pairs.
2956  Overall, it looks like {"M", "0:K+", "1:p+", "3:e-"}.
2957  The code is thus divided in two parts:
2958  1) Parsing. A loop over the elements of `arguments` that first separates the variable from the rest, and then splits all the index:particle
2959  pairs, filling a std::vector with the indexes and another one with the new mass values.
2960  2) Replacing: A loop over the particle's daughters. We take the 4-momentum of each of them, recalculating it with a new mass if needed, and then we calculate
2961  the variable value using the sum of all the 4-momenta, both updated and non-updated ones.
2962  */
2963 
2964  // Expect 2 or more arguments.
2965  if (arguments.size() >= 2) {
2966 
2967  //----
2968  // 1) parsing
2969  //----
2970 
2971  // First argument is the variable name
2972  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
2973 
2974  // Parses the other arguments, which are in the form of index:particleName pairs,
2975  // and stores indexes and pdgs in std::unordered_map
2976  std::unordered_map<unsigned int, int> mapOfReplacedDaughters;
2977 
2978  // Loop over the arguments to parse them
2979  for (unsigned int iCoord = 1; iCoord < arguments.size(); iCoord++) {
2980  auto replacedDauString = arguments[iCoord];
2981  // Split the string in index and new mass
2982  std::vector<std::string> indexAndMass;
2983  boost::split(indexAndMass, replacedDauString, boost::is_any_of(":"));
2984 
2985  // Checks that the index:particleName pair is properly formatted.
2986  if (indexAndMass.size() > 2) {
2987  B2WARNING("The string indicating which daughter's mass should be replaced contains more than two elements separated by a colon. Perhaps you tried to pass a generalized index, which is not supported yet for this variable. The offending string is "
2988  << replacedDauString << ", while a correct syntax looks like 0:K+.");
2989  return nullptr;
2990  }
2991 
2992  if (indexAndMass.size() < 2) {
2993  B2WARNING("The string indicating which daughter's mass should be replaced contains only one colon-separated element instead of two. The offending string is "
2994  << replacedDauString << ", while a correct syntax looks like 0:K+.");
2995  return nullptr;
2996  }
2997 
2998  // indexAndMass[0] is the daughter index as string. Try to convert it
2999  int dauIndex = 0;
3000  try {
3001  dauIndex = Belle2::convertString<int>(indexAndMass[0]);
3002  } catch (std::invalid_argument&) {
3003  B2FATAL("Found the string " << indexAndMass[0] << "instead of a daughter index.");
3004  }
3005 
3006  // Determine PDG code corresponding to indexAndMass[1] using the particle names defined in evt.pdl
3007  TParticlePDG* particlePDG = TDatabasePDG::Instance()->GetParticle(indexAndMass[1].c_str());
3008  if (!particlePDG) {
3009  B2WARNING("Particle not in evt.pdl file! " << indexAndMass[1]);
3010  return nullptr;
3011  }
3012 
3013  // Stores the indexes and the pdgs in the map that will be passed to the lambda function
3014  int pdgCode = particlePDG->PdgCode();
3015  mapOfReplacedDaughters[dauIndex] = pdgCode;
3016  } // End of parsing
3017 
3018  // Check the size of mapOfReplacedDaughters
3019  if (mapOfReplacedDaughters.size() != arguments.size() - 1)
3020  B2FATAL("Overlapped daughter's index is detected in the meta-variable useAlternativeDaughterHypothesis");
3021 
3022  //----
3023  // 2) replacing
3024  //----
3025 
3026  // Core function: creates a new particle from the original one changing
3027  // some of the daughters' masses
3028  auto func = [var, mapOfReplacedDaughters](const Particle * particle) -> double {
3029  if (particle == nullptr)
3030  {
3031  B2WARNING("Trying to access a particle that does not exist. Skipping");
3032  return std::numeric_limits<float>::quiet_NaN();
3033  }
3034 
3035  const auto& frame = ReferenceFrame::GetCurrent();
3036 
3037  // Create a dummy particle from the given particle to overwrite its kinematics
3038  Particle* dummy = ParticleCopy::copyParticle(particle);
3039 
3040  // Sum of the 4-momenta of all the daughters with the new mass assumptions
3041  ROOT::Math::PxPyPzMVector pSum(0, 0, 0, 0);
3042 
3043  for (unsigned int iDau = 0; iDau < particle->getNDaughters(); iDau++)
3044  {
3045  const Particle* dauPart = particle->getDaughter(iDau);
3046  if (not dauPart) {
3047  B2WARNING("Trying to access a daughter that does not exist. Index = " << iDau);
3048  return std::numeric_limits<float>::quiet_NaN();
3049  }
3050 
3051  ROOT::Math::PxPyPzMVector dauMom = ROOT::Math::PxPyPzMVector(frame.getMomentum(dauPart));
3052 
3053  int pdgCode;
3054  try {
3055  pdgCode = mapOfReplacedDaughters.at(iDau);
3056  } catch (std::out_of_range&) {
3057  // iDau is not in mapOfReplacedDaughters
3058  pSum += dauMom;
3059  continue;
3060  }
3061 
3062  // overwrite the daughter's kinematics
3063  double p_x = dauMom.Px();
3064  double p_y = dauMom.Py();
3065  double p_z = dauMom.Pz();
3066  dauMom.SetCoordinates(p_x, p_y, p_z, TDatabasePDG::Instance()->GetParticle(pdgCode)->Mass());
3067  const_cast<Particle*>(dummy->getDaughter(iDau))->set4VectorDividingByMomentumScaling(ROOT::Math::PxPyPzEVector(dauMom));
3068 
3069  // overwrite the daughter's pdg
3070  const int charge = dummy->getDaughter(iDau)->getCharge();
3071  if (TDatabasePDG::Instance()->GetParticle(pdgCode)->Charge() / 3.0 == charge)
3072  const_cast<Particle*>(dummy->getDaughter(iDau))->setPDGCode(pdgCode);
3073  else
3074  const_cast<Particle*>(dummy->getDaughter(iDau))->setPDGCode(-1 * pdgCode);
3075 
3076  pSum += dauMom;
3077  } // End of loop over number of daughter
3078 
3079  // overwrite the particle's kinematics
3080  dummy->set4Vector(ROOT::Math::PxPyPzEVector(pSum));
3081 
3082  auto var_result = var->function(dummy);
3083 
3084  // Calculate the variable on the dummy particle
3085  if (std::holds_alternative<double>(var_result))
3086  {
3087  return std::get<double>(var_result);
3088  } else if (std::holds_alternative<int>(var_result))
3089  {
3090  return std::get<int>(var_result);
3091  } else if (std::holds_alternative<bool>(var_result))
3092  {
3093  return std::get<bool>(var_result);
3094  } else return std::numeric_limits<double>::quiet_NaN();
3095  }; // end of lambda function
3096  return func;
3097  }// end of check on number of arguments
3098  else
3099  B2FATAL("Wrong number of arguments for meta function useAlternativeDaughterHypothesis");
3100  }
3101 
3102  Manager::FunctionPtr varForFirstMCAncestorOfType(const std::vector<std::string>& arguments)
3103  {
3104  if (arguments.size() == 2) {
3105  int pdg_code = -1;
3106  std::string arg = arguments[0];
3107  const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[1]);
3108  TParticlePDG* part = TDatabasePDG::Instance()->GetParticle(arg.c_str());
3109 
3110  if (part != nullptr) {
3111  pdg_code = std::abs(part->PdgCode());
3112  } else {
3113  try {
3114  pdg_code = Belle2::convertString<int>(arg);
3115  } catch (std::exception& e) {}
3116  }
3117 
3118  if (pdg_code == -1) {
3119  B2FATAL("Ancestor " + arg + " is not recognised. Please provide valid PDG code or particle name.");
3120  }
3121 
3122  auto func = [pdg_code, var](const Particle * particle) -> double {
3123  const Particle* p = particle;
3124 
3125  int ancestor_level = std::get<double>(Manager::Instance().getVariable("hasAncestor(" + std::to_string(pdg_code) + ", 0)")->function(p));
3126  if ((ancestor_level <= 0) or (std::isnan(ancestor_level)))
3127  {
3128  return std::numeric_limits<float>::quiet_NaN();
3129  }
3130 
3131  const MCParticle* i_p = p->getMCParticle();
3132 
3133  for (int a = 0; a < ancestor_level ; a = a + 1)
3134  {
3135  i_p = i_p->getMother();
3136  }
3137  Particle m_p(i_p);
3138  auto var_result = var->function(&m_p);
3139  if (std::holds_alternative<double>(var_result))
3140  {
3141  return std::get<double>(var_result);
3142  } else if (std::holds_alternative<int>(var_result))
3143  {
3144  return std::get<int>(var_result);
3145  } else if (std::holds_alternative<bool>(var_result))
3146  {
3147  return std::get<bool>(var_result);
3148  } else return std::numeric_limits<double>::quiet_NaN();
3149  };
3150  return func;
3151  } else {
3152  B2FATAL("Wrong number of arguments for meta function varForFirstMCAncestorOfType (expected 2: type and variable of interest)");
3153  }
3154  }
3155 
3156  VARIABLE_GROUP("MetaFunctions");
3157  REGISTER_METAVARIABLE("nCleanedECLClusters(cut)", nCleanedECLClusters,
3158  "[Eventbased] Returns the number of clean Clusters in the event\n"
3159  "Clean clusters are defined by the clusters which pass the given cut assuming a photon hypothesis.",
3160  Manager::VariableDataType::c_int);
3161  REGISTER_METAVARIABLE("nCleanedTracks(cut)", nCleanedTracks,
3162  "[Eventbased] Returns the number of clean Tracks in the event\n"
3163  "Clean tracks are defined by the tracks which pass the given cut assuming a pion hypothesis.", Manager::VariableDataType::c_int);
3164  REGISTER_METAVARIABLE("formula(v1 + v2 * [v3 - v4] / v5^v6)", formula, R"DOCSTRING(
3165 Returns the result of the given formula, where v1 to vN are variables or floating
3166 point numbers. Currently the only supported operations are addition (``+``),
3167 subtraction (``-``), multiplication (``*``), division (``/``) and power (``^``
3168 or ``**``). Parenthesis can be in the form of square brackets ``[v1 * v2]``
3169 or normal brackets ``(v1 * v2)``. It will work also with variables taking
3170 arguments. Operator precedence is taken into account. For example ::
3171 
3172  (daughter(0, E) + daughter(1, E))**2 - p**2 + 0.138
3173 
3174 .. versionchanged:: release-03-00-00
3175  now both, ``[]`` and ``()`` can be used for grouping operations, ``**`` can
3176  be used for exponent and float literals are possible directly in the
3177  formula.
3178 )DOCSTRING", Manager::VariableDataType::c_double);
3179  REGISTER_METAVARIABLE("useRestFrame(variable)", useRestFrame,
3180  "Returns the value of the variable using the rest frame of the given particle as current reference frame.\n"
3181  "E.g. ``useRestFrame(daughter(0, p))`` returns the total momentum of the first daughter in its mother's rest-frame", Manager::VariableDataType::c_double);
3182  REGISTER_METAVARIABLE("useCMSFrame(variable)", useCMSFrame,
3183  "Returns the value of the variable using the CMS frame as current reference frame.\n"
3184  "E.g. ``useCMSFrame(E)`` returns the energy of a particle in the CMS frame.", Manager::VariableDataType::c_double);
3185  REGISTER_METAVARIABLE("useLabFrame(variable)", useLabFrame, R"DOC(
3186 Returns the value of ``variable`` in the *lab* frame.
3187 
3188 .. tip::
3189  The lab frame is the default reference frame, usually you don't need to use this meta-variable.
3190  E.g. ``useLabFrame(E)`` returns the energy of a particle in the Lab frame, same as just ``E``.
3191 
3192 Specifying the lab frame is useful in some corner-cases. For example:
3193 ``useRestFrame(daughter(0, formula(E - useLabFrame(E))))`` which is the difference of the first daughter's energy in the rest frame of the mother (current particle) with the same daughter's lab-frame energy.
3194 )DOC", Manager::VariableDataType::c_double);
3195  REGISTER_METAVARIABLE("useTagSideRecoilRestFrame(variable, daughterIndexTagB)", useTagSideRecoilRestFrame,
3196  "Returns the value of the variable in the rest frame of the recoiling particle to the tag side B meson.\n"
3197  "The variable should only be applied to an Upsilon(4S) list.\n"
3198  "E.g. ``useTagSideRecoilRestFrame(daughter(1, daughter(1, p)), 0)`` applied on a Upsilon(4S) list (``Upsilon(4S)->B+:tag B-:sig``) returns the momentum of the second daughter of the signal B meson in the signal B meson rest frame.", Manager::VariableDataType::c_double);
3199  REGISTER_METAVARIABLE("useParticleRestFrame(variable, particleList)", useParticleRestFrame,
3200  "Returns the value of the variable in the rest frame of the first Particle contained in the given ParticleList.\n"
3201  "It is strongly recommended to pass a ParticleList that contains at most only one Particle in each event. "
3202  "When more than one Particle is present in the ParticleList, only the first Particle in the list is used for "
3203  "computing the rest frame and a warning is thrown. If the given ParticleList is empty in an event, it returns NaN.", Manager::VariableDataType::c_double);
3204  REGISTER_METAVARIABLE("useRecoilParticleRestFrame(variable, particleList)", useRecoilParticleRestFrame,
3205  "Returns the value of the variable in the rest frame of recoil system against the first Particle contained in the given ParticleList.\n"
3206  "It is strongly recommended to pass a ParticleList that contains at most only one Particle in each event. "
3207  "When more than one Particle is present in the ParticleList, only the first Particle in the list is used for "
3208  "computing the rest frame and a warning is thrown. If the given ParticleList is empty in an event, it returns NaN.", Manager::VariableDataType::c_double);
3209  REGISTER_METAVARIABLE("useDaughterRestFrame(variable, daughterIndex_1, [daughterIndex_2, ... daughterIndex_3])", useDaughterRestFrame,
3210  "Returns the value of the variable in the rest frame of the selected daughter particle.\n"
3211  "The daughter is identified via generalized daughter index, e.g. ``0:1`` identifies the second daughter (1) "
3212  "of the first daughter (0). If the daughter index is invalid, it returns NaN.\n"
3213  "If two or more indices are given, the rest frame of the sum of the daughters is used.",
3214  Manager::VariableDataType::c_double);
3215  REGISTER_METAVARIABLE("passesCut(cut)", passesCut,
3216  "Returns 1 if particle passes the cut otherwise 0.\n"
3217  "Useful if you want to write out if a particle would have passed a cut or not.", Manager::VariableDataType::c_bool);
3218  REGISTER_METAVARIABLE("passesEventCut(cut)", passesEventCut,
3219  "[Eventbased] Returns 1 if event passes the cut otherwise 0.\n"
3220  "Useful if you want to select events passing a cut without looping into particles, such as for skimming.\n", Manager::VariableDataType::c_bool);
3221  REGISTER_METAVARIABLE("countDaughters(cut)", countDaughters,
3222  "Returns number of direct daughters which satisfy the cut.\n"
3223  "Used by the skimming package (for what exactly?)", Manager::VariableDataType::c_int);
3224  REGISTER_METAVARIABLE("countFSPDaughters(cut)", countDescendants,
3225  "Returns number of final-state daughters which satisfy the cut.",
3226  Manager::VariableDataType::c_int);
3227  REGISTER_METAVARIABLE("countDescendants(cut)", countDescendants,
3228  "Returns number of descendants for all generations which satisfy the cut.",
3229  Manager::VariableDataType::c_int);
3230  REGISTER_METAVARIABLE("varFor(pdgCode, variable)", varFor,
3231  "Returns the value of the variable for the given particle if its abs(pdgCode) agrees with the given one.\n"
3232  "E.g. ``varFor(11, p)`` returns the momentum if the particle is an electron or a positron.", Manager::VariableDataType::c_double);
3233  REGISTER_METAVARIABLE("varForMCGen(variable)", varForMCGen,
3234  "Returns the value of the variable for the given particle if the MC particle related to it is primary, not virtual, and not initial.\n"
3235  "If no MC particle is related to the given particle, or the MC particle is not primary, virtual, or initial, NaN will be returned.\n"
3236  "E.g. ``varForMCGen(PDG)`` returns the PDG code of the MC particle related to the given particle if it is primary, not virtual, and not initial.", Manager::VariableDataType::c_double);
3237  REGISTER_METAVARIABLE("nParticlesInList(particleListName)", nParticlesInList,
3238  "[Eventbased] Returns number of particles in the given particle List.", Manager::VariableDataType::c_int);
3239  REGISTER_METAVARIABLE("isInList(particleListName)", isInList,
3240  "Returns 1 if the particle is in the list provided, 0 if not. Note that this only checks the particle given. For daughters of composite particles, please see :b2:var:`isDaughterOfList`.", Manager::VariableDataType::c_bool);
3241  REGISTER_METAVARIABLE("isDaughterOfList(particleListNames)", isDaughterOfList,
3242  "Returns 1 if the given particle is a daughter of at least one of the particles in the given particle Lists.", Manager::VariableDataType::c_bool);
3243  REGISTER_METAVARIABLE("isDescendantOfList(particleListName[, anotherParticleListName][, generationFlag = -1])", isDescendantOfList, R"DOC(
3244  Returns 1 if the given particle appears in the decay chain of the particles in the given ParticleLists.
3245 
3246  Passing an integer as the last argument, allows to check if the particle belongs to the specific generation:
3247 
3248  * ``isDescendantOfList(<particle_list>,1)`` returns 1 if particle is a daughter of the list,
3249  * ``isDescendantOfList(<particle_list>,2)`` returns 1 if particle is a granddaughter of the list,
3250  * ``isDescendantOfList(<particle_list>,3)`` returns 1 if particle is a great-granddaughter of the list, etc.
3251  * Default value is ``-1`` that is inclusive for all generations.
3252  )DOC", Manager::VariableDataType::c_bool);
3253  REGISTER_METAVARIABLE("isMCDescendantOfList(particleListName[, anotherParticleListName][, generationFlag = -1])", isMCDescendantOfList, R"DOC(
3254  Returns 1 if the given particle is linked to the same MC particle as any reconstructed daughter of the decay lists.
3255 
3256  Passing an integer as the last argument, allows to check if the particle belongs to the specific generation:
3257 
3258  * ``isMCDescendantOfList(<particle_list>,1)`` returns 1 if particle is matched to the same particle as any daughter of the list,
3259  * ``isMCDescendantOfList(<particle_list>,2)`` returns 1 if particle is matched to the same particle as any granddaughter of the list,
3260  * ``isMCDescendantOfList(<particle_list>,3)`` returns 1 if particle is matched to the same particle as any great-granddaughter of the list, etc.
3261  * Default value is ``-1`` that is inclusive for all generations.
3262 
3263  It makes only sense for lists created with `fillParticleListFromMC` function with ``addDaughters=True`` argument.
3264  )DOC", Manager::VariableDataType::c_bool);
3265 
3266  REGISTER_METAVARIABLE("sourceObjectIsInList(particleListName)", sourceObjectIsInList, R"DOC(
3267 Returns 1 if the underlying mdst object (e.g. track, or cluster) was used to create a particle in ``particleListName``, 0 if not.
3268 
3269 .. note::
3270  This only makes sense for particles that are not composite. Returns -1 for composite particles.
3271 )DOC", Manager::VariableDataType::c_int);
3272 
3273  REGISTER_METAVARIABLE("mcParticleIsInMCList(particleListName)", mcParticleIsInMCList, R"DOC(
3274 Returns 1 if the particle's matched MC particle is also matched to a particle in ``particleListName``
3275 (or if either of the lists were filled from generator level `modularAnalysis.fillParticleListFromMC`.)
3276 
3277 .. seealso:: :b2:var:`isMCDescendantOfList` to check daughters.
3278 )DOC", Manager::VariableDataType::c_bool);
3279 
3280  REGISTER_METAVARIABLE("isGrandDaughterOfList(particleListNames)", isGrandDaughterOfList,
3281  "Returns 1 if the given particle is a grand daughter of at least one of the particles in the given particle Lists.", Manager::VariableDataType::c_bool);
3282  REGISTER_METAVARIABLE("originalParticle(variable)", originalParticle, R"DOC(
3283  Returns value of variable for the original particle from which the given particle is copied.
3284 
3285  The copy of particle is created, for example, when the vertex fit updates the daughters and `modularAnalysis.copyParticles` is called.
3286  Returns NaN if the given particle is not copied and so there is no original particle.
3287  )DOC", Manager::VariableDataType::c_double);
3288  REGISTER_METAVARIABLE("daughter(i, variable)", daughter, R"DOC(
3289  Returns value of variable for the i-th daughter. E.g.
3290 
3291  * ``daughter(0, p)`` returns the total momentum of the first daughter.
3292  * ``daughter(0, daughter(1, p)`` returns the total momentum of the second daughter of the first daughter.
3293 
3294  Returns NaN if particle is nullptr or if the given daughter-index is out of bound (>= amount of daughters).
3295  )DOC", Manager::VariableDataType::c_double);
3296  REGISTER_METAVARIABLE("originalDaughter(i, variable)", originalDaughter, R"DOC(
3297  Returns value of variable for the original particle from which the i-th daughter is copied.
3298 
3299  The copy of particle is created, for example, when the vertex fit updates the daughters and `modularAnalysis.copyParticles` is called.
3300  Returns NaN if the daughter is not copied and so there is no original daughter.
3301 
3302  Returns NaN if particle is nullptr or if the given daughter-index is out of bound (>= amount of daughters).
3303  )DOC", Manager::VariableDataType::c_double);
3304  REGISTER_METAVARIABLE("mcDaughter(i, variable)", mcDaughter, R"DOC(
3305  Returns the value of the requested variable for the i-th Monte Carlo daughter of the particle.
3306 
3307  Returns NaN if the particle is nullptr, if the particle is not matched to an MC particle,
3308  or if the i-th MC daughter does not exist.
3309 
3310  E.g. ``mcDaughter(0, PDG)`` will return the PDG code of the first MC daughter of the matched MC
3311  particle of the reconstructed particle the function is applied to.
3312 
3313  The meta variable can also be nested: ``mcDaughter(0, mcDaughter(1, PDG))``.
3314  )DOC", Manager::VariableDataType::c_double);
3315  REGISTER_METAVARIABLE("mcMother(variable)", mcMother, R"DOC(
3316  Returns the value of the requested variable for the Monte Carlo mother of the particle.
3317 
3318  Returns NaN if the particle is nullptr, if the particle is not matched to an MC particle,
3319  or if the MC mother does not exist.
3320 
3321  E.g. ``mcMother(PDG)`` will return the PDG code of the MC mother of the matched MC
3322  particle of the reconstructed particle the function is applied to.
3323 
3324  The meta variable can also be nested: ``mcMother(mcMother(PDG))``.
3325  )DOC", Manager::VariableDataType::c_double);
3326  REGISTER_METAVARIABLE("genParticle(index, variable)", genParticle, R"DOC(
3327 [Eventbased] Returns the ``variable`` for the ith generator particle.
3328 The arguments of the function must be the ``index`` of the particle in the MCParticle Array,
3329 and ``variable``, the name of the function or variable for that generator particle.
3330 If ``index`` goes beyond the length of the MCParticles array, NaN will be returned.
3331 
3332 E.g. ``genParticle(0, p)`` returns the total momentum of the first MCParticle, which is
3333 the Upsilon(4S) in a generic decay.
3334 ``genParticle(0, mcDaughter(1, p)`` returns the total momentum of the second daughter of
3335 the first MC Particle, which is the momentum of the second B meson in a generic decay.
3336 )DOC", Manager::VariableDataType::c_double);
3337  REGISTER_METAVARIABLE("genUpsilon4S(variable)", genUpsilon4S, R"DOC(
3338 [Eventbased] Returns the ``variable`` evaluated for the generator-level :math:`\Upsilon(4S)`.
3339 If no generator level :math:`\Upsilon(4S)` exists for the event, NaN will be returned.
3340 
3341 E.g. ``genUpsilon4S(p)`` returns the total momentum of the :math:`\Upsilon(4S)` in a generic decay.
3342 ``genUpsilon4S(mcDaughter(1, p)`` returns the total momentum of the second daughter of the
3343 generator-level :math:`\Upsilon(4S)` (i.e. the momentum of the second B meson in a generic decay.
3344 )DOC", Manager::VariableDataType::c_double);
3345  REGISTER_METAVARIABLE("daughterProductOf(variable)", daughterProductOf,
3346  "Returns product of a variable over all daughters.\n"
3347  "E.g. ``daughterProductOf(extraInfo(SignalProbability))`` returns the product of the SignalProbabilitys of all daughters.", Manager::VariableDataType::c_double);
3348  REGISTER_METAVARIABLE("daughterSumOf(variable)", daughterSumOf,
3349  "Returns sum of a variable over all daughters.\n"
3350  "E.g. ``daughterSumOf(nDaughters)`` returns the number of grand-daughters.", Manager::VariableDataType::c_double);
3351  REGISTER_METAVARIABLE("daughterLowest(variable)", daughterLowest,
3352  "Returns the lowest value of the given variable among all daughters.\n"
3353  "E.g. ``useCMSFrame(daughterLowest(p))`` returns the lowest momentum in CMS frame.", Manager::VariableDataType::c_double);
3354  REGISTER_METAVARIABLE("daughterHighest(variable)", daughterHighest,
3355  "Returns the highest value of the given variable among all daughters.\n"
3356  "E.g. ``useCMSFrame(daughterHighest(p))`` returns the highest momentum in CMS frame.", Manager::VariableDataType::c_double);
3357  REGISTER_METAVARIABLE("daughterDiffOf(i, j, variable)", daughterDiffOf,
3358  "Returns the difference of a variable between the two given daughters.\n"
3359  "E.g. ``useRestFrame(daughterDiffOf(0, 1, p))`` returns the momentum difference between first and second daughter in the rest frame of the given particle.\n"
3360  "(That means that it returns :math:`p_j - p_i`)\n"
3361  "Nota Bene: for the particular case 'variable=phi' you should use the :b2:var:`daughterDiffOfPhi` function.", Manager::VariableDataType::c_double);
3362  REGISTER_METAVARIABLE("mcDaughterDiffOf(i, j, variable)", mcDaughterDiffOf,
3363  "MC matched version of the `daughterDiffOf` function.", Manager::VariableDataType::c_double);
3364  REGISTER_METAVARIABLE("grandDaughterDiffOf(i, j, variable)", grandDaughterDiffOf,
3365  "Returns the difference of a variable between the first daughters of the two given daughters.\n"
3366  "E.g. ``useRestFrame(grandDaughterDiffOf(0, 1, p))`` returns the momentum difference between the first daughters of the first and second daughter in the rest frame of the given particle.\n"
3367  "(That means that it returns :math:`p_j - p_i`)\n"
3368  "Nota Bene: for the particular case 'variable=phi' you should use the :b2:var:`grandDaughterDiffOfPhi` function.", Manager::VariableDataType::c_double);
3369  REGISTER_METAVARIABLE("daughterDiffOfPhi(i, j)", daughterDiffOfPhi,
3370  "Returns the difference in :math:`\\phi` between the two given daughters. The unit of the angle is ``rad``.\n"
3371  "The difference is signed and takes account of the ordering of the given daughters.\n"
3372  "The function returns :math:`\\phi_j - \\phi_i`.", Manager::VariableDataType::c_double);
3373  MAKE_DEPRECATED("daughterDiffOfPhi", false, "release-06-00-00", R"DOC(
3374  The difference of the azimuthal angle :math:`\\phi` of two daughters can be calculated with the generic variable :b2:var:`daughterDiffOf`.)DOC");
3375  REGISTER_METAVARIABLE("mcDaughterDiffOfPhi(i, j)", mcDaughterDiffOfPhi,
3376  "MC matched version of the `daughterDiffOfPhi` function. The unit of the angle is ``rad``", Manager::VariableDataType::c_double);
3377  MAKE_DEPRECATED("mcDaughterDiffOfPhi", false, "release-06-00-00", R"DOC(
3378  The difference of the azimuthal angle :math:`\\phi` of the MC partners of two daughters can be calculated with the generic variable :b2:var:`mcDaughterDiffOf`.)DOC");
3379  REGISTER_METAVARIABLE("grandDaughterDiffOfPhi(i, j)", grandDaughterDiffOfPhi,
3380  "Returns the difference in :math:`\\phi` between the first daughters of the two given daughters. The unit of the angle is ``rad``.\n"
3381  "The difference is signed and takes account of the ordering of the given daughters.\n"
3382  "The function returns :math:`\\phi_j - \\phi_i`.\n", Manager::VariableDataType::c_double);
3383  MAKE_DEPRECATED("grandDaughterDiffOfPhi", false, "release-06-00-00", R"DOC(
3384  The difference of the azimuthal angle :math:`\\phi` of two granddaughters can be calculated with the generic variable :b2:var:`grandDaughterDiffOf`.)DOC");
3385  REGISTER_METAVARIABLE("daughterDiffOfClusterPhi(i, j)", daughterDiffOfClusterPhi,
3386  "Returns the difference in :math:`\\phi` between the ECLClusters of two given daughters. The unit of the angle is ``rad``.\n"
3387  "The difference is signed and takes account of the ordering of the given daughters.\n"
3388  "The function returns :math:`\\phi_j - \\phi_i`.\n"
3389  "The function returns NaN if at least one of the daughters is not matched to or not based on an ECLCluster.", Manager::VariableDataType::c_double);
3390  MAKE_DEPRECATED("daughterDiffOfClusterPhi", false, "release-06-00-00", R"DOC(
3391  The difference of the azimuthal angle :math:`\\phi` of the related ECL clusters of two daughters can be calculated with the generic variable :b2:var:`daughterDiffOf`.)DOC");
3392  REGISTER_METAVARIABLE("grandDaughterDiffOfClusterPhi(i, j)", grandDaughterDiffOfClusterPhi,
3393  "Returns the difference in :math:`\\phi` between the ECLClusters of the daughters of the two given daughters. The unit of the angle is ``rad``.\n"
3394  "The difference is signed and takes account of the ordering of the given daughters.\n"
3395  "The function returns :math:`\\phi_j - \\phi_i`.\n"
3396  "The function returns NaN if at least one of the daughters is not matched to or not based on an ECLCluster.\n", Manager::VariableDataType::c_double);
3397  MAKE_DEPRECATED("grandDaughterDiffOfClusterPhi", false, "release-06-00-00", R"DOC(
3398  The difference of the azimuthal angle :math:`\\phi` of the related ECL clusters of two granddaughters can be calculated with the generic variable :b2:var:`grandDaughterDiffOf`.)DOC");
3399  REGISTER_METAVARIABLE("daughterDiffOfPhiCMS(i, j)", daughterDiffOfPhiCMS,
3400  "Returns the difference in :math:`\\phi` between the two given daughters in the CMS frame. The unit of the angle is ``rad``.\n"
3401  "The difference is signed and takes account of the ordering of the given daughters.\n"
3402  "The function returns :math:`\\phi_j - \\phi_i`.", Manager::VariableDataType::c_double);
3403  MAKE_DEPRECATED("daughterDiffOfPhiCMS", false, "release-06-00-00", R"DOC(
3404  The difference of the azimuthal angle :math:`\\phi` of two daughters in the CMS frame can be calculated with the generic variable :b2:var:`daughterDiffOf`.)DOC");
3405  REGISTER_METAVARIABLE("mcDaughterDiffOfPhiCMS(i, j)", daughterDiffOfPhiCMS,
3406  "MC matched version of the `daughterDiffOfPhiCMS` function. The unit of the angle is ``rad``", Manager::VariableDataType::c_double);
3407  MAKE_DEPRECATED("mcDaughterDiffOfPhiCMS", false, "release-06-00-00", R"DOC(
3408  The difference of the azimuthal angle :math:`\\phi` of the MC partners of two daughters in the CMS frame can be calculated with the generic variable :b2:var:`mcDaughterDiffOf`.)DOC");
3409  REGISTER_METAVARIABLE("daughterDiffOfClusterPhiCMS(i, j)", daughterDiffOfClusterPhiCMS,
3410  "Returns the difference in :math:`\\phi` between the ECLClusters of two given daughters in the CMS frame. The unit of the angle is ``rad``.\n"
3411  "The difference is signed and takes account of the ordering of the given daughters.\n"
3412  "The function returns :math:`\\phi_j - \\phi_i``.\n"
3413  "The function returns NaN if at least one of the daughters is not matched to or not based on an ECLCluster.", Manager::VariableDataType::c_double);
3414  MAKE_DEPRECATED("daughterDiffOfClusterPhiCMS", false, "release-06-00-00", R"DOC(
3415  The difference of the azimuthal angle :math:`\\phi` of the related ECL clusters of two daughters in the CMS frame can be calculated with the generic variable :b2:var:`daughterDiffOf`.)DOC");
3416  REGISTER_METAVARIABLE("daughterNormDiffOf(i, j, variable)", daughterNormDiffOf,
3417  "Returns the normalized difference of a variable between the two given daughters.\n"
3418  "E.g. ``daughterNormDiffOf(0, 1, p)`` returns the normalized momentum difference between first and second daughter in the lab frame.", Manager::VariableDataType::c_double);
3419  REGISTER_METAVARIABLE("daughterMotherDiffOf(i, variable)", daughterMotherDiffOf,
3420  "Returns the difference of a variable between the given daughter and the mother particle itself.\n"
3421  "E.g. ``useRestFrame(daughterMotherDiffOf(0, p))`` returns the momentum difference between the given particle and its first daughter in the rest frame of the mother.", Manager::VariableDataType::c_double);
3422  REGISTER_METAVARIABLE("daughterMotherNormDiffOf(i, variable)", daughterMotherNormDiffOf,
3423  "Returns the normalized difference of a variable between the given daughter and the mother particle itself.\n"
3424  "E.g. ``daughterMotherNormDiffOf(1, p)`` returns the normalized momentum difference between the given particle and its second daughter in the lab frame.", Manager::VariableDataType::c_double);
3425  REGISTER_METAVARIABLE("daughterAngle(daughterIndex_1, daughterIndex_2[, daughterIndex_3])", daughterAngle, R"DOC(
3426  Returns the angle in between any pair of particles belonging to the same decay tree.
3427  The unit of the angle is ``rad``.
3428 
3429  The particles are identified via generalized daughter indexes, which are simply colon-separated lists of
3430  daughter indexes, ordered starting from the root particle. For example, ``0:1:3`` identifies the fourth
3431  daughter (3) of the second daughter (1) of the first daughter (0) of the mother particle. ``1`` simply
3432  identifies the second daughter of the root particle.
3433 
3434  Both two and three generalized indexes can be given to ``daughterAngle``. If two indices are given, the
3435  variable returns the angle between the momenta of the two given particles. If three indices are given, the
3436  variable returns the angle between the momentum of the third particle and a vector which is the sum of the
3437  first two daughter momenta.
3438 
3439  .. tip::
3440  ``daughterAngle(0, 3)`` will return the angle between the first and fourth daughter.
3441  ``daughterAngle(0, 1, 3)`` will return the angle between the fourth daughter and the sum of the first and
3442  second daughter.
3443  ``daughterAngle(0:0, 3:0)`` will return the angle between the first daughter of the first daughter, and
3444  the first daughter of the fourth daughter.
3445 
3446  )DOC", Manager::VariableDataType::c_double);
3447  REGISTER_METAVARIABLE("mcDaughterAngle(daughterIndex_1, daughterIndex_2, [daughterIndex_3])", mcDaughterAngle,
3448  "MC matched version of the `daughterAngle` function. Also works if applied directly to MC particles. The unit of the angle is ``rad``", Manager::VariableDataType::c_double);
3449  REGISTER_VARIABLE("grandDaughterDecayAngle(i, j)", grandDaughterDecayAngle,
3450  "Returns the decay angle of the granddaughter in the daughter particle's rest frame.\n"
3451  "It is calculated with respect to the reverted momentum vector of the particle.\n"
3452  "Two arguments representing the daughter and granddaughter indices have to be provided as arguments.\n\n", "rad");
3453  REGISTER_VARIABLE("daughterClusterAngleInBetween(i, j)", daughterClusterAngleInBetween,
3454  "Returns the angle between clusters associated to the two daughters."
3455  "If two indices given: returns the angle between the momenta of the clusters associated to the two given daughters."
3456  "If three indices given: returns the angle between the momentum of the third particle's cluster and a vector "
3457  "which is the sum of the first two daughter's cluster momenta."
3458  "Returns nan if any of the daughters specified don't have an associated cluster."
3459  "The arguments in the argument vector must be integers corresponding to the ith and jth (and kth) daughters.\n\n", "rad");
3460  REGISTER_METAVARIABLE("daughterInvM(i[, j, ...])", daughterInvM, R"DOC(
3461  Returns the invariant mass adding the Lorentz vectors of the given daughters. The unit of the invariant mass is GeV/:math:`\text{c}^2`
3462  E.g. ``daughterInvM(0, 1, 2)`` returns the invariant Mass :math:`m = \sqrt{(p_0 + p_1 + p_2)^2}` of the first, second and third daughter.
3463 
3464  Daughters from different generations of the decay tree can be combined using generalized daughter indexes,
3465  which are simply colon-separated daughter indexes for each generation, starting from the root particle. For
3466  example, ``0:1:3`` identifies the fourth daughter (3) of the second daughter (1) of the first daughter(0) of
3467  the mother particle.
3468 
3469  Returns NaN if the given daughter-index is out of bound (>= number of daughters))DOC", Manager::VariableDataType::c_double);
3470  REGISTER_METAVARIABLE("extraInfo(name)", extraInfo,
3471  "Returns extra info stored under the given name.\n"
3472  "The extraInfo has to be set by a module first.\n"
3473  "E.g. ``extraInfo(SignalProbability)`` returns the SignalProbability calculated by the ``MVAExpert`` module.\n"
3474  "If nothing is set under the given name or if the particle is a nullptr, NaN is returned.\n"
3475  "In the latter case please use `eventExtraInfo` if you want to access an EventExtraInfo variable.", Manager::VariableDataType::c_double);
3476  REGISTER_METAVARIABLE("eventExtraInfo(name)", eventExtraInfo,
3477  "[Eventbased] Returns extra info stored under the given name in the event extra info.\n"
3478  "The extraInfo has to be set first by another module like MVAExpert in event mode.\n"
3479  "If nothing is set under this name, NaN is returned.", Manager::VariableDataType::c_double);
3480  REGISTER_METAVARIABLE("eventCached(variable)", eventCached,
3481  "[Eventbased] Returns value of event-based variable and caches this value in the EventExtraInfo.\n"
3482  "The result of second call to this variable in the same event will be provided from the cache.\n"
3483  "It is recommended to use this variable in order to declare custom aliases as event-based. This is "
3484  "necessary if using the eventwise mode of variablesToNtuple).", Manager::VariableDataType::c_double);
3485  REGISTER_METAVARIABLE("particleCached(variable)", particleCached,
3486  "Returns value of given variable and caches this value in the ParticleExtraInfo of the provided particle.\n"
3487  "The result of second call to this variable on the same particle will be provided from the cache.", Manager::VariableDataType::c_double);
3488  REGISTER_METAVARIABLE("modulo(variable, n)", modulo,
3489  "Returns rest of division of variable by n.", Manager::VariableDataType::c_int);
3490  REGISTER_METAVARIABLE("abs(variable)", abs,
3491  "Returns absolute value of the given variable.\n"
3492  "E.g. abs(mcPDG) returns the absolute value of the mcPDG, which is often useful for cuts.", Manager::VariableDataType::c_double);
3493  REGISTER_METAVARIABLE("max(var1,var2)", max, "Returns max value of two variables.\n", Manager::VariableDataType::c_double);
3494  REGISTER_METAVARIABLE("min(var1,var2)", min, "Returns min value of two variables.\n", Manager::VariableDataType::c_double);
3495  REGISTER_METAVARIABLE("sin(variable)", sin, "Returns sine value of the given variable.", Manager::VariableDataType::c_double);
3496  REGISTER_METAVARIABLE("asin(variable)", asin, "Returns arcsine of the given variable. The unit of the asin() is ``rad``", Manager::VariableDataType::c_double);
3497  REGISTER_METAVARIABLE("cos(variable)", cos, "Returns cosine value of the given variable.", Manager::VariableDataType::c_double);
3498  REGISTER_METAVARIABLE("acos(variable)", acos, "Returns arccosine value of the given variable. The unit of the acos() is ``rad``", Manager::VariableDataType::c_double);
3499  REGISTER_METAVARIABLE("tan(variable)", tan, "Returns tangent value of the given variable.", Manager::VariableDataType::c_double);
3500  REGISTER_METAVARIABLE("atan(variable)", atan, "Returns arctangent value of the given variable. The unit of the atan() is ``rad``", Manager::VariableDataType::c_double);
3501  REGISTER_METAVARIABLE("exp(variable)", exp, "Returns exponential evaluated for the given variable.", Manager::VariableDataType::c_double);
3502  REGISTER_METAVARIABLE("log(variable)", log, "Returns natural logarithm evaluated for the given variable.", Manager::VariableDataType::c_double);
3503  REGISTER_METAVARIABLE("log10(variable)", log10, "Returns base-10 logarithm evaluated for the given variable.", Manager::VariableDataType::c_double);
3504  REGISTER_METAVARIABLE("isNAN(variable)", isNAN,
3505  "Returns true if variable value evaluates to nan (determined via std::isnan(double)).\n"
3506  "Useful for debugging.", Manager::VariableDataType::c_bool);
3507  REGISTER_METAVARIABLE("ifNANgiveX(variable, x)", ifNANgiveX,
3508  "Returns x (has to be a number) if variable value is nan (determined via std::isnan(double)).\n"
3509  "Useful for technical purposes while training MVAs.", Manager::VariableDataType::c_double);
3510  REGISTER_METAVARIABLE("isInfinity(variable)", isInfinity,
3511  "Returns true if variable value evaluates to infinity (determined via std::isinf(double)).\n"
3512  "Useful for debugging.", Manager::VariableDataType::c_bool);
3513  REGISTER_METAVARIABLE("unmask(variable, flag1, flag2, ...)", unmask,
3514  "unmask(variable, flag1, flag2, ...) or unmask(variable, mask) sets certain bits in the variable to zero.\n"
3515  "For example, if you want to set the second, fourth and fifth bits to zero, you could call \n"
3516  "``unmask(variable, 2, 8, 16)`` or ``unmask(variable, 26)``.\n"
3517  "", Manager::VariableDataType::c_double);
3518  REGISTER_METAVARIABLE("conditionalVariableSelector(cut, variableIfTrue, variableIfFalse)", conditionalVariableSelector,
3519  "Returns one of the two supplied variables, depending on whether the particle passes the supplied cut.\n"
3520  "The first variable is returned if the particle passes the cut, and the second variable is returned otherwise.", Manager::VariableDataType::c_double);
3521  REGISTER_METAVARIABLE("pValueCombination(p1, p2, ...)", pValueCombination,
3522  "Returns the combined p-value of the provided p-values according to the formula given in `Nucl. Instr. and Meth. A 411 (1998) 449 <https://doi.org/10.1016/S0168-9002(98)00293-9>`_ .\n"
3523  "If any of the p-values is invalid, i.e. smaller than zero, -1 is returned.", Manager::VariableDataType::c_double);
3524  REGISTER_METAVARIABLE("veto(particleList, cut, pdgCode = 11)", veto,
3525  "Combines current particle with particles from the given particle list and returns 1 if the combination passes the provided cut. \n"
3526  "For instance one can apply this function on a signal Photon and provide a list of all photons in the rest of event and a cut \n"
3527  "around the neutral Pion mass (e.g. ``0.130 < M < 0.140``). \n"
3528  "If a combination of the signal Photon with a ROE photon fits this criteria, hence looks like a neutral pion, the veto-Metavariable will return 1", Manager::VariableDataType::c_bool);
3529  REGISTER_METAVARIABLE("matchedMC(variable)", matchedMC,
3530  "Returns variable output for the matched MCParticle by constructing a temporary Particle from it.\n"
3531  "This may not work too well if your variable requires accessing daughters of the particle.\n"
3532  "E.g. ``matchedMC(p)`` returns the total momentum of the related MCParticle.\n"
3533  "Returns NaN if no matched MCParticle exists.", Manager::VariableDataType::c_double);
3534  REGISTER_METAVARIABLE("clusterBestMatchedMCParticle(variable)", clusterBestMatchedMCParticle,
3535  "Returns variable output for the MCParticle that is best-matched with the ECLCluster of the given Particle.\n"
3536  "E.g. To get the energy of the MCParticle that matches best with an ECLCluster, one could use ``clusterBestMatchedMCParticle(E)``\n"
3537  "When the variable is called for ``gamma`` and if the ``gamma`` is matched with MCParticle, it works same as `matchedMC`.\n"
3538  "If the variable is called for ``gamma`` that fails to match with an MCParticle, it provides the mdst-level MCMatching information abouth the ECLCluster.\n"
3539  "Returns NaN if the particle is not matched to an ECLCluster, or if the ECLCluster has no matching MCParticles", Manager::VariableDataType::c_double);
3540  REGISTER_METAVARIABLE("countInList(particleList, cut='')", countInList, "[Eventbased] "
3541  "Returns number of particle which pass given in cut in the specified particle list.\n"
3542  "Useful for creating statistics about the number of particles in a list.\n"
3543  "E.g. ``countInList(e+, isSignal == 1)`` returns the number of correctly reconstructed electrons in the event.\n"
3544  "The variable is event-based and does not need a valid particle pointer as input.", Manager::VariableDataType::c_int);
3545  REGISTER_METAVARIABLE("getVariableByRank(particleList, rankedVariableName, variableName, rank)", getVariableByRank, R"DOC(
3546  [Eventbased] Returns the value of ``variableName`` for the candidate in the ``particleList`` with the requested ``rank``.
3547 
3548  .. note::
3549  The `BestCandidateSelection` module available via `rankByHighest` / `rankByLowest` has to be used before.
3550 
3551  .. warning::
3552  The first candidate matching the given rank is used.
3553  Thus, it is not recommended to use this variable in conjunction with ``allowMultiRank`` in the `BestCandidateSelection` module.
3554 
3555  The suffix ``_rank`` is automatically added to the argument ``rankedVariableName``,
3556  which either has to be the name of the variable used to order the candidates or the selected outputVariable name without the ending ``_rank``.
3557  This means that your selected name for the rank variable has to end with ``_rank``.
3558 
3559  An example of this variable's usage is given in the tutorial `B2A602-BestCandidateSelection <https://gitlab.desy.de/belle2/software/basf2/-/tree/main/analysis/examples/tutorials/B2A602-BestCandidateSelection.py>`_
3560  )DOC", Manager::VariableDataType::c_double);
3561  REGISTER_VARIABLE("matchedMCHasPDG(PDGCode)", matchedMCHasPDG,
3562  "Returns if the absolute value of the PDGCode of the MCParticle related to the Particle matches a given PDGCode."
3563  "Returns 0/NAN/1 if PDGCode does not match/is not available/ matches");
3564  REGISTER_METAVARIABLE("numberOfNonOverlappingParticles(pList1, pList2, ...)", numberOfNonOverlappingParticles,
3565  "Returns the number of non-overlapping particles in the given particle lists"
3566  "Useful to check if there is additional physics going on in the detector if one reconstructed the Y4S", Manager::VariableDataType::c_int);
3567  REGISTER_METAVARIABLE("totalEnergyOfParticlesInList(particleListName)", totalEnergyOfParticlesInList,
3568  "Returns the total energy of particles in the given particle List. The unit of the energy is ``GeV``", Manager::VariableDataType::c_double);
3569  REGISTER_METAVARIABLE("totalPxOfParticlesInList(particleListName)", totalPxOfParticlesInList,
3570  "Returns the total momentum Px of particles in the given particle List. The unit of the momentum is ``GeV/c``", Manager::VariableDataType::c_double);
3571  REGISTER_METAVARIABLE("totalPyOfParticlesInList(particleListName)", totalPyOfParticlesInList,
3572  "Returns the total momentum Py of particles in the given particle List. The unit of the momentum is ``GeV/c``", Manager::VariableDataType::c_double);
3573  REGISTER_METAVARIABLE("totalPzOfParticlesInList(particleListName)", totalPzOfParticlesInList,
3574  "Returns the total momentum Pz of particles in the given particle List. The unit of the momentum is ``GeV/c``", Manager::VariableDataType::c_double);
3575  REGISTER_METAVARIABLE("invMassInLists(pList1, pList2, ...)", invMassInLists,
3576  "Returns the invariant mass of the combination of particles in the given particle lists. The unit of the invariant mass is GeV/:math:`\\text{c}^2` ", Manager::VariableDataType::c_double);
3577  REGISTER_METAVARIABLE("totalECLEnergyOfParticlesInList(particleListName)", totalECLEnergyOfParticlesInList,
3578  "Returns the total ECL energy of particles in the given particle List. The unit of the energy is ``GeV``", Manager::VariableDataType::c_double);
3579  REGISTER_METAVARIABLE("maxPtInList(particleListName)", maxPtInList,
3580  "Returns maximum transverse momentum Pt in the given particle List. The unit of the transverse momentum is ``GeV/c``", Manager::VariableDataType::c_double);
3581  REGISTER_METAVARIABLE("eclClusterSpecialTrackMatched(cut)", eclClusterTrackMatchedWithCondition,
3582  "Returns if at least one Track that satisfies the given condition is related to the ECLCluster of the Particle.", Manager::VariableDataType::c_double);
3583  REGISTER_METAVARIABLE("averageValueInList(particleListName, variable)", averageValueInList,
3584  "Returns the arithmetic mean of the given variable of the particles in the given particle list.", Manager::VariableDataType::c_double);
3585  REGISTER_METAVARIABLE("medianValueInList(particleListName, variable)", medianValueInList,
3586  "Returns the median value of the given variable of the particles in the given particle list.", Manager::VariableDataType::c_double);
3587  REGISTER_METAVARIABLE("angleToClosestInList(particleListName)", angleToClosestInList,
3588  "Returns the angle between this particle and the closest particle (smallest opening angle) in the list provided. The unit of the angle is ``rad`` ", Manager::VariableDataType::c_double);
3589  REGISTER_METAVARIABLE("closestInList(particleListName, variable)", closestInList,
3590  "Returns `variable` for the closest particle (smallest opening angle) in the list provided.", Manager::VariableDataType::c_double);
3591  REGISTER_METAVARIABLE("angleToMostB2BInList(particleListName)", angleToMostB2BInList,
3592  "Returns the angle between this particle and the most back-to-back particle (closest opening angle to 180) in the list provided. The unit of the angle is ``rad`` ", Manager::VariableDataType::c_double);
3593  REGISTER_METAVARIABLE("mostB2BInList(particleListName, variable)", mostB2BInList,
3594  "Returns `variable` for the most back-to-back particle (closest opening angle to 180) in the list provided.", Manager::VariableDataType::c_double);
3595  REGISTER_METAVARIABLE("maxOpeningAngleInList(particleListName)", maxOpeningAngleInList,
3596  "Returns maximum opening angle in the given particle List. The unit of the angle is ``rad`` ", Manager::VariableDataType::c_double);
3597  REGISTER_METAVARIABLE("daughterCombination(variable, daughterIndex_1, daughterIndex_2 ... daughterIndex_n)", daughterCombination,R"DOC(
3598 Returns a ``variable`` function only of the 4-momentum calculated on an arbitrary set of (grand)daughters.
3599 
3600 .. warning::
3601  ``variable`` can only be a function of the daughters' 4-momenta.
3602 
3603 Daughters from different generations of the decay tree can be combined using generalized daughter indexes, which are simply colon-separated
3604 the list of daughter indexes, starting from the root particle: for example, ``0:1:3`` identifies the fourth
3605 daughter (3) of the second daughter (1) of the first daughter (0) of the mother particle.
3606 
3607 .. tip::
3608  ``daughterCombination(M, 0, 3, 4)`` will return the invariant mass of the system made of the first, fourth and fifth daughter of particle.
3609  ``daughterCombination(M, 0:0, 3:0)`` will return the invariant mass of the system made of the first daughter of the first daughter and the first daughter of the fourth daughter.
3610 
3611 )DOC", Manager::VariableDataType::c_double);
3612  REGISTER_METAVARIABLE("useAlternativeDaughterHypothesis(variable, daughterIndex_1:newMassHyp_1, ..., daughterIndex_n:newMassHyp_n)", useAlternativeDaughterHypothesis,R"DOC(
3613 Returns a ``variable`` calculated using new mass hypotheses for (some of) the particle's daughters.
3614 
3615 .. warning::
3616  ``variable`` can only be a function of the particle 4-momentum, which is re-calculated as the sum of the daughters' 4-momenta, and the daughters' 4-momentum.
3617  This means that if you made a kinematic fit without updating the daughters' momenta, the result of this variable will not reflect the effect of the kinematic fit.
3618  Also, the track fit is not performed again: the variable only re-calculates the 4-vectors using different mass assumptions.
3619  In the variable, a copy of the given particle is created with daughters' alternative mass assumption (i.e. the original particle and daughters are not changed).
3620 
3621 .. warning::
3622  Generalized daughter indexes are not supported (yet!): this variable can be used only on first-generation daughters.
3623 
3624 .. tip::
3625  ``useAlternativeDaughterHypothesis(M, 0:K+, 2:pi-)`` will return the invariant mass of the particle assuming that the first daughter is a kaon and the third is a pion, instead of whatever was used in reconstructing the decay.
3626  ``useAlternativeDaughterHypothesis(mRecoil, 1:p+)`` will return the recoil mass of the particle assuming that the second daughter is a proton instead of whatever was used in reconstructing the decay.
3627 
3628 )DOC", Manager::VariableDataType::c_double);
3629  REGISTER_METAVARIABLE("varForFirstMCAncestorOfType(type, variable)",varForFirstMCAncestorOfType,R"DOC(Returns requested variable of the first ancestor of the given type.
3630 Ancestor type can be set up by PDG code or by particle name (check evt.pdl for valid particle names))DOC", Manager::VariableDataType::c_double)
3631 
3632  }
3634 }
DataType Angle(const B2Vector3< DataType > &q) const
The angle w.r.t.
Definition: B2Vector3.h:302
int getPDGCode() const
PDG code.
Definition: Const.h:464
static const ChargedStable pion
charged pion particle
Definition: Const.h:652
static const ChargedStable electron
electron particle
Definition: Const.h:650
EHypothesisBit
The hypothesis bits for this ECLCluster (Connected region (CR) is split using this hypothesis.
Definition: ECLCluster.h:31
@ c_nPhotons
CR is split into n photons (N1)
static std::unique_ptr< GeneralCut > compile(const std::string &cut)
Creates an instance of a cut and returns a unique_ptr to it, if you need a copy-able object instead y...
Definition: GeneralCut.h:84
@ c_Initial
bit 5: Particle is initial such as e+ or e- and not going to Geant4
Definition: MCParticle.h:57
@ c_PrimaryParticle
bit 0: Particle is primary particle.
Definition: MCParticle.h:47
@ c_IsVirtual
bit 4: Particle is virtual and not going to Geant4.
Definition: MCParticle.h:55
static std::string makeROOTCompatible(std::string str)
Remove special characters that ROOT dislikes in branch names, e.g.
const MCParticle * getMCParticle() const
Returns the pointer to the MCParticle object that was used to create this Particle (ParticleType == c...
Definition: Particle.cc:946
EParticleSourceObject
particle source enumerators
Definition: Particle.h:81
const Particle * getParticleFromGeneralizedIndexString(const std::string &generalizedIndex) const
Explores the decay tree of the particle and returns the (grand^n)daughter identified by a generalized...
Definition: Particle.cc:960
@ c_Unflavored
Is its own antiparticle or we don't know whether it is a particle/antiparticle.
Definition: Particle.h:94
@ c_Flavored
Is either particle or antiparticle.
Definition: Particle.h:95
const Particle * getDaughter(unsigned i) const
Returns a pointer to the i-th daughter particle.
Definition: Particle.cc:635
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:113
const Var * getVariable(std::string name)
Get the variable belonging to the given key.
Definition: Manager.cc:57
static Manager & Instance()
get singleton instance.
Definition: Manager.cc:25
Class to store variables with their name which were sent to the logging service.
#define MAKE_DEPRECATED(name, make_fatal, version, description)
Registers a variable as deprecated.
Definition: Manager.h:443
B2Vector3< double > B2Vector3D
typedef for common usage with double
Definition: B2Vector3.h:516
double charge(int pdgCode)
Returns electric charge of a particle with given pdg code.
Definition: EvtPDLUtil.cc:44
bool hasAntiParticle(int pdgCode)
Checks if the particle with given pdg code has an anti-particle or not.
Definition: EvtPDLUtil.cc:12
Particle * copyParticle(const Particle *original)
Function takes argument Particle and creates a copy of it and copies of all its (grand-)^n-daughters.
Definition: ParticleCopy.cc:18
Abstract base class for different kinds of events.
Definition: ClusterUtils.h:23