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