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