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