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