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