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