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