Belle II Software development
MetaVariables.cc
1/**************************************************************************
2 * basf2 (Belle II Analysis Software Framework) *
3 * Author: The Belle II Collaboration *
4 * *
5 * See git log for contributors and copyright holders. *
6 * This file is licensed under LGPL-3.0, see LICENSE.md. *
7 **************************************************************************/
8
9// Own header.
10#include <analysis/variables/MetaVariables.h>
11#include <analysis/variables/MCTruthVariables.h>
12
13#include <analysis/VariableManager/Utility.h>
14#include <analysis/dataobjects/Particle.h>
15#include <analysis/dataobjects/ParticleList.h>
16#include <analysis/dataobjects/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 } else if (std::holds_alternative<bool>(var_result1))
1691 {
1692 val1 = std::get<bool>(var_result1);
1693 } else
1694 {
1695 B2FATAL("A variable in meta function max holds no double, int or bool values");
1696 }
1697 if (std::holds_alternative<double>(var_result2))
1698 {
1699 val2 = std::get<double>(var_result2);
1700 } else if (std::holds_alternative<int>(var_result2))
1701 {
1702 val2 = std::get<int>(var_result2);
1703 } else if (std::holds_alternative<bool>(var_result2))
1704 {
1705 val2 = std::get<bool>(var_result2);
1706 } else
1707 {
1708 B2FATAL("A variable in meta function max holds no double, int or bool values");
1709 }
1710 return std::max(val1, val2);
1711 };
1712 return func;
1713 } else {
1714 B2FATAL("Wrong number of arguments for meta function max");
1715 }
1716 }
1717
1718 Manager::FunctionPtr min(const std::vector<std::string>& arguments)
1719 {
1720 if (arguments.size() == 2) {
1721 const Variable::Manager::Var* var1 = Manager::Instance().getVariable(arguments[0]);
1722 const Variable::Manager::Var* var2 = Manager::Instance().getVariable(arguments[1]);
1723
1724 if (!var1 or !var2)
1725 B2FATAL("One or both of the used variables doesn't exist!");
1726
1727 auto func = [var1, var2](const Particle * particle) -> double {
1728 double val1, val2;
1729 auto var_result1 = var1->function(particle);
1730 auto var_result2 = var2->function(particle);
1731 if (std::holds_alternative<double>(var_result1))
1732 {
1733 val1 = std::get<double>(var_result1);
1734 } else if (std::holds_alternative<int>(var_result1))
1735 {
1736 val1 = std::get<int>(var_result1);
1737 } else if (std::holds_alternative<bool>(var_result1))
1738 {
1739 val1 = std::get<bool>(var_result1);
1740 } else
1741 {
1742 B2FATAL("A variable in meta function min holds no double, int or bool values");
1743 }
1744 if (std::holds_alternative<double>(var_result2))
1745 {
1746 val2 = std::get<double>(var_result2);
1747 } else if (std::holds_alternative<int>(var_result2))
1748 {
1749 val2 = std::get<int>(var_result2);
1750 } else if (std::holds_alternative<bool>(var_result2))
1751 {
1752 val2 = std::get<bool>(var_result2);
1753 } else
1754 {
1755 B2FATAL("A variable in meta function min holds no double, int or bool values");
1756 }
1757 return std::min(val1, val2);
1758 };
1759 return func;
1760 } else {
1761 B2FATAL("Wrong number of arguments for meta function min");
1762 }
1763 }
1764
1765 Manager::FunctionPtr sin(const std::vector<std::string>& arguments)
1766 {
1767 if (arguments.size() == 1) {
1768 const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
1769 auto func = [var](const Particle * particle) -> double {
1770 auto var_result = var->function(particle);
1771 if (std::holds_alternative<double>(var_result))
1772 return std::sin(std::get<double>(var_result));
1773 else if (std::holds_alternative<int>(var_result))
1774 return std::sin(std::get<int>(var_result));
1775 else return Const::doubleNaN;
1776 };
1777 return func;
1778 } else {
1779 B2FATAL("Wrong number of arguments for meta function sin");
1780 }
1781 }
1782
1783 Manager::FunctionPtr asin(const std::vector<std::string>& arguments)
1784 {
1785 if (arguments.size() == 1) {
1786 const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
1787 auto func = [var](const Particle * particle) -> double {
1788 auto var_result = var->function(particle);
1789 if (std::holds_alternative<double>(var_result))
1790 return std::asin(std::get<double>(var_result));
1791 else if (std::holds_alternative<int>(var_result))
1792 return std::asin(std::get<int>(var_result));
1793 else return Const::doubleNaN;
1794 };
1795 return func;
1796 } else {
1797 B2FATAL("Wrong number of arguments for meta function asin");
1798 }
1799 }
1800
1801 Manager::FunctionPtr cos(const std::vector<std::string>& arguments)
1802 {
1803 if (arguments.size() == 1) {
1804 const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
1805 auto func = [var](const Particle * particle) -> double {
1806 auto var_result = var->function(particle);
1807 if (std::holds_alternative<double>(var_result))
1808 return std::cos(std::get<double>(var_result));
1809 else if (std::holds_alternative<int>(var_result))
1810 return std::cos(std::get<int>(var_result));
1811 else return Const::doubleNaN;
1812 };
1813 return func;
1814 } else {
1815 B2FATAL("Wrong number of arguments for meta function cos");
1816 }
1817 }
1818
1819 Manager::FunctionPtr acos(const std::vector<std::string>& arguments)
1820 {
1821 if (arguments.size() == 1) {
1822 const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
1823 auto func = [var](const Particle * particle) -> double {
1824 auto var_result = var->function(particle);
1825 if (std::holds_alternative<double>(var_result))
1826 return std::acos(std::get<double>(var_result));
1827 else if (std::holds_alternative<int>(var_result))
1828 return std::acos(std::get<int>(var_result));
1829 else return Const::doubleNaN;
1830 };
1831 return func;
1832 } else {
1833 B2FATAL("Wrong number of arguments for meta function acos");
1834 }
1835 }
1836
1837 Manager::FunctionPtr tan(const std::vector<std::string>& arguments)
1838 {
1839 if (arguments.size() == 1) {
1840 const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
1841 auto func = [var](const Particle * particle) -> double { return std::tan(std::get<double>(var->function(particle))); };
1842 return func;
1843 } else {
1844 B2FATAL("Wrong number of arguments for meta function tan");
1845 }
1846 }
1847
1848 Manager::FunctionPtr atan(const std::vector<std::string>& arguments)
1849 {
1850 if (arguments.size() == 1) {
1851 const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
1852 auto func = [var](const Particle * particle) -> double { return std::atan(std::get<double>(var->function(particle))); };
1853 return func;
1854 } else {
1855 B2FATAL("Wrong number of arguments for meta function atan");
1856 }
1857 }
1858
1859 Manager::FunctionPtr exp(const std::vector<std::string>& arguments)
1860 {
1861 if (arguments.size() == 1) {
1862 const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
1863 auto func = [var](const Particle * particle) -> double {
1864 auto var_result = var->function(particle);
1865 if (std::holds_alternative<double>(var_result))
1866 return std::exp(std::get<double>(var_result));
1867 else if (std::holds_alternative<int>(var_result))
1868 return std::exp(std::get<int>(var_result));
1869 else return Const::doubleNaN;
1870 };
1871 return func;
1872 } else {
1873 B2FATAL("Wrong number of arguments for meta function exp");
1874 }
1875 }
1876
1877 Manager::FunctionPtr log(const std::vector<std::string>& arguments)
1878 {
1879 if (arguments.size() == 1) {
1880 const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
1881 auto func = [var](const Particle * particle) -> double {
1882 auto var_result = var->function(particle);
1883 if (std::holds_alternative<double>(var_result))
1884 return std::log(std::get<double>(var_result));
1885 else if (std::holds_alternative<int>(var_result))
1886 return std::log(std::get<int>(var_result));
1887 else return Const::doubleNaN;
1888 };
1889 return func;
1890 } else {
1891 B2FATAL("Wrong number of arguments for meta function log");
1892 }
1893 }
1894
1895 Manager::FunctionPtr log10(const std::vector<std::string>& arguments)
1896 {
1897 if (arguments.size() == 1) {
1898 const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
1899 auto func = [var](const Particle * particle) -> double {
1900 auto var_result = var->function(particle);
1901 if (std::holds_alternative<double>(var_result))
1902 return std::log10(std::get<double>(var_result));
1903 else if (std::holds_alternative<int>(var_result))
1904 return std::log10(std::get<int>(var_result));
1905 else return Const::doubleNaN;
1906 };
1907 return func;
1908 } else {
1909 B2FATAL("Wrong number of arguments for meta function log10");
1910 }
1911 }
1912
1913 Manager::FunctionPtr originalParticle(const std::vector<std::string>& arguments)
1914 {
1915 if (arguments.size() == 1) {
1916 const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
1917 auto func = [var](const Particle * particle) -> double {
1918 if (particle == nullptr)
1919 return Const::doubleNaN;
1920
1921 StoreArray<Particle> particles;
1922 if (!particle->hasExtraInfo("original_index"))
1923 return Const::doubleNaN;
1924
1925 auto originalParticle = particles[particle->getExtraInfo("original_index")];
1926 if (!originalParticle)
1927 return Const::doubleNaN;
1928 auto var_result = var->function(originalParticle);
1929 if (std::holds_alternative<double>(var_result))
1930 {
1931 return std::get<double>(var_result);
1932 } else if (std::holds_alternative<int>(var_result))
1933 {
1934 return std::get<int>(var_result);
1935 } else if (std::holds_alternative<bool>(var_result))
1936 {
1937 return std::get<bool>(var_result);
1938 } else return Const::doubleNaN;
1939 };
1940 return func;
1941 } else {
1942 B2FATAL("Wrong number of arguments for meta function originalParticle");
1943 }
1944 }
1945
1946 Manager::FunctionPtr daughter(const std::vector<std::string>& arguments)
1947 {
1948 if (arguments.size() == 2) {
1949 auto daughterFunction = convertToDaughterIndex({arguments[0]});
1950 const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[1]);
1951 auto func = [var, daughterFunction](const Particle * particle) -> double {
1952 if (particle == nullptr)
1953 return Const::doubleNaN;
1954 int daughterNumber = std::get<int>(daughterFunction(particle));
1955 if (daughterNumber >= int(particle->getNDaughters()) or daughterNumber < 0)
1956 return Const::doubleNaN;
1957 auto var_result = var->function(particle->getDaughter(daughterNumber));
1958 if (std::holds_alternative<double>(var_result))
1959 {
1960 return std::get<double>(var_result);
1961 } else if (std::holds_alternative<int>(var_result))
1962 {
1963 return std::get<int>(var_result);
1964 } else if (std::holds_alternative<bool>(var_result))
1965 {
1966 return std::get<bool>(var_result);
1967 } else return Const::doubleNaN;
1968 };
1969 return func;
1970 } else {
1971 B2FATAL("Wrong number of arguments for meta function daughter");
1972 }
1973 }
1974
1975 Manager::FunctionPtr originalDaughter(const std::vector<std::string>& arguments)
1976 {
1977 if (arguments.size() == 2) {
1978 auto daughterFunction = convertToDaughterIndex({arguments[0]});
1979 const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[1]);
1980 auto func = [var, daughterFunction](const Particle * particle) -> double {
1981 if (particle == nullptr)
1982 return Const::doubleNaN;
1983 int daughterNumber = std::get<int>(daughterFunction(particle));
1984 if (daughterNumber >= int(particle->getNDaughters()) or daughterNumber < 0)
1985 return Const::doubleNaN;
1986 else
1987 {
1988 StoreArray<Particle> particles;
1989 if (!particle->getDaughter(daughterNumber)->hasExtraInfo("original_index"))
1990 return Const::doubleNaN;
1991 auto originalDaughter = particles[particle->getDaughter(daughterNumber)->getExtraInfo("original_index")];
1992 if (!originalDaughter)
1993 return Const::doubleNaN;
1994
1995 auto var_result = var->function(originalDaughter);
1996 if (std::holds_alternative<double>(var_result)) {
1997 return std::get<double>(var_result);
1998 } else if (std::holds_alternative<int>(var_result)) {
1999 return std::get<int>(var_result);
2000 } else if (std::holds_alternative<bool>(var_result)) {
2001 return std::get<bool>(var_result);
2002 } else return Const::doubleNaN;
2003 }
2004 };
2005 return func;
2006 } else {
2007 B2FATAL("Wrong number of arguments for meta function daughter");
2008 }
2009 }
2010
2011 Manager::FunctionPtr convertToDaughterIndex(const std::vector<std::string>& arguments)
2012 {
2013 if (arguments.size() == 1) {
2014 std::string daughterString = arguments[0];
2015 auto func = [daughterString](const Particle * particle) -> int {
2016 if (particle == nullptr)
2017 return -1;
2018 int daughterNumber = 0;
2019 try
2020 {
2021 daughterNumber = convertString<int>(daughterString);
2022 } catch (std::invalid_argument&)
2023 {
2024 auto daughterFunction = convertToInt({daughterString, "-1"});
2025 auto daughterVarResult = daughterFunction(particle);
2026 daughterNumber = std::get<int>(daughterVarResult);
2027 }
2028 return daughterNumber;
2029 };
2030 return func;
2031 } else {
2032 B2FATAL("Wrong number of arguments for meta function convertToDaughterIndex");
2033 }
2034 }
2035
2036 Manager::FunctionPtr mcDaughter(const std::vector<std::string>& arguments)
2037 {
2038 if (arguments.size() == 2) {
2039 auto daughterFunction = convertToDaughterIndex({arguments[0]});
2040 const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[1]);
2041 auto func = [var, daughterFunction](const Particle * particle) -> double {
2042 if (particle == nullptr)
2043 return Const::doubleNaN;
2044 if (particle->getMCParticle()) // has MC match or is MCParticle
2045 {
2046 int daughterNumber = std::get<int>(daughterFunction(particle));
2047 if (daughterNumber >= int(particle->getMCParticle()->getNDaughters()) or daughterNumber < 0)
2048 return Const::doubleNaN;
2049 Particle tempParticle = Particle(particle->getMCParticle()->getDaughters().at(daughterNumber));
2050 auto var_result = var->function(&tempParticle);
2051 if (std::holds_alternative<double>(var_result)) {
2052 return std::get<double>(var_result);
2053 } else if (std::holds_alternative<int>(var_result)) {
2054 return std::get<int>(var_result);
2055 } else if (std::holds_alternative<bool>(var_result)) {
2056 return std::get<bool>(var_result);
2057 } else {
2058 return Const::doubleNaN;
2059 }
2060 } else
2061 {
2062 return Const::doubleNaN;
2063 }
2064 };
2065 return func;
2066 } else {
2067 B2FATAL("Wrong number of arguments for meta function mcDaughter");
2068 }
2069 }
2070
2071 Manager::FunctionPtr mcMother(const std::vector<std::string>& arguments)
2072 {
2073 if (arguments.size() == 1) {
2074 const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
2075 auto func = [var](const Particle * particle) -> double {
2076 if (particle == nullptr)
2077 return Const::doubleNaN;
2078 if (particle->getMCParticle()) // has MC match or is MCParticle
2079 {
2080 if (particle->getMCParticle()->getMother() == nullptr) {
2081 return Const::doubleNaN;
2082 }
2083 Particle tempParticle = Particle(particle->getMCParticle()->getMother());
2084 auto var_result = var->function(&tempParticle);
2085 if (std::holds_alternative<double>(var_result)) {
2086 return std::get<double>(var_result);
2087 } else if (std::holds_alternative<int>(var_result)) {
2088 return std::get<int>(var_result);
2089 } else if (std::holds_alternative<bool>(var_result)) {
2090 return std::get<bool>(var_result);
2091 } else return Const::doubleNaN;
2092 } else
2093 {
2094 return Const::doubleNaN;
2095 }
2096 };
2097 return func;
2098 } else {
2099 B2FATAL("Wrong number of arguments for meta function mcMother");
2100 }
2101 }
2102
2103 Manager::FunctionPtr genParticle(const std::vector<std::string>& arguments)
2104 {
2105 if (arguments.size() == 2) {
2106 int particleNumber = 0;
2107 try {
2108 particleNumber = convertString<int>(arguments[0]);
2109 } catch (std::invalid_argument&) {
2110 B2FATAL("First argument of genParticle meta function must be integer!");
2111 }
2112 const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[1]);
2113
2114 auto func = [var, particleNumber](const Particle*) -> double {
2115 StoreArray<MCParticle> mcParticles("MCParticles");
2116 if (particleNumber >= mcParticles.getEntries())
2117 {
2118 return Const::doubleNaN;
2119 }
2120
2121 MCParticle* mcParticle = mcParticles[particleNumber];
2122 Particle part = Particle(mcParticle);
2123 auto var_result = var->function(&part);
2124 if (std::holds_alternative<double>(var_result))
2125 {
2126 return std::get<double>(var_result);
2127 } else if (std::holds_alternative<int>(var_result))
2128 {
2129 return std::get<int>(var_result);
2130 } else if (std::holds_alternative<bool>(var_result))
2131 {
2132 return std::get<bool>(var_result);
2133 } else return Const::doubleNaN;
2134 };
2135 return func;
2136 } else {
2137 B2FATAL("Wrong number of arguments for meta function genParticle");
2138 }
2139 }
2140
2141 Manager::FunctionPtr genUpsilon4S(const std::vector<std::string>& arguments)
2142 {
2143 if (arguments.size() == 1) {
2144 const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
2145
2146 auto func = [var](const Particle*) -> double {
2147 StoreArray<MCParticle> mcParticles("MCParticles");
2148 if (mcParticles.getEntries() == 0)
2149 {
2150 return Const::doubleNaN;
2151 }
2152
2153 MCParticle* mcUpsilon4S = mcParticles[0];
2154 if (mcUpsilon4S->isInitial()) mcUpsilon4S = mcParticles[2];
2155 if (mcUpsilon4S->getPDG() != 300553)
2156 {
2157 return Const::doubleNaN;
2158 }
2159
2160 Particle upsilon4S = Particle(mcUpsilon4S);
2161 auto var_result = var->function(&upsilon4S);
2162 if (std::holds_alternative<double>(var_result))
2163 {
2164 return std::get<double>(var_result);
2165 } else if (std::holds_alternative<int>(var_result))
2166 {
2167 return std::get<int>(var_result);
2168 } else if (std::holds_alternative<bool>(var_result))
2169 {
2170 return std::get<bool>(var_result);
2171 } else return Const::doubleNaN;
2172 };
2173 return func;
2174 } else {
2175 B2FATAL("Wrong number of arguments for meta function genUpsilon4S");
2176 }
2177 }
2178
2179 Manager::FunctionPtr getVariableByRank(const std::vector<std::string>& arguments)
2180 {
2181 if (arguments.size() == 4) {
2182 std::string listName = arguments[0];
2183 std::string rankedVariableName = arguments[1];
2184 std::string returnVariableName = arguments[2];
2185 std::string extraInfoName = rankedVariableName + "_rank";
2186 int rank = 1;
2187 try {
2188 rank = convertString<int>(arguments[3]);
2189 } catch (std::invalid_argument&) {
2190 B2ERROR("3rd argument of getVariableByRank meta function (Rank) must be an integer!");
2191 return nullptr;
2192 }
2193
2194 const Variable::Manager::Var* var = Manager::Instance().getVariable(returnVariableName);
2195 auto func = [var, rank, extraInfoName, listName](const Particle*)-> double {
2196 StoreObjPtr<ParticleList> list(listName);
2197
2198 const unsigned int numParticles = list->getListSize();
2199 for (unsigned int i = 0; i < numParticles; i++)
2200 {
2201 const Particle* p = list->getParticle(i);
2202 if (p->getExtraInfo(extraInfoName) == rank) {
2203 auto var_result = var->function(p);
2204 if (std::holds_alternative<double>(var_result)) {
2205 return std::get<double>(var_result);
2206 } else if (std::holds_alternative<int>(var_result)) {
2207 return std::get<int>(var_result);
2208 } else if (std::holds_alternative<bool>(var_result)) {
2209 return std::get<bool>(var_result);
2210 } else return Const::doubleNaN;
2211 }
2212 }
2213 // return 0;
2214 return std::numeric_limits<double>::signaling_NaN();
2215 };
2216 return func;
2217 } else {
2218 B2FATAL("Wrong number of arguments for meta function getVariableByRank");
2219 }
2220 }
2221
2222 Manager::FunctionPtr countInList(const std::vector<std::string>& arguments)
2223 {
2224 if (arguments.size() == 1 or arguments.size() == 2) {
2225
2226 std::string listName = arguments[0];
2227 std::string cutString = "";
2228
2229 if (arguments.size() == 2) {
2230 cutString = arguments[1];
2231 }
2232
2233 std::shared_ptr<Variable::Cut> cut = std::shared_ptr<Variable::Cut>(Variable::Cut::compile(cutString));
2234
2235 auto func = [listName, cut](const Particle*) -> int {
2236
2237 StoreObjPtr<ParticleList> list(listName);
2238 int sum = 0;
2239 for (unsigned int i = 0; i < list->getListSize(); i++)
2240 {
2241 const Particle* particle = list->getParticle(i);
2242 if (cut->check(particle)) {
2243 sum++;
2244 }
2245 }
2246 return sum;
2247 };
2248 return func;
2249 } else {
2250 B2FATAL("Wrong number of arguments for meta function countInList");
2251 }
2252 }
2253
2254 Manager::FunctionPtr veto(const std::vector<std::string>& arguments)
2255 {
2256 if (arguments.size() == 2 or arguments.size() == 3) {
2257
2258 std::string roeListName = arguments[0];
2259 std::string cutString = arguments[1];
2260 int pdgCode = Const::electron.getPDGCode();
2261 if (arguments.size() == 2) {
2262 B2INFO("Use pdgCode of electron as default in meta variable veto, other arguments: " << roeListName << ", " << cutString);
2263 } else {
2264 try {
2265 pdgCode = convertString<int>(arguments[2]);;
2266 } catch (std::invalid_argument&) {
2267 B2FATAL("Third argument of veto meta function must be integer!");
2268 }
2269 }
2270
2271 auto flavourType = (EvtPDLUtil::hasAntiParticle(pdgCode)) ? Particle::c_Flavored : Particle::c_Unflavored;
2272 std::shared_ptr<Variable::Cut> cut = std::shared_ptr<Variable::Cut>(Variable::Cut::compile(cutString));
2273
2274 auto func = [roeListName, cut, pdgCode, flavourType](const Particle * particle) -> bool {
2275 StoreObjPtr<ParticleList> roeList(roeListName);
2276 ROOT::Math::PxPyPzEVector vec = particle->get4Vector();
2277 for (unsigned int i = 0; i < roeList->getListSize(); i++)
2278 {
2279 const Particle* roeParticle = roeList->getParticle(i);
2280 if (not particle->overlapsWith(roeParticle)) {
2281 ROOT::Math::PxPyPzEVector tempCombination = roeParticle->get4Vector() + vec;
2282 std::vector<int> indices = { particle->getArrayIndex(), roeParticle->getArrayIndex() };
2283 Particle tempParticle = Particle(tempCombination, pdgCode, flavourType, indices, particle->getArrayPointer());
2284 if (cut->check(&tempParticle)) {
2285 return 1;
2286 }
2287 }
2288 }
2289 return 0;
2290 };
2291 return func;
2292 } else {
2293 B2FATAL("Wrong number of arguments for meta function veto");
2294 }
2295 }
2296
2297 Manager::FunctionPtr countDaughters(const std::vector<std::string>& arguments)
2298 {
2299 if (arguments.size() == 1) {
2300 std::string cutString = arguments[0];
2301 std::shared_ptr<Variable::Cut> cut = std::shared_ptr<Variable::Cut>(Variable::Cut::compile(cutString));
2302 auto func = [cut](const Particle * particle) -> int {
2303 int n = 0;
2304 for (auto& daughter : particle->getDaughters())
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 countDaughters");
2314 }
2315 }
2316
2317 Manager::FunctionPtr countFSPDaughters(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*> fspDaughters;
2325 particle->fillFSPDaughters(fspDaughters);
2326
2327 int n = 0;
2328 for (auto& daughter : fspDaughters)
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 countFSPDaughters");
2338 }
2339 }
2340
2341 Manager::FunctionPtr countDescendants(const std::vector<std::string>& arguments)
2342 {
2343 if (arguments.size() == 1) {
2344 std::string cutString = arguments[0];
2345 std::shared_ptr<Variable::Cut> cut = std::shared_ptr<Variable::Cut>(Variable::Cut::compile(cutString));
2346 auto func = [cut](const Particle * particle) -> int {
2347
2348 std::vector<const Particle*> allDaughters;
2349 particle->fillAllDaughters(allDaughters);
2350
2351 int n = 0;
2352 for (auto& daughter : allDaughters)
2353 {
2354 if (cut->check(daughter))
2355 ++n;
2356 }
2357 return n;
2358 };
2359 return func;
2360 } else {
2361 B2FATAL("Wrong number of arguments for meta function countDescendants");
2362 }
2363 }
2364
2365 Manager::FunctionPtr numberOfNonOverlappingParticles(const std::vector<std::string>& arguments)
2366 {
2367
2368 auto func = [arguments](const Particle * particle) -> int {
2369
2370 int _numberOfNonOverlappingParticles = 0;
2371 for (const auto& listName : arguments)
2372 {
2373 StoreObjPtr<ParticleList> list(listName);
2374 if (not list.isValid()) {
2375 B2FATAL("Invalid list named " << listName << " encountered in numberOfNonOverlappingParticles.");
2376 }
2377 for (unsigned int i = 0; i < list->getListSize(); i++) {
2378 const Particle* p = list->getParticle(i);
2379 if (not particle->overlapsWith(p)) {
2380 _numberOfNonOverlappingParticles++;
2381 }
2382 }
2383 }
2384 return _numberOfNonOverlappingParticles;
2385 };
2386
2387 return func;
2388
2389 }
2390
2391 void appendDaughtersRecursive(Particle* mother, StoreArray<Particle>& container)
2392 {
2393
2394 auto* mcmother = mother->getRelated<MCParticle>();
2395
2396 if (!mcmother)
2397 return;
2398
2399 for (auto* mcdaughter : mcmother->getDaughters()) {
2400 if (!mcdaughter->hasStatus(MCParticle::c_PrimaryParticle)) continue;
2401 Particle tmp_daughter(mcdaughter);
2402 Particle* new_daughter = container.appendNew(tmp_daughter);
2403 new_daughter->addRelationTo(mcdaughter);
2404 mother->appendDaughter(new_daughter, false);
2405
2406 if (mcdaughter->getNDaughters() > 0)
2407 appendDaughtersRecursive(new_daughter, container);
2408 }
2409 }
2410
2411 Manager::FunctionPtr matchedMC(const std::vector<std::string>& arguments)
2412 {
2413 if (arguments.size() == 1) {
2414 const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
2415 auto func = [var](const Particle * particle) -> double {
2416 const MCParticle* mcp = particle->getMCParticle();
2417 if (!mcp) // Has no MC match and is no MCParticle
2418 {
2419 return Const::doubleNaN;
2420 }
2421 StoreArray<Particle> tempParticles("tempParticles");
2422 tempParticles.clear();
2423 Particle tmpPart(mcp);
2424 Particle* newPart = tempParticles.appendNew(tmpPart);
2425 newPart->addRelationTo(mcp);
2426
2427 appendDaughtersRecursive(newPart, tempParticles);
2428
2429 auto var_result = var->function(newPart);
2430 if (std::holds_alternative<double>(var_result))
2431 {
2432 return std::get<double>(var_result);
2433 } else if (std::holds_alternative<int>(var_result))
2434 {
2435 return std::get<int>(var_result);
2436 } else if (std::holds_alternative<bool>(var_result))
2437 {
2438 return std::get<bool>(var_result);
2439 } else return Const::doubleNaN;
2440 };
2441 return func;
2442 } else {
2443 B2FATAL("Wrong number of arguments for meta function matchedMC");
2444 }
2445 }
2446
2447 Manager::FunctionPtr clusterBestMatchedMCParticle(const std::vector<std::string>& arguments)
2448 {
2449 if (arguments.size() == 1) {
2450 const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
2451
2452 auto func = [var](const Particle * particle) -> double {
2453
2454 const ECLCluster* cluster = particle->getECLCluster();
2455 if (!cluster) return Const::doubleNaN;
2456
2457 auto mcps = cluster->getRelationsTo<MCParticle>();
2458 if (mcps.size() == 0) return Const::doubleNaN;
2459
2460 std::vector<std::pair<double, int>> weightsAndIndices;
2461 for (unsigned int i = 0; i < mcps.size(); ++i)
2462 weightsAndIndices.emplace_back(mcps.weight(i), i);
2463
2464 // sort descending by weight
2465 std::sort(weightsAndIndices.begin(), weightsAndIndices.end(),
2466 ValueIndexPairSorting::higherPair<decltype(weightsAndIndices)::value_type>);
2467
2468 // cppcheck-suppress containerOutOfBounds
2469 const MCParticle* mcp = mcps.object(weightsAndIndices[0].second);
2470
2471 StoreArray<Particle> tempParticles("tempParticles");
2472 tempParticles.clear();
2473 Particle tmpPart(mcp);
2474 Particle* newPart = tempParticles.appendNew(tmpPart);
2475 newPart->addRelationTo(mcp);
2476
2477 appendDaughtersRecursive(newPart, tempParticles);
2478
2479 auto var_result = var->function(newPart);
2480 if (std::holds_alternative<double>(var_result))
2481 {
2482 return std::get<double>(var_result);
2483 } else if (std::holds_alternative<int>(var_result))
2484 {
2485 return std::get<int>(var_result);
2486 } else if (std::holds_alternative<bool>(var_result))
2487 {
2488 return std::get<bool>(var_result);
2489 } else
2490 {
2491 return Const::doubleNaN;
2492 }
2493 };
2494
2495 return func;
2496 } else {
2497 B2FATAL("Wrong number of arguments for meta function clusterBestMatchedMCParticle");
2498 }
2499 }
2500
2501 Manager::FunctionPtr clusterBestMatchedMCKlong(const std::vector<std::string>& arguments)
2502 {
2503 if (arguments.size() == 1) {
2504 const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
2505
2506 auto func = [var](const Particle * particle) -> double {
2507
2508 const ECLCluster* cluster = particle->getECLCluster();
2509 if (!cluster) return Const::doubleNaN;
2510
2511 auto mcps = cluster->getRelationsTo<MCParticle>();
2512 if (mcps.size() == 0) return Const::doubleNaN;
2513
2514 std::map<int, double> mapMCParticleIndxAndWeight;
2515 getKlongWeightMap(particle, mapMCParticleIndxAndWeight);
2516
2517 // Klong is not found
2518 if (mapMCParticleIndxAndWeight.size() == 0)
2519 return Const::doubleNaN;
2520
2521 // find max totalWeight
2522 auto maxMap = std::max_element(mapMCParticleIndxAndWeight.begin(), mapMCParticleIndxAndWeight.end(),
2523 [](const auto & x, const auto & y) { return x.second < y.second; }
2524 );
2525
2526 StoreArray<MCParticle> mcparticles;
2527 const MCParticle* mcKlong = mcparticles[maxMap->first];
2528
2529 Particle tmpPart(mcKlong);
2530 auto var_result = var->function(&tmpPart);
2531 if (std::holds_alternative<double>(var_result))
2532 {
2533 return std::get<double>(var_result);
2534 } else if (std::holds_alternative<int>(var_result))
2535 {
2536 return std::get<int>(var_result);
2537 } else if (std::holds_alternative<bool>(var_result))
2538 {
2539 return std::get<bool>(var_result);
2540 } else
2541 {
2542 return Const::doubleNaN;
2543 }
2544 };
2545
2546 return func;
2547 } else {
2548 B2FATAL("Wrong number of arguments for meta function clusterBestMatchedMCKlong");
2549 }
2550 }
2551
2552 double matchedMCHasPDG(const Particle* particle, const std::vector<double>& pdgCode)
2553 {
2554 if (pdgCode.size() != 1) {
2555 B2FATAL("Too many arguments provided to matchedMCHasPDG!");
2556 }
2557 int inputPDG = std::lround(pdgCode[0]);
2558
2559 const MCParticle* mcp = particle->getMCParticle();
2560 if (!mcp)
2561 return Const::doubleNaN;
2562
2563 return std::abs(mcp->getPDG()) == inputPDG;
2564 }
2565
2566 Manager::FunctionPtr totalEnergyOfParticlesInList(const std::vector<std::string>& arguments)
2567 {
2568 if (arguments.size() == 1) {
2569 std::string listName = arguments[0];
2570 auto func = [listName](const Particle * particle) -> double {
2571
2572 (void) particle;
2573 StoreObjPtr<ParticleList> listOfParticles(listName);
2574
2575 if (!(listOfParticles.isValid())) B2FATAL("Invalid Listname " << listName << " given to totalEnergyOfParticlesInList");
2576 double totalEnergy = 0;
2577 int nParticles = listOfParticles->getListSize();
2578 for (int i = 0; i < nParticles; i++)
2579 {
2580 const Particle* part = listOfParticles->getParticle(i);
2581 const auto& frame = ReferenceFrame::GetCurrent();
2582 totalEnergy += frame.getMomentum(part).E();
2583 }
2584 return totalEnergy;
2585
2586 };
2587 return func;
2588 } else {
2589 B2FATAL("Wrong number of arguments for meta function totalEnergyOfParticlesInList");
2590 }
2591 }
2592
2593 Manager::FunctionPtr totalPxOfParticlesInList(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 totalPxOfParticlesInList");
2601 double totalPx = 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 totalPx += frame.getMomentum(part).Px();
2608 }
2609 return totalPx;
2610 };
2611 return func;
2612 } else {
2613 B2FATAL("Wrong number of arguments for meta function totalPxOfParticlesInList");
2614 }
2615 }
2616
2617 Manager::FunctionPtr totalPyOfParticlesInList(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 totalPyOfParticlesInList");
2625 double totalPy = 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 totalPy += frame.getMomentum(part).Py();
2632 }
2633 return totalPy;
2634 };
2635 return func;
2636 } else {
2637 B2FATAL("Wrong number of arguments for meta function totalPyOfParticlesInList");
2638 }
2639 }
2640
2641 Manager::FunctionPtr totalPzOfParticlesInList(const std::vector<std::string>& arguments)
2642 {
2643 if (arguments.size() == 1) {
2644 std::string listName = arguments[0];
2645 auto func = [listName](const Particle*) -> double {
2646 StoreObjPtr<ParticleList> listOfParticles(listName);
2647
2648 if (!(listOfParticles.isValid())) B2FATAL("Invalid Listname " << listName << " given to totalPzOfParticlesInList");
2649 double totalPz = 0;
2650 int nParticles = listOfParticles->getListSize();
2651 const auto& frame = ReferenceFrame::GetCurrent();
2652 for (int i = 0; i < nParticles; i++)
2653 {
2654 const Particle* part = listOfParticles->getParticle(i);
2655 totalPz += frame.getMomentum(part).Pz();
2656 }
2657 return totalPz;
2658 };
2659 return func;
2660 } else {
2661 B2FATAL("Wrong number of arguments for meta function totalPzOfParticlesInList");
2662 }
2663 }
2664
2665 Manager::FunctionPtr invMassInLists(const std::vector<std::string>& arguments)
2666 {
2667 if (arguments.size() > 0) {
2668
2669 auto func = [arguments](const Particle * particle) -> double {
2670
2671 ROOT::Math::PxPyPzEVector total4Vector;
2672 // To make sure particles in particlesList don't overlap.
2673 std::vector<Particle*> particlePool;
2674
2675 (void) particle;
2676 for (const auto& argument : arguments)
2677 {
2678 StoreObjPtr <ParticleList> listOfParticles(argument);
2679
2680 if (!(listOfParticles.isValid())) B2FATAL("Invalid Listname " << argument << " given to invMassInLists");
2681 int nParticles = listOfParticles->getListSize();
2682 for (int i = 0; i < nParticles; i++) {
2683 bool overlaps = false;
2684 Particle* part = listOfParticles->getParticle(i);
2685 for (auto poolPart : particlePool) {
2686 if (part->overlapsWith(poolPart)) {
2687 overlaps = true;
2688 break;
2689 }
2690 }
2691 if (!overlaps) {
2692 total4Vector += part->get4Vector();
2693 particlePool.push_back(part);
2694 }
2695 }
2696 }
2697 double invariantMass = total4Vector.M();
2698 return invariantMass;
2699
2700 };
2701 return func;
2702 } else {
2703 B2FATAL("Wrong number of arguments for meta function invMassInLists");
2704 }
2705 }
2706
2707 Manager::FunctionPtr totalECLEnergyOfParticlesInList(const std::vector<std::string>& arguments)
2708 {
2709 if (arguments.size() == 1) {
2710 std::string listName = arguments[0];
2711 auto func = [listName](const Particle * particle) -> double {
2712
2713 (void) particle;
2714 StoreObjPtr<ParticleList> listOfParticles(listName);
2715
2716 if (!(listOfParticles.isValid())) B2FATAL("Invalid Listname " << listName << " given to totalEnergyOfParticlesInList");
2717 double totalEnergy = 0;
2718 int nParticles = listOfParticles->getListSize();
2719 for (int i = 0; i < nParticles; i++)
2720 {
2721 const Particle* part = listOfParticles->getParticle(i);
2722 const ECLCluster* cluster = part->getECLCluster();
2723 const ECLCluster::EHypothesisBit clusterHypothesis = part->getECLClusterEHypothesisBit();
2724 if (cluster != nullptr) {
2725 totalEnergy += cluster->getEnergy(clusterHypothesis);
2726 }
2727 }
2728 return totalEnergy;
2729
2730 };
2731 return func;
2732 } else {
2733 B2FATAL("Wrong number of arguments for meta function totalECLEnergyOfParticlesInList");
2734 }
2735 }
2736
2737 Manager::FunctionPtr maxPtInList(const std::vector<std::string>& arguments)
2738 {
2739 if (arguments.size() == 1) {
2740 std::string listName = arguments[0];
2741 auto func = [listName](const Particle*) -> double {
2742 StoreObjPtr<ParticleList> listOfParticles(listName);
2743
2744 if (!(listOfParticles.isValid())) B2FATAL("Invalid Listname " << listName << " given to maxPtInList");
2745 int nParticles = listOfParticles->getListSize();
2746 const auto& frame = ReferenceFrame::GetCurrent();
2747 double maxPt = 0;
2748 for (int i = 0; i < nParticles; i++)
2749 {
2750 const Particle* part = listOfParticles->getParticle(i);
2751 const double Pt = frame.getMomentum(part).Pt();
2752 if (Pt > maxPt) maxPt = Pt;
2753 }
2754 return maxPt;
2755 };
2756 return func;
2757 } else {
2758 B2FATAL("Wrong number of arguments for meta function maxPtInList");
2759 }
2760 }
2761
2762 Manager::FunctionPtr eclClusterTrackMatchedWithCondition(const std::vector<std::string>& arguments)
2763 {
2764 if (arguments.size() <= 1) {
2765
2766 std::string cutString;
2767 if (arguments.size() == 1)
2768 cutString = arguments[0];
2769 std::shared_ptr<Variable::Cut> cut = std::shared_ptr<Variable::Cut>(Variable::Cut::compile(cutString));
2770 auto func = [cut](const Particle * particle) -> double {
2771
2772 if (particle == nullptr)
2773 return Const::doubleNaN;
2774
2775 const ECLCluster* cluster = particle->getECLCluster();
2776
2777 if (cluster)
2778 {
2779 auto tracks = cluster->getRelationsFrom<Track>();
2780
2781 for (const auto& track : tracks) {
2782 Particle trackParticle(&track, Const::pion);
2783
2784 if (cut->check(&trackParticle))
2785 return 1;
2786 }
2787 return 0;
2788 }
2789 return Const::doubleNaN;
2790 };
2791 return func;
2792 } else {
2793 B2FATAL("Wrong number of arguments for meta function eclClusterSpecialTrackMatched");
2794 }
2795 }
2796
2797 Manager::FunctionPtr averageValueInList(const std::vector<std::string>& arguments)
2798 {
2799 if (arguments.size() == 2) {
2800 std::string listName = arguments[0];
2801 const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[1]);
2802
2803 auto func = [listName, var](const Particle*) -> double {
2804 StoreObjPtr<ParticleList> listOfParticles(listName);
2805
2806 if (!(listOfParticles.isValid())) B2FATAL("Invalid list name " << listName << " given to averageValueInList");
2807 int nParticles = listOfParticles->getListSize();
2808 if (nParticles == 0)
2809 {
2810 return Const::doubleNaN;
2811 }
2812 double average = 0;
2813 if (std::holds_alternative<double>(var->function(listOfParticles->getParticle(0))))
2814 {
2815 for (int i = 0; i < nParticles; i++) {
2816 average += std::get<double>(var->function(listOfParticles->getParticle(i))) / nParticles;
2817 }
2818 } else if (std::holds_alternative<int>(var->function(listOfParticles->getParticle(0))))
2819 {
2820 for (int i = 0; i < nParticles; i++) {
2821 average += std::get<int>(var->function(listOfParticles->getParticle(i))) / nParticles;
2822 }
2823 } else return Const::doubleNaN;
2824 return average;
2825 };
2826 return func;
2827 } else {
2828 B2FATAL("Wrong number of arguments for meta function averageValueInList");
2829 }
2830 }
2831
2832 Manager::FunctionPtr medianValueInList(const std::vector<std::string>& arguments)
2833 {
2834 if (arguments.size() == 2) {
2835 std::string listName = arguments[0];
2836 const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[1]);
2837
2838 auto func = [listName, var](const Particle*) -> double {
2839 StoreObjPtr<ParticleList> listOfParticles(listName);
2840
2841 if (!(listOfParticles.isValid())) B2FATAL("Invalid list name " << listName << " given to medianValueInList");
2842 int nParticles = listOfParticles->getListSize();
2843 if (nParticles == 0)
2844 {
2845 return Const::doubleNaN;
2846 }
2847 std::vector<double> valuesInList;
2848 if (std::holds_alternative<double>(var->function(listOfParticles->getParticle(0))))
2849 {
2850 for (int i = 0; i < nParticles; i++) {
2851 valuesInList.push_back(std::get<double>(var->function(listOfParticles->getParticle(i))));
2852 }
2853 } else if (std::holds_alternative<int>(var->function(listOfParticles->getParticle(0))))
2854 {
2855 for (int i = 0; i < nParticles; i++) {
2856 valuesInList.push_back(std::get<int>(var->function(listOfParticles->getParticle(i))));
2857 }
2858 } else return Const::doubleNaN;
2859 std::sort(valuesInList.begin(), valuesInList.end());
2860 if (nParticles % 2 != 0)
2861 {
2862 return valuesInList[nParticles / 2];
2863 } else
2864 {
2865 return 0.5 * (valuesInList[nParticles / 2] + valuesInList[nParticles / 2 - 1]);
2866 }
2867 };
2868 return func;
2869 } else {
2870 B2FATAL("Wrong number of arguments for meta function medianValueInList");
2871 }
2872 }
2873
2874 Manager::FunctionPtr sumValueInList(const std::vector<std::string>& arguments)
2875 {
2876 if (arguments.size() == 2) {
2877 std::string listName = arguments[0];
2878 const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[1]);
2879
2880 auto func = [listName, var](const Particle*) -> double {
2881 StoreObjPtr<ParticleList> listOfParticles(listName);
2882
2883 if (!(listOfParticles.isValid())) B2FATAL("Invalid list name " << listName << " given to sumValueInList");
2884 int nParticles = listOfParticles->getListSize();
2885 if (nParticles == 0)
2886 {
2887 return Const::doubleNaN;
2888 }
2889 double sum = 0;
2890 if (std::holds_alternative<double>(var->function(listOfParticles->getParticle(0))))
2891 {
2892 for (int i = 0; i < nParticles; i++) {
2893 sum += std::get<double>(var->function(listOfParticles->getParticle(i)));
2894 }
2895 } else if (std::holds_alternative<int>(var->function(listOfParticles->getParticle(0))))
2896 {
2897 for (int i = 0; i < nParticles; i++) {
2898 sum += std::get<int>(var->function(listOfParticles->getParticle(i)));
2899 }
2900 } else return Const::doubleNaN;
2901 return sum;
2902 };
2903 return func;
2904 } else {
2905 B2FATAL("Wrong number of arguments for meta function sumValueInList");
2906 }
2907 }
2908
2909 Manager::FunctionPtr productValueInList(const std::vector<std::string>& arguments)
2910 {
2911 if (arguments.size() == 2) {
2912 std::string listName = arguments[0];
2913 const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[1]);
2914
2915 auto func = [listName, var](const Particle*) -> double {
2916 StoreObjPtr<ParticleList> listOfParticles(listName);
2917
2918 if (!(listOfParticles.isValid())) B2FATAL("Invalid list name " << listName << " given to productValueInList");
2919 int nParticles = listOfParticles->getListSize();
2920 if (nParticles == 0)
2921 {
2922 return Const::doubleNaN;
2923 }
2924 double product = 1;
2925 if (std::holds_alternative<double>(var->function(listOfParticles->getParticle(0))))
2926 {
2927 for (int i = 0; i < nParticles; i++) {
2928 product *= std::get<double>(var->function(listOfParticles->getParticle(i)));
2929 }
2930 } else if (std::holds_alternative<int>(var->function(listOfParticles->getParticle(0))))
2931 {
2932 for (int i = 0; i < nParticles; i++) {
2933 product *= std::get<int>(var->function(listOfParticles->getParticle(i)));
2934 }
2935 } else return Const::doubleNaN;
2936 return product;
2937 };
2938 return func;
2939 } else {
2940 B2FATAL("Wrong number of arguments for meta function productValueInList");
2941 }
2942 }
2943
2944 Manager::FunctionPtr angleToClosestInList(const std::vector<std::string>& arguments)
2945 {
2946 // expecting the list name
2947 if (arguments.size() != 1)
2948 B2FATAL("Wrong number of arguments for meta function angleToClosestInList");
2949
2950 std::string listname = arguments[0];
2951
2952 auto func = [listname](const Particle * particle) -> double {
2953 // get the list and check it's valid
2954 StoreObjPtr<ParticleList> list(listname);
2955 if (not list.isValid())
2956 B2FATAL("Invalid particle list name " << listname << " given to angleToClosestInList");
2957
2958 // check the list isn't empty
2959 if (list->getListSize() == 0)
2960 return Const::doubleNaN;
2961
2962 // respect the current frame and get the momentum of our input
2963 const auto& frame = ReferenceFrame::GetCurrent();
2964 const auto p_this = frame.getMomentum(particle);
2965
2966 // find the particle index with the smallest opening angle
2967 double minAngle = 2 * M_PI;
2968 for (unsigned int i = 0; i < list->getListSize(); ++i)
2969 {
2970 const Particle* compareme = list->getParticle(i);
2971 const auto p_compare = frame.getMomentum(compareme);
2972 double angle = ROOT::Math::VectorUtil::Angle(p_compare, p_this);
2973 if (minAngle > angle) minAngle = angle;
2974 }
2975 return minAngle;
2976 };
2977 return func;
2978 }
2979
2980 Manager::FunctionPtr closestInList(const std::vector<std::string>& arguments)
2981 {
2982 // expecting the list name and a variable name
2983 if (arguments.size() != 2)
2984 B2FATAL("Wrong number of arguments for meta function closestInList");
2985
2986 std::string listname = arguments[0];
2987
2988 // the requested variable and check it exists
2989 const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[1]);
2990
2991 auto func = [listname, var](const Particle * particle) -> double {
2992 // get the list and check it's valid
2993 StoreObjPtr<ParticleList> list(listname);
2994 if (not list.isValid())
2995 B2FATAL("Invalid particle list name " << listname << " given to closestInList");
2996
2997 // respect the current frame and get the momentum of our input
2998 const auto& frame = ReferenceFrame::GetCurrent();
2999 const auto p_this = frame.getMomentum(particle);
3000
3001 // find the particle index with the smallest opening angle
3002 double minAngle = 2 * M_PI;
3003 int iClosest = -1;
3004 for (unsigned int i = 0; i < list->getListSize(); ++i)
3005 {
3006 const Particle* compareme = list->getParticle(i);
3007 const auto p_compare = frame.getMomentum(compareme);
3008 double angle = ROOT::Math::VectorUtil::Angle(p_compare, p_this);
3009 if (minAngle > angle) {
3010 minAngle = angle;
3011 iClosest = i;
3012 }
3013 }
3014
3015 // final check that the list wasn't empty (or some other problem)
3016 if (iClosest == -1) return Const::doubleNaN;
3017 auto var_result = var->function(list->getParticle(iClosest));
3018 if (std::holds_alternative<double>(var_result))
3019 {
3020 return std::get<double>(var_result);
3021 } else if (std::holds_alternative<int>(var_result))
3022 {
3023 return std::get<int>(var_result);
3024 } else if (std::holds_alternative<bool>(var_result))
3025 {
3026 return std::get<bool>(var_result);
3027 } else return Const::doubleNaN;
3028 };
3029 return func;
3030 }
3031
3032 Manager::FunctionPtr angleToMostB2BInList(const std::vector<std::string>& arguments)
3033 {
3034 // expecting the list name
3035 if (arguments.size() != 1)
3036 B2FATAL("Wrong number of arguments for meta function angleToMostB2BInList");
3037
3038 std::string listname = arguments[0];
3039
3040 auto func = [listname](const Particle * particle) -> double {
3041 // get the list and check it's valid
3042 StoreObjPtr<ParticleList> list(listname);
3043 if (not list.isValid())
3044 B2FATAL("Invalid particle list name " << listname << " given to angleToMostB2BInList");
3045
3046 // check the list isn't empty
3047 if (list->getListSize() == 0)
3048 return Const::doubleNaN;
3049
3050 // respect the current frame and get the momentum of our input
3051 const auto& frame = ReferenceFrame::GetCurrent();
3052 const auto p_this = frame.getMomentum(particle);
3053
3054 // find the most back-to-back (the largest opening angle before they
3055 // start getting smaller again!)
3056 double maxAngle = 0;
3057 for (unsigned int i = 0; i < list->getListSize(); ++i)
3058 {
3059 const Particle* compareme = list->getParticle(i);
3060 const auto p_compare = frame.getMomentum(compareme);
3061 double angle = ROOT::Math::VectorUtil::Angle(p_compare, p_this);
3062 if (maxAngle < angle) maxAngle = angle;
3063 }
3064 return maxAngle;
3065 };
3066 return func;
3067 }
3068
3069 Manager::FunctionPtr deltaPhiToMostB2BPhiInList(const std::vector<std::string>& arguments)
3070 {
3071 // expecting the list name
3072 if (arguments.size() != 1)
3073 B2FATAL("Wrong number of arguments for meta function deltaPhiToMostB2BPhiInList");
3074
3075 std::string listname = arguments[0];
3076
3077 auto func = [listname](const Particle * particle) -> double {
3078 // get the list and check it's valid
3079 StoreObjPtr<ParticleList> list(listname);
3080 if (not list.isValid())
3081 B2FATAL("Invalid particle list name " << listname << " given to deltaPhiToMostB2BPhiInList");
3082
3083 // check the list isn't empty
3084 if (list->getListSize() == 0)
3085 return Const::doubleNaN;
3086
3087 // respect the current frame and get the momentum of our input
3088 const auto& frame = ReferenceFrame::GetCurrent();
3089 const auto phi_this = frame.getMomentum(particle).Phi();
3090
3091 // find the most back-to-back in phi (largest absolute value of delta phi)
3092 double maxAngle = 0;
3093 for (unsigned int i = 0; i < list->getListSize(); ++i)
3094 {
3095 const Particle* compareme = list->getParticle(i);
3096 const auto phi_compare = frame.getMomentum(compareme).Phi();
3097 double angle = std::abs(phi_compare - phi_this);
3098 if (angle > M_PI) {angle = 2 * M_PI - angle;}
3099 if (maxAngle < angle) maxAngle = angle;
3100 }
3101 return maxAngle;
3102 };
3103 return func;
3104 }
3105
3106 Manager::FunctionPtr mostB2BInList(const std::vector<std::string>& arguments)
3107 {
3108 // expecting the list name and a variable name
3109 if (arguments.size() != 2)
3110 B2FATAL("Wrong number of arguments for meta function mostB2BInList");
3111
3112 std::string listname = arguments[0];
3113
3114 // the requested variable and check it exists
3115 const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[1]);
3116
3117 auto func = [listname, var](const Particle * particle) -> double {
3118 // get the list and check it's valid
3119 StoreObjPtr<ParticleList> list(listname);
3120 if (not list.isValid())
3121 B2FATAL("Invalid particle list name " << listname << " given to mostB2BInList");
3122
3123 // respect the current frame and get the momentum of our input
3124 const auto& frame = ReferenceFrame::GetCurrent();
3125 const auto p_this = frame.getMomentum(particle);
3126
3127 // find the most back-to-back (the largest opening angle before they
3128 // start getting smaller again!)
3129 double maxAngle = -1.0;
3130 int iMostB2B = -1;
3131 for (unsigned int i = 0; i < list->getListSize(); ++i)
3132 {
3133 const Particle* compareme = list->getParticle(i);
3134 const auto p_compare = frame.getMomentum(compareme);
3135 double angle = ROOT::Math::VectorUtil::Angle(p_compare, p_this);
3136 if (maxAngle < angle) {
3137 maxAngle = angle;
3138 iMostB2B = i;
3139 }
3140 }
3141
3142 // final check that the list wasn't empty (or some other problem)
3143 if (iMostB2B == -1) return Const::doubleNaN;
3144 auto var_result = var->function(list->getParticle(iMostB2B));
3145 if (std::holds_alternative<double>(var_result))
3146 {
3147 return std::get<double>(var_result);
3148 } else if (std::holds_alternative<int>(var_result))
3149 {
3150 return std::get<int>(var_result);
3151 } else if (std::holds_alternative<bool>(var_result))
3152 {
3153 return std::get<bool>(var_result);
3154 } else return Const::doubleNaN;
3155 };
3156 return func;
3157 }
3158
3159 Manager::FunctionPtr maxOpeningAngleInList(const std::vector<std::string>& arguments)
3160 {
3161 if (arguments.size() == 1) {
3162 std::string listName = arguments[0];
3163 auto func = [listName](const Particle*) -> double {
3164 StoreObjPtr<ParticleList> listOfParticles(listName);
3165
3166 if (!(listOfParticles.isValid())) B2FATAL("Invalid Listname " << listName << " given to maxOpeningAngleInList");
3167 int nParticles = listOfParticles->getListSize();
3168 // return NaN if number of particles is less than 2
3169 if (nParticles < 2) return Const::doubleNaN;
3170
3171 const auto& frame = ReferenceFrame::GetCurrent();
3172 double maxOpeningAngle = -1;
3173 for (int i = 0; i < nParticles; i++)
3174 {
3175 ROOT::Math::PxPyPzEVector v1 = frame.getMomentum(listOfParticles->getParticle(i));
3176 for (int j = i + 1; j < nParticles; j++) {
3177 ROOT::Math::PxPyPzEVector v2 = frame.getMomentum(listOfParticles->getParticle(j));
3178 const double angle = ROOT::Math::VectorUtil::Angle(v1, v2);
3179 if (angle > maxOpeningAngle) maxOpeningAngle = angle;
3180 }
3181 }
3182 return maxOpeningAngle;
3183 };
3184 return func;
3185 } else {
3186 B2FATAL("Wrong number of arguments for meta function maxOpeningAngleInList");
3187 }
3188 }
3189
3190 Manager::FunctionPtr daughterCombination(const std::vector<std::string>& arguments)
3191 {
3192 // Expect 2 or more arguments.
3193 if (arguments.size() >= 2) {
3194 // First argument is the variable name
3195 const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
3196
3197 // Core function: calculates a variable combining an arbitrary number of particles
3198 auto func = [var, arguments](const Particle * particle) -> double {
3199 if (particle == nullptr)
3200 {
3201 B2WARNING("Trying to access a daughter that does not exist. Skipping");
3202 return Const::doubleNaN;
3203 }
3204 const auto& frame = ReferenceFrame::GetCurrent();
3205
3206 // Sum of the 4-momenta of all the selected daughters
3207 ROOT::Math::PxPyPzEVector pSum(0, 0, 0, 0);
3208
3209 // Loop over the arguments. Each one of them is a generalizedIndex,
3210 // pointing to a particle in the decay tree.
3211 for (unsigned int iCoord = 1; iCoord < arguments.size(); iCoord++)
3212 {
3213 auto generalizedIndex = arguments[iCoord];
3214 const Particle* dauPart = particle->getParticleFromGeneralizedIndexString(generalizedIndex);
3215 if (dauPart)
3216 pSum += frame.getMomentum(dauPart);
3217 else {
3218 B2WARNING("Trying to access a daughter that does not exist. Index = " << generalizedIndex);
3219 return Const::doubleNaN;
3220 }
3221 }
3222
3223 // Make a dummy particle out of the sum of the 4-momenta of the selected daughters
3224 Particle sumOfDaughters(pSum, 100); // 100 is one of the special numbers
3225
3226 auto var_result = var->function(&sumOfDaughters);
3227 // Calculate the variable on the dummy particle
3228 if (std::holds_alternative<double>(var_result))
3229 {
3230 return std::get<double>(var_result);
3231 } else if (std::holds_alternative<int>(var_result))
3232 {
3233 return std::get<int>(var_result);
3234 } else if (std::holds_alternative<bool>(var_result))
3235 {
3236 return std::get<bool>(var_result);
3237 } else return Const::doubleNaN;
3238 };
3239 return func;
3240 } else
3241 B2FATAL("Wrong number of arguments for meta function daughterCombination");
3242 }
3243
3244 Manager::FunctionPtr useAlternativeDaughterHypothesis(const std::vector<std::string>& arguments)
3245 {
3246 /*
3247 `arguments` contains the variable to calculate and a list of colon-separated index-particle pairs.
3248 Overall, it looks like {"M", "0:K+", "1:p+", "3:e-"}.
3249 The code is thus divided in two parts:
3250 1) Parsing. A loop over the elements of `arguments` that first separates the variable from the rest, and then splits all the index:particle
3251 pairs, filling a std::vector with the indexes and another one with the new mass values.
3252 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
3253 the variable value using the sum of all the 4-momenta, both updated and non-updated ones.
3254 */
3255
3256 // Expect 2 or more arguments.
3257 if (arguments.size() >= 2) {
3258
3259 //----
3260 // 1) parsing
3261 //----
3262
3263 // First argument is the variable name
3264 const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
3265
3266 // Parses the other arguments, which are in the form of index:particleName pairs,
3267 // and stores indexes and pdgs in std::unordered_map
3268 std::unordered_map<unsigned int, int> mapOfReplacedDaughters;
3269
3270 // Loop over the arguments to parse them
3271 for (unsigned int iCoord = 1; iCoord < arguments.size(); iCoord++) {
3272 auto replacedDauString = arguments[iCoord];
3273 // Split the string in index and new mass
3274 std::vector<std::string> indexAndMass;
3275 boost::split(indexAndMass, replacedDauString, boost::is_any_of(":"));
3276
3277 // Checks that the index:particleName pair is properly formatted.
3278 if (indexAndMass.size() > 2) {
3279 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 "
3280 << replacedDauString << ", while a correct syntax looks like 0:K+.");
3281 return nullptr;
3282 }
3283
3284 if (indexAndMass.size() < 2) {
3285 B2WARNING("The string indicating which daughter's mass should be replaced contains only one colon-separated element instead of two. The offending string is "
3286 << replacedDauString << ", while a correct syntax looks like 0:K+.");
3287 return nullptr;
3288 }
3289
3290 // indexAndMass[0] is the daughter index as string. Try to convert it
3291 int dauIndex = 0;
3292 try {
3293 dauIndex = convertString<int>(indexAndMass[0]);
3294 } catch (std::invalid_argument&) {
3295 B2FATAL("Found the string " << indexAndMass[0] << "instead of a daughter index.");
3296 }
3297
3298 // Determine PDG code corresponding to indexAndMass[1] using the particle names defined in evt.pdl
3299 TParticlePDG* particlePDG = TDatabasePDG::Instance()->GetParticle(indexAndMass[1].c_str());
3300 if (!particlePDG) {
3301 B2WARNING("Particle not in evt.pdl file! " << indexAndMass[1]);
3302 return nullptr;
3303 }
3304
3305 // Stores the indexes and the pdgs in the map that will be passed to the lambda function
3306 int pdgCode = particlePDG->PdgCode();
3307 mapOfReplacedDaughters[dauIndex] = pdgCode;
3308 } // End of parsing
3309
3310 // Check the size of mapOfReplacedDaughters
3311 if (mapOfReplacedDaughters.size() != arguments.size() - 1)
3312 B2FATAL("Overlapped daughter's index is detected in the meta-variable useAlternativeDaughterHypothesis");
3313
3314 //----
3315 // 2) replacing
3316 //----
3317
3318 // Core function: creates a new particle from the original one changing
3319 // some of the daughters' masses
3320 auto func = [var, mapOfReplacedDaughters](const Particle * particle) -> double {
3321 if (particle == nullptr)
3322 {
3323 B2WARNING("Trying to access a particle that does not exist. Skipping");
3324 return Const::doubleNaN;
3325 }
3326
3327 const auto& frame = ReferenceFrame::GetCurrent();
3328
3329 // Create a dummy particle from the given particle to overwrite its kinematics
3330 Particle* dummy = ParticleCopy::copyParticle(particle);
3331
3332 // Sum of the 4-momenta of all the daughters with the new mass assumptions
3333 ROOT::Math::PxPyPzMVector pSum(0, 0, 0, 0);
3334
3335 for (unsigned int iDau = 0; iDau < particle->getNDaughters(); iDau++)
3336 {
3337 const Particle* dauPart = particle->getDaughter(iDau);
3338 if (not dauPart) {
3339 B2WARNING("Trying to access a daughter that does not exist. Index = " << iDau);
3340 return Const::doubleNaN;
3341 }
3342
3343 ROOT::Math::PxPyPzMVector dauMom = ROOT::Math::PxPyPzMVector(frame.getMomentum(dauPart));
3344
3345 int pdgCode;
3346 try {
3347 pdgCode = mapOfReplacedDaughters.at(iDau);
3348 } catch (std::out_of_range&) {
3349 // iDau is not in mapOfReplacedDaughters
3350 pSum += dauMom;
3351 continue;
3352 }
3353
3354 // overwrite the daughter's kinematics
3355 double p_x = dauMom.Px();
3356 double p_y = dauMom.Py();
3357 double p_z = dauMom.Pz();
3358 dauMom.SetCoordinates(p_x, p_y, p_z, TDatabasePDG::Instance()->GetParticle(pdgCode)->Mass());
3359 const_cast<Particle*>(dummy->getDaughter(iDau))->set4VectorDividingByMomentumScaling(ROOT::Math::PxPyPzEVector(dauMom));
3360
3361 // overwrite the daughter's pdg
3362 const int charge = dummy->getDaughter(iDau)->getCharge();
3363 if (TDatabasePDG::Instance()->GetParticle(pdgCode)->Charge() / 3.0 == charge)
3364 const_cast<Particle*>(dummy->getDaughter(iDau))->setPDGCode(pdgCode);
3365 else
3366 const_cast<Particle*>(dummy->getDaughter(iDau))->setPDGCode(-1 * pdgCode);
3367
3368 pSum += dauMom;
3369 } // End of loop over number of daughter
3370
3371 // overwrite the particle's kinematics
3372 dummy->set4Vector(ROOT::Math::PxPyPzEVector(pSum));
3373
3374 auto var_result = var->function(dummy);
3375
3376 // Calculate the variable on the dummy particle
3377 if (std::holds_alternative<double>(var_result))
3378 {
3379 return std::get<double>(var_result);
3380 } else if (std::holds_alternative<int>(var_result))
3381 {
3382 return std::get<int>(var_result);
3383 } else if (std::holds_alternative<bool>(var_result))
3384 {
3385 return std::get<bool>(var_result);
3386 } else return Const::doubleNaN;
3387 }; // end of lambda function
3388 return func;
3389 }// end of check on number of arguments
3390 else
3391 B2FATAL("Wrong number of arguments for meta function useAlternativeDaughterHypothesis");
3392 }
3393
3394 Manager::FunctionPtr varForFirstMCAncestorOfType(const std::vector<std::string>& arguments)
3395 {
3396 if (arguments.size() == 2) {
3397 int pdg_code = -1;
3398 std::string arg = arguments[0];
3399 const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[1]);
3400 TParticlePDG* part = TDatabasePDG::Instance()->GetParticle(arg.c_str());
3401
3402 if (part != nullptr) {
3403 pdg_code = std::abs(part->PdgCode());
3404 } else {
3405 try {
3406 pdg_code = convertString<int>(arg);
3407 } catch (std::exception& e) {}
3408 }
3409
3410 if (pdg_code == -1) {
3411 B2FATAL("Ancestor " + arg + " is not recognised. Please provide valid PDG code or particle name.");
3412 }
3413
3414 auto func = [pdg_code, var](const Particle * particle) -> double {
3415 const Particle* p = particle;
3416
3417 int ancestor_level = std::get<double>(Manager::Instance().getVariable("hasAncestor(" + std::to_string(pdg_code) + ", 0)")->function(p));
3418 if ((ancestor_level <= 0) or (std::isnan(ancestor_level)))
3419 {
3420 return Const::doubleNaN;
3421 }
3422
3423 const MCParticle* i_p = p->getMCParticle();
3424
3425 for (int a = 0; a < ancestor_level ; a = a + 1)
3426 {
3427 i_p = i_p->getMother();
3428 }
3429
3430 StoreArray<Particle> tempParticles("tempParticles");
3431 tempParticles.clear();
3432 Particle m_p(i_p);
3433 Particle* newPart = tempParticles.appendNew(m_p);
3434 newPart->addRelationTo(i_p);
3435
3436 appendDaughtersRecursive(newPart, tempParticles);
3437
3438 auto var_result = var->function(newPart);
3439 if (std::holds_alternative<double>(var_result))
3440 {
3441 return std::get<double>(var_result);
3442 } else if (std::holds_alternative<int>(var_result))
3443 {
3444 return std::get<int>(var_result);
3445 } else if (std::holds_alternative<bool>(var_result))
3446 {
3447 return std::get<bool>(var_result);
3448 } else return Const::doubleNaN;
3449 };
3450 return func;
3451 } else {
3452 B2FATAL("Wrong number of arguments for meta function varForFirstMCAncestorOfType (expected 2: type and variable of interest)");
3453 }
3454 }
3455
3456 Manager::FunctionPtr nTrackFitResults(const std::vector<std::string>& arguments)
3457 {
3458 if (arguments.size() != 1) {
3459 B2FATAL("Number of arguments for nTrackFitResults must be 1, particleType or PDGcode");
3460 }
3461
3462 std::string arg = arguments[0];
3463 TParticlePDG* part = TDatabasePDG::Instance()->GetParticle(arg.c_str());
3464 int absPdg;
3465 if (part != nullptr) {
3466 absPdg = std::abs(part->PdgCode());
3467 } else {
3468 try {
3469 absPdg = convertString<int>(arg);
3470 } catch (std::exception& e) {}
3471 }
3472
3473 auto func = [absPdg](const Particle*) -> int {
3474
3475 Const::ChargedStable type(absPdg);
3476 StoreArray<Track> tracks;
3477
3478 int nTrackFitResults = 0;
3479
3480 for (const auto& track : tracks)
3481 {
3482 const TrackFitResult* trackFit = track.getTrackFitResultWithClosestMass(type);
3483
3484 if (!trackFit) continue;
3485 if (trackFit->getChargeSign() == 0) continue;
3486
3487 nTrackFitResults++;
3488 }
3489
3490 return nTrackFitResults;
3491
3492 };
3493 return func;
3494 }
3495
3496
3497 Manager::FunctionPtr convertToInt(const std::vector<std::string>& arguments)
3498 {
3499 if (arguments.size() == 2) {
3500 const Variable::Manager::Var* var = Manager::Instance().getVariable(arguments[0]);
3501 int default_val = convertString<int>(arguments[1]);
3502 auto func = [var, default_val](const Particle * particle) -> int {
3503 auto var_result = var->function(particle);
3504 if (std::holds_alternative<double>(var_result))
3505 {
3506 double value = std::get<double>(var_result);
3507 if (value > std::numeric_limits<int>::max())
3508 value = std::numeric_limits<int>::max();
3509 if (value < std::numeric_limits<int>::min())
3510 value = std::numeric_limits<int>::min();
3511 if (std::isnan(value))
3512 value = default_val;
3513 return static_cast<int>(value);
3514 } else if (std::holds_alternative<int>(var_result))
3515 return std::get<int>(var_result);
3516 else if (std::holds_alternative<bool>(var_result))
3517 return static_cast<int>(std::get<bool>(var_result));
3518 else return default_val;
3519 };
3520 return func;
3521 } else {
3522 B2FATAL("Wrong number of arguments for meta function int, please provide variable name and replacement value for NaN!");
3523 }
3524 }
3525
3526 VARIABLE_GROUP("MetaFunctions");
3527 REGISTER_METAVARIABLE("nCleanedECLClusters(cut)", nCleanedECLClusters,
3528 "[Eventbased] Returns the number of clean Clusters in the event\n"
3529 "Clean clusters are defined by the clusters which pass the given cut assuming a photon hypothesis.",
3530 Manager::VariableDataType::c_int);
3531 REGISTER_METAVARIABLE("nCleanedTracks(cut)", nCleanedTracks,
3532 "[Eventbased] Returns the number of clean Tracks in the event\n"
3533 "Clean tracks are defined by the tracks which pass the given cut assuming a pion hypothesis.", Manager::VariableDataType::c_int);
3534 REGISTER_METAVARIABLE("formula(v1 + v2 * [v3 - v4] / v5^v6)", formula, R"DOCSTRING(
3535Returns the result of the given formula, where v1 to vN are variables or floating
3536point numbers. Currently the only supported operations are addition (``+``),
3537subtraction (``-``), multiplication (``*``), division (``/``) and power (``^``
3538or ``**``). Parenthesis can be in the form of square brackets ``[v1 * v2]``
3539or normal brackets ``(v1 * v2)``. It will work also with variables taking
3540arguments. Operator precedence is taken into account. For example ::
3541
3542 (daughter(0, E) + daughter(1, E))**2 - p**2 + 0.138
3543
3544.. versionchanged:: release-03-00-00
3545 now both, ``[]`` and ``()`` can be used for grouping operations, ``**`` can
3546 be used for exponent and float literals are possible directly in the
3547 formula.
3548)DOCSTRING", Manager::VariableDataType::c_double);
3549 REGISTER_METAVARIABLE("useRestFrame(variable)", useRestFrame,
3550 "Returns the value of the variable using the rest frame of the given particle as current reference frame.\n"
3551 "E.g. ``useRestFrame(daughter(0, p))`` returns the total momentum of the first daughter in its mother's rest-frame", Manager::VariableDataType::c_double);
3552 REGISTER_METAVARIABLE("useCMSFrame(variable)", useCMSFrame,
3553 "Returns the value of the variable using the CMS frame as current reference frame.\n"
3554 "E.g. ``useCMSFrame(E)`` returns the energy of a particle in the CMS frame.", Manager::VariableDataType::c_double);
3555 REGISTER_METAVARIABLE("useLabFrame(variable)", useLabFrame, R"DOC(
3556Returns the value of ``variable`` in the *lab* frame.
3557
3558.. tip::
3559 The lab frame is the default reference frame, usually you don't need to use this meta-variable.
3560 E.g. ``useLabFrame(E)`` returns the energy of a particle in the Lab frame, same as just ``E``.
3561
3562Specifying the lab frame is useful in some corner-cases. For example:
3563``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.
3564)DOC", Manager::VariableDataType::c_double);
3565 REGISTER_METAVARIABLE("useTagSideRecoilRestFrame(variable, daughterIndexTagB)", useTagSideRecoilRestFrame,
3566 "Returns the value of the variable in the rest frame of the recoiling particle to the tag side B meson.\n"
3567 "The variable should only be applied to an Upsilon(4S) list.\n"
3568 "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);
3569 REGISTER_METAVARIABLE("useParticleRestFrame(variable, particleList)", useParticleRestFrame,
3570 "Returns the value of the variable in the rest frame of the first Particle contained in the given ParticleList.\n"
3571 "It is strongly recommended to pass a ParticleList that contains at most only one Particle in each event. "
3572 "When more than one Particle is present in the ParticleList, only the first Particle in the list is used for "
3573 "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);
3574 REGISTER_METAVARIABLE("useRecoilParticleRestFrame(variable, particleList)", useRecoilParticleRestFrame,
3575 "Returns the value of the variable in the rest frame of recoil system against the first Particle contained in the given ParticleList.\n"
3576 "It is strongly recommended to pass a ParticleList that contains at most only one Particle in each event. "
3577 "When more than one Particle is present in the ParticleList, only the first Particle in the list is used for "
3578 "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);
3579 REGISTER_METAVARIABLE("useDaughterRestFrame(variable, daughterIndex_1, [daughterIndex_2, ... daughterIndex_3])", useDaughterRestFrame,
3580 "Returns the value of the variable in the rest frame of the selected daughter particle.\n"
3581 "The daughter is identified via generalized daughter index, e.g. ``0:1`` identifies the second daughter (1) "
3582 "of the first daughter (0). If the daughter index is invalid, it returns NaN.\n"
3583 "If two or more indices are given, the rest frame of the sum of the daughters is used.",
3584 Manager::VariableDataType::c_double);
3585 REGISTER_METAVARIABLE("useDaughterRecoilRestFrame(variable, daughterIndex_1, [daughterIndex_2, ... daughterIndex_3])", useDaughterRecoilRestFrame,
3586 "Returns the value of the variable in the rest frame of the recoil of the selected daughter particle.\n"
3587 "The daughter is identified via generalized daughter index, e.g. ``0:1`` identifies the second daughter (1) "
3588 "of the first daughter (0). If the daughter index is invalid, it returns NaN.\n"
3589 "If two or more indices are given, the rest frame of the sum of the daughters is used.",
3590 Manager::VariableDataType::c_double);
3591 REGISTER_METAVARIABLE("useMCancestorBRestFrame(variable)", useMCancestorBRestFrame,
3592 "Returns the value of the variable in the rest frame of the ancestor B MC particle.\n"
3593 "If no B or no MC-matching is found, it returns NaN.", Manager::VariableDataType::c_double);
3594 REGISTER_METAVARIABLE("passesCut(cut)", passesCut,
3595 "Returns 1 if particle passes the cut otherwise 0.\n"
3596 "Useful if you want to write out if a particle would have passed a cut or not.", Manager::VariableDataType::c_bool);
3597 REGISTER_METAVARIABLE("passesEventCut(cut)", passesEventCut,
3598 "[Eventbased] Returns 1 if event passes the cut otherwise 0.\n"
3599 "Useful if you want to select events passing a cut without looping into particles, such as for skimming.\n", Manager::VariableDataType::c_bool);
3600 REGISTER_METAVARIABLE("countDaughters(cut)", countDaughters,
3601 "Returns number of direct daughters which satisfy the cut.\n"
3602 "Used by the skimming package (for what exactly?)", Manager::VariableDataType::c_int);
3603 REGISTER_METAVARIABLE("countFSPDaughters(cut)", countDescendants,
3604 "Returns number of final-state daughters which satisfy the cut.",
3605 Manager::VariableDataType::c_int);
3606 REGISTER_METAVARIABLE("countDescendants(cut)", countDescendants,
3607 "Returns number of descendants for all generations which satisfy the cut.",
3608 Manager::VariableDataType::c_int);
3609 REGISTER_METAVARIABLE("varFor(pdgCode, variable)", varFor,
3610 "Returns the value of the variable for the given particle if its abs(pdgCode) agrees with the given one.\n"
3611 "E.g. ``varFor(11, p)`` returns the momentum if the particle is an electron or a positron.", Manager::VariableDataType::c_double);
3612 REGISTER_METAVARIABLE("varForMCGen(variable)", varForMCGen,
3613 "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"
3614 "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"
3615 "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);
3616 REGISTER_METAVARIABLE("nParticlesInList(particleListName)", nParticlesInList,
3617 "[Eventbased] Returns number of particles in the given particle List.", Manager::VariableDataType::c_int);
3618 REGISTER_METAVARIABLE("isInList(particleListName)", isInList,
3619 "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);
3620 REGISTER_METAVARIABLE("isDaughterOfList(particleListNames)", isDaughterOfList,
3621 "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);
3622 REGISTER_METAVARIABLE("isDescendantOfList(particleListName[, anotherParticleListName][, generationFlag = -1])", isDescendantOfList, R"DOC(
3623 Returns 1 if the given particle appears in the decay chain of the particles in the given ParticleLists.
3624
3625 Passing an integer as the last argument, allows to check if the particle belongs to the specific generation:
3626
3627 * ``isDescendantOfList(<particle_list>,1)`` returns 1 if particle is a daughter of the list,
3628 * ``isDescendantOfList(<particle_list>,2)`` returns 1 if particle is a granddaughter of the list,
3629 * ``isDescendantOfList(<particle_list>,3)`` returns 1 if particle is a great-granddaughter of the list, etc.
3630 * Default value is ``-1`` that is inclusive for all generations.
3631 )DOC", Manager::VariableDataType::c_bool);
3632 REGISTER_METAVARIABLE("isMCDescendantOfList(particleListName[, anotherParticleListName][, generationFlag = -1])", isMCDescendantOfList, R"DOC(
3633 Returns 1 if the given particle is linked to the same MC particle as any reconstructed daughter of the decay lists.
3634
3635 Passing an integer as the last argument, allows to check if the particle belongs to the specific generation:
3636
3637 * ``isMCDescendantOfList(<particle_list>,1)`` returns 1 if particle is matched to the same particle as any daughter of the list,
3638 * ``isMCDescendantOfList(<particle_list>,2)`` returns 1 if particle is matched to the same particle as any granddaughter of the list,
3639 * ``isMCDescendantOfList(<particle_list>,3)`` returns 1 if particle is matched to the same particle as any great-granddaughter of the list, etc.
3640 * Default value is ``-1`` that is inclusive for all generations.
3641
3642 It makes only sense for lists created with `fillParticleListFromMC` function with ``addDaughters=True`` argument.
3643 )DOC", Manager::VariableDataType::c_bool);
3644
3645 REGISTER_METAVARIABLE("sourceObjectIsInList(particleListName)", sourceObjectIsInList, R"DOC(
3646Returns 1 if the underlying mdst object (e.g. track, or cluster) was used to create a particle in ``particleListName``, 0 if not.
3647
3648.. note::
3649 This only makes sense for particles that are not composite. Returns -1 for composite particles.
3650)DOC", Manager::VariableDataType::c_int);
3651
3652 REGISTER_METAVARIABLE("mcParticleIsInMCList(particleListName)", mcParticleIsInMCList, R"DOC(
3653Returns 1 if the particle's matched MC particle is also matched to a particle in ``particleListName``
3654(or if either of the lists were filled from generator level `modularAnalysis.fillParticleListFromMC`.)
3655
3656.. seealso:: :b2:var:`isMCDescendantOfList` to check daughters.
3657)DOC", Manager::VariableDataType::c_bool);
3658
3659 REGISTER_METAVARIABLE("isGrandDaughterOfList(particleListNames)", isGrandDaughterOfList,
3660 "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);
3661 REGISTER_METAVARIABLE("originalParticle(variable)", originalParticle, R"DOC(
3662 Returns value of variable for the original particle from which the given particle is copied.
3663
3664 The copy of particle is created, for example, when the vertex fit updates the daughters and `modularAnalysis.copyParticles` is called.
3665 Returns NaN if the given particle is not copied and so there is no original particle.
3666 )DOC", Manager::VariableDataType::c_double);
3667 REGISTER_METAVARIABLE("daughter(i, variable)", daughter, R"DOC(
3668 Returns value of variable for the i-th daughter. E.g.
3669
3670 * ``daughter(0, p)`` returns the total momentum of the first daughter.
3671 * ``daughter(0, daughter(1, p)`` returns the total momentum of the second daughter of the first daughter.
3672
3673 Returns NaN if particle is nullptr or if the given daughter-index is out of bound (>= amount of daughters).
3674 )DOC", Manager::VariableDataType::c_double);
3675 REGISTER_METAVARIABLE("originalDaughter(i, variable)", originalDaughter, R"DOC(
3676 Returns value of variable for the original particle from which the i-th daughter is copied.
3677
3678 The copy of particle is created, for example, when the vertex fit updates the daughters and `modularAnalysis.copyParticles` is called.
3679 Returns NaN if the daughter is not copied and so there is no original daughter.
3680
3681 Returns NaN if particle is nullptr or if the given daughter-index is out of bound (>= amount of daughters).
3682 )DOC", Manager::VariableDataType::c_double);
3683 REGISTER_METAVARIABLE("mcDaughter(i, variable)", mcDaughter, R"DOC(
3684 Returns the value of the requested variable for the i-th Monte Carlo daughter of the particle.
3685
3686 Returns NaN if the particle is nullptr, if the particle is not matched to an MC particle,
3687 or if the i-th MC daughter does not exist.
3688
3689 E.g. ``mcDaughter(0, PDG)`` will return the PDG code of the first MC daughter of the matched MC
3690 particle of the reconstructed particle the function is applied to.
3691
3692 The meta variable can also be nested: ``mcDaughter(0, mcDaughter(1, PDG))``.
3693 )DOC", Manager::VariableDataType::c_double);
3694 REGISTER_METAVARIABLE("mcMother(variable)", mcMother, R"DOC(
3695 Returns the value of the requested variable for the Monte Carlo mother of the particle.
3696
3697 Returns NaN if the particle is nullptr, if the particle is not matched to an MC particle,
3698 or if the MC mother does not exist.
3699
3700 E.g. ``mcMother(PDG)`` will return the PDG code of the MC mother of the matched MC
3701 particle of the reconstructed particle the function is applied to.
3702
3703 The meta variable can also be nested: ``mcMother(mcMother(PDG))``.
3704 )DOC", Manager::VariableDataType::c_double);
3705 REGISTER_METAVARIABLE("genParticle(index, variable)", genParticle, R"DOC(
3706[Eventbased] Returns the ``variable`` for the ith generator particle.
3707The arguments of the function must be the ``index`` of the particle in the MCParticle Array,
3708and ``variable``, the name of the function or variable for that generator particle.
3709If ``index`` goes beyond the length of the MCParticles array, NaN will be returned.
3710
3711E.g. ``genParticle(0, p)`` returns the total momentum of the first MCParticle, which in a generic decay up to MC15 is
3712the Upsilon(4S) and for MC16 and beyond the initial electron.
3713)DOC", Manager::VariableDataType::c_double);
3714 REGISTER_METAVARIABLE("genUpsilon4S(variable)", genUpsilon4S, R"DOC(
3715[Eventbased] Returns the ``variable`` evaluated for the generator-level :math:`\Upsilon(4S)`.
3716If no generator level :math:`\Upsilon(4S)` exists for the event, NaN will be returned.
3717
3718E.g. ``genUpsilon4S(p)`` returns the total momentum of the :math:`\Upsilon(4S)` in a generic decay.
3719``genUpsilon4S(mcDaughter(1, p))`` returns the total momentum of the second daughter of the
3720generator-level :math:`\Upsilon(4S)` (i.e. the momentum of the second B meson in a generic decay).
3721)DOC", Manager::VariableDataType::c_double);
3722 REGISTER_METAVARIABLE("daughterProductOf(variable)", daughterProductOf,
3723 "Returns product of a variable over all daughters.\n"
3724 "E.g. ``daughterProductOf(extraInfo(SignalProbability))`` returns the product of the SignalProbabilitys of all daughters.", Manager::VariableDataType::c_double);
3725 REGISTER_METAVARIABLE("daughterSumOf(variable)", daughterSumOf,
3726 "Returns sum of a variable over all daughters.\n"
3727 "E.g. ``daughterSumOf(nDaughters)`` returns the number of grand-daughters.", Manager::VariableDataType::c_double);
3728 REGISTER_METAVARIABLE("daughterLowest(variable)", daughterLowest,
3729 "Returns the lowest value of the given variable among all daughters.\n"
3730 "E.g. ``useCMSFrame(daughterLowest(p))`` returns the lowest momentum in CMS frame.", Manager::VariableDataType::c_double);
3731 REGISTER_METAVARIABLE("daughterHighest(variable)", daughterHighest,
3732 "Returns the highest value of the given variable among all daughters.\n"
3733 "E.g. ``useCMSFrame(daughterHighest(p))`` returns the highest momentum in CMS frame.", Manager::VariableDataType::c_double);
3734 REGISTER_METAVARIABLE("daughterDiffOf(daughterIndex_i, daughterIndex_j, variable)", daughterDiffOf, R"DOC(
3735 Returns the difference of a variable between the two given daughters.
3736 E.g. ``useRestFrame(daughterDiffOf(0, 1, p))`` returns the momentum difference between first and second daughter in the rest frame of the given particle.
3737 (That means that it returns :math:`p_j - p_i`)
3738
3739 The daughters can be provided as generalized daughter indexes, which are simply colon-separated
3740 lists of daughter indexes, ordered starting from the root particle. For example, ``0:1``
3741 identifies the second daughter (1) of the first daughter (0) of the mother particle.
3742
3743 )DOC", Manager::VariableDataType::c_double);
3744 REGISTER_METAVARIABLE("mcDaughterDiffOf(i, j, variable)", mcDaughterDiffOf,
3745 "MC matched version of the `daughterDiffOf` function.", Manager::VariableDataType::c_double);
3746 REGISTER_METAVARIABLE("grandDaughterDiffOf(i, j, variable)", grandDaughterDiffOf,
3747 "Returns the difference of a variable between the first daughters of the two given daughters.\n"
3748 "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"
3749 "(That means that it returns :math:`p_j - p_i`)", Manager::VariableDataType::c_double);
3750 MAKE_DEPRECATED("grandDaughterDiffOf", false, "light-2402-ocicat", R"DOC(
3751 The difference between any combination of (grand-)daughters can be calculated with the more general variable :b2:var:`daughterDiffOf`
3752 by using generalized daughter indexes.)DOC");
3753 REGISTER_METAVARIABLE("daughterNormDiffOf(i, j, variable)", daughterNormDiffOf,
3754 "Returns the normalized difference of a variable between the two given daughters.\n"
3755 "E.g. ``daughterNormDiffOf(0, 1, p)`` returns the normalized momentum difference between first and second daughter in the lab frame.", Manager::VariableDataType::c_double);
3756 REGISTER_METAVARIABLE("daughterMotherDiffOf(i, variable)", daughterMotherDiffOf,
3757 "Returns the difference of a variable between the given daughter and the mother particle itself.\n"
3758 "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);
3759 REGISTER_METAVARIABLE("daughterMotherNormDiffOf(i, variable)", daughterMotherNormDiffOf,
3760 "Returns the normalized difference of a variable between the given daughter and the mother particle itself.\n"
3761 "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);
3762 REGISTER_METAVARIABLE("angleBetweenDaughterAndRecoil(daughterIndex_1, daughterIndex_2, ... )", angleBetweenDaughterAndRecoil, R"DOC(
3763 Returns the angle between the momentum recoiling against the particle and the sum of the momenta of the given daughters.
3764 The unit of the angle is ``rad``.
3765
3766 The particles are identified via generalized daughter indexes, which are simply colon-separated lists of
3767 daughter indexes, ordered starting from the root particle. For example, ``0:1:3`` identifies the fourth
3768 daughter (3) of the second daughter (1) of the first daughter (0) of the mother particle. ``1`` simply
3769 identifies the second daughter of the root particle.
3770
3771 At least one generalized index has to be given to ``angleBetweenDaughterAndRecoil``.
3772
3773 .. tip::
3774 ``angleBetweenDaughterAndRecoil(0)`` will return the angle between pRecoil and the momentum of the first daughter.
3775
3776 ``angleBetweenDaughterAndRecoil(0, 1)`` will return the angle between pRecoil and the sum of the momenta of the first and second daughter.
3777
3778 ``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
3779 the first daughter of the fourth daughter.)DOC", Manager::VariableDataType::c_double);
3780 REGISTER_METAVARIABLE("angleBetweenDaughterAndMissingMomentum(daughterIndex_1, daughterIndex_2, ... )", angleBetweenDaughterAndMissingMomentum, R"DOC(
3781 Returns the angle between the missing momentum in the event and the sum of the momenta of the given daughters.
3782 The unit of the angle is ``rad``. EventKinematics module has to be called to use this.
3783
3784 The particles are identified via generalized daughter indexes, which are simply colon-separated lists of
3785 daughter indexes, ordered starting from the root particle. For example, ``0:1:3`` identifies the fourth
3786 daughter (3) of the second daughter (1) of the first daughter (0) of the mother particle. ``1`` simply
3787 identifies the second daughter of the root particle.
3788
3789 At least one generalized index has to be given to ``angleBetweenDaughterAndMissingMomentum``.
3790
3791 .. tip::
3792 ``angleBetweenDaughterAndMissingMomentum(0)`` will return the angle between missMom and the momentum of the first daughter.
3793
3794 ``angleBetweenDaughterAndMissingMomentum(0, 1)`` will return the angle between missMom and the sum of the momenta of the first and second daughter.
3795
3796 ``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
3797 the first daughter of the fourth daughter.)DOC", Manager::VariableDataType::c_double);
3798 REGISTER_METAVARIABLE("daughterAngle(daughterIndex_1, daughterIndex_2[, daughterIndex_3])", daughterAngle, R"DOC(
3799 Returns the angle in between any pair of particles belonging to the same decay tree.
3800 The unit of the angle is ``rad``.
3801
3802 The particles are identified via generalized daughter indexes, which are simply colon-separated lists of
3803 daughter indexes, ordered starting from the root particle. For example, ``0:1:3`` identifies the fourth
3804 daughter (3) of the second daughter (1) of the first daughter (0) of the mother particle. ``1`` simply
3805 identifies the second daughter of the root particle.
3806
3807 Both two and three generalized indexes can be given to ``daughterAngle``. If two indices are given, the
3808 variable returns the angle between the momenta of the two given particles. If three indices are given, the
3809 variable returns the angle between the momentum of the third particle and a vector which is the sum of the
3810 first two daughter momenta.
3811
3812 .. tip::
3813 ``daughterAngle(0, 3)`` will return the angle between the first and fourth daughter.
3814 ``daughterAngle(0, 1, 3)`` will return the angle between the fourth daughter and the sum of the first and
3815 second daughter.
3816 ``daughterAngle(0:0, 3:0)`` will return the angle between the first daughter of the first daughter, and
3817 the first daughter of the fourth daughter.
3818
3819 )DOC", Manager::VariableDataType::c_double);
3820 REGISTER_METAVARIABLE("mcDaughterAngle(daughterIndex_1, daughterIndex_2, [daughterIndex_3])", mcDaughterAngle,
3821 "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);
3822 REGISTER_VARIABLE("grandDaughterDecayAngle(i, j)", grandDaughterDecayAngle,
3823 "Returns the decay angle of the granddaughter in the daughter particle's rest frame.\n"
3824 "It is calculated with respect to the reverted momentum vector of the particle.\n"
3825 "Two arguments representing the daughter and granddaughter indices have to be provided as arguments.\n\n", "rad");
3826 REGISTER_VARIABLE("daughterClusterAngleInBetween(i, j)", daughterClusterAngleInBetween,
3827 "Returns the angle between clusters associated to the two daughters."
3828 "If two indices given: returns the angle between the momenta of the clusters associated to the two given daughters."
3829 "If three indices given: returns the angle between the momentum of the third particle's cluster and a vector "
3830 "which is the sum of the first two daughter's cluster momenta."
3831 "Returns nan if any of the daughters specified don't have an associated cluster."
3832 "The arguments in the argument vector must be integers corresponding to the ith and jth (and kth) daughters.\n\n", "rad");
3833 REGISTER_METAVARIABLE("daughterInvM(i[, j, ...])", daughterInvM, R"DOC(
3834 Returns the invariant mass adding the Lorentz vectors of the given daughters. The unit of the invariant mass is GeV/:math:`\text{c}^2`
3835 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.
3836
3837 Daughters from different generations of the decay tree can be combined using generalized daughter indexes,
3838 which are simply colon-separated daughter indexes for each generation, starting from the root particle. For
3839 example, ``0:1:3`` identifies the fourth daughter (3) of the second daughter (1) of the first daughter(0) of
3840 the mother particle.
3841
3842 Returns NaN if the given daughter-index is out of bound (>= number of daughters))DOC", Manager::VariableDataType::c_double);
3843 REGISTER_METAVARIABLE("extraInfo(name)", extraInfo,
3844 "Returns extra info stored under the given name.\n"
3845 "The extraInfo has to be set by a module first.\n"
3846 "E.g. ``extraInfo(SignalProbability)`` returns the SignalProbability calculated by the ``MVAExpert`` module.\n"
3847 "If nothing is set under the given name or if the particle is a nullptr, NaN is returned.\n"
3848 "In the latter case please use `eventExtraInfo` if you want to access an EventExtraInfo variable.", Manager::VariableDataType::c_double);
3849 REGISTER_METAVARIABLE("eventExtraInfo(name)", eventExtraInfo,
3850 "[Eventbased] Returns extra info stored under the given name in the event extra info.\n"
3851 "The extraInfo has to be set first by another module like MVAExpert in event mode.\n"
3852 "If nothing is set under this name, NaN is returned.", Manager::VariableDataType::c_double);
3853 REGISTER_METAVARIABLE("eventCached(variable)", eventCached,
3854 "[Eventbased] Returns value of event-based variable and caches this value in the EventExtraInfo.\n"
3855 "The result of second call to this variable in the same event will be provided from the cache.\n"
3856 "It is recommended to use this variable in order to declare custom aliases as event-based. This is "
3857 "necessary if using the eventwise mode of variablesToNtuple).", Manager::VariableDataType::c_double);
3858 REGISTER_METAVARIABLE("particleCached(variable)", particleCached,
3859 "Returns value of given variable and caches this value in the ParticleExtraInfo of the provided particle.\n"
3860 "The result of second call to this variable on the same particle will be provided from the cache.", Manager::VariableDataType::c_double);
3861 REGISTER_METAVARIABLE("modulo(variable, n)", modulo,
3862 "Returns rest of division of variable by n.", Manager::VariableDataType::c_int);
3863 REGISTER_METAVARIABLE("abs(variable)", abs,
3864 "Returns absolute value of the given variable.\n"
3865 "E.g. abs(mcPDG) returns the absolute value of the mcPDG, which is often useful for cuts.", Manager::VariableDataType::c_double);
3866 REGISTER_METAVARIABLE("max(var1,var2)", max, "Returns max value of two variables.\n", Manager::VariableDataType::c_double);
3867 REGISTER_METAVARIABLE("min(var1,var2)", min, "Returns min value of two variables.\n", Manager::VariableDataType::c_double);
3868 REGISTER_METAVARIABLE("sin(variable)", sin, "Returns sine value of the given variable.", Manager::VariableDataType::c_double);
3869 REGISTER_METAVARIABLE("asin(variable)", asin, "Returns arcsine of the given variable. The unit of the asin() is ``rad``", Manager::VariableDataType::c_double);
3870 REGISTER_METAVARIABLE("cos(variable)", cos, "Returns cosine value of the given variable.", Manager::VariableDataType::c_double);
3871 REGISTER_METAVARIABLE("acos(variable)", acos, "Returns arccosine value of the given variable. The unit of the acos() is ``rad``", Manager::VariableDataType::c_double);
3872 REGISTER_METAVARIABLE("tan(variable)", tan, "Returns tangent value of the given variable.", Manager::VariableDataType::c_double);
3873 REGISTER_METAVARIABLE("atan(variable)", atan, "Returns arctangent value of the given variable. The unit of the atan() is ``rad``", Manager::VariableDataType::c_double);
3874 REGISTER_METAVARIABLE("exp(variable)", exp, "Returns exponential evaluated for the given variable.", Manager::VariableDataType::c_double);
3875 REGISTER_METAVARIABLE("log(variable)", log, "Returns natural logarithm evaluated for the given variable.", Manager::VariableDataType::c_double);
3876 REGISTER_METAVARIABLE("log10(variable)", log10, "Returns base-10 logarithm evaluated for the given variable.", Manager::VariableDataType::c_double);
3877 REGISTER_METAVARIABLE("int(variable, nan_replacement)", convertToInt, R"DOC(
3878 Casts the output of the variable to an integer value.
3879
3880 .. note::
3881 Overflow and underflow are clipped at maximum and minimum values, respectively. NaN values are replaced with the value of the 2nd argument.
3882
3883 )DOC", Manager::VariableDataType::c_int);
3884 REGISTER_METAVARIABLE("isNAN(variable)", isNAN,
3885 "Returns true if variable value evaluates to nan (determined via std::isnan(double)).\n"
3886 "Useful for debugging.", Manager::VariableDataType::c_bool);
3887 REGISTER_METAVARIABLE("ifNANgiveX(variable, x)", ifNANgiveX,
3888 "Returns x (has to be a number) if variable value is nan (determined via std::isnan(double)).\n"
3889 "Useful for technical purposes while training MVAs.", Manager::VariableDataType::c_double);
3890 REGISTER_METAVARIABLE("isInfinity(variable)", isInfinity,
3891 "Returns true if variable value evaluates to infinity (determined via std::isinf(double)).\n"
3892 "Useful for debugging.", Manager::VariableDataType::c_bool);
3893 REGISTER_METAVARIABLE("unmask(variable, flag1, flag2, ...)", unmask,
3894 "unmask(variable, flag1, flag2, ...) or unmask(variable, mask) sets certain bits in the variable to zero.\n"
3895 "For example, if you want to set the second, fourth and fifth bits to zero, you could call \n"
3896 "``unmask(variable, 2, 8, 16)`` or ``unmask(variable, 26)``.\n"
3897 "", Manager::VariableDataType::c_double);
3898 REGISTER_METAVARIABLE("conditionalVariableSelector(cut, variableIfTrue, variableIfFalse)", conditionalVariableSelector,
3899 "Returns one of the two supplied variables, depending on whether the particle passes the supplied cut.\n"
3900 "The first variable is returned if the particle passes the cut, and the second variable is returned otherwise.", Manager::VariableDataType::c_double);
3901 REGISTER_METAVARIABLE("pValueCombination(p1, p2, ...)", pValueCombination,
3902 "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"
3903 "If any of the p-values is invalid, i.e. smaller than zero, -1 is returned.", Manager::VariableDataType::c_double);
3904 REGISTER_METAVARIABLE("pValueCombinationOfDaughters(variable)", pValueCombinationOfDaughters,
3905 "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"
3906 "If any of the p-values is invalid, i.e. smaller than zero, -1 is returned.", Manager::VariableDataType::c_double);
3907 REGISTER_METAVARIABLE("veto(particleList, cut, pdgCode = 11)", veto,
3908 "Combines current particle with particles from the given particle list and returns 1 if the combination passes the provided cut. \n"
3909 "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"
3910 "around the neutral Pion mass (e.g. ``0.130 < M < 0.140``). \n"
3911 "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);
3912 REGISTER_METAVARIABLE("matchedMC(variable)", matchedMC,
3913 "Returns variable output for the matched MCParticle by constructing a temporary Particle from it.\n"
3914 "This may not work too well if your variable requires accessing daughters of the particle.\n"
3915 "E.g. ``matchedMC(p)`` returns the total momentum of the related MCParticle.\n"
3916 "Returns NaN if no matched MCParticle exists.", Manager::VariableDataType::c_double);
3917 REGISTER_METAVARIABLE("clusterBestMatchedMCParticle(variable)", clusterBestMatchedMCParticle,
3918 "Returns variable output for the MCParticle that is best-matched with the ECLCluster of the given Particle.\n"
3919 "E.g. To get the energy of the MCParticle that matches best with an ECLCluster, one could use ``clusterBestMatchedMCParticle(E)``\n"
3920 "When the variable is called for ``gamma`` and if the ``gamma`` is matched with MCParticle, it works same as `matchedMC`.\n"
3921 "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"
3922 "Returns NaN if the particle is not matched to an ECLCluster, or if the ECLCluster has no matching MCParticles", Manager::VariableDataType::c_double);
3923 REGISTER_METAVARIABLE("varForBestMatchedMCKlong(variable)", clusterBestMatchedMCKlong,
3924 "Returns variable output for the Klong MCParticle which has the best match with the ECLCluster of the given Particle.\n"
3925 "Returns NaN if the particle is not matched to an ECLCluster, or if the ECLCluster has no matching Klong MCParticle", Manager::VariableDataType::c_double);
3926
3927 REGISTER_METAVARIABLE("countInList(particleList, cut='')", countInList, "[Eventbased] "
3928 "Returns number of particle which pass given in cut in the specified particle list.\n"
3929 "Useful for creating statistics about the number of particles in a list.\n"
3930 "E.g. ``countInList(e+, isSignal == 1)`` returns the number of correctly reconstructed electrons in the event.\n"
3931 "The variable is event-based and does not need a valid particle pointer as input.", Manager::VariableDataType::c_int);
3932 REGISTER_METAVARIABLE("getVariableByRank(particleList, rankedVariableName, variableName, rank)", getVariableByRank, R"DOC(
3933 [Eventbased] Returns the value of ``variableName`` for the candidate in the ``particleList`` with the requested ``rank``.
3934
3935 .. note::
3936 The `BestCandidateSelection` module available via `rankByHighest` / `rankByLowest` has to be used before.
3937
3938 .. warning::
3939 The first candidate matching the given rank is used.
3940 Thus, it is not recommended to use this variable in conjunction with ``allowMultiRank`` in the `BestCandidateSelection` module.
3941
3942 The suffix ``_rank`` is automatically added to the argument ``rankedVariableName``,
3943 which either has to be the name of the variable used to order the candidates or the selected outputVariable name without the ending ``_rank``.
3944 This means that your selected name for the rank variable has to end with ``_rank``.
3945
3946 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>`_
3947 )DOC", Manager::VariableDataType::c_double);
3948 REGISTER_VARIABLE("matchedMCHasPDG(PDGCode)", matchedMCHasPDG,
3949 "Returns if the absolute value of the PDGCode of the MCParticle related to the Particle matches a given PDGCode."
3950 "Returns 0/NAN/1 if PDGCode does not match/is not available/ matches");
3951 REGISTER_METAVARIABLE("numberOfNonOverlappingParticles(pList1, pList2, ...)", numberOfNonOverlappingParticles,
3952 "Returns the number of non-overlapping particles in the given particle lists"
3953 "Useful to check if there is additional physics going on in the detector if one reconstructed the Y4S", Manager::VariableDataType::c_int);
3954 REGISTER_METAVARIABLE("totalEnergyOfParticlesInList(particleListName)", totalEnergyOfParticlesInList,
3955 "[Eventbased] Returns the total energy of particles in the given particle List. The unit of the energy is ``GeV``", Manager::VariableDataType::c_double);
3956 REGISTER_METAVARIABLE("totalPxOfParticlesInList(particleListName)", totalPxOfParticlesInList,
3957 "[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);
3958 REGISTER_METAVARIABLE("totalPyOfParticlesInList(particleListName)", totalPyOfParticlesInList,
3959 "[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);
3960 REGISTER_METAVARIABLE("totalPzOfParticlesInList(particleListName)", totalPzOfParticlesInList,
3961 "[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);
3962 REGISTER_METAVARIABLE("invMassInLists(pList1, pList2, ...)", invMassInLists,
3963 "[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);
3964 REGISTER_METAVARIABLE("totalECLEnergyOfParticlesInList(particleListName)", totalECLEnergyOfParticlesInList,
3965 "[Eventbased] Returns the total ECL energy of particles in the given particle List. The unit of the energy is ``GeV``", Manager::VariableDataType::c_double);
3966 REGISTER_METAVARIABLE("maxPtInList(particleListName)", maxPtInList,
3967 "[Eventbased] Returns maximum transverse momentum Pt in the given particle List. The unit of the transverse momentum is ``GeV/c``", Manager::VariableDataType::c_double);
3968 REGISTER_METAVARIABLE("eclClusterSpecialTrackMatched(cut)", eclClusterTrackMatchedWithCondition,
3969 "Returns if at least one Track that satisfies the given condition is related to the ECLCluster of the Particle.", Manager::VariableDataType::c_double);
3970 REGISTER_METAVARIABLE("averageValueInList(particleListName, variable)", averageValueInList,
3971 "[Eventbased] Returns the arithmetic mean of the given variable of the particles in the given particle list.", Manager::VariableDataType::c_double);
3972 REGISTER_METAVARIABLE("medianValueInList(particleListName, variable)", medianValueInList,
3973 "[Eventbased] Returns the median value of the given variable of the particles in the given particle list.", Manager::VariableDataType::c_double);
3974 REGISTER_METAVARIABLE("sumValueInList(particleListName, variable)", sumValueInList,
3975 "[Eventbased] Returns the sum of the given variable of the particles in the given particle list.", Manager::VariableDataType::c_double);
3976 REGISTER_METAVARIABLE("productValueInList(particleListName, variable)", productValueInList,
3977 "[Eventbased] Returns the product of the given variable of the particles in the given particle list.", Manager::VariableDataType::c_double);
3978 REGISTER_METAVARIABLE("angleToClosestInList(particleListName)", angleToClosestInList,
3979 "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);
3980 REGISTER_METAVARIABLE("closestInList(particleListName, variable)", closestInList,
3981 "Returns `variable` for the closest particle (smallest opening angle) in the list provided.", Manager::VariableDataType::c_double);
3982 REGISTER_METAVARIABLE("angleToMostB2BInList(particleListName)", angleToMostB2BInList,
3983 "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);
3984 REGISTER_METAVARIABLE("deltaPhiToMostB2BPhiInList(particleListName)", deltaPhiToMostB2BPhiInList,
3985 "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);
3986 REGISTER_METAVARIABLE("mostB2BInList(particleListName, variable)", mostB2BInList,
3987 "Returns `variable` for the most back-to-back particle (closest opening angle to 180) in the list provided.", Manager::VariableDataType::c_double);
3988 REGISTER_METAVARIABLE("maxOpeningAngleInList(particleListName)", maxOpeningAngleInList,
3989 "[Eventbased] Returns maximum opening angle in the given particle List. The unit of the angle is ``rad`` ", Manager::VariableDataType::c_double);
3990 REGISTER_METAVARIABLE("daughterCombination(variable, daughterIndex_1, daughterIndex_2 ... daughterIndex_n)", daughterCombination,R"DOC(
3991Returns a ``variable`` function only of the 4-momentum calculated on an arbitrary set of (grand)daughters.
3992
3993.. warning::
3994 ``variable`` can only be a function of the daughters' 4-momenta.
3995
3996Daughters from different generations of the decay tree can be combined using generalized daughter indexes, which are simply colon-separated
3997the list of daughter indexes, starting from the root particle: for example, ``0:1:3`` identifies the fourth
3998daughter (3) of the second daughter (1) of the first daughter (0) of the mother particle.
3999
4000.. tip::
4001 ``daughterCombination(M, 0, 3, 4)`` will return the invariant mass of the system made of the first, fourth and fifth daughter of particle.
4002 ``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.
4003
4004)DOC", Manager::VariableDataType::c_double);
4005 REGISTER_METAVARIABLE("useAlternativeDaughterHypothesis(variable, daughterIndex_1:newMassHyp_1, ..., daughterIndex_n:newMassHyp_n)", useAlternativeDaughterHypothesis,R"DOC(
4006Returns a ``variable`` calculated using new mass hypotheses for (some of) the particle's daughters.
4007
4008.. warning::
4009 ``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.
4010 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.
4011 Also, the track fit is not performed again: the variable only re-calculates the 4-vectors using different mass assumptions.
4012 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).
4013
4014.. warning::
4015 Generalized daughter indexes are not supported (yet!): this variable can be used only on first-generation daughters.
4016
4017.. tip::
4018 ``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.
4019 ``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.
4020
4021)DOC", Manager::VariableDataType::c_double);
4022 REGISTER_METAVARIABLE("varForFirstMCAncestorOfType(type, variable)",varForFirstMCAncestorOfType,R"DOC(Returns requested variable of the first ancestor of the given type.
4023Ancestor type can be set up by PDG code or by particle name (check evt.pdl for valid particle names))DOC", Manager::VariableDataType::c_double);
4024
4025 REGISTER_METAVARIABLE("nTrackFitResults(particleType)", nTrackFitResults,
4026 "[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).",
4027 Manager::VariableDataType::c_int);
4028
4029 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);
4030
4031 }
4033}
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).
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.