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