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