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