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