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