Belle II Software  release-08-01-10
variables.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 #include <analysis/variables/Variables.h>
10 #include <analysis/variables/BasicParticleInformation.h>
11 #include <analysis/variables/VertexVariables.h>
12 #include <analysis/variables/PIDVariables.h>
13 #include <analysis/variables/TrackVariables.h>
14 
15 #include <analysis/VariableManager/Manager.h>
16 #include <analysis/VariableManager/Utility.h>
17 
18 #include <analysis/dataobjects/Particle.h>
19 #include <analysis/dataobjects/ParticleExtraInfoMap.h>
20 #include <analysis/dataobjects/ParticleList.h>
21 #include <framework/dataobjects/EventExtraInfo.h>
22 #include <analysis/dataobjects/RestOfEvent.h>
23 #include <analysis/utility/ReferenceFrame.h>
24 
25 #include <framework/datastore/StoreArray.h>
26 #include <framework/datastore/StoreObjPtr.h>
27 #include <framework/utilities/TestHelpers.h>
28 #include <framework/logging/Logger.h>
29 #include <framework/gearbox/Gearbox.h>
30 #include <framework/gearbox/Const.h>
31 
32 #include <mdst/dataobjects/MCParticle.h>
33 #include <mdst/dataobjects/MCParticleGraph.h>
34 #include <mdst/dataobjects/PIDLikelihood.h>
35 #include <mdst/dataobjects/Track.h>
36 #include <mdst/dataobjects/V0.h>
37 #include <mdst/dataobjects/ECLCluster.h>
38 #include <mdst/dataobjects/KLMCluster.h>
39 
40 #include <gtest/gtest.h>
41 
42 #include <TMatrixFSym.h>
43 #include <TRandom3.h>
44 #include <Math/Cartesian2D.h>
45 #include <Math/Vector3D.h>
46 #include <Math/Vector4D.h>
47 #include <TMath.h>
48 #include <utility>
49 
50 using namespace std;
51 using namespace Belle2;
52 using namespace Belle2::Variable;
53 using namespace ROOT::Math;
54 
55 namespace {
56 
58  TEST(KinematicVariableTest, Variable)
59  {
60 
61  // Connect gearbox for CMS variables
62 
63  Gearbox& gearbox = Gearbox::getInstance();
64  gearbox.setBackends({std::string("file:")});
65  gearbox.close();
66  gearbox.open("geometry/Belle2.xml", false);
67 
68  {
69  Particle pH({0.290582573157898, 0, 6.99796952744559, 7.004}, 11);
70  Particle pL({0.166035330010433, 0, -3.99855423973071, 4.002}, -11);
71 
72  Particle p({0, 0.999999869440028, 0, 1.0}, -11);
73  const double eps = 1e-15;
75 
76  EXPECT_NEAR(0.0, particlePx(&pH), eps);
77  EXPECT_NEAR(0.0, particlePy(&pH), eps);
78  EXPECT_NEAR(0.0, particlePx(&pL), eps);
79  EXPECT_NEAR(0.0, particlePy(&pL), eps);
80  EXPECT_FLOAT_EQ(5.289778893721573, particlePz(&pH));
81  EXPECT_FLOAT_EQ(-5.289778893721573, particlePz(&pL));
82  EXPECT_FLOAT_EQ(10.579557836806245064 / 2, particleE(&pH));
83  EXPECT_FLOAT_EQ(10.579557836806245064 / 2, particleE(&pL));
84 
85  EXPECT_FLOAT_EQ(0.999999869440028, particlePy(&p));
86  }
87 
88  {
89  Particle p({ 0.1, -0.4, 0.8, 1.0 }, 411);
90 
91  TMatrixFSym error(7);
92  error.Zero();
93  error(0, 0) = 0.05;
94  error(1, 1) = 0.2;
95  error(2, 2) = 0.4;
96  error(0, 1) = -0.1;
97  error(0, 2) = 0.9;
98  p.setMomentumVertexErrorMatrix(error);
99 
100  EXPECT_FLOAT_EQ(0.9, particleP(&p));
101  EXPECT_FLOAT_EQ(1.0, particleE(&p));
102  EXPECT_FLOAT_EQ(0.1, particlePx(&p));
103  EXPECT_FLOAT_EQ(-0.4, particlePy(&p));
104  EXPECT_FLOAT_EQ(0.8, particlePz(&p));
105  EXPECT_FLOAT_EQ(0.412310562, particlePt(&p));
106  EXPECT_FLOAT_EQ(0.8 / 0.9, particleCosTheta(&p));
107  EXPECT_FLOAT_EQ(-1.325817664, particlePhi(&p));
108 
109  EXPECT_FLOAT_EQ(0.737446378, particlePErr(&p));
110  EXPECT_FLOAT_EQ(sqrt(0.05), particlePxErr(&p));
111  EXPECT_FLOAT_EQ(sqrt(0.2), particlePyErr(&p));
112  EXPECT_FLOAT_EQ(sqrt(0.4), particlePzErr(&p));
113  EXPECT_FLOAT_EQ(0.488093530, particlePtErr(&p));
114  EXPECT_FLOAT_EQ(0.156402664, particleCosThetaErr(&p));
115  EXPECT_FLOAT_EQ(0.263066820, particlePhiErr(&p));
116 
117 
118  {
120  EXPECT_FLOAT_EQ(0.68174648, particleP(&p));
121  EXPECT_FLOAT_EQ(0.80918372, particleE(&p));
122  EXPECT_FLOAT_EQ(0.058562335, particlePx(&p));
123  EXPECT_FLOAT_EQ(-0.40000001, particlePy(&p));
124  EXPECT_FLOAT_EQ(0.5489524, particlePz(&p));
125  EXPECT_FLOAT_EQ(0.40426421, particlePt(&p));
126  EXPECT_FLOAT_EQ(0.80521482, particleCosTheta(&p));
127  EXPECT_FLOAT_EQ(-1.4254233, particlePhi(&p));
128 
129  EXPECT_FLOAT_EQ(sqrt(0.2), particlePyErr(&p));
130  }
131 
132  {
134  EXPECT_ALL_NEAR(particleP(&p), 0.0, 1e-9);
135  EXPECT_FLOAT_EQ(0.4358899, particleE(&p));
136  EXPECT_ALL_NEAR(0.0, particlePx(&p), 1e-9);
137  EXPECT_ALL_NEAR(0.0, particlePy(&p), 1e-9);
138  EXPECT_ALL_NEAR(0.0, particlePz(&p), 1e-9);
139  EXPECT_ALL_NEAR(0.0, particlePt(&p), 1e-9);
140 
141  }
142 
143  {
145  EXPECT_FLOAT_EQ(0.9, particleP(&p));
146  EXPECT_FLOAT_EQ(1.0, particleE(&p));
147  EXPECT_FLOAT_EQ(0.1, particlePx(&p));
148  EXPECT_FLOAT_EQ(-0.4, particlePy(&p));
149  EXPECT_FLOAT_EQ(0.8, particlePz(&p));
150  EXPECT_FLOAT_EQ(0.412310562, particlePt(&p));
151  EXPECT_FLOAT_EQ(0.8 / 0.9, particleCosTheta(&p));
152  EXPECT_FLOAT_EQ(-1.325817664, particlePhi(&p));
153 
154  EXPECT_FLOAT_EQ(0.737446378, particlePErr(&p));
155  EXPECT_FLOAT_EQ(sqrt(0.05), particlePxErr(&p));
156  EXPECT_FLOAT_EQ(sqrt(0.2), particlePyErr(&p));
157  EXPECT_FLOAT_EQ(sqrt(0.4), particlePzErr(&p));
158  EXPECT_FLOAT_EQ(0.488093530, particlePtErr(&p));
159  EXPECT_FLOAT_EQ(0.156402664, particleCosThetaErr(&p));
160  EXPECT_FLOAT_EQ(0.263066820, particlePhiErr(&p));
161  }
162 
163  {
164  UseReferenceFrame<RotationFrame> dummy(XYZVector(1, 0, 0), XYZVector(0, 1, 0), XYZVector(0, 0, 1));
165  EXPECT_FLOAT_EQ(0.9, particleP(&p));
166  EXPECT_FLOAT_EQ(1.0, particleE(&p));
167  EXPECT_FLOAT_EQ(0.1, particlePx(&p));
168  EXPECT_FLOAT_EQ(-0.4, particlePy(&p));
169  EXPECT_FLOAT_EQ(0.8, particlePz(&p));
170  EXPECT_FLOAT_EQ(0.412310562, particlePt(&p));
171  EXPECT_FLOAT_EQ(0.8 / 0.9, particleCosTheta(&p));
172  EXPECT_FLOAT_EQ(-1.325817664, particlePhi(&p));
173 
174  EXPECT_FLOAT_EQ(0.737446378, particlePErr(&p));
175  EXPECT_FLOAT_EQ(sqrt(0.05), particlePxErr(&p));
176  EXPECT_FLOAT_EQ(sqrt(0.2), particlePyErr(&p));
177  EXPECT_FLOAT_EQ(sqrt(0.4), particlePzErr(&p));
178  EXPECT_FLOAT_EQ(0.488093530, particlePtErr(&p));
179  EXPECT_FLOAT_EQ(0.156402664, particleCosThetaErr(&p));
180  EXPECT_FLOAT_EQ(0.263066820, particlePhiErr(&p));
181 
182  const auto& frame = ReferenceFrame::GetCurrent();
183  EXPECT_FLOAT_EQ(-0.1, frame.getMomentumErrorMatrix(&p)(0, 1));
184  EXPECT_FLOAT_EQ(0.9, frame.getMomentumErrorMatrix(&p)(0, 2));
185  }
186 
187  {
188  UseReferenceFrame<RotationFrame> dummy(XYZVector(1, 0, 0), XYZVector(0, 0, -1), XYZVector(0, 1, 0));
189  EXPECT_FLOAT_EQ(0.9, particleP(&p));
190  EXPECT_FLOAT_EQ(1.0, particleE(&p));
191  EXPECT_FLOAT_EQ(0.1, particlePx(&p));
192  EXPECT_FLOAT_EQ(-0.8, particlePy(&p));
193  EXPECT_FLOAT_EQ(-0.4, particlePz(&p));
194 
195  EXPECT_FLOAT_EQ(0.737446378, particlePErr(&p));
196  EXPECT_FLOAT_EQ(sqrt(0.05), particlePxErr(&p));
197  EXPECT_FLOAT_EQ(sqrt(0.4), particlePyErr(&p));
198  EXPECT_FLOAT_EQ(sqrt(0.2), particlePzErr(&p));
199 
200  const auto& frame = ReferenceFrame::GetCurrent();
201  EXPECT_FLOAT_EQ(-0.9, frame.getMomentumErrorMatrix(&p)(0, 1));
202  EXPECT_FLOAT_EQ(-0.1, frame.getMomentumErrorMatrix(&p)(0, 2));
203  }
204 
205  {
206  UseReferenceFrame<CMSRotationFrame> dummy(XYZVector(1, 0, 0), XYZVector(0, 1, 0), XYZVector(0, 0, 1));
207  EXPECT_FLOAT_EQ(0.68174648, particleP(&p));
208  EXPECT_FLOAT_EQ(0.80918372, particleE(&p));
209  EXPECT_FLOAT_EQ(0.058562335, particlePx(&p));
210  EXPECT_FLOAT_EQ(-0.40000001, particlePy(&p));
211  EXPECT_FLOAT_EQ(0.5489524, particlePz(&p));
212  EXPECT_FLOAT_EQ(0.40426421, particlePt(&p));
213  EXPECT_FLOAT_EQ(0.80521482, particleCosTheta(&p));
214  EXPECT_FLOAT_EQ(-1.4254233, particlePhi(&p));
215 
216  EXPECT_FLOAT_EQ(sqrt(0.2), particlePyErr(&p));
217  }
218 
219  {
220  Particle pinv({ -0.1, 0.4, -0.8, 1.0 }, 411);
221  UseReferenceFrame<RestFrame> dummy(&pinv);
222  Particle p2({ 0.0, 0.0, 0.0, 0.4358899}, 411);
223  EXPECT_FLOAT_EQ(0.9, particleP(&p2));
224  EXPECT_FLOAT_EQ(1.0, particleE(&p2));
225  EXPECT_FLOAT_EQ(0.1, particlePx(&p2));
226  EXPECT_FLOAT_EQ(-0.4, particlePy(&p2));
227  EXPECT_FLOAT_EQ(0.8, particlePz(&p2));
228  EXPECT_FLOAT_EQ(0.412310562, particlePt(&p2));
229  EXPECT_FLOAT_EQ(0.8 / 0.9, particleCosTheta(&p2));
230  EXPECT_FLOAT_EQ(-1.325817664, particlePhi(&p2));
231  }
232  }
233 
234  {
235  Particle p({ 0.0, 0.0, 0.0, 0.0 }, 411);
236  EXPECT_FLOAT_EQ(0.0, particleP(&p));
237  EXPECT_FLOAT_EQ(0.0, particleE(&p));
238  EXPECT_FLOAT_EQ(0.0, particlePx(&p));
239  EXPECT_FLOAT_EQ(0.0, particlePy(&p));
240  EXPECT_FLOAT_EQ(0.0, particlePz(&p));
241  EXPECT_FLOAT_EQ(0.0, particlePt(&p));
242  EXPECT_FLOAT_EQ(1.0, particleCosTheta(&p));
243  EXPECT_FLOAT_EQ(0.0, particlePhi(&p));
244 
246  EXPECT_FLOAT_EQ(0.0, particleP(&p));
247  EXPECT_FLOAT_EQ(0.0, particleE(&p));
248  EXPECT_FLOAT_EQ(0.0, particlePx(&p));
249  EXPECT_FLOAT_EQ(0.0, particlePy(&p));
250  EXPECT_FLOAT_EQ(0.0, particlePz(&p));
251  EXPECT_FLOAT_EQ(0.0, particlePt(&p));
252  EXPECT_FLOAT_EQ(1.0, particleCosTheta(&p));
253  EXPECT_FLOAT_EQ(0.0, particlePhi(&p));
254  }
255 
256  {
257  DataStore::Instance().setInitializeActive(true);
258  StoreArray<Particle> particles;
259  particles.registerInDataStore();
260  DataStore::Instance().setInitializeActive(false);
262  PxPyPzEVector vec0 = {0.0, 0.0, 0.0, T.getCMSEnergy()};
263  PxPyPzEVector vec1 = {0.0, +0.332174566, 0.0, T.getCMSEnergy() / 2.};
264  PxPyPzEVector vec2 = {0.0, -0.332174566, 0.0, T.getCMSEnergy() / 2.};
265  Particle* p0 = particles.appendNew(Particle(T.rotateCmsToLab() * vec0, 22));
266  Particle* p1 = particles.appendNew(Particle(T.rotateCmsToLab() * vec1, 22, Particle::c_Unflavored, Particle::c_Undefined, 1));
267  Particle* p2 = particles.appendNew(Particle(T.rotateCmsToLab() * vec2, 22, Particle::c_Unflavored, Particle::c_Undefined, 2));
268 
269  p0->appendDaughter(p1->getArrayIndex());
270  p0->appendDaughter(p2->getArrayIndex());
271 
272  EXPECT_ALL_NEAR(m2RecoilSignalSide(p0), 0.0, 1e-7);
273  }
274 
275 
276  }
277 
278 
279  TEST(VertexVariableTest, Variable)
280  {
281 
282  // Connect gearbox for CMS variables
283 
284  Gearbox& gearbox = Gearbox::getInstance();
285  gearbox.setBackends({std::string("file:")});
286  gearbox.close();
287  gearbox.open("geometry/Belle2.xml", false);
288 
289  Particle p({ 0.1, -0.4, 0.8, 1.0 }, 11);
290  p.setPValue(0.5);
291  p.setVertex(XYZVector(1.0, 2.0, 2.0));
292 
293  EXPECT_FLOAT_EQ(1.0, particleDX(&p));
294  EXPECT_FLOAT_EQ(2.0, particleDY(&p));
295  EXPECT_FLOAT_EQ(2.0, particleDZ(&p));
296  EXPECT_FLOAT_EQ(std::sqrt(5.0), particleDRho(&p));
297  EXPECT_FLOAT_EQ(3.0, particleDistance(&p));
298  EXPECT_FLOAT_EQ(0.5, particlePvalue(&p));
299 
300  {
302  EXPECT_FLOAT_EQ(1.026177, particleDX(&p));
303  EXPECT_FLOAT_EQ(2.0, particleDY(&p));
304  EXPECT_FLOAT_EQ(2.2568872, particleDZ(&p));
305  EXPECT_FLOAT_EQ(hypot(2.0, 1.026177), particleDRho(&p));
306  EXPECT_FLOAT_EQ(3.1853695, particleDistance(&p));
307  EXPECT_FLOAT_EQ(0.5, particlePvalue(&p));
308  }
309 
310  {
311  Particle p2({ 0.1, -0.4, 0.8, 1.0 }, 11);
312  p2.setPValue(0.5);
313  p2.setVertex(XYZVector(1.0, 2.0, 2.0));
314 
315  UseReferenceFrame<RestFrame> dummy(&p2);
316  EXPECT_FLOAT_EQ(0.0, particleDX(&p));
317  EXPECT_FLOAT_EQ(0.0, particleDY(&p));
318  EXPECT_FLOAT_EQ(0.0, particleDZ(&p));
319  EXPECT_FLOAT_EQ(0.0, particleDRho(&p));
320  EXPECT_FLOAT_EQ(0.0, particleDistance(&p));
321  EXPECT_FLOAT_EQ(0.5, particlePvalue(&p));
322  }
323 
324  /* Test with a distance between mother and daughter vertex. One
325  * has to calculate the result by hand to test the code....
326 
327  {
328  Particle p2({ 0.0 , 1.0, 0.0, 1.0 }, 11);
329  p2.setPValue(0.5);
330  p2.setVertex(XYZVector(1.0, 0.0, 2.0));
331 
332  UseReferenceFrame<RestFrame> dummy(&p2);
333  EXPECT_FLOAT_EQ(0.0, particleDX(&p));
334  EXPECT_FLOAT_EQ(2.0, particleDY(&p));
335  EXPECT_FLOAT_EQ(0.0, particleDZ(&p));
336  EXPECT_FLOAT_EQ(2.0, particleDRho(&p));
337  EXPECT_FLOAT_EQ(2.0, particleDistance(&p));
338  EXPECT_FLOAT_EQ(0.5, particlePvalue(&p));
339  }
340  */
341 
342  }
343 
344  TEST(TrackVariablesTest, Variable)
345  {
346  DataStore::Instance().setInitializeActive(true);
347  StoreArray<TrackFitResult> myResults;
348  StoreArray<Track> myTracks;
349  StoreArray<V0> myV0s;
350  StoreArray<Particle> myParticles;
351  myResults.registerInDataStore();
352  myTracks.registerInDataStore();
353  myV0s.registerInDataStore();
354  myParticles.registerInDataStore();
355  DataStore::Instance().setInitializeActive(false);
356 
357  TRandom3 generator;
358 
359  const float pValue = 0.5;
360  const float bField = 1.5;
361  const int charge = 1;
362  TMatrixDSym cov6(6);
363 
364  // Generate a random put orthogonal pair of vectors in the r-phi plane
365  ROOT::Math::Cartesian2D d(generator.Uniform(-1, 1), generator.Uniform(-1, 1));
366  ROOT::Math::Cartesian2D pt(generator.Uniform(-1, 1), generator.Uniform(-1, 1));
367  d.SetXY(d.X(), -(d.X()*pt.X()) / pt.Y());
368 
369  // Add a random z component
370  ROOT::Math::XYZVector position(d.X(), d.Y(), generator.Uniform(-1, 1));
371  ROOT::Math::XYZVector momentum(pt.X(), pt.Y(), generator.Uniform(-1, 1));
372 
373  auto CDCValue = static_cast<unsigned long long int>(0x300000000000000);
374 
375  myResults.appendNew(position, momentum, cov6, charge, Const::electron, pValue, bField, CDCValue, 16777215, 0);
376  Track mytrack;
377  mytrack.setTrackFitResultIndex(Const::electron, 0);
378  Track* savedTrack = myTracks.appendNew(mytrack);
379 
380  Particle* part = myParticles.appendNew(savedTrack, Const::ChargedStable(11));
381 
382  const Manager::Var* vIsFromECL = Manager::Instance().getVariable("isFromECL");
383  const Manager::Var* vIsFromKLM = Manager::Instance().getVariable("isFromKLM");
384  const Manager::Var* vIsFromTrack = Manager::Instance().getVariable("isFromTrack");
385  const Manager::Var* vIsFromV0 = Manager::Instance().getVariable("isFromV0");
386 
387  EXPECT_TRUE(std::get<bool>(vIsFromTrack->function(part)));
388  EXPECT_FALSE(std::get<bool>(vIsFromECL->function(part)));
389  EXPECT_FALSE(std::get<bool>(vIsFromKLM->function(part)));
390  EXPECT_FALSE(std::get<bool>(vIsFromV0->function(part)));
391  EXPECT_FLOAT_EQ(0.5, trackPValue(part));
392  EXPECT_FLOAT_EQ(position.Z(), trackZ0(part));
393  EXPECT_FLOAT_EQ(position.Rho(), trackD0(part));
394  EXPECT_FLOAT_EQ(particleDRho(part), std::fabs(trackD0FromIP(part)));
395  EXPECT_FLOAT_EQ(particleDZ(part), trackZ0FromIP(part));
396  EXPECT_FLOAT_EQ(3, trackNCDCHits(part));
397  EXPECT_FLOAT_EQ(24, trackNSVDHits(part));
398  EXPECT_FLOAT_EQ(12, trackNPXDHits(part));
399 
400  //-----------------------------------------------------------------------
401  // now add another track and mock up a V0 and a V0-based particle
402  myResults.appendNew(position, momentum, cov6, charge * -1,
403  Const::electron, pValue, bField, CDCValue, 16777215, 0);
404  Track secondTrack;
405  secondTrack.setTrackFitResultIndex(Const::electron, 1);
406  Track* savedTrack2 = myTracks.appendNew(secondTrack);
407  myParticles.appendNew(savedTrack2, Const::ChargedStable(11));
408  myV0s.appendNew(V0(std::pair(savedTrack, myResults[0]), std::pair(savedTrack2, myResults[1]), 0.0, 0.0, 0.0));
409  const PxPyPzEVector v0Momentum(2 * momentum.X(), 2 * momentum.Y(), 2 * momentum.Z(), (momentum * 2).R());
410  auto v0particle = myParticles.appendNew(v0Momentum, 22,
411  Particle::c_Unflavored, Particle::c_V0, 0);
412  v0particle->appendDaughter(0, false);
413  v0particle->appendDaughter(1, false);
414  //-----------------------------------------------------------------------
415 
416  EXPECT_FALSE(std::get<bool>(vIsFromTrack->function(v0particle)));
417  EXPECT_FALSE(std::get<bool>(vIsFromECL->function(v0particle)));
418  EXPECT_FALSE(std::get<bool>(vIsFromKLM->function(v0particle)));
419  EXPECT_TRUE(std::get<bool>(vIsFromV0->function(v0particle)));
420 
421  const Manager::Var* vNDaughters = Manager::Instance().getVariable("nDaughters");
422  EXPECT_EQ(std::get<int>(vNDaughters->function(v0particle)), 2);
423  }
424 
425  class MCTruthVariablesTest : public ::testing::Test {
426  protected:
427  virtual void SetUp()
428  {
429  // datastore things
430  DataStore::Instance().reset();
431  DataStore::Instance().setInitializeActive(true);
432 
433  // needed to mock up
434  StoreArray<ECLCluster> clusters;
435  StoreArray<MCParticle> mcparticles;
436  StoreArray<Track> tracks;
437  StoreArray<TrackFitResult> trackfits;
438  StoreArray<Particle> particles;
439 
440  // register the arrays
441  clusters.registerInDataStore();
442  mcparticles.registerInDataStore();
443  tracks.registerInDataStore();
444  trackfits.registerInDataStore();
445  particles.registerInDataStore();
446 
447  // register the relations for mock up mcmatching
448  clusters.registerRelationTo(mcparticles);
449  tracks.registerRelationTo(mcparticles);
450  particles.registerRelationTo(mcparticles);
451 
452  // register the relation for mock up track <--> cluster matching
453  //clusters.registerRelationTo(tracks);
454  tracks.registerRelationTo(clusters);
455 
456  // end datastore things
457  DataStore::Instance().setInitializeActive(false);
458 
459  /* mock up an electron (track with a cluster AND a track-cluster match)
460  * and a photon (cluster, no track) and MCParticles for both
461  *
462  * this assumes that everything (tracking, clustering, track-cluster
463  * matching *and* mcmatching all worked)
464  *
465  * this can be extended to pions, kaons, etc but leave it simple for now
466  */
467 
468  // create the true underlying mcparticles
469  auto* true_photon = mcparticles.appendNew(MCParticle());
470  true_photon->setPDG(Const::photon.getPDGCode());
471  auto* true_electron = mcparticles.appendNew(MCParticle());
472  true_electron->setPDG(Const::electron.getPDGCode());
473  auto* true_pion = mcparticles.appendNew(MCParticle());
474  true_pion->setPDG(-Const::pion.getPDGCode());
475 
476  // create the reco clusters
477  auto* cl0 = clusters.appendNew(ECLCluster());
478  cl0->setEnergy(1.0);
479  cl0->setHypothesis(ECLCluster::EHypothesisBit::c_nPhotons);
480  cl0->setClusterId(0);
481 
482  auto* cl1 = clusters.appendNew(ECLCluster());
483  cl1->setEnergy(0.5);
484  cl1->setHypothesis(ECLCluster::EHypothesisBit::c_nPhotons);
485  cl1->setClusterId(1);
486 
487  // create a reco track (one has to also mock up a track fit result)
488  TMatrixDSym cov(6);
489  trackfits.appendNew(
490  ROOT::Math::XYZVector(), ROOT::Math::XYZVector(), cov, -1, Const::electron, 0.5, 1.5,
491  static_cast<unsigned long long int>(0x300000000000000), 16777215, 0);
492  auto* electron_tr = tracks.appendNew(Track());
493  electron_tr->setTrackFitResultIndex(Const::electron, 0);
494  electron_tr->addRelationTo(cl1); // a track <--> cluster match
495 
496  TMatrixDSym cov1(6);
497  trackfits.appendNew(
498  ROOT::Math::XYZVector(), ROOT::Math::XYZVector(), cov1, -1, Const::pion, 0.51, 1.5,
499  static_cast<unsigned long long int>(0x300000000000000), 16777215, 0);
500  auto* pion_tr = tracks.appendNew(Track());
501  pion_tr->setTrackFitResultIndex(Const::pion, 0);
502  pion_tr->addRelationTo(cl1); // a track <--> cluster match
503 
504  // now set mcmatch relations
505  cl0->addRelationTo(true_photon, 12.3);
506  cl0->addRelationTo(true_electron, 2.3);
507  cl1->addRelationTo(true_electron, 45.6);
508  cl1->addRelationTo(true_photon, 5.6);
509  cl1->addRelationTo(true_pion, 15.6);
510 
511  electron_tr->addRelationTo(true_electron);
512  pion_tr->addRelationTo(true_pion);
513 
514  // create belle2::Particles from the mdst objects
515  const auto* photon = particles.appendNew(Particle(cl0));
516  const auto* electron = particles.appendNew(Particle(electron_tr, Const::electron));
517  const auto* pion = particles.appendNew(Particle(pion_tr, Const::pion));
518  const auto* misid_photon = particles.appendNew(Particle(cl1));
519 
520  // now set mcmatch relations
521  photon->addRelationTo(true_photon);
522  electron->addRelationTo(true_electron);
523  pion->addRelationTo(true_pion);
524  misid_photon->addRelationTo(true_electron); // assume MC matching caught this
525  }
526 
527  virtual void TearDown()
528  {
529  DataStore::Instance().reset();
530  }
531  };
532 
533  TEST_F(MCTruthVariablesTest, mcCosThetaBetweenParticleAndNominalB)
534  {
535  DataStore::Instance().reset();
536  DataStore::Instance().setInitializeActive(true);
537  StoreArray<MCParticle> mcParticles;
538  StoreArray<Particle> particles;
539  particles.registerInDataStore();
540  mcParticles.registerInDataStore();
541  particles.registerRelationTo(mcParticles);
542  DataStore::Instance().setInitializeActive(false);
543 
544  // Create MC graph for B- -> (D0 -> K- e+ nu_e) pi-
545  MCParticleGraph mcGraph;
546 
547  MCParticleGraph::GraphParticle& graphParticleMother = mcGraph.addParticle();
548  MCParticleGraph::GraphParticle& graphParticleDaughter1 = mcGraph.addParticle();
549  MCParticleGraph::GraphParticle& graphParticleDaughter2 = mcGraph.addParticle();
550  MCParticleGraph::GraphParticle& graphParticleGranddaughter1 = mcGraph.addParticle();
551  MCParticleGraph::GraphParticle& graphParticleGranddaughter2 = mcGraph.addParticle();
552  MCParticleGraph::GraphParticle& graphParticleGranddaughter3 = mcGraph.addParticle();
553 
554  graphParticleDaughter1.comesFrom(graphParticleMother);
555  graphParticleDaughter2.comesFrom(graphParticleMother);
556  graphParticleGranddaughter1.comesFrom(graphParticleDaughter1);
557  graphParticleGranddaughter2.comesFrom(graphParticleDaughter1);
558  graphParticleGranddaughter3.comesFrom(graphParticleDaughter1);
559 
560  graphParticleMother.setPDG(-521);
561  graphParticleDaughter1.setPDG(421);
562  graphParticleDaughter2.setPDG(-Const::pion.getPDGCode());
563  graphParticleGranddaughter1.setPDG(-Const::kaon.getPDGCode());
564  graphParticleGranddaughter2.setPDG(-Const::electron.getPDGCode());
565  graphParticleGranddaughter3.setPDG(12);
566 
567  // Create the two 4-vectors that will factor into calculation, and set a mass that corresponds
568  // to the length of the 4-vector
570  graphParticleMother.set4Vector(T.rotateCmsToLab() * PxPyPzEVector(3.0, 4.0, 5.0, 18.0));
571  graphParticleGranddaughter3.set4Vector(T.rotateCmsToLab() * PxPyPzEVector(0.0, 0.0, 5.0, 5.0));
572  graphParticleMother.setMass(16.55294535724685);
573 
574  // The following masses and momenta do not factor into the calculation, but we will set them non-zero
575  PxPyPzEVector dummyP4(1, 2, 1, 5);
576  double dummyM = 4.3589;
577  graphParticleDaughter1.set4Vector(dummyP4);
578  graphParticleDaughter1.setMass(dummyM);
579  graphParticleDaughter2.set4Vector(dummyP4);
580  graphParticleDaughter2.setMass(dummyM);
581  graphParticleGranddaughter1.set4Vector(dummyP4);
582  graphParticleGranddaughter1.setMass(dummyM);
583  graphParticleGranddaughter2.set4Vector(dummyP4);
584  graphParticleGranddaughter2.setMass(dummyM);
585 
586  mcGraph.generateList();
587 
588  // Create mockup particles and add relations to MC particles
589  auto* pMother = particles.appendNew(dummyP4, -521);
590  pMother->addRelationTo(mcParticles[0]);
591 
592  particles.appendNew(dummyP4, 421)->addRelationTo(mcParticles[1]);
593  particles.appendNew(dummyP4, -211)->addRelationTo(mcParticles[2]);
594  particles.appendNew(dummyP4, -321)->addRelationTo(mcParticles[3]);
595  particles.appendNew(dummyP4, -11)->addRelationTo(mcParticles[4]);
596  particles.appendNew(dummyP4, 12)->addRelationTo(mcParticles[5]);
597 
598  double E_B = T.getCMSEnergy() / 2.0;
599  double M_B = pMother->getPDGMass();
600  double p_B = std::sqrt(E_B * E_B - M_B * M_B);
601 
602  PxPyPzEVector p4_Y_CMS = T.rotateLabToCms() * (graphParticleMother.get4Vector() - graphParticleGranddaughter3.get4Vector());
603  double E_Y = p4_Y_CMS.E(); // E_Mother - E_Granddaughter3
604  double p_Y = p4_Y_CMS.P(); // |p_Mother - p_Granddaughter3|
605  double M_Y = p4_Y_CMS.M(); // sqrt((p4_Mother - p4_Granddaughter3)^2)
606 
607  double expectedCosBY = (2 * E_B * E_Y - M_B * M_B - M_Y * M_Y) / (2 * p_B * p_Y);
608 
609  const auto* mcCosBY = Manager::Instance().getVariable("mcCosThetaBetweenParticleAndNominalB");
610 
611  EXPECT_NEAR(std::get<double>(mcCosBY->function(pMother)), expectedCosBY, 1e-4);
612  }
613 
614  TEST_F(MCTruthVariablesTest, ECLMCMatchWeightVariable)
615  {
616  StoreArray<Particle> particles{};
617  const auto* photon = particles[0];
618  const auto* electron = particles[1];
619  const auto* pion = particles[2];
620 
621  const auto* weight = Manager::Instance().getVariable("clusterMCMatchWeight");
622  EXPECT_FLOAT_EQ(std::get<double>(weight->function(photon)), 12.3);
623  EXPECT_FLOAT_EQ(std::get<double>(weight->function(electron)), 45.6);
624  EXPECT_FLOAT_EQ(std::get<double>(weight->function(pion)), 15.6);
625  }
626 
627  TEST_F(MCTruthVariablesTest, ECLBestMCMatchVariables)
628  {
629  StoreArray<Particle> particles{};
630  const auto* photon = particles[0];
631  const auto* electron = particles[1];
632  const auto* pion = particles[2];
633  const auto* misid_photon = particles[3];
634 
635 
636  const auto* pdgcode = Manager::Instance().getVariable("clusterBestMCPDG");
637  EXPECT_EQ(std::get<double>(pdgcode->function(photon)), Const::photon.getPDGCode());
638  EXPECT_EQ(std::get<double>(pdgcode->function(electron)), Const::electron.getPDGCode());
639  EXPECT_EQ(std::get<double>(pdgcode->function(pion)), Const::electron.getPDGCode());
640  EXPECT_EQ(std::get<double>(pdgcode->function(misid_photon)), Const::electron.getPDGCode());
641 
642  const auto* weight = Manager::Instance().getVariable("clusterBestMCMatchWeight");
643  EXPECT_FLOAT_EQ(std::get<double>(weight->function(photon)), 12.3);
644  EXPECT_FLOAT_EQ(std::get<double>(weight->function(electron)), 45.6);
645  EXPECT_FLOAT_EQ(std::get<double>(weight->function(pion)), 45.6);
646  EXPECT_FLOAT_EQ(std::get<double>(weight->function(misid_photon)), 45.6);
647  }
648 
649 
650  class EventVariableTest : public ::testing::Test {
651  protected:
653  void SetUp() override
654  {
655  DataStore::Instance().setInitializeActive(true);
658  DataStore::Instance().setInitializeActive(false);
659 
660  }
661 
663  void TearDown() override
664  {
665  DataStore::Instance().reset();
666  }
667  };
668 
669  TEST_F(EventVariableTest, ExperimentRunEventDateAndTime)
670  {
671  const Manager::Var* exp = Manager::Instance().getVariable("expNum");
672  const Manager::Var* run = Manager::Instance().getVariable("runNum");
673  const Manager::Var* evt = Manager::Instance().getVariable("evtNum");
674  const Manager::Var* date = Manager::Instance().getVariable("date");
675  const Manager::Var* year = Manager::Instance().getVariable("year");
676  const Manager::Var* time = Manager::Instance().getVariable("eventTimeSeconds");
677 
678  // there is no EventMetaData so expect nan
679  EXPECT_FALSE(std::get<double>(date->function(nullptr)) == std::get<double>(date->function(nullptr)));
680  EXPECT_FALSE(std::get<double>(year->function(nullptr)) == std::get<double>(year->function(nullptr)));
681  EXPECT_FALSE(std::get<double>(time->function(nullptr)) == std::get<double>(time->function(nullptr)));
682 
683  DataStore::Instance().setInitializeActive(true);
684  StoreObjPtr<EventMetaData> evtMetaData;
685  evtMetaData.registerInDataStore();
686  DataStore::Instance().setInitializeActive(false);
687  evtMetaData.create();
688  evtMetaData->setExperiment(1337);
689  evtMetaData->setRun(12345);
690  evtMetaData->setEvent(54321);
691  evtMetaData->setTime(1288569600e9);
692  // 01/11/2010 is the date TDR was uploaded to arXiv ... experiment's birthday?
693 
694 
695  // -
696  EXPECT_EQ(std::get<int>(exp->function(nullptr)), 1337);
697  EXPECT_EQ(std::get<int>(run->function(nullptr)), 12345);
698  EXPECT_EQ(std::get<int>(evt->function(nullptr)), 54321);
699  EXPECT_FLOAT_EQ(std::get<double>(date->function(nullptr)), 20101101.);
700  EXPECT_FLOAT_EQ(std::get<double>(year->function(nullptr)), 2010.);
701  EXPECT_FLOAT_EQ(std::get<double>(time->function(nullptr)), 1288569600);
702  }
703 
704  TEST_F(EventVariableTest, TestGlobalCounters)
705  {
706  StoreArray<MCParticle> mcParticles; // empty
707  const Manager::Var* var = Manager::Instance().getVariable("nMCParticles");
708  EXPECT_EQ(std::get<int>(var->function(nullptr)), 0);
709 
710  for (unsigned i = 0; i < 10; ++i)
711  mcParticles.appendNew();
712 
713  EXPECT_EQ(std::get<int>(var->function(nullptr)), 10);
714 
715  // TODO: add other counters nTracks etc in here
716  }
717 
718  TEST_F(EventVariableTest, TestIfContinuumEvent_ForContinuumEvent)
719  {
720  DataStore::Instance().setInitializeActive(true);
721  StoreArray<MCParticle> mcParticles;
722  StoreArray<Particle> particles;
723  particles.registerRelationTo(mcParticles);
724  DataStore::Instance().setInitializeActive(false);
725 
726  auto* mcParticle = mcParticles.appendNew();
727  mcParticle->setPDG(Const::electron.getPDGCode());
728  mcParticle->setStatus(MCParticle::c_PrimaryParticle);
729  auto* p1 = particles.appendNew(PxPyPzEVector({ 0.0, -0.4, 0.8, 1.0}), 11);
730  p1->addRelationTo(mcParticle);
731 
732  mcParticle = mcParticles.appendNew();
733  mcParticle->setPDG(-Const::electron.getPDGCode());
734  mcParticle->setStatus(MCParticle::c_PrimaryParticle);
735  auto* p2 = particles.appendNew(PxPyPzEVector({ 0.0, -0.4, 0.8, 1.0}), 11);
736  p2->addRelationTo(mcParticle);
737 
738  const Manager::Var* var = Manager::Instance().getVariable("isContinuumEvent");
739  ASSERT_NE(var, nullptr);
740  EXPECT_TRUE(std::get<bool>(var->function(p1)));
741  EXPECT_TRUE(std::get<bool>(var->function(p2)));
742  const Manager::Var* varN = Manager::Instance().getVariable("isNotContinuumEvent");
743  ASSERT_NE(varN, nullptr);
744  EXPECT_FALSE(std::get<bool>(varN->function(p1)));
745  EXPECT_FALSE(std::get<bool>(varN->function(p2)));
746  }
747 
748  TEST_F(EventVariableTest, TestIfContinuumEvent_ForUpsilon4SEvent)
749  {
750  DataStore::Instance().setInitializeActive(true);
751  StoreArray<MCParticle> mcParticles2;
752  StoreArray<Particle> particles2;
753  particles2.registerRelationTo(mcParticles2);
754  DataStore::Instance().setInitializeActive(false);
755 
756  auto* mcParticle = mcParticles2.appendNew();
757  mcParticle->setPDG(Const::photon.getPDGCode());
758  mcParticle->setStatus(MCParticle::c_PrimaryParticle);
759  auto* p3 = particles2.appendNew(PxPyPzEVector({ 0.0, -0.4, 0.8, 1.0}), 11);
760  p3->addRelationTo(mcParticle);
761 
762  mcParticle = mcParticles2.appendNew();
763  mcParticle->setPDG(300553);
764  mcParticle->setStatus(MCParticle::c_PrimaryParticle);
765  auto* p4 = particles2.appendNew(PxPyPzEVector({ 0.0, -0.4, 0.8, 1.0}), 300553);
766  p4->addRelationTo(mcParticle);
767 
768  const Manager::Var* var2 = Manager::Instance().getVariable("isContinuumEvent");
769  ASSERT_NE(var2, nullptr);
770  EXPECT_FALSE(std::get<bool>(var2->function(p3)));
771  EXPECT_FALSE(std::get<bool>(var2->function(p4)));
772  const Manager::Var* var2N = Manager::Instance().getVariable("isNotContinuumEvent");
773  ASSERT_NE(var2N, nullptr);
774  EXPECT_TRUE(std::get<bool>(var2N->function(p3)));
775  EXPECT_TRUE(std::get<bool>(var2N->function(p4)));
776  }
777 
778  TEST_F(EventVariableTest, TestIfContinuumEvent_ForWrongReconstructedUpsilon4SEvent)
779  {
780  DataStore::Instance().setInitializeActive(true);
781  StoreArray<MCParticle> mcParticles3;
782  StoreArray<Particle> particles3;
783  particles3.registerRelationTo(mcParticles3);
784  DataStore::Instance().setInitializeActive(false);
785 
786  auto* mcParticle = mcParticles3.appendNew();
787  mcParticle->setPDG(Const::photon.getPDGCode());
788  mcParticle->setStatus(MCParticle::c_PrimaryParticle);
789  auto* p5 = particles3.appendNew(PxPyPzEVector({ 0.0, -0.4, 0.8, 1.0}), 11);
790  p5->addRelationTo(mcParticle);
791 
792  mcParticle = mcParticles3.appendNew();
793  mcParticle->setPDG(300553);
794  mcParticle->setStatus(MCParticle::c_PrimaryParticle);
795  auto* p6 = particles3.appendNew(PxPyPzEVector({ 0.0, -0.4, 0.8, 1.0}), 15);
796  p6->addRelationTo(mcParticle);
797 
798  const Manager::Var* var3 = Manager::Instance().getVariable("isContinuumEvent");
799  ASSERT_NE(var3, nullptr);
800  EXPECT_FALSE(std::get<bool>(var3->function(p5)));
801  EXPECT_FALSE(std::get<bool>(var3->function(p6)));
802  const Manager::Var* var3N = Manager::Instance().getVariable("isNotContinuumEvent");
803  ASSERT_NE(var3N, nullptr);
804  EXPECT_TRUE(std::get<bool>(var3N->function(p5)));
805  EXPECT_TRUE(std::get<bool>(var3N->function(p6)));
806  }
807 
808 
809  class MetaVariableTest : public ::testing::Test {
810  protected:
812  void SetUp() override
813  {
814  DataStore::Instance().setInitializeActive(true);
819  DataStore::Instance().setInitializeActive(false);
820  }
821 
823  void TearDown() override
824  {
825  DataStore::Instance().reset();
826  }
827  };
828 
829  TEST_F(MetaVariableTest, countDaughters)
830  {
831  PxPyPzEVector momentum;
832  const int nDaughters = 6;
833  StoreArray<Particle> particles;
834  std::vector<int> daughterIndices;
835  for (int i = 0; i < nDaughters; i++) {
836  Particle d(PxPyPzEVector(1, 0, 0, 3.0), (i % 2) ? 211 : -211);
837  momentum += d.get4Vector();
838  Particle* newDaughters = particles.appendNew(d);
839  daughterIndices.push_back(newDaughters->getArrayIndex());
840  }
841  const Particle* p = particles.appendNew(momentum, 411, Particle::c_Unflavored, daughterIndices);
842 
843  const Manager::Var* var = Manager::Instance().getVariable("countDaughters(charge > 0)");
844  ASSERT_NE(var, nullptr);
845  EXPECT_EQ(std::get<int>(var->function(p)), 3);
846 
847  var = Manager::Instance().getVariable("countDaughters(abs(charge) > 0)");
848  ASSERT_NE(var, nullptr);
849  EXPECT_EQ(std::get<int>(var->function(p)), 6);
850 
851  }
852 
853  TEST_F(MetaVariableTest, useRestFrame)
854  {
855  Gearbox& gearbox = Gearbox::getInstance();
856  gearbox.setBackends({std::string("file:")});
857  gearbox.close();
858  gearbox.open("geometry/Belle2.xml", false);
859 
860  Particle p({ 0.1, -0.4, 0.8, 1.0 }, 411);
861  p.setVertex(XYZVector(1.0, 2.0, 2.0));
862 
863  const Manager::Var* var = Manager::Instance().getVariable("p");
864  ASSERT_NE(var, nullptr);
865  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), 0.9);
866 
867  var = Manager::Instance().getVariable("E");
868  ASSERT_NE(var, nullptr);
869  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), 1.0);
870 
871  var = Manager::Instance().getVariable("distance");
872  ASSERT_NE(var, nullptr);
873  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), 3.0);
874 
875  var = Manager::Instance().getVariable("useRestFrame(p)");
876  ASSERT_NE(var, nullptr);
877  EXPECT_NEAR(std::get<double>(var->function(&p)), 0.0, 1e-9);
878 
879  var = Manager::Instance().getVariable("useRestFrame(E)");
880  ASSERT_NE(var, nullptr);
881  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), 0.4358899);
882 
883  var = Manager::Instance().getVariable("useRestFrame(distance)");
884  ASSERT_NE(var, nullptr);
885  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), 0.0);
886  }
887 
888  TEST_F(MetaVariableTest, useLabFrame)
889  {
890  Particle p({ 0.1, -0.4, 0.8, 1.0 }, 411);
891  p.setVertex(XYZVector(1.0, 2.0, 2.0));
892 
893  const Manager::Var* var = Manager::Instance().getVariable("p");
894  ASSERT_NE(var, nullptr);
895  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), 0.9);
896 
897  var = Manager::Instance().getVariable("E");
898  ASSERT_NE(var, nullptr);
899  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), 1.0);
900 
901  var = Manager::Instance().getVariable("distance");
902  ASSERT_NE(var, nullptr);
903  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), 3.0);
904 
905  var = Manager::Instance().getVariable("useLabFrame(p)");
906  ASSERT_NE(var, nullptr);
907  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), 0.9);
908 
909  var = Manager::Instance().getVariable("useLabFrame(E)");
910  ASSERT_NE(var, nullptr);
911  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), 1.0);
912 
913  var = Manager::Instance().getVariable("useLabFrame(distance)");
914  ASSERT_NE(var, nullptr);
915  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), 3.0);
916  }
917 
918  TEST_F(MetaVariableTest, useCMSFrame)
919  {
920  Gearbox& gearbox = Gearbox::getInstance();
921  gearbox.setBackends({std::string("file:")});
922  gearbox.close();
923  gearbox.open("geometry/Belle2.xml", false);
924 
925  Particle p({ 0.1, -0.4, 0.8, 1.0 }, 411);
926  p.setVertex(XYZVector(1.0, 2.0, 2.0));
927 
928  const Manager::Var* var = Manager::Instance().getVariable("p");
929  ASSERT_NE(var, nullptr);
930  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), 0.9);
931 
932  var = Manager::Instance().getVariable("E");
933  ASSERT_NE(var, nullptr);
934  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), 1.0);
935 
936  var = Manager::Instance().getVariable("distance");
937  ASSERT_NE(var, nullptr);
938  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), 3.0);
939 
940  var = Manager::Instance().getVariable("useCMSFrame(p)");
941  ASSERT_NE(var, nullptr);
942  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), 0.68174650327489894064);
943 
944  var = Manager::Instance().getVariable("useCMSFrame(E)");
945  ASSERT_NE(var, nullptr);
946  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), 0.80918372124478121776);
947 
948  var = Manager::Instance().getVariable("useCMSFrame(distance)");
949  ASSERT_NE(var, nullptr);
950  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), 3.1853695);
951  }
952 
953  TEST_F(MetaVariableTest, useTagSideRecoilRestFrame)
954  {
955  DataStore::Instance().setInitializeActive(true);
956  StoreArray<Particle> particles;
957  particles.registerInDataStore();
958  DataStore::Instance().setInitializeActive(false);
960  PxPyPzEVector vec0 = {0.0, 0.0, 0.0, T.getCMSEnergy()};
961  PxPyPzEVector vec1 = {0.0, +0.332174566, 0.0, T.getCMSEnergy() / 2.};
962  PxPyPzEVector vec2 = {0.0, -0.332174566, 0.0, T.getCMSEnergy() / 2.};
963  Particle* p0 = particles.appendNew(Particle(T.rotateCmsToLab() * vec0, 300553));
964  Particle* p1 = particles.appendNew(Particle(T.rotateCmsToLab() * vec1, 511, Particle::c_Unflavored, Particle::c_Undefined, 1));
965  Particle* p2 = particles.appendNew(Particle(T.rotateCmsToLab() * vec2, -511, Particle::c_Unflavored, Particle::c_Undefined, 2));
966 
967  p0->appendDaughter(p1->getArrayIndex());
968  p0->appendDaughter(p2->getArrayIndex());
969 
970  const Manager::Var* var = Manager::Instance().getVariable("useTagSideRecoilRestFrame(daughter(1, p), 0)");
971  ASSERT_NE(var, nullptr);
972  EXPECT_NEAR(std::get<double>(var->function(p0)), 0., 1e-6);
973 
974  var = Manager::Instance().getVariable("useTagSideRecoilRestFrame(daughter(1, px), 0)");
975  ASSERT_NE(var, nullptr);
976  EXPECT_NEAR(std::get<double>(var->function(p0)), 0., 1e-6);
977 
978  var = Manager::Instance().getVariable("useTagSideRecoilRestFrame(daughter(1, py), 0)");
979  ASSERT_NE(var, nullptr);
980  EXPECT_NEAR(std::get<double>(var->function(p0)), 0., 1e-6);
981 
982  var = Manager::Instance().getVariable("useTagSideRecoilRestFrame(daughter(1, pz), 0)");
983  ASSERT_NE(var, nullptr);
984  EXPECT_NEAR(std::get<double>(var->function(p0)), 0., 1e-6);
985 
986  var = Manager::Instance().getVariable("useTagSideRecoilRestFrame(daughter(1, E), 0)");
987  ASSERT_NE(var, nullptr);
988  EXPECT_NEAR(std::get<double>(var->function(p0)), p1->getMass(), 1e-6);
989  }
990 
991 
992  TEST_F(MetaVariableTest, extraInfo)
993  {
994  Particle p({ 0.1, -0.4, 0.8, 1.0 }, 11);
995  p.addExtraInfo("pi", 3.14);
996 
997  const Manager::Var* var = Manager::Instance().getVariable("extraInfo(pi)");
998  ASSERT_NE(var, nullptr);
999  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), 3.14);
1000 
1001  // If nullptr is given, NaN is returned
1002  EXPECT_TRUE(std::isnan(std::get<double>(var->function(nullptr))));
1003  }
1004 
1005  TEST_F(MetaVariableTest, eventExtraInfo)
1006  {
1007  StoreObjPtr<EventExtraInfo> eventExtraInfo;
1008  if (not eventExtraInfo.isValid())
1009  eventExtraInfo.create();
1010  eventExtraInfo->addExtraInfo("pi", 3.14);
1011  const Manager::Var* var = Manager::Instance().getVariable("eventExtraInfo(pi)");
1012  ASSERT_NE(var, nullptr);
1013  EXPECT_FLOAT_EQ(std::get<double>(var->function(nullptr)), 3.14);
1014  }
1015 
1016  TEST_F(MetaVariableTest, eventCached)
1017  {
1018  const Manager::Var* var = Manager::Instance().getVariable("eventCached(constant(3.14))");
1019  ASSERT_NE(var, nullptr);
1020  EXPECT_FLOAT_EQ(std::get<double>(var->function(nullptr)), 3.14);
1021  StoreObjPtr<EventExtraInfo> eventExtraInfo;
1022  EXPECT_TRUE(eventExtraInfo.isValid());
1023  EXPECT_TRUE(eventExtraInfo->hasExtraInfo("__constant__bo3__pt14__bc"));
1024  EXPECT_FLOAT_EQ(eventExtraInfo->getExtraInfo("__constant__bo3__pt14__bc"), 3.14);
1025  eventExtraInfo->addExtraInfo("__eventExtraInfo__bopi__bc", 3.14);
1026  var = Manager::Instance().getVariable("eventCached(eventExtraInfo(pi))");
1027  ASSERT_NE(var, nullptr);
1028  EXPECT_FLOAT_EQ(std::get<double>(var->function(nullptr)), 3.14);
1029  }
1030 
1031  TEST_F(MetaVariableTest, particleCached)
1032  {
1033  Particle p({ 0.1, -0.4, 0.8, 2.0 }, 11);
1034  const Manager::Var* var = Manager::Instance().getVariable("particleCached(px)");
1035  ASSERT_NE(var, nullptr);
1036  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), 0.1);
1037  EXPECT_TRUE(p.hasExtraInfo("__px"));
1038  EXPECT_FLOAT_EQ(p.getExtraInfo("__px"), 0.1);
1039  p.addExtraInfo("__py", -0.5); // NOT -0.4 because we want to see if the cache is used instead of py!
1040  var = Manager::Instance().getVariable("particleCached(py)");
1041  ASSERT_NE(var, nullptr);
1042  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), -0.5);
1043  }
1044 
1045  TEST_F(MetaVariableTest, basicMathTest)
1046  {
1047  Particle p({ 0.1, -0.4, 0.8, 2.0 }, 411);
1048 
1049  const Manager::Var* var = Manager::Instance().getVariable("abs(py)");
1050  ASSERT_NE(var, nullptr);
1051  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), 0.4);
1052 
1053  var = Manager::Instance().getVariable("min(E, pz)");
1054  ASSERT_NE(var, nullptr);
1055  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), 0.8);
1056 
1057  var = Manager::Instance().getVariable("max(E, pz)");
1058  ASSERT_NE(var, nullptr);
1059  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), 2.0);
1060 
1061  var = Manager::Instance().getVariable("log10(px)");
1062  ASSERT_NE(var, nullptr);
1063  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), -1.0);
1064 
1065  // sin 30 = 0.5
1066  var = Manager::Instance().getVariable("sin(0.5235987755983)");
1067  ASSERT_NE(var, nullptr);
1068  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), 0.5);
1069 
1070  // sin 90 = 1
1071  var = Manager::Instance().getVariable("sin(1.5707963267948966)");
1072  ASSERT_NE(var, nullptr);
1073  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), 1.0);
1074 
1075  // asin 1 = 90
1076  var = Manager::Instance().getVariable("asin(1.0)");
1077  ASSERT_NE(var, nullptr);
1078  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), 1.5707963267948966);
1079 
1080  // cos 60 = 0.5
1081  var = Manager::Instance().getVariable("cos(1.0471975511965976)");
1082  ASSERT_NE(var, nullptr);
1083  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), 0.5);
1084 
1085  // acos 0 = 90
1086  var = Manager::Instance().getVariable("acos(0)");
1087  ASSERT_NE(var, nullptr);
1088  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), 1.5707963267948966);
1089 
1090  }
1091 
1092  TEST_F(MetaVariableTest, formula)
1093  {
1094  // see also unit tests in framework/formula_parser.cc
1095  //
1096  // keep particle-based tests here, and operator precedence tests (etc) in
1097  // framework with the parser itself
1098 
1099  Particle p({ 0.1, -0.4, 0.8, 2.0 }, -411);
1100 
1101  const Manager::Var* var = Manager::Instance().getVariable("formula(px + py)");
1102  ASSERT_NE(var, nullptr);
1103  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), -0.3);
1104 
1105  var = Manager::Instance().getVariable("formula(px - py)");
1106  ASSERT_NE(var, nullptr);
1107  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), 0.5);
1108 
1109  var = Manager::Instance().getVariable("formula(px * py)");
1110  ASSERT_NE(var, nullptr);
1111  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), -0.04);
1112 
1113  var = Manager::Instance().getVariable("formula(py / px)");
1114  ASSERT_NE(var, nullptr);
1115  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), -4.0);
1116 
1117  var = Manager::Instance().getVariable("formula(px ^ E)");
1118  ASSERT_NE(var, nullptr);
1119  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), 0.01);
1120 
1121  var = Manager::Instance().getVariable("formula(px * py + pz)");
1122  ASSERT_NE(var, nullptr);
1123  EXPECT_NEAR(std::get<double>(var->function(&p)), 0.76, 1e-6);
1124 
1125  var = Manager::Instance().getVariable("formula(pz + px * py)");
1126  ASSERT_NE(var, nullptr);
1127  EXPECT_NEAR(std::get<double>(var->function(&p)), 0.76, 1e-6);
1128 
1129  var = Manager::Instance().getVariable("formula(pt)");
1130  ASSERT_NE(var, nullptr);
1131  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), 0.41231057);
1132  double pt = std::get<double>(var->function(&p));
1133 
1134  var = Manager::Instance().getVariable("formula((px**2 + py**2)**(1/2))");
1135  ASSERT_NE(var, nullptr);
1136  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), pt);
1137 
1138  var = Manager::Instance().getVariable("formula(charge)");
1139  ASSERT_NE(var, nullptr);
1140  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), -1.0);
1141 
1142  var = Manager::Instance().getVariable("formula(charge**2)");
1143  ASSERT_NE(var, nullptr);
1144  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), 1.0);
1145 
1146  var = Manager::Instance().getVariable("formula(charge^2)");
1147  ASSERT_NE(var, nullptr);
1148  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), 1.0);
1149 
1150  var = Manager::Instance().getVariable("formula(PDG * charge)");
1151  ASSERT_NE(var, nullptr);
1152  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), 411.0);
1153 
1154  var = Manager::Instance().getVariable("formula(PDG**2 * charge)");
1155  ASSERT_NE(var, nullptr);
1156  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), -168921.0);
1157 
1158  var = Manager::Instance().getVariable("formula(10.58 - (px + py + pz - E)**2)");
1159  ASSERT_NE(var, nullptr);
1160  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), 8.33);
1161 
1162  var = Manager::Instance().getVariable("formula(-10.58 + (px + py + pz - E)**2)");
1163  ASSERT_NE(var, nullptr);
1164  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), -8.33);
1165 
1166  var = Manager::Instance().getVariable("formula(-1.0 * PDG)");
1167  ASSERT_NE(var, nullptr);
1168  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), 411);
1169  }
1170 
1171  TEST_F(MetaVariableTest, passesCut)
1172  {
1173  Particle p({ 0.1, -0.4, 0.8, 2.0 }, 411);
1174  Particle p2({ 0.1, -0.4, 0.8, 4.0 }, 411);
1175 
1176  const Manager::Var* var = Manager::Instance().getVariable("passesCut(E < 3)");
1177  ASSERT_NE(var, nullptr);
1178  EXPECT_TRUE(std::get<bool>(var->function(&p)));
1179  EXPECT_FALSE(std::get<bool>(var->function(&p2)));
1180  // EXPECT_TRUE(std::isnan(std::get<double>(var->function(nullptr)))); // Check that particle is present has been removed to allow change to bool as return type
1181 
1182  }
1183 
1184  TEST_F(MetaVariableTest, unmask)
1185  {
1186  DataStore::Instance().setInitializeActive(true);
1187  StoreArray<MCParticle> mcParticles;
1188  StoreArray<Particle> particles;
1189  particles.registerInDataStore();
1190  mcParticles.registerInDataStore();
1191  particles.registerRelationTo(mcParticles);
1192  DataStore::Instance().setInitializeActive(false);
1193 
1194  // Create MC graph for B -> (muon -> electron + muon_neutrino) + anti_muon_neutrino
1195  MCParticleGraph mcGraph;
1196 
1197  MCParticleGraph::GraphParticle& graphParticleGrandMother = mcGraph.addParticle();
1198 
1199  MCParticleGraph::GraphParticle& graphParticleMother = mcGraph.addParticle();
1200  MCParticleGraph::GraphParticle& graphParticleAunt = mcGraph.addParticle();
1201 
1202  MCParticleGraph::GraphParticle& graphParticleDaughter1 = mcGraph.addParticle();
1203  MCParticleGraph::GraphParticle& graphParticleDaughter2 = mcGraph.addParticle();
1204 
1205  graphParticleGrandMother.setPDG(-521);
1206  graphParticleMother.setPDG(Const::muon.getPDGCode());
1207  graphParticleAunt.setPDG(-14);
1208  graphParticleDaughter1.setPDG(Const::electron.getPDGCode());
1209  graphParticleDaughter2.setPDG(14);
1210 
1211  graphParticleMother.comesFrom(graphParticleGrandMother);
1212  graphParticleAunt.comesFrom(graphParticleGrandMother);
1213  graphParticleDaughter1.comesFrom(graphParticleMother);
1214  graphParticleDaughter2.comesFrom(graphParticleMother);
1215  mcGraph.generateList();
1216 
1217 
1218  // Get MC Particles from StoreArray
1219  auto* mcGrandMother = mcParticles[0];
1220  mcGrandMother->setStatus(MCParticle::c_PrimaryParticle);
1221 
1222  auto* mcMother = mcParticles[1];
1223  mcMother->setStatus(MCParticle::c_PrimaryParticle);
1224 
1225  auto* mcAunt = mcParticles[2];
1226  mcAunt->setStatus(MCParticle::c_PrimaryParticle);
1227 
1228  auto* mcDaughter1 = mcParticles[3];
1229  mcDaughter1->setStatus(MCParticle::c_PrimaryParticle);
1230 
1231  auto* mcDaughter2 = mcParticles[4];
1232  mcDaughter2->setStatus(MCParticle::c_PrimaryParticle);
1233 
1234  auto* pGrandMother = particles.appendNew(PxPyPzEVector({ 0.0, -0.4, 0.8, 1.0}), -521);
1235  pGrandMother->addRelationTo(mcGrandMother);
1236 
1237  auto* pMother = particles.appendNew(PxPyPzEVector({ 0.0, -0.4, 0.8, 1.0}), 13);
1238  pMother->addRelationTo(mcMother);
1239 
1240 
1241  pMother->writeExtraInfo("mcErrors", 8);
1242  pGrandMother->writeExtraInfo("mcErrors", 8 | 16);
1243  const Manager::Var* var1 = Manager::Instance().getVariable("unmask(mcErrors, 8)");
1244  const Manager::Var* var2 = Manager::Instance().getVariable("unmask(mcErrors, 8, 16, 32, 64)");
1245  ASSERT_NE(var1, nullptr);
1246  EXPECT_FLOAT_EQ(std::get<double>(var1->function(pMother)), 0);
1247  EXPECT_FLOAT_EQ(std::get<double>(var1->function(pGrandMother)), 16);
1248  ASSERT_NE(var2, nullptr);
1249  EXPECT_FLOAT_EQ(std::get<double>(var2->function(pMother)), 0);
1250  EXPECT_FLOAT_EQ(std::get<double>(var2->function(pGrandMother)), 0);
1251 
1252 
1253  pMother->writeExtraInfo("mcErrors", 8 | 128);
1254  pGrandMother->writeExtraInfo("mcErrors", 8 | 16 | 512);
1255  ASSERT_NE(var1, nullptr);
1256  EXPECT_FLOAT_EQ(std::get<double>(var1->function(pMother)), 128);
1257  EXPECT_FLOAT_EQ(std::get<double>(var1->function(pGrandMother)), 16 | 512);
1258  ASSERT_NE(var2, nullptr);
1259  EXPECT_FLOAT_EQ(std::get<double>(var2->function(pMother)), 128);
1260  EXPECT_FLOAT_EQ(std::get<double>(var2->function(pGrandMother)), 512);
1261 
1262  // unmask variable needs at least two arguments
1263  EXPECT_B2FATAL(Manager::Instance().getVariable("unmask(mcErrors)"));
1264 
1265  // all but the first argument have to be integers
1266  EXPECT_B2FATAL(Manager::Instance().getVariable("unmask(mcErrors, NOTINT)"));
1267  }
1268 
1269  TEST_F(MetaVariableTest, conditionalVariableSelector)
1270  {
1271  Particle p({ 0.1, -0.4, 0.8, 2.0 }, 411);
1272 
1273  const Manager::Var* var = Manager::Instance().getVariable("conditionalVariableSelector(E>1, px, py)");
1274  ASSERT_NE(var, nullptr);
1275  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), 0.1);
1276 
1277  var = Manager::Instance().getVariable("conditionalVariableSelector(E<1, px, py)");
1278  ASSERT_NE(var, nullptr);
1279  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), -0.4);
1280 
1281  }
1282 
1283  TEST_F(MetaVariableTest, nCleanedTracks)
1284  {
1285  DataStore::Instance().setInitializeActive(true);
1286  StoreArray<TrackFitResult> track_fit_results;
1287  StoreArray<Track> tracks;
1288  track_fit_results.registerInDataStore();
1289  tracks.registerInDataStore();
1290  DataStore::Instance().setInitializeActive(false);
1291 
1292  Particle p({ 0.1, -0.4, 0.8, 2.0 }, 11);
1293  Particle p2({ 0.1, -0.4, 0.8, 4.0 }, 11);
1294 
1295  track_fit_results.appendNew(ROOT::Math::XYZVector(0.1, 0.1, 0.1), ROOT::Math::XYZVector(0.1, 0.0, 0.0),
1296  TMatrixDSym(6), 1, Const::pion, 0.01, 1.5, 0, 0, 0);
1297  track_fit_results.appendNew(ROOT::Math::XYZVector(0.1, 0.1, 0.1), ROOT::Math::XYZVector(0.15, 0.0, 0.0),
1298  TMatrixDSym(6), 1, Const::pion, 0.01, 1.5, 0, 0, 0);
1299  track_fit_results.appendNew(ROOT::Math::XYZVector(0.1, 0.1, 0.1), ROOT::Math::XYZVector(0.4, 0.0, 0.0),
1300  TMatrixDSym(6), 1, Const::pion, 0.01, 1.5, 0, 0, 0);
1301  track_fit_results.appendNew(ROOT::Math::XYZVector(0.1, 0.1, 0.1), ROOT::Math::XYZVector(0.6, 0.0, 0.0),
1302  TMatrixDSym(6), 1, Const::pion, 0.01, 1.5, 0, 0, 0);
1303 
1304  tracks.appendNew()->setTrackFitResultIndex(Const::pion, 0);
1305  tracks.appendNew()->setTrackFitResultIndex(Const::pion, 1);
1306  tracks.appendNew()->setTrackFitResultIndex(Const::pion, 2);
1307  tracks.appendNew()->setTrackFitResultIndex(Const::pion, 3);
1308 
1309  const Manager::Var* var1 = Manager::Instance().getVariable("nCleanedTracks(p > 0.5)");
1310  EXPECT_FLOAT_EQ(std::get<int>(var1->function(nullptr)), 1);
1311 
1312  const Manager::Var* var2 = Manager::Instance().getVariable("nCleanedTracks(p > 0.2)");
1313  EXPECT_FLOAT_EQ(std::get<int>(var2->function(nullptr)), 2);
1314 
1315  const Manager::Var* var3 = Manager::Instance().getVariable("nCleanedTracks()");
1316  EXPECT_FLOAT_EQ(std::get<int>(var3->function(nullptr)), 4);
1317 
1318 
1319  }
1320 
1321  TEST_F(MetaVariableTest, NumberOfMCParticlesInEvent)
1322  {
1323  Particle p({ 0.1, -0.4, 0.8, 2.0 }, 11);
1324  Particle p2({ 0.1, -0.4, 0.8, 4.0 }, 11);
1325 
1326  StoreArray<MCParticle> mcParticles;
1327  auto* mcParticle = mcParticles.appendNew();
1328  mcParticle->setPDG(Const::electron.getPDGCode());
1329  mcParticle->setStatus(MCParticle::c_PrimaryParticle);
1330  mcParticle = mcParticles.appendNew();
1331  mcParticle->setPDG(Const::photon.getPDGCode());
1332  mcParticle->setStatus(MCParticle::c_PrimaryParticle);
1333  mcParticle = mcParticles.appendNew();
1334  mcParticle->setPDG(-Const::electron.getPDGCode());
1335  mcParticle->setStatus(MCParticle::c_PrimaryParticle);
1336  mcParticle = mcParticles.appendNew();
1337  mcParticle->setPDG(Const::electron.getPDGCode());
1338 
1339 
1340  const Manager::Var* var = Manager::Instance().getVariable("NumberOfMCParticlesInEvent(11)");
1341  ASSERT_NE(var, nullptr);
1342  EXPECT_EQ(std::get<int>(var->function(nullptr)), 2);
1343 
1344  }
1345 
1346  TEST_F(MetaVariableTest, daughterInvM)
1347  {
1348  PxPyPzEVector momentum;
1349  const int nDaughters = 6;
1350  StoreArray<Particle> particles;
1351  std::vector<int> daughterIndices;
1352  for (int i = 0; i < nDaughters; i++) {
1353  Particle d(PxPyPzEVector(2, 2, 2, 4.0), (i % 2) ? 213 : -213);
1354  momentum += d.get4Vector();
1355  Particle* newDaughters = particles.appendNew(d);
1356  daughterIndices.push_back(newDaughters->getArrayIndex());
1357  }
1358  const Particle* p = particles.appendNew(momentum, 411, Particle::c_Unflavored, daughterIndices);
1359 
1360  const Manager::Var* var = Manager::Instance().getVariable("daughterInvM(6,5)");
1361  ASSERT_NE(var, nullptr);
1362  EXPECT_TRUE(std::isnan(std::get<double>(var->function(p))));
1363 
1364  var = Manager::Instance().getVariable("daughterInvM(0, 1)");
1365  ASSERT_NE(var, nullptr);
1366  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), 4.0);
1367 
1368  var = Manager::Instance().getVariable("daughterInvM(0, 1, 2)");
1369  ASSERT_NE(var, nullptr);
1370  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), 6.0);
1371  }
1372 
1373  TEST_F(MetaVariableTest, daughter)
1374  {
1375  PxPyPzEVector momentum;
1376  const int nDaughters = 6;
1377  StoreArray<Particle> particles;
1378  std::vector<int> daughterIndices;
1379  for (int i = 0; i < nDaughters; i++) {
1380  Particle d(PxPyPzEVector(i * 1.0, 1, 1, 1), (i % 2) ? 211 : -211);
1381  momentum += d.get4Vector();
1382  Particle* newDaughters = particles.appendNew(d);
1383  daughterIndices.push_back(newDaughters->getArrayIndex());
1384  }
1385  const Particle* p = particles.appendNew(momentum, 411, Particle::c_Unflavored, daughterIndices);
1386 
1387  const Manager::Var* var = Manager::Instance().getVariable("daughter(6, px)");
1388  ASSERT_NE(var, nullptr);
1389  EXPECT_TRUE(std::isnan(std::get<double>(var->function(p))));
1390 
1391  var = Manager::Instance().getVariable("daughter(0, px)");
1392  ASSERT_NE(var, nullptr);
1393  EXPECT_NEAR(std::get<double>(var->function(p)), 0.0, 1e-6);
1394 
1395  var = Manager::Instance().getVariable("daughter(1, px)");
1396  ASSERT_NE(var, nullptr);
1397  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), 1.0);
1398 
1399  var = Manager::Instance().getVariable("daughter(2, px)");
1400  ASSERT_NE(var, nullptr);
1401  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), 2.0);
1402  }
1403 
1404  TEST_F(MetaVariableTest, mcDaughter)
1405  {
1406  DataStore::Instance().setInitializeActive(true);
1407  StoreArray<MCParticle> mcParticles;
1408  StoreArray<Particle> particles;
1409  particles.registerInDataStore();
1410  mcParticles.registerInDataStore();
1411  particles.registerRelationTo(mcParticles);
1412  DataStore::Instance().setInitializeActive(false);
1413 
1414  // Create MC graph for B -> (muon -> electron + muon_neutrino) + anti_muon_neutrino
1415  MCParticleGraph mcGraph;
1416 
1417  MCParticleGraph::GraphParticle& graphParticleGrandMother = mcGraph.addParticle();
1418 
1419  MCParticleGraph::GraphParticle& graphParticleMother = mcGraph.addParticle();
1420  MCParticleGraph::GraphParticle& graphParticleAunt = mcGraph.addParticle();
1421 
1422  MCParticleGraph::GraphParticle& graphParticleDaughter1 = mcGraph.addParticle();
1423  MCParticleGraph::GraphParticle& graphParticleDaughter2 = mcGraph.addParticle();
1424 
1425  graphParticleGrandMother.setPDG(-521);
1426  graphParticleMother.setPDG(Const::muon.getPDGCode());
1427  graphParticleAunt.setPDG(-14);
1428  graphParticleDaughter1.setPDG(Const::electron.getPDGCode());
1429  graphParticleDaughter2.setPDG(14);
1430 
1431  graphParticleMother.comesFrom(graphParticleGrandMother);
1432  graphParticleAunt.comesFrom(graphParticleGrandMother);
1433  graphParticleDaughter1.comesFrom(graphParticleMother);
1434  graphParticleDaughter2.comesFrom(graphParticleMother);
1435  mcGraph.generateList();
1436 
1437  // Get MC Particles from StoreArray
1438  auto* mcGrandMother = mcParticles[0];
1439  mcGrandMother->setStatus(MCParticle::c_PrimaryParticle);
1440 
1441  auto* mcMother = mcParticles[1];
1442  mcMother->setStatus(MCParticle::c_PrimaryParticle);
1443 
1444  auto* mcAunt = mcParticles[2];
1445  mcAunt->setStatus(MCParticle::c_PrimaryParticle);
1446 
1447  auto* mcDaughter1 = mcParticles[3];
1448  mcDaughter1->setStatus(MCParticle::c_PrimaryParticle);
1449 
1450  auto* mcDaughter2 = mcParticles[4];
1451  mcDaughter2->setStatus(MCParticle::c_PrimaryParticle);
1452 
1453  auto* pGrandMother = particles.appendNew(PxPyPzEVector({ 0.0, -0.4, 0.8, 1.0}), -521);
1454  pGrandMother->addRelationTo(mcGrandMother);
1455 
1456  auto* pMother = particles.appendNew(PxPyPzEVector({ 0.0, -0.4, 0.8, 1.0}), 13);
1457  pMother->addRelationTo(mcMother);
1458 
1459  // Test for particle that has no MC match
1460  auto* p_noMC = particles.appendNew(PxPyPzEVector({ 0.0, -0.4, 0.8, 1.0}), 13);
1461 
1462  // Test for particle that has MC match, but MC match has no daughter
1463  auto* p_noDaughter = particles.appendNew(PxPyPzEVector({ 0.0, -0.4, 0.8, 1.0}), 11);
1464  p_noDaughter->addRelationTo(mcDaughter1);
1465 
1466  const Manager::Var* var = Manager::Instance().getVariable("mcDaughter(0, PDG)");
1467  ASSERT_NE(var, nullptr);
1468  EXPECT_FLOAT_EQ(std::get<double>(var->function(pGrandMother)), 13);
1469  EXPECT_FLOAT_EQ(std::get<double>(var->function(pMother)), 11);
1470  EXPECT_TRUE(std::isnan(std::get<double>(var->function(p_noMC))));
1471  EXPECT_TRUE(std::isnan(std::get<double>(var->function(p_noDaughter))));
1472  var = Manager::Instance().getVariable("mcDaughter(1, PDG)");
1473  EXPECT_FLOAT_EQ(std::get<double>(var->function(pGrandMother)), -14);
1474  EXPECT_FLOAT_EQ(std::get<double>(var->function(pMother)), 14);
1475  // Test for particle where mc daughter index is out of range of mc daughters
1476  var = Manager::Instance().getVariable("mcDaughter(2, PDG)");
1477  EXPECT_TRUE(std::isnan(std::get<double>(var->function(pGrandMother))));
1478  EXPECT_TRUE(std::isnan(std::get<double>(var->function(pMother))));
1479  // Test nested application of mcDaughter
1480  var = Manager::Instance().getVariable("mcDaughter(0, mcDaughter(0, PDG))");
1481  EXPECT_FLOAT_EQ(std::get<double>(var->function(pGrandMother)), 11);
1482  EXPECT_TRUE(std::isnan(std::get<double>(var->function(pMother))));
1483  var = Manager::Instance().getVariable("mcDaughter(0, mcDaughter(1, PDG))");
1484  EXPECT_FLOAT_EQ(std::get<double>(var->function(pGrandMother)), 14);
1485  var = Manager::Instance().getVariable("mcDaughter(0, mcDaughter(2, PDG))");
1486  EXPECT_TRUE(std::isnan(std::get<double>(var->function(pGrandMother))));
1487  var = Manager::Instance().getVariable("mcDaughter(1, mcDaughter(0, PDG))");
1488  EXPECT_TRUE(std::isnan(std::get<double>(var->function(pGrandMother))));
1489  }
1490 
1491  TEST_F(MetaVariableTest, mcMother)
1492  {
1493  DataStore::Instance().setInitializeActive(true);
1494  StoreArray<MCParticle> mcParticles;
1495  StoreArray<Particle> particles;
1496  particles.registerInDataStore();
1497  mcParticles.registerInDataStore();
1498  particles.registerRelationTo(mcParticles);
1499  DataStore::Instance().setInitializeActive(false);
1500 
1501  // Create MC graph for B -> (muon -> electron + muon_neutrino) + anti_muon_neutrino
1502  MCParticleGraph mcGraph;
1503 
1504  MCParticleGraph::GraphParticle& graphParticleGrandMother = mcGraph.addParticle();
1505 
1506  MCParticleGraph::GraphParticle& graphParticleMother = mcGraph.addParticle();
1507  MCParticleGraph::GraphParticle& graphParticleAunt = mcGraph.addParticle();
1508 
1509  MCParticleGraph::GraphParticle& graphParticleDaughter1 = mcGraph.addParticle();
1510  MCParticleGraph::GraphParticle& graphParticleDaughter2 = mcGraph.addParticle();
1511 
1512  graphParticleGrandMother.setPDG(-521);
1513  graphParticleMother.setPDG(Const::muon.getPDGCode());
1514  graphParticleAunt.setPDG(-14);
1515  graphParticleDaughter1.setPDG(Const::electron.getPDGCode());
1516  graphParticleDaughter2.setPDG(14);
1517 
1518  graphParticleMother.comesFrom(graphParticleGrandMother);
1519  graphParticleAunt.comesFrom(graphParticleGrandMother);
1520  graphParticleDaughter1.comesFrom(graphParticleMother);
1521  graphParticleDaughter2.comesFrom(graphParticleMother);
1522 
1523  mcGraph.generateList();
1524 
1525  // Get MC Particles from StoreArray
1526  auto* mcGrandMother = mcParticles[0];
1527  mcGrandMother->setStatus(MCParticle::c_PrimaryParticle);
1528 
1529  auto* mcMother = mcParticles[1];
1530  mcMother->setStatus(MCParticle::c_PrimaryParticle);
1531 
1532  auto* mcAunt = mcParticles[2];
1533  mcAunt->setStatus(MCParticle::c_PrimaryParticle);
1534 
1535  auto* mcDaughter1 = mcParticles[3];
1536  mcDaughter1->setStatus(MCParticle::c_PrimaryParticle);
1537 
1538  auto* mcDaughter2 = mcParticles[4];
1539  mcDaughter2->setStatus(MCParticle::c_PrimaryParticle);
1540 
1541  auto* p1 = particles.appendNew(PxPyPzEVector({ 0.0, -0.4, 0.8, 1.0}), 11);
1542  p1->addRelationTo(mcDaughter1);
1543 
1544  auto* p2 = particles.appendNew(PxPyPzEVector({ 0.0, -0.4, 0.8, 1.0}), 14);
1545  p2->addRelationTo(mcDaughter2);
1546 
1547  auto* pMother = particles.appendNew(PxPyPzEVector({ 0.0, -0.4, 0.8, 1.0}), 13);
1548  pMother->addRelationTo(mcMother);
1549 
1550  // For test of particle that has no MC match
1551  auto* p_noMC = particles.appendNew(PxPyPzEVector({ 0.0, -0.4, 0.8, 1.0}), 11);
1552 
1553  // For test of particle that has MC match, but MC match has no mother
1554  auto* p_noMother = particles.appendNew(PxPyPzEVector({ 0.0, -0.4, 0.8, 1.0}), -521);
1555  p_noMother->addRelationTo(mcGrandMother);
1556 
1557  const Manager::Var* var = Manager::Instance().getVariable("mcMother(PDG)");
1558  ASSERT_NE(var, nullptr);
1559  EXPECT_FLOAT_EQ(std::get<double>(var->function(p1)), 13);
1560  EXPECT_FLOAT_EQ(std::get<double>(var->function(p2)), 13);
1561  EXPECT_FLOAT_EQ(std::get<double>(var->function(pMother)), -521);
1562  EXPECT_TRUE(std::isnan(std::get<double>(var->function(p_noMC))));
1563  EXPECT_TRUE(std::isnan(std::get<double>(var->function(p_noMother))));
1564 
1565  // Test if nested calls of mcMother work correctly
1566  var = Manager::Instance().getVariable("mcMother(mcMother(PDG))");
1567  EXPECT_FLOAT_EQ(std::get<double>(var->function(p1)), -521);
1568  }
1569 
1570  TEST_F(MetaVariableTest, genParticle)
1571  {
1572  DataStore::Instance().setInitializeActive(true);
1573  StoreArray<MCParticle> mcParticles;
1574  StoreArray<Particle> particles;
1575  particles.registerInDataStore();
1576  mcParticles.registerInDataStore();
1577  particles.registerRelationTo(mcParticles);
1578  DataStore::Instance().setInitializeActive(false);
1579 
1580  // Create MC graph for Upsilon(4S) -> (B^- -> electron + anti_electron_neutrino) + B^+
1581  MCParticleGraph mcGraph;
1582 
1583  MCParticleGraph::GraphParticle& graphParticleGrandMother = mcGraph.addParticle();
1584 
1585  MCParticleGraph::GraphParticle& graphParticleMother = mcGraph.addParticle();
1586  MCParticleGraph::GraphParticle& graphParticleAunt = mcGraph.addParticle();
1587 
1588  MCParticleGraph::GraphParticle& graphParticleDaughter1 = mcGraph.addParticle();
1589  MCParticleGraph::GraphParticle& graphParticleDaughter2 = mcGraph.addParticle();
1590 
1591  graphParticleGrandMother.setPDG(300553);
1592  graphParticleMother.setPDG(-521);
1593  graphParticleAunt.setPDG(521);
1594  graphParticleDaughter1.setPDG(Const::electron.getPDGCode());
1595  graphParticleDaughter2.setPDG(-12);
1596 
1597  graphParticleGrandMother.setMomentum(0.0, 0.0, 0.4);
1598  graphParticleMother.setMomentum(1.1, 1.3, 1.5);
1599 
1600  graphParticleMother.comesFrom(graphParticleGrandMother);
1601  graphParticleAunt.comesFrom(graphParticleGrandMother);
1602  graphParticleDaughter1.comesFrom(graphParticleMother);
1603  graphParticleDaughter2.comesFrom(graphParticleMother);
1604 
1605  mcGraph.generateList();
1606 
1607  // Get MC Particles from StoreArray
1608  auto* mcGrandMother = mcParticles[0];
1609  mcGrandMother->setStatus(MCParticle::c_PrimaryParticle);
1610 
1611  auto* mcMother = mcParticles[1];
1612  mcMother->setStatus(MCParticle::c_PrimaryParticle);
1613 
1614  auto* mcAunt = mcParticles[2];
1615  mcAunt->setStatus(MCParticle::c_PrimaryParticle);
1616 
1617  auto* mcDaughter1 = mcParticles[3];
1618  mcDaughter1->setStatus(MCParticle::c_PrimaryParticle);
1619 
1620  auto* mcDaughter2 = mcParticles[4];
1621  mcDaughter2->setStatus(MCParticle::c_PrimaryParticle);
1622 
1623  auto* p1 = particles.appendNew(PxPyPzEVector({ 0.0, -0.4, 0.8, 1.0}), 11);
1624  p1->addRelationTo(mcDaughter1);
1625 
1626  // For test of particle that has no MC match
1627  auto* p_noMC = particles.appendNew(PxPyPzEVector({ 0.0, -0.4, 0.8, 1.0}), 211);
1628 
1629  const Manager::Var* var = Manager::Instance().getVariable("genParticle(0, PDG)");
1630  ASSERT_NE(var, nullptr);
1631  EXPECT_FLOAT_EQ(std::get<double>(var->function(p1)), 300553);
1632  EXPECT_FLOAT_EQ(std::get<double>(var->function(p_noMC)), 300553);
1633 
1634  var = Manager::Instance().getVariable("genParticle(0, matchedMC(pz))");
1635  ASSERT_NE(var, nullptr);
1636  EXPECT_FLOAT_EQ(std::get<double>(var->function(p1)), 0.4);
1637  EXPECT_FLOAT_EQ(std::get<double>(var->function(p_noMC)), 0.4);
1638 
1639  var = Manager::Instance().getVariable("genParticle(0, mcDaughter(0, PDG))");
1640  ASSERT_NE(var, nullptr);
1641  EXPECT_FLOAT_EQ(std::get<double>(var->function(p1)), -521);
1642  EXPECT_FLOAT_EQ(std::get<double>(var->function(p_noMC)), -521);
1643 
1644  var = Manager::Instance().getVariable("genParticle(0, mcDaughter(0, matchedMC(px)))");
1645  ASSERT_NE(var, nullptr);
1646  EXPECT_FLOAT_EQ(std::get<double>(var->function(p1)), 1.1);
1647  EXPECT_FLOAT_EQ(std::get<double>(var->function(p_noMC)), 1.1);
1648 
1649  var = Manager::Instance().getVariable("genParticle(1, PDG)");
1650  ASSERT_NE(var, nullptr);
1651  EXPECT_FLOAT_EQ(std::get<double>(var->function(p1)), -521);
1652  EXPECT_FLOAT_EQ(std::get<double>(var->function(p_noMC)), -521);
1653 
1654  var = Manager::Instance().getVariable("genParticle(4, PDG)");
1655  ASSERT_NE(var, nullptr);
1656  EXPECT_FLOAT_EQ(std::get<double>(var->function(p1)), -12);
1657  EXPECT_FLOAT_EQ(std::get<double>(var->function(p_noMC)), -12);
1658 
1659  var = Manager::Instance().getVariable("genParticle(5, PDG)");
1660  ASSERT_NE(var, nullptr);
1661  EXPECT_TRUE(std::isnan(std::get<double>(var->function(p1))));
1662  EXPECT_TRUE(std::isnan(std::get<double>(var->function(p_noMC))));
1663  }
1664 
1665  TEST_F(MetaVariableTest, genUpsilon4S)
1666  {
1667  DataStore::Instance().setInitializeActive(true);
1668  StoreArray<MCParticle> mcParticles;
1669  StoreArray<Particle> particles;
1670  particles.registerInDataStore();
1671  mcParticles.registerInDataStore();
1672  particles.registerRelationTo(mcParticles);
1673  DataStore::Instance().setInitializeActive(false);
1674 
1675  // Create MC graph for Upsilon(4S) -> (B^- -> electron + anti_electron_neutrino) + B^+
1676  MCParticleGraph mcGraph;
1677 
1678  MCParticleGraph::GraphParticle& graphParticleGrandMother = mcGraph.addParticle();
1679 
1680  MCParticleGraph::GraphParticle& graphParticleMother = mcGraph.addParticle();
1681  MCParticleGraph::GraphParticle& graphParticleAunt = mcGraph.addParticle();
1682 
1683  MCParticleGraph::GraphParticle& graphParticleDaughter1 = mcGraph.addParticle();
1684  MCParticleGraph::GraphParticle& graphParticleDaughter2 = mcGraph.addParticle();
1685 
1686  graphParticleGrandMother.setPDG(300553);
1687  graphParticleMother.setPDG(-521);
1688  graphParticleAunt.setPDG(521);
1689  graphParticleDaughter1.setPDG(Const::electron.getPDGCode());
1690  graphParticleDaughter2.setPDG(-12);
1691 
1692  graphParticleGrandMother.setMomentum(0.0, 0.0, 0.4);
1693  graphParticleMother.setMomentum(1.1, 1.3, 1.5);
1694 
1695  graphParticleMother.comesFrom(graphParticleGrandMother);
1696  graphParticleAunt.comesFrom(graphParticleGrandMother);
1697  graphParticleDaughter1.comesFrom(graphParticleMother);
1698  graphParticleDaughter2.comesFrom(graphParticleMother);
1699 
1700  mcGraph.generateList();
1701 
1702  // Get MC Particles from StoreArray
1703  auto* mcGrandMother = mcParticles[0];
1704  mcGrandMother->setStatus(MCParticle::c_PrimaryParticle);
1705 
1706  auto* mcMother = mcParticles[1];
1707  mcMother->setStatus(MCParticle::c_PrimaryParticle);
1708 
1709  auto* mcAunt = mcParticles[2];
1710  mcAunt->setStatus(MCParticle::c_PrimaryParticle);
1711 
1712  auto* mcDaughter1 = mcParticles[3];
1713  mcDaughter1->setStatus(MCParticle::c_PrimaryParticle);
1714 
1715  auto* mcDaughter2 = mcParticles[4];
1716  mcDaughter2->setStatus(MCParticle::c_PrimaryParticle);
1717 
1718  auto* p1 = particles.appendNew(PxPyPzEVector({ 0.0, -0.4, 0.8, 1.0}), 11);
1719  p1->addRelationTo(mcDaughter1);
1720 
1721  // For test of particle that has no MC match
1722  auto* p_noMC = particles.appendNew(PxPyPzEVector({ 0.0, -0.4, 0.8, 1.0}), 211);
1723 
1724  const Manager::Var* var = Manager::Instance().getVariable("genUpsilon4S(PDG)");
1725  ASSERT_NE(var, nullptr);
1726  EXPECT_FLOAT_EQ(std::get<double>(var->function(p1)), 300553);
1727  EXPECT_FLOAT_EQ(std::get<double>(var->function(p_noMC)), 300553);
1728 
1729  var = Manager::Instance().getVariable("genUpsilon4S(matchedMC(pz))");
1730  ASSERT_NE(var, nullptr);
1731  EXPECT_FLOAT_EQ(std::get<double>(var->function(p1)), 0.4);
1732  EXPECT_FLOAT_EQ(std::get<double>(var->function(p_noMC)), 0.4);
1733 
1734  var = Manager::Instance().getVariable("genUpsilon4S(mcDaughter(0, PDG))");
1735  ASSERT_NE(var, nullptr);
1736  EXPECT_FLOAT_EQ(std::get<double>(var->function(p1)), -521);
1737  EXPECT_FLOAT_EQ(std::get<double>(var->function(p_noMC)), -521);
1738 
1739  var = Manager::Instance().getVariable("genUpsilon4S(mcDaughter(0, matchedMC(px)))");
1740  ASSERT_NE(var, nullptr);
1741  EXPECT_FLOAT_EQ(std::get<double>(var->function(p1)), 1.1);
1742  EXPECT_FLOAT_EQ(std::get<double>(var->function(p_noMC)), 1.1);
1743 
1745  mcParticles.clear();
1746  particles.clear();
1747  MCParticleGraph mcGraph2;
1748 
1749  MCParticleGraph::GraphParticle& graphParticle1 = mcGraph2.addParticle();
1750  MCParticleGraph::GraphParticle& graphParticle2 = mcGraph2.addParticle();
1751 
1752  graphParticle1.setPDG(Const::electron.getPDGCode());
1753  graphParticle2.setPDG(-Const::electron.getPDGCode());
1754 
1755  graphParticle1.setMomentum(1.1, 1.3, 1.4);
1756  graphParticle1.setMomentum(-1.1, -1.3, 1.4);
1757 
1758  mcGraph2.generateList();
1759 
1760  auto* mcP1 = mcParticles[0];
1761  mcP1->setStatus(MCParticle::c_PrimaryParticle);
1762 
1763  auto* mcP2 = mcParticles[1];
1764  mcP2->setStatus(MCParticle::c_PrimaryParticle);
1765 
1766  auto* someParticle = particles.appendNew(PxPyPzEVector({ 0.0, -0.4, 0.8, 1.0}), 11);
1767  someParticle->addRelationTo(mcP1);
1768 
1769  var = Manager::Instance().getVariable("genUpsilon4S(PDG)");
1770  ASSERT_NE(var, nullptr);
1771  EXPECT_TRUE(std::isnan(std::get<double>(var->function(someParticle))));
1772  }
1773 
1774  TEST_F(MetaVariableTest, daughterProductOf)
1775  {
1776  PxPyPzEVector momentum;
1777  const int nDaughters = 4;
1778  StoreArray<Particle> particles;
1779  std::vector<int> daughterIndices;
1780  for (int i = 0; i < nDaughters; i++) {
1781  Particle d(PxPyPzEVector(1, 1, 1, i * 1.0 + 2.0), (i % 2) ? 213 : -213);
1782  momentum += d.get4Vector();
1783  Particle* newDaughters = particles.appendNew(d);
1784  daughterIndices.push_back(newDaughters->getArrayIndex());
1785  }
1786  const Particle* p = particles.appendNew(momentum, 411, Particle::c_Unflavored, daughterIndices);
1787 
1788  const Manager::Var* var = Manager::Instance().getVariable("daughterProductOf(E)");
1789  ASSERT_NE(var, nullptr);
1790  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), 120.0);
1791  }
1792 
1793  TEST_F(MetaVariableTest, daughterSumOf)
1794  {
1795  PxPyPzEVector momentum;
1796  const int nDaughters = 4;
1797  StoreArray<Particle> particles;
1798  std::vector<int> daughterIndices;
1799  for (int i = 0; i < nDaughters; i++) {
1800  Particle d(PxPyPzEVector(1, 1, 1, i * 1.0 + 2.0), (i % 2) ? 213 : -213);
1801  momentum += d.get4Vector();
1802  Particle* newDaughters = particles.appendNew(d);
1803  daughterIndices.push_back(newDaughters->getArrayIndex());
1804  }
1805  const Particle* p = particles.appendNew(momentum, 411, Particle::c_Unflavored, daughterIndices);
1806 
1807  const Manager::Var* var = Manager::Instance().getVariable("daughterSumOf(E)");
1808  ASSERT_NE(var, nullptr);
1809  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), 14.0);
1810  }
1811 
1812  TEST_F(MetaVariableTest, daughterLowest)
1813  {
1814  PxPyPzEVector momentum;
1815  const int nDaughters = 4;
1816  StoreArray<Particle> particles;
1817  std::vector<int> daughterIndices;
1818  for (int i = 0; i < nDaughters; i++) {
1819  Particle d(PxPyPzEVector(1, 1, 1, i * 1.0 + 2.0), (i % 2) ? 213 : -213);
1820  momentum += d.get4Vector();
1821  Particle* newDaughters = particles.appendNew(d);
1822  daughterIndices.push_back(newDaughters->getArrayIndex());
1823  }
1824  const Particle* p = particles.appendNew(momentum, 411, Particle::c_Unflavored, daughterIndices);
1825 
1826  const Manager::Var* var = Manager::Instance().getVariable("daughterLowest(E)");
1827  ASSERT_NE(var, nullptr);
1828  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), 2.0);
1829  }
1830 
1831  TEST_F(MetaVariableTest, daughterHighest)
1832  {
1833  PxPyPzEVector momentum;
1834  const int nDaughters = 4;
1835  StoreArray<Particle> particles;
1836  std::vector<int> daughterIndices;
1837  for (int i = 0; i < nDaughters; i++) {
1838  Particle d(PxPyPzEVector(1, 1, 1, i * 1.0 + 1.0), (i % 2) ? 213 : -213);
1839  momentum += d.get4Vector();
1840  Particle* newDaughters = particles.appendNew(d);
1841  daughterIndices.push_back(newDaughters->getArrayIndex());
1842  }
1843  const Particle* p = particles.appendNew(momentum, 411, Particle::c_Unflavored, daughterIndices);
1844 
1845  const Manager::Var* var = Manager::Instance().getVariable("daughterHighest(E)");
1846  ASSERT_NE(var, nullptr);
1847  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), 4.0);
1848  }
1849 
1850  TEST_F(MetaVariableTest, daughterDiffOf)
1851  {
1852  PxPyPzEVector momentum;
1853  const int nDaughters = 4;
1854  StoreArray<Particle> particles;
1855  std::vector<int> daughterIndices;
1856  for (int i = 0; i < nDaughters; i++) {
1857  Particle d(PxPyPzEVector(-1, 1.0 - 2 * (i % 2), 1, i * 1.0 + 2.0), (i % 2) ? -11 : 211);
1858  momentum += d.get4Vector();
1859  Particle* newDaughters = particles.appendNew(d);
1860  daughterIndices.push_back(newDaughters->getArrayIndex());
1861  }
1862  const Particle* p = particles.appendNew(momentum, 411, Particle::c_Unflavored, daughterIndices);
1863 
1864  const Manager::Var* var = Manager::Instance().getVariable("daughterDiffOf(0, 1, PDG)");
1865  ASSERT_NE(var, nullptr);
1866  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), -222);
1867 
1868  var = Manager::Instance().getVariable("daughterDiffOf(1, 0, PDG)");
1869  ASSERT_NE(var, nullptr);
1870  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), 222);
1871 
1872  var = Manager::Instance().getVariable("daughterDiffOf(0, 1, abs(PDG))");
1873  ASSERT_NE(var, nullptr);
1874  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), -200);
1875 
1876  var = Manager::Instance().getVariable("daughterDiffOf(1, 1, PDG)");
1877  ASSERT_NE(var, nullptr);
1878  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), 0);
1879 
1880  var = Manager::Instance().getVariable("daughterDiffOf(1, 3, abs(PDG))");
1881  ASSERT_NE(var, nullptr);
1882  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), 0);
1883 
1884  var = Manager::Instance().getVariable("daughterDiffOf(0, 2, PDG)");
1885  ASSERT_NE(var, nullptr);
1886  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), 0);
1887 
1888  var = Manager::Instance().getVariable("daughterDiffOf(1, 0, phi)");
1889  ASSERT_NE(var, nullptr);
1890  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), -1.5707964);
1891 
1892  var = Manager::Instance().getVariable("daughterDiffOf(1, 0, useCMSFrame(phi))");
1893  ASSERT_NE(var, nullptr);
1894  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), -1.5004894);
1895 
1896  EXPECT_B2FATAL(Manager::Instance().getVariable("daughterDiffOf(0, NOTINT, PDG)"));
1897  }
1898 
1899 
1900  TEST_F(MetaVariableTest, mcDaughterDiffOf)
1901  {
1902  DataStore::Instance().setInitializeActive(true);
1903  PxPyPzEVector momentum;
1904  const int nDaughters = 4;
1905  StoreArray<Particle> particles;
1906  StoreArray<MCParticle> mcParticles;
1907  particles.registerRelationTo(mcParticles);
1908  std::vector<int> daughterIndices;
1909  DataStore::Instance().setInitializeActive(false);
1910 
1911  for (int i = 0; i < nDaughters; i++) {
1912  Particle d(PxPyPzEVector(1, 1, 1, i * 1.0 + 1.0), (i % 2) ? -11 : 211);
1913  momentum += d.get4Vector();
1914  Particle* newDaughters = particles.appendNew(d);
1915  daughterIndices.push_back(newDaughters->getArrayIndex());
1916  auto* mcParticle = mcParticles.appendNew();
1917  mcParticle->setPDG((i % 2) ? -Const::electron.getPDGCode() : Const::pion.getPDGCode());
1918  mcParticle->setStatus(MCParticle::c_PrimaryParticle);
1919  newDaughters->addRelationTo(mcParticle);
1920  }
1921  const Particle* p = particles.appendNew(momentum, 411, Particle::c_Unflavored, daughterIndices);
1922 
1923  const Manager::Var* var = Manager::Instance().getVariable("mcDaughterDiffOf(0, 1, PDG)");
1924  ASSERT_NE(var, nullptr);
1925  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), -222);
1926 
1927  var = Manager::Instance().getVariable("mcDaughterDiffOf(1, 0, PDG)");
1928  ASSERT_NE(var, nullptr);
1929  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), 222);
1930 
1931  var = Manager::Instance().getVariable("mcDaughterDiffOf(0, 1, abs(PDG))");
1932  ASSERT_NE(var, nullptr);
1933  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), -200);
1934 
1935  var = Manager::Instance().getVariable("mcDaughterDiffOf(1, 1, PDG)");
1936  ASSERT_NE(var, nullptr);
1937  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), 0);
1938 
1939  var = Manager::Instance().getVariable("mcDaughterDiffOf(1, 3, abs(PDG))");
1940  ASSERT_NE(var, nullptr);
1941  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), 0);
1942 
1943  var = Manager::Instance().getVariable("mcDaughterDiffOf(0, 2, PDG)");
1944  ASSERT_NE(var, nullptr);
1945  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), 0);
1946 
1947  EXPECT_B2FATAL(Manager::Instance().getVariable("mcDaughterDiffOf(0, NOTINT, PDG)"));
1948  }
1949 
1950 
1951 
1952  TEST_F(MetaVariableTest, daughterClusterAngleInBetween)
1953  {
1954  // declare all the array we need
1955  StoreArray<Particle> particles;
1956  std::vector<int> daughterIndices, daughterIndices_noclst;
1957 
1958  //proxy initialize where to declare the needed array
1959  DataStore::Instance().setInitializeActive(true);
1960  StoreArray<ECLCluster> eclclusters;
1961  eclclusters.registerInDataStore();
1962  particles.registerRelationTo(eclclusters);
1963  DataStore::Instance().setInitializeActive(false);
1964 
1965  // create two Lorentz vectors that are back to back in the CMS and boost them to the Lab frame
1966  const float px_CM = 2.;
1967  const float py_CM = 1.;
1968  const float pz_CM = 3.;
1969  float E_CM;
1970  E_CM = sqrt(pow(px_CM, 2) + pow(py_CM, 2) + pow(pz_CM, 2));
1971  PxPyPzEVector momentum, momentum_noclst;
1972  PxPyPzEVector dau0_4vec_CM(px_CM, py_CM, pz_CM, E_CM), dau1_4vec_CM(-px_CM, -py_CM, -pz_CM, E_CM);
1973  PxPyPzEVector dau0_4vec_Lab, dau1_4vec_Lab;
1974  dau0_4vec_Lab = PCmsLabTransform::cmsToLab(
1975  dau0_4vec_CM); //why is everybody using the extended method when there are the functions that do all the steps for us?
1976  dau1_4vec_Lab = PCmsLabTransform::cmsToLab(dau1_4vec_CM);
1977 
1978  // add the two photons (now in the Lab frame) as the two daughters of some particle and create the latter
1979  Particle dau0_noclst(dau0_4vec_Lab, 22);
1980  momentum += dau0_noclst.get4Vector();
1981  Particle* newDaughter0_noclst = particles.appendNew(dau0_noclst);
1982  daughterIndices_noclst.push_back(newDaughter0_noclst->getArrayIndex());
1983  Particle dau1_noclst(dau1_4vec_Lab, 22);
1984  momentum += dau1_noclst.get4Vector();
1985  Particle* newDaughter1_noclst = particles.appendNew(dau1_noclst);
1986  daughterIndices_noclst.push_back(newDaughter1_noclst->getArrayIndex());
1987  const Particle* par_noclst = particles.appendNew(momentum, 111, Particle::c_Unflavored, daughterIndices_noclst);
1988 
1989  // grab variables
1990  const Manager::Var* var = Manager::Instance().getVariable("daughterClusterAngleInBetween(0, 1)");
1991  const Manager::Var* varCMS = Manager::Instance().getVariable("useCMSFrame(daughterClusterAngleInBetween(0, 1))");
1992 
1993  // when no relations are set between the particles and the eclClusters, nan is expected to be returned
1994  ASSERT_NE(var, nullptr);
1995  EXPECT_TRUE(std::isnan(std::get<double>(var->function(par_noclst))));
1996 
1997  // set relations between particles and eclClusters
1998  ECLCluster* eclst0 = eclclusters.appendNew(ECLCluster());
1999  eclst0->setEnergy(dau0_4vec_Lab.E());
2000  eclst0->setHypothesis(ECLCluster::EHypothesisBit::c_nPhotons);
2001  eclst0->setClusterId(1);
2002  eclst0->setTheta(dau0_4vec_Lab.Theta());
2003  eclst0->setPhi(dau0_4vec_Lab.Phi());
2004  eclst0->setR(148.4);
2005  ECLCluster* eclst1 = eclclusters.appendNew(ECLCluster());
2006  eclst1->setEnergy(dau1_4vec_Lab.E());
2007  eclst1->setHypothesis(ECLCluster::EHypothesisBit::c_nPhotons);
2008  eclst1->setClusterId(2);
2009  eclst1->setTheta(dau1_4vec_Lab.Theta());
2010  eclst1->setPhi(dau1_4vec_Lab.Phi());
2011  eclst1->setR(148.5);
2012 
2013  const Particle* newDaughter0 = particles.appendNew(Particle(eclclusters[0]));
2014  daughterIndices.push_back(newDaughter0->getArrayIndex());
2015  const Particle* newDaughter1 = particles.appendNew(Particle(eclclusters[1]));
2016  daughterIndices.push_back(newDaughter1->getArrayIndex());
2017 
2018  const Particle* par = particles.appendNew(momentum, 111, Particle::c_Unflavored, daughterIndices);
2019 
2020  //now we expect non-nan results
2021  EXPECT_FLOAT_EQ(std::get<double>(var->function(par)), 2.8613892);
2022  EXPECT_FLOAT_EQ(std::get<double>(varCMS->function(par)), M_PI);
2023  }
2024 
2025  TEST_F(MetaVariableTest, grandDaughterDiffOfs)
2026  {
2027  // declare all the array we need
2028  StoreArray<Particle> particles;
2029  std::vector<int> daughterIndices0_noclst, daughterIndices1_noclst, daughterIndices2_noclst;
2030  std::vector<int> daughterIndices0, daughterIndices1, daughterIndices2;
2031 
2032  //proxy initialize where to declare the needed array
2033  DataStore::Instance().setInitializeActive(true);
2034  StoreArray<ECLCluster> eclclusters;
2035  eclclusters.registerInDataStore();
2036  particles.registerRelationTo(eclclusters);
2037  DataStore::Instance().setInitializeActive(false);
2038 
2039  // create two Lorentz vectors
2040  const float px_0 = 2.;
2041  const float py_0 = 1.;
2042  const float pz_0 = 3.;
2043  const float px_1 = 1.5;
2044  const float py_1 = 1.5;
2045  const float pz_1 = 2.5;
2046  float E_0, E_1;
2047  E_0 = sqrt(pow(px_0, 2) + pow(py_0, 2) + pow(pz_0, 2));
2048  E_1 = sqrt(pow(px_1, 2) + pow(py_1, 2) + pow(pz_1, 2));
2049  PxPyPzEVector momentum_0, momentum_1, momentum;
2050  PxPyPzEVector dau0_4vec(px_0, py_0, pz_0, E_0), dau1_4vec(px_1, py_1, pz_1, E_1);
2051 
2052  // add the two photons as the two daughters of some particle and create the latter
2053  // Particle dau0_noclst(dau0_4vec, 22);
2054  // momentum += dau0_noclst.get4Vector();
2055  // Particle* newDaughter0_noclst = particles.appendNew(dau0_noclst);
2056  // daughterIndices_noclst.push_back(newDaughter0_noclst->getArrayIndex());
2057  // Particle dau1_noclst(dau1_4vec, 22);
2058  // momentum += dau1_noclst.get4Vector();
2059  // Particle* newDaughter1_noclst = particles.appendNew(dau1_noclst);
2060  // daughterIndices_noclst.push_back(newDaughter1_noclst->getArrayIndex());
2061  // const Particle* par_noclst = particles.appendNew(momentum, 111, Particle::c_Unflavored, daughterIndices_noclst);
2062 
2063  Particle dau0_noclst(dau0_4vec, 22);
2064  momentum_0 = dau0_4vec;
2065  Particle* newDaughter0_noclst = particles.appendNew(dau0_noclst);
2066  daughterIndices0_noclst.push_back(newDaughter0_noclst->getArrayIndex());
2067  const Particle* par0_noclst = particles.appendNew(momentum_0, 111, Particle::c_Unflavored, daughterIndices0_noclst);
2068  Particle dau1_noclst(dau1_4vec, 22);
2069  momentum_1 = dau1_4vec;
2070  Particle* newDaughter1_noclst = particles.appendNew(dau1_noclst);
2071  daughterIndices1_noclst.push_back(newDaughter1_noclst->getArrayIndex());
2072  const Particle* par1_noclst = particles.appendNew(momentum_1, 111, Particle::c_Unflavored, daughterIndices1_noclst);
2073 
2074  momentum = momentum_0 + momentum_1;
2075  daughterIndices2_noclst.push_back(par0_noclst->getArrayIndex());
2076  daughterIndices2_noclst.push_back(par1_noclst->getArrayIndex());
2077  const Particle* parGranny_noclst = particles.appendNew(momentum, 111, Particle::c_Unflavored, daughterIndices2_noclst);
2078 
2079  // grab variables
2080  const Manager::Var* var_Theta = Manager::Instance().getVariable("grandDaughterDiffOf(0,1,0,0,theta)");
2081  const Manager::Var* var_ClusterTheta = Manager::Instance().getVariable("grandDaughterDiffOf(0,1,0,0,clusterTheta)");
2082  const Manager::Var* var_E = Manager::Instance().getVariable("grandDaughterDiffOf(0,1,0,0,E)");
2083  const Manager::Var* var_ClusterE = Manager::Instance().getVariable("grandDaughterDiffOf(0,1,0,0,clusterE)");
2084  const Manager::Var* var_E_wrongIndexes = Manager::Instance().getVariable("grandDaughterDiffOf(0,1,2,3,E)");
2085  const Manager::Var* var_ClusterE_wrongIndexes = Manager::Instance().getVariable("grandDaughterDiffOf(0,1,2,3,clusterE)");
2086 
2087  const Manager::Var* var_ClusterPhi = Manager::Instance().getVariable("grandDaughterDiffOf(0,1,0,0,clusterPhi)");
2088  const Manager::Var* var_Phi = Manager::Instance().getVariable("grandDaughterDiffOf(0,1,0,0,phi)");
2089  const Manager::Var* var_ClusterPhi_wrongIndexes = Manager::Instance().getVariable("grandDaughterDiffOf(0,1,2,3,clusterPhi)");
2090  const Manager::Var* var_Phi_wrongIndexes = Manager::Instance().getVariable("grandDaughterDiffOf(0,1,2,3,phi)");
2091 
2092  // when no relations are set between the particles and the eclClusters, nan is expected to be returned for the Cluster- vars
2093  // no problems are supposed to happen for non-Cluster- vars
2094  // also, we expect NaN when we pass wrong indexes
2095  ASSERT_NE(var_ClusterPhi, nullptr);
2096  EXPECT_TRUE(std::isnan(std::get<double>(var_ClusterPhi->function(parGranny_noclst))));
2097  EXPECT_TRUE(std::isnan(std::get<double>(var_ClusterTheta->function(parGranny_noclst))));
2098  EXPECT_TRUE(std::isnan(std::get<double>(var_ClusterE->function(parGranny_noclst))));
2099  EXPECT_FLOAT_EQ(std::get<double>(var_Phi->function(parGranny_noclst)), 0.32175055);
2100  EXPECT_FLOAT_EQ(std::get<double>(var_Theta->function(parGranny_noclst)), 0.06311664);
2101  EXPECT_FLOAT_EQ(std::get<double>(var_E->function(parGranny_noclst)), -0.46293807);
2102  EXPECT_TRUE(std::isnan(std::get<double>(var_ClusterPhi_wrongIndexes->function(parGranny_noclst))));
2103  EXPECT_TRUE(std::isnan(std::get<double>(var_Phi_wrongIndexes->function(parGranny_noclst))));
2104  EXPECT_TRUE(std::isnan(std::get<double>(var_ClusterE_wrongIndexes->function(parGranny_noclst))));
2105  EXPECT_TRUE(std::isnan(std::get<double>(var_E_wrongIndexes->function(parGranny_noclst))));
2106 
2107  // set relations between particles and eclClusters
2108  ECLCluster* eclst0 = eclclusters.appendNew(ECLCluster());
2109  eclst0->setEnergy(dau0_4vec.E());
2110  eclst0->setHypothesis(ECLCluster::EHypothesisBit::c_nPhotons);
2111  eclst0->setClusterId(1);
2112  eclst0->setTheta(dau0_4vec.Theta());
2113  eclst0->setPhi(dau0_4vec.Phi());
2114  eclst0->setR(148.4);
2115  ECLCluster* eclst1 = eclclusters.appendNew(ECLCluster());
2116  eclst1->setEnergy(dau1_4vec.E());
2117  eclst1->setHypothesis(ECLCluster::EHypothesisBit::c_nPhotons);
2118  eclst1->setClusterId(2);
2119  eclst1->setTheta(dau1_4vec.Theta());
2120  eclst1->setPhi(dau1_4vec.Phi());
2121  eclst1->setR(148.5);
2122 
2123  const Particle* newDaughter0 = particles.appendNew(Particle(eclclusters[0]));
2124  daughterIndices0.push_back(newDaughter0->getArrayIndex());
2125  const Particle* par0 = particles.appendNew(momentum_0, 111, Particle::c_Unflavored, daughterIndices0);
2126 
2127  const Particle* newDaughter1 = particles.appendNew(Particle(eclclusters[1]));
2128  daughterIndices1.push_back(newDaughter1->getArrayIndex());
2129  const Particle* par1 = particles.appendNew(momentum_1, 111, Particle::c_Unflavored, daughterIndices1);
2130 
2131  daughterIndices2.push_back(par0->getArrayIndex());
2132  daughterIndices2.push_back(par1->getArrayIndex());
2133  const Particle* parGranny = particles.appendNew(momentum, 111, Particle::c_Unflavored, daughterIndices2);
2134  //const Particle* par = particles.appendNew(momentum, 111, Particle::c_Unflavored, daughterIndices);
2135 
2136  //now we expect non-nan results
2137  EXPECT_FLOAT_EQ(std::get<double>(var_ClusterPhi->function(parGranny)), 0.32175055);
2138  EXPECT_FLOAT_EQ(std::get<double>(var_Phi->function(parGranny)), 0.32175055);
2139  EXPECT_FLOAT_EQ(std::get<double>(var_ClusterTheta->function(parGranny)), 0.06311664);
2140  EXPECT_FLOAT_EQ(std::get<double>(var_Theta->function(parGranny)), 0.06311664);
2141  EXPECT_FLOAT_EQ(std::get<double>(var_ClusterE->function(parGranny)), -0.46293831);
2142  EXPECT_FLOAT_EQ(std::get<double>(var_E->function(parGranny)), -0.46293831);
2143  }
2144 
2145  TEST_F(MetaVariableTest, daughterNormDiffOf)
2146  {
2147  PxPyPzEVector momentum;
2148  const int nDaughters = 4;
2149  StoreArray<Particle> particles;
2150  std::vector<int> daughterIndices;
2151  for (int i = 0; i < nDaughters; i++) {
2152  Particle d(PxPyPzEVector(1, 1, 1, i * 1.0 + 1.0), (i % 2) ? -11 : 211);
2153  momentum += d.get4Vector();
2154  Particle* newDaughters = particles.appendNew(d);
2155  daughterIndices.push_back(newDaughters->getArrayIndex());
2156  }
2157  const Particle* p = particles.appendNew(momentum, 411, Particle::c_Unflavored, daughterIndices);
2158 
2159  const Manager::Var* var = Manager::Instance().getVariable("daughterNormDiffOf(0, 1, PDG)");
2160  ASSERT_NE(var, nullptr);
2161  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), -222 / 200.);
2162 
2163  var = Manager::Instance().getVariable("daughterNormDiffOf(1, 0, PDG)");
2164  ASSERT_NE(var, nullptr);
2165  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), 222 / 200.);
2166 
2167  var = Manager::Instance().getVariable("daughterNormDiffOf(0, 1, abs(PDG))");
2168  ASSERT_NE(var, nullptr);
2169  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), -200 / 222.);
2170 
2171  var = Manager::Instance().getVariable("daughterNormDiffOf(1, 1, PDG)");
2172  ASSERT_NE(var, nullptr);
2173  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), -0 / 22.);
2174 
2175  var = Manager::Instance().getVariable("daughterNormDiffOf(1, 3, abs(PDG))");
2176  ASSERT_NE(var, nullptr);
2177  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), 0 / 22.);
2178 
2179  var = Manager::Instance().getVariable("daughterNormDiffOf(0, 2, PDG)");
2180  ASSERT_NE(var, nullptr);
2181  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), 0 / 422.);
2182 
2183  }
2184 
2185  TEST_F(MetaVariableTest, daughterMotherDiffOf)
2186  {
2187  PxPyPzEVector momentum;
2188  const int nDaughters = 4;
2189  StoreArray<Particle> particles;
2190  std::vector<int> daughterIndices;
2191  for (int i = 0; i < nDaughters; i++) {
2192  Particle d(PxPyPzEVector(1, 1, 1, i * 1.0 + 1.0), (i % 2) ? -11 : 211);
2193  momentum += d.get4Vector();
2194  Particle* newDaughters = particles.appendNew(d);
2195  daughterIndices.push_back(newDaughters->getArrayIndex());
2196  }
2197  const Particle* p = particles.appendNew(momentum, 411, Particle::c_Unflavored, daughterIndices);
2198 
2199  const Manager::Var* var = Manager::Instance().getVariable("daughterMotherDiffOf(1, PDG)");
2200  ASSERT_NE(var, nullptr);
2201  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), 422);
2202 
2203  var = Manager::Instance().getVariable("daughterMotherDiffOf(1, abs(PDG))");
2204  ASSERT_NE(var, nullptr);
2205  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), 400);
2206 
2207  var = Manager::Instance().getVariable("daughterMotherDiffOf(0, PDG)");
2208  ASSERT_NE(var, nullptr);
2209  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), 200);
2210 
2211  }
2212 
2213  TEST_F(MetaVariableTest, daughterMotherNormDiffOf)
2214  {
2215  PxPyPzEVector momentum;
2216  const int nDaughters = 4;
2217  StoreArray<Particle> particles;
2218  std::vector<int> daughterIndices;
2219  for (int i = 0; i < nDaughters; i++) {
2220  Particle d(PxPyPzEVector(1, 1, 1, i * 1.0 + 1.0), (i % 2) ? -11 : 211);
2221  momentum += d.get4Vector();
2222  Particle* newDaughters = particles.appendNew(d);
2223  daughterIndices.push_back(newDaughters->getArrayIndex());
2224  }
2225  const Particle* p = particles.appendNew(momentum, 411, Particle::c_Unflavored, daughterIndices);
2226 
2227  const Manager::Var* var = Manager::Instance().getVariable("daughterMotherNormDiffOf(1, PDG)");
2228  ASSERT_NE(var, nullptr);
2229  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), 422 / 400.);
2230 
2231  var = Manager::Instance().getVariable("daughterMotherNormDiffOf(1, abs(PDG))");
2232  ASSERT_NE(var, nullptr);
2233  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), 400 / 422.);
2234 
2235  var = Manager::Instance().getVariable("daughterMotherNormDiffOf(0, PDG)");
2236  ASSERT_NE(var, nullptr);
2237  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), 200 / 622.);
2238 
2239  }
2240 
2241  TEST_F(MetaVariableTest, constant)
2242  {
2243 
2244  const Manager::Var* var = Manager::Instance().getVariable("constant(1)");
2245  ASSERT_NE(var, nullptr);
2246  EXPECT_FLOAT_EQ(std::get<double>(var->function(nullptr)), 1.0);
2247 
2248  var = Manager::Instance().getVariable("constant(0)");
2249  ASSERT_NE(var, nullptr);
2250  EXPECT_FLOAT_EQ(std::get<double>(var->function(nullptr)), 0.0);
2251 
2252  }
2253 
2254  TEST_F(MetaVariableTest, abs)
2255  {
2256  Particle p({ 0.1, -0.4, 0.8, 2.0 }, 11);
2257  Particle p2({ -0.1, -0.4, 0.8, 4.0 }, -11);
2258 
2259  const Manager::Var* var = Manager::Instance().getVariable("abs(px)");
2260  ASSERT_NE(var, nullptr);
2261  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), 0.1);
2262  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p2)), 0.1);
2263 
2264  }
2265 
2266  TEST_F(MetaVariableTest, sin)
2267  {
2268  Particle p({ 3.14159265359 / 2.0, -0.4, 0.8, 1.0}, 11);
2269  Particle p2({ 0.0, -0.4, 0.8, 1.0 }, -11);
2270 
2271  const Manager::Var* var = Manager::Instance().getVariable("sin(px)");
2272  ASSERT_NE(var, nullptr);
2273  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), 1.0);
2274  EXPECT_NEAR(std::get<double>(var->function(&p2)), 0.0, 1e-6);
2275 
2276  }
2277 
2278  TEST_F(MetaVariableTest, cos)
2279  {
2280  Particle p({ 3.14159265359 / 2.0, -0.4, 0.8, 1.0}, 11);
2281  Particle p2({ 0.0, -0.4, 0.8, 1.0 }, -11);
2282 
2283  const Manager::Var* var = Manager::Instance().getVariable("cos(px)");
2284  ASSERT_NE(var, nullptr);
2285  EXPECT_NEAR(std::get<double>(var->function(&p)), 0.0, 1e-6);
2286  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p2)), 1.0);
2287 
2288  }
2289 
2290  TEST_F(MetaVariableTest, NBDeltaIfMissingDeathTest)
2291  {
2292  //Variable got removed, test for absence
2293  EXPECT_B2FATAL(Manager::Instance().getVariable("NBDeltaIfMissing(TOP, 11)"));
2294  EXPECT_B2FATAL(Manager::Instance().getVariable("NBDeltaIfMissing(ARICH, 11)"));
2295  }
2296 
2297  TEST_F(MetaVariableTest, matchedMC)
2298  {
2299  DataStore::Instance().setInitializeActive(true);
2300  StoreArray<MCParticle> mcParticles;
2301  StoreArray<Particle> particles;
2302  particles.registerRelationTo(mcParticles);
2303  DataStore::Instance().setInitializeActive(false);
2304 
2305  auto* mcParticle = mcParticles.appendNew();
2306  mcParticle->setPDG(Const::electron.getPDGCode());
2307  mcParticle->setStatus(MCParticle::c_PrimaryParticle);
2308  auto* p1 = particles.appendNew(PxPyPzEVector({ 0.0, -0.4, 0.8, 1.0}), 11);
2309  p1->addRelationTo(mcParticle);
2310 
2311  mcParticle = mcParticles.appendNew();
2312  mcParticle->setPDG(-Const::electron.getPDGCode());
2313  mcParticle->setStatus(MCParticle::c_PrimaryParticle);
2314  auto* p2 = particles.appendNew(PxPyPzEVector({ 0.0, -0.4, 0.8, 1.0}), 11);
2315  p2->addRelationTo(mcParticle);
2316 
2317  mcParticle = mcParticles.appendNew();
2318  mcParticle->setPDG(Const::photon.getPDGCode());
2319  mcParticle->setStatus(MCParticle::c_PrimaryParticle);
2320  auto* p3 = particles.appendNew(PxPyPzEVector({ 0.0, -0.4, 0.8, 1.0}), 11);
2321  p3->addRelationTo(mcParticle);
2322 
2323  // Test if matchedMC also works for particle which already is an MCParticle.
2324  auto* p4 = particles.appendNew(mcParticle);
2325 
2326  const Manager::Var* var = Manager::Instance().getVariable("matchedMC(charge)");
2327  ASSERT_NE(var, nullptr);
2328  EXPECT_FLOAT_EQ(std::get<double>(var->function(p1)), -1);
2329  EXPECT_FLOAT_EQ(std::get<double>(var->function(p2)), 1);
2330  EXPECT_FLOAT_EQ(std::get<double>(var->function(p3)), 0);
2331  EXPECT_FLOAT_EQ(std::get<double>(var->function(p4)), 0);
2332  }
2333 
2334  TEST_F(MetaVariableTest, countInList)
2335  {
2336  StoreArray<Particle> particles;
2337  DataStore::EStoreFlags flags = DataStore::c_DontWriteOut;
2338 
2339  StoreObjPtr<ParticleList> outputList("pList1");
2340  DataStore::Instance().setInitializeActive(true);
2341  outputList.registerInDataStore(flags);
2342  DataStore::Instance().setInitializeActive(false);
2343  outputList.create();
2344  outputList->initialize(22, "pList1");
2345 
2346  particles.appendNew(Particle({0.5, 0.4, 0.5, 0.8}, 22, Particle::c_Unflavored, Particle::c_Undefined, 2));
2347  particles.appendNew(Particle({0.5, 0.2, 0.7, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 3));
2348  particles.appendNew(Particle({0.4, 0.2, 0.7, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 4));
2349  particles.appendNew(Particle({0.5, 0.4, 0.8, 1.1}, 22, Particle::c_Unflavored, Particle::c_Undefined, 5));
2350  particles.appendNew(Particle({0.3, 0.3, 0.4, 0.6}, 22, Particle::c_Unflavored, Particle::c_Undefined, 6));
2351 
2352  outputList->addParticle(0, 22, Particle::c_Unflavored);
2353  outputList->addParticle(1, 22, Particle::c_Unflavored);
2354  outputList->addParticle(2, 22, Particle::c_Unflavored);
2355  outputList->addParticle(3, 22, Particle::c_Unflavored);
2356  outputList->addParticle(4, 22, Particle::c_Unflavored);
2357 
2358  const Manager::Var* var = Manager::Instance().getVariable("countInList(pList1, E < 0.85)");
2359  ASSERT_NE(var, nullptr);
2360  EXPECT_EQ(std::get<int>(var->function(nullptr)), 2);
2361 
2362  var = Manager::Instance().getVariable("countInList(pList1)");
2363  ASSERT_NE(var, nullptr);
2364  EXPECT_EQ(std::get<int>(var->function(nullptr)), 5);
2365 
2366  var = Manager::Instance().getVariable("countInList(pList1, E > 5)");
2367  ASSERT_NE(var, nullptr);
2368  EXPECT_EQ(std::get<int>(var->function(nullptr)), 0);
2369 
2370  var = Manager::Instance().getVariable("countInList(pList1, E < 5)");
2371  ASSERT_NE(var, nullptr);
2372  EXPECT_EQ(std::get<int>(var->function(nullptr)), 5);
2373  }
2374 
2375  TEST_F(MetaVariableTest, isInList)
2376  {
2377  // we need the particles StoreArray
2378  StoreArray<Particle> particles;
2379  DataStore::EStoreFlags flags = DataStore::c_DontWriteOut;
2380 
2381  // create a photon list for testing
2382  StoreObjPtr<ParticleList> gammalist("testGammaList");
2383  DataStore::Instance().setInitializeActive(true);
2384  gammalist.registerInDataStore(flags);
2385  DataStore::Instance().setInitializeActive(false);
2386  gammalist.create();
2387  gammalist->initialize(22, "testGammaList");
2388 
2389  // mock up two photons
2390  Particle goingin({0.5, 0.4, 0.5, 0.8}, 22, Particle::c_Unflavored, Particle::c_Undefined, 0);
2391  Particle notgoingin({0.3, 0.3, 0.4, 0.6}, 22, Particle::c_Unflavored, Particle::c_Undefined, 1);
2392  auto* inthelist = particles.appendNew(goingin);
2393  auto* notinthelist = particles.appendNew(notgoingin);
2394 
2395  // put the the zeroth one in the list the first on not in the list
2396  gammalist->addParticle(0, 22, Particle::c_Unflavored);
2397 
2398  // get the variables
2399  const Manager::Var* vnonsense = Manager::Instance().getVariable("isInList(NONEXISTANTLIST)");
2400  const Manager::Var* vsensible = Manager::Instance().getVariable("isInList(testGammaList)");
2401 
2402  // -
2403  EXPECT_B2FATAL(std::get<bool>(vnonsense->function(notinthelist)));
2404  EXPECT_TRUE(std::get<bool>(vsensible->function(inthelist)));
2405  EXPECT_FALSE(std::get<bool>(vsensible->function(notinthelist)));
2406  }
2407 
2409  TEST_F(MetaVariableTest, cutIsInList)
2410  {
2411  StoreArray<Particle> particles;
2412  DataStore::EStoreFlags flags = DataStore::c_DontWriteOut;
2413 
2414  // create a photon list for testing
2415  const std::string listname {"wil/d(-+)'':l*"};
2416 
2417  StoreObjPtr<ParticleList> particlelist(listname);
2418  DataStore::Instance().setInitializeActive(true);
2419  particlelist.registerInDataStore(flags);
2420  DataStore::Instance().setInitializeActive(false);
2421  particlelist.create();
2422  particlelist->initialize(22, listname);
2423 
2424  Particle goingin({0.5, 0.4, 0.5, 0.8}, 22, Particle::c_Unflavored, Particle::c_Undefined, 0);
2425  Particle notgoingin({0.3, 0.3, 0.4, 0.6}, 22, Particle::c_Unflavored, Particle::c_Undefined, 1);
2426  auto* inthelist = particles.appendNew(goingin);
2427  auto* notinthelist = particles.appendNew(notgoingin);
2428 
2429  // put the the zeroth one in the list the first on not in the list
2430  particlelist->addParticle(0, 22, Particle::c_Unflavored);
2431 
2432  // use passesCut metavariable to check cut parsing of isInList particle list.
2433  const Manager::Var* nonexistlist = Manager::Instance().getVariable("passesCut(isInList(NONEXISTANTLIST))");
2434  const Manager::Var* existlist = Manager::Instance().getVariable("passesCut(isInList(" + listname + "))");
2435 
2436  EXPECT_B2FATAL(std::get<bool>(nonexistlist->function(inthelist)));
2437  EXPECT_FALSE(std::get<bool>(existlist->function(notinthelist)));
2438  EXPECT_TRUE(std::get<bool>(existlist->function(inthelist)));
2439  }
2440 
2441  TEST_F(MetaVariableTest, sourceObjectIsInList)
2442  {
2443  // datastore things
2444  DataStore::Instance().reset();
2445  DataStore::Instance().setInitializeActive(true);
2446 
2447  // needed to mock up
2448  StoreArray<ECLCluster> clusters;
2449  StoreArray<Particle> particles;
2450  StoreObjPtr<ParticleList> gammalist("testGammaList");
2451 
2452  clusters.registerInDataStore();
2453  particles.registerInDataStore();
2454  DataStore::EStoreFlags flags = DataStore::c_DontWriteOut;
2455  gammalist.registerInDataStore(flags);
2456 
2457  // end datastore things
2458  DataStore::Instance().setInitializeActive(false);
2459 
2460  // of course we have to create the list...
2461  gammalist.create();
2462  gammalist->initialize(22, "testGammaList");
2463 
2464  // mock up two clusters from the ECL let's say they both came from true Klongs
2465  // but one looked a little bit photon-like
2466  auto* cl0 = clusters.appendNew(ECLCluster());
2467  cl0->setEnergy(1.0);
2468  cl0->setHypothesis(ECLCluster::EHypothesisBit::c_nPhotons);
2469  cl0->addHypothesis(ECLCluster::EHypothesisBit::c_neutralHadron);
2470  cl0->setClusterId(0);
2471  auto* cl1 = clusters.appendNew(ECLCluster());
2472  cl1->setEnergy(1.0);
2473  cl1->setHypothesis(ECLCluster::EHypothesisBit::c_neutralHadron);
2474  cl1->setClusterId(1);
2475 
2476  // create particles from the clusters
2477  Particle myphoton(cl0, Const::photon);
2478  Particle iscopiedin(cl0, Const::Klong);
2479  Particle notcopiedin(cl1, Const::Klong);
2480 
2481  // add the particle created from cluster zero to the gamma list
2482  auto* myphoton_ = particles.appendNew(myphoton);
2483  gammalist->addParticle(myphoton_);
2484 
2485  auto* iscopied = particles.appendNew(iscopiedin); // a clone of this guy is now in the gamma list
2486  auto* notcopied = particles.appendNew(notcopiedin);
2487 
2488  // get the variables
2489  const Manager::Var* vnonsense = Manager::Instance().getVariable("sourceObjectIsInList(NONEXISTANTLIST)");
2490  const Manager::Var* vsensible = Manager::Instance().getVariable("sourceObjectIsInList(testGammaList)");
2491 
2492  // -
2493  EXPECT_B2FATAL(std::get<int>(vnonsense->function(iscopied)));
2494  EXPECT_EQ(std::get<int>(vsensible->function(iscopied)), 1);
2495  EXPECT_EQ(std::get<int>(vsensible->function(notcopied)), 0);
2496 
2497  // now mock up some other type particles
2498  Particle composite({0.5, 0.4, 0.5, 0.8}, 512, Particle::c_Unflavored, Particle::c_Composite, 0);
2499  Particle undefined({0.3, 0.3, 0.4, 0.6}, 22, Particle::c_Unflavored, Particle::c_Undefined, 1);
2500  auto* composite_ = particles.appendNew(undefined);
2501  auto* undefined_ = particles.appendNew(composite);
2502  EXPECT_EQ(std::get<int>(vsensible->function(composite_)), -1);
2503  EXPECT_EQ(std::get<int>(vsensible->function(undefined_)), -1);
2504  }
2505 
2506  TEST_F(MetaVariableTest, mcParticleIsInMCList)
2507  {
2508  // datastore things
2509  DataStore::Instance().reset();
2510  DataStore::Instance().setInitializeActive(true);
2511 
2512  // needed to mock up
2513  StoreArray<MCParticle> mcparticles;
2514  StoreArray<Particle> particles;
2515  StoreObjPtr<ParticleList> list("testList");
2516  StoreObjPtr<ParticleList> anotherlist("supplimentaryList");
2517 
2518  mcparticles.registerInDataStore();
2519  particles.registerInDataStore();
2520  particles.registerRelationTo(mcparticles);
2521  DataStore::EStoreFlags flags = DataStore::c_DontWriteOut;
2522  list.registerInDataStore(flags);
2523  anotherlist.registerInDataStore(flags);
2524 
2525  DataStore::Instance().setInitializeActive(false);
2526  // end datastore setup
2527 
2528  list.create();
2529  list->initialize(22, "testList");
2530 
2531  anotherlist.create();
2532  anotherlist->initialize(22, "supplimentaryList");
2533 
2534  // MCParticles
2535  auto* mcphoton = mcparticles.appendNew();
2536  mcphoton->setPDG(Const::photon.getPDGCode());
2537  mcphoton->setStatus(MCParticle::c_PrimaryParticle);
2538 
2539  auto* mcelectron = mcparticles.appendNew();
2540  mcelectron->setPDG(Const::electron.getPDGCode());
2541  mcelectron->setStatus(MCParticle::c_PrimaryParticle);
2542 
2543  auto* mcanotherelectron = mcparticles.appendNew();
2544  mcanotherelectron->setPDG(Const::photon.getPDGCode());
2545  mcanotherelectron->setStatus(MCParticle::c_PrimaryParticle);
2546 
2547  auto* mcyetanotherelectron = mcparticles.appendNew();
2548  mcyetanotherelectron->setPDG(Const::photon.getPDGCode());
2549  mcyetanotherelectron->setStatus(MCParticle::c_PrimaryParticle);
2550 
2551  // particles
2552  auto* photon = particles.appendNew(PxPyPzEVector({ 0.0, -0.4, 0.8, 1.0}), 22);
2553  photon->addRelationTo(mcphoton);
2554  list->addParticle(photon);
2555 
2556  auto* electron = particles.appendNew(PxPyPzEVector({ 0.0, -0.4, 0.8, 1.0}), 22);
2557  electron->addRelationTo(mcelectron);
2558  list->addParticle(electron);
2559 
2560  auto* other = particles.appendNew(PxPyPzEVector({ 0.0, -0.4, 0.8, 1.0}), 22);
2561  other->addRelationTo(mcanotherelectron);
2562 
2563  auto* yetanotherelectron = particles.appendNew(PxPyPzEVector({ 0.0, -0.4, 0.8, 1.0}), 22);
2564  yetanotherelectron->addRelationTo(mcyetanotherelectron);
2565  anotherlist->addParticle(yetanotherelectron);
2566  // not in the list
2567 
2568  // get the variable
2569  const Manager::Var* vnonsense = Manager::Instance().getVariable("mcParticleIsInMCList(NONEXISTANTLIST)");
2570  const Manager::Var* vsensible = Manager::Instance().getVariable("mcParticleIsInMCList(testList)");
2571 
2572  // -
2573  EXPECT_B2FATAL(std::get<bool>(vnonsense->function(photon)));
2574  EXPECT_TRUE(std::get<bool>(vsensible->function(photon)));
2575  EXPECT_TRUE(std::get<bool>(vsensible->function(electron)));
2576  EXPECT_FALSE(std::get<bool>(vsensible->function(other)));
2577  EXPECT_FALSE(std::get<bool>(vsensible->function(yetanotherelectron)));
2578 
2579  // now mock up some other type particles
2580  Particle composite({0.5, 0.4, 0.5, 0.8}, 512, Particle::c_Unflavored, Particle::c_Composite, 0);
2581  Particle undefined({0.3, 0.3, 0.4, 0.6}, 22, Particle::c_Unflavored, Particle::c_Undefined, 1);
2582  auto* composite_ = particles.appendNew(undefined);
2583  auto* undefined_ = particles.appendNew(composite);
2584  EXPECT_FALSE(std::get<bool>(vsensible->function(composite_)));
2585  EXPECT_FALSE(std::get<bool>(vsensible->function(undefined_)));
2586  }
2587 
2588  TEST_F(MetaVariableTest, mostB2BAndClosestParticles)
2589  {
2590  /* Mock up an event with a "photon" and an "electron" which are nearly back to
2591  * back, and second "photon" which is close-ish to the "electron".
2592  *
2593  * Other test of non-existent / empty lists and variables also included.
2594  */
2595 
2596  // Connect gearbox for CMS variables
2597  Gearbox& gearbox = Gearbox::getInstance();
2598  gearbox.setBackends({std::string("file:")});
2599  gearbox.close();
2600  gearbox.open("geometry/Belle2.xml", false);
2601 
2602  // we need the particles StoreArray
2603  StoreArray<Particle> particles;
2604  DataStore::EStoreFlags flags = DataStore::c_DontWriteOut;
2605 
2606  // create a photon list for testing
2607  StoreObjPtr<ParticleList> gammalist("testGammaList");
2608  StoreObjPtr<ParticleList> emptylist("testEmptyList");
2609  DataStore::Instance().setInitializeActive(true);
2610  gammalist.registerInDataStore(flags);
2611  emptylist.registerInDataStore(flags);
2612  DataStore::Instance().setInitializeActive(false);
2613  gammalist.create();
2614  gammalist->initialize(22, "testGammaList");
2615  emptylist.create();
2616  emptylist->initialize(22, "testEmptyList");
2617 
2618  // create some photons in an stdvector
2619  std::vector<Particle> gammavector = {
2620  Particle({ -1.0, -1.0, 0.8, 1.7}, // this should be the most b2b to our reference particle
2621  22, Particle::c_Unflavored, Particle::c_Undefined, 0),
2622  Particle({0.2, 0.7, 0.9, 3.4}, // should be the closest
2623  22, Particle::c_Unflavored, Particle::c_Undefined, 1),
2624  };
2625  // put the photons in the StoreArray
2626  for (const auto& g : gammavector)
2627  particles.appendNew(g);
2628 
2629  // put the photons in the test list
2630  for (size_t i = 0; i < gammavector.size(); i++)
2631  gammalist->addParticle(i, 22, Particle::c_Unflavored);
2632 
2633  // add the reference particle (electron) to the StoreArray
2634  const auto* electron = particles.appendNew(
2635  Particle({1.0, 1.0, 0.5, 1.6}, // somewhere in the +ve quarter of the detector
2636  11, Particle::c_Unflavored, Particle::c_Undefined, 2) // needs to be incremented if we add to gamma vector
2637  );
2638 
2639  {
2640  EXPECT_B2FATAL(Manager::Instance().getVariable("angleToClosestInList"));
2641  EXPECT_B2FATAL(Manager::Instance().getVariable("angleToClosestInList(A, B)"));
2642 
2643  const auto* nonexistent = Manager::Instance().getVariable("angleToClosestInList(NONEXISTANTLIST)");
2644  EXPECT_B2FATAL(std::get<double>(nonexistent->function(electron)));
2645 
2646  const auto* empty = Manager::Instance().getVariable("angleToClosestInList(testEmptyList)");
2647  EXPECT_TRUE(std::isnan(std::get<double>(empty->function(electron))));
2648 
2649  const auto* closest = Manager::Instance().getVariable("angleToClosestInList(testGammaList)");
2650  EXPECT_FLOAT_EQ(std::get<double>(closest->function(electron)), 0.68014491);
2651 
2652  const auto* closestCMS = Manager::Instance().getVariable("useCMSFrame(angleToClosestInList(testGammaList))");
2653  EXPECT_FLOAT_EQ(std::get<double>(closestCMS->function(electron)), 0.67901474);
2654  }
2655 
2656  {
2657  EXPECT_B2FATAL(Manager::Instance().getVariable("closestInList"));
2658  EXPECT_B2FATAL(Manager::Instance().getVariable("closestInList(A, B, C)"));
2659 
2660  const auto* nonexistent = Manager::Instance().getVariable("closestInList(NONEXISTANTLIST, E)");
2661  EXPECT_B2FATAL(std::get<double>(nonexistent->function(electron)));
2662 
2663  const auto* empty = Manager::Instance().getVariable("closestInList(testEmptyList, E)");
2664  EXPECT_TRUE(std::isnan(std::get<double>(empty->function(electron))));
2665 
2666  const auto* closest = Manager::Instance().getVariable("closestInList(testGammaList, E)");
2667  EXPECT_FLOAT_EQ(std::get<double>(closest->function(electron)), 3.4);
2668 
2669  const auto* closestCMS = Manager::Instance().getVariable("useCMSFrame(closestInList(testGammaList, E))");
2670  EXPECT_FLOAT_EQ(std::get<double>(closestCMS->function(electron)), 3.2732551); // the energy gets smeared because of boost
2671 
2672  const auto* closestCMSLabE = Manager::Instance().getVariable("useCMSFrame(closestInList(testGammaList, useLabFrame(E)))");
2673  EXPECT_FLOAT_EQ(std::get<double>(closestCMSLabE->function(electron)), 3.4); // aaand should be back to the lab frame value
2674  }
2675 
2676  {
2677  EXPECT_B2FATAL(Manager::Instance().getVariable("angleToMostB2BInList"));
2678  EXPECT_B2FATAL(Manager::Instance().getVariable("angleToMostB2BInList(A, B)"));
2679 
2680  const auto* nonexistent = Manager::Instance().getVariable("angleToMostB2BInList(NONEXISTANTLIST)");
2681  EXPECT_B2FATAL(std::get<double>(nonexistent->function(electron)));
2682 
2683  const auto* empty = Manager::Instance().getVariable("angleToMostB2BInList(testEmptyList)");
2684  EXPECT_TRUE(std::isnan(std::get<double>(empty->function(electron))));
2685 
2686  const auto* mostB2B = Manager::Instance().getVariable("angleToMostB2BInList(testGammaList)");
2687  EXPECT_FLOAT_EQ(std::get<double>(mostB2B->function(electron)), 2.2869499);
2688 
2689  const auto* mostB2BCMS = Manager::Instance().getVariable("useCMSFrame(angleToMostB2BInList(testGammaList))");
2690  EXPECT_FLOAT_EQ(std::get<double>(mostB2BCMS->function(electron)), 2.8312778);
2691  }
2692 
2693  {
2694  EXPECT_B2FATAL(Manager::Instance().getVariable("mostB2BInList"));
2695  EXPECT_B2FATAL(Manager::Instance().getVariable("mostB2BInList(A, B, C)"));
2696 
2697  const auto* nonexistent = Manager::Instance().getVariable("mostB2BInList(NONEXISTANTLIST, E)");
2698  EXPECT_B2FATAL(std::get<double>(nonexistent->function(electron)));
2699 
2700  const auto* empty = Manager::Instance().getVariable("mostB2BInList(testEmptyList, E)");
2701  EXPECT_TRUE(std::isnan(std::get<double>(empty->function(electron))));
2702 
2703  const auto* mostB2B = Manager::Instance().getVariable("mostB2BInList(testGammaList, E)");
2704  EXPECT_FLOAT_EQ(std::get<double>(mostB2B->function(electron)), 1.7);
2705 
2706  const auto* mostB2BCMS = Manager::Instance().getVariable("useCMSFrame(mostB2BInList(testGammaList, E))");
2707  EXPECT_FLOAT_EQ(std::get<double>(mostB2BCMS->function(electron)), 1.5848758); // the energy gets smeared because of boost
2708 
2709  const auto* mostB2BCMSLabE = Manager::Instance().getVariable("useCMSFrame(mostB2BInList(testGammaList, useLabFrame(E)))");
2710  EXPECT_FLOAT_EQ(std::get<double>(mostB2BCMSLabE->function(electron)), 1.7); // aaand should be back to the lab frame value
2711  }
2712  }
2713 
2714  TEST_F(MetaVariableTest, totalEnergyOfParticlesInList)
2715  {
2716  // we need the particles StoreArray
2717  StoreArray<Particle> particles;
2718  DataStore::EStoreFlags flags = DataStore::c_DontWriteOut;
2719 
2720  // create a photon list for testing
2721  StoreObjPtr<ParticleList> gammalist("testGammaList");
2722  DataStore::Instance().setInitializeActive(true);
2723  gammalist.registerInDataStore(flags);
2724  DataStore::Instance().setInitializeActive(false);
2725  gammalist.create();
2726  gammalist->initialize(22, "testGammaList");
2727 
2728  // create some photons in an stdvector
2729  std::vector<Particle> gammavector = {
2730  Particle({0.5, 0.4, 0.4, 0.8}, 22, Particle::c_Unflavored, Particle::c_Undefined, 0),
2731  Particle({0.5, 0.2, 0.7, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 1),
2732  Particle({0.4, 0.2, 0.7, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 2),
2733  Particle({0.5, 0.4, 0.8, 1.1}, 22, Particle::c_Unflavored, Particle::c_Undefined, 3),
2734  Particle({0.3, 0.3, 0.4, 0.6}, 22, Particle::c_Unflavored, Particle::c_Undefined, 4)
2735  };
2736 
2737  // put the photons in the StoreArray
2738  for (const auto& g : gammavector)
2739  particles.appendNew(g);
2740 
2741  // put the photons in the test list
2742  for (size_t i = 0; i < gammavector.size(); i++)
2743  gammalist->addParticle(i, 22, Particle::c_Unflavored);
2744 
2745  // get their total energy
2746  const Manager::Var* vnonsense = Manager::Instance().getVariable(
2747  "totalEnergyOfParticlesInList(NONEXISTANTLIST)");
2748  const Manager::Var* vsensible = Manager::Instance().getVariable(
2749  "totalEnergyOfParticlesInList(testGammaList)");
2750 
2751  // -
2752  EXPECT_B2FATAL(std::get<double>(vnonsense->function(nullptr)));
2753  EXPECT_FLOAT_EQ(std::get<double>(vsensible->function(nullptr)), 4.3);
2754  }
2755  TEST_F(MetaVariableTest, totalPxOfParticlesInList)
2756  {
2757  // we need the particles StoreArray
2758  StoreArray<Particle> particles;
2759  DataStore::EStoreFlags flags = DataStore::c_DontWriteOut;
2760 
2761  // create a photon list for testing
2762  StoreObjPtr<ParticleList> gammalist("testGammaList");
2763  DataStore::Instance().setInitializeActive(true);
2764  gammalist.registerInDataStore(flags);
2765  DataStore::Instance().setInitializeActive(false);
2766  gammalist.create();
2767  gammalist->initialize(22, "testGammaList");
2768 
2769  // create some photons in an stdvector
2770  std::vector<Particle> gammavector = {
2771  Particle({0.5, 0.4, 0.5, 0.8}, 22, Particle::c_Unflavored, Particle::c_Undefined, 0),
2772  Particle({0.5, 0.2, 0.7, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 1),
2773  Particle({0.4, 0.2, 0.7, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 2),
2774  Particle({0.5, 0.4, 0.8, 1.1}, 22, Particle::c_Unflavored, Particle::c_Undefined, 3),
2775  Particle({0.3, 0.3, 0.4, 0.6}, 22, Particle::c_Unflavored, Particle::c_Undefined, 4)
2776  };
2777 
2778  // put the photons in the StoreArray
2779  for (const auto& g : gammavector)
2780  particles.appendNew(g);
2781 
2782  // put the photons in the test list
2783  for (size_t i = 0; i < gammavector.size(); i++)
2784  gammalist->addParticle(i, 22, Particle::c_Unflavored);
2785 
2786  // get their total energy
2787  const Manager::Var* vnonsense = Manager::Instance().getVariable(
2788  "totalPxOfParticlesInList(NONEXISTANTLIST)");
2789  const Manager::Var* vsensible = Manager::Instance().getVariable(
2790  "totalPxOfParticlesInList(testGammaList)");
2791 
2792  // -
2793  EXPECT_B2FATAL(std::get<double>(vnonsense->function(nullptr)));
2794  EXPECT_FLOAT_EQ(std::get<double>(vsensible->function(nullptr)), 2.2);
2795  }
2796  TEST_F(MetaVariableTest, totalPyOfParticlesInList)
2797  {
2798  // we need the particles StoreArray
2799  StoreArray<Particle> particles;
2800  DataStore::EStoreFlags flags = DataStore::c_DontWriteOut;
2801 
2802  // create a photon list for testing
2803  StoreObjPtr<ParticleList> gammalist("testGammaList");
2804  DataStore::Instance().setInitializeActive(true);
2805  gammalist.registerInDataStore(flags);
2806  DataStore::Instance().setInitializeActive(false);
2807  gammalist.create();
2808  gammalist->initialize(22, "testGammaList");
2809 
2810  // create some photons in an stdvector
2811  std::vector<Particle> gammavector = {
2812  Particle({0.5, 0.4, 0.5, 0.8}, 22, Particle::c_Unflavored, Particle::c_Undefined, 0),
2813  Particle({0.5, 0.2, 0.7, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 1),
2814  Particle({0.4, 0.2, 0.7, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 2),
2815  Particle({0.5, 0.4, 0.8, 1.1}, 22, Particle::c_Unflavored, Particle::c_Undefined, 3),
2816  Particle({0.3, 0.3, 0.4, 0.6}, 22, Particle::c_Unflavored, Particle::c_Undefined, 4)
2817  };
2818 
2819  // put the photons in the StoreArray
2820  for (const auto& g : gammavector)
2821  particles.appendNew(g);
2822 
2823  // put the photons in the test list
2824  for (size_t i = 0; i < gammavector.size(); i++)
2825  gammalist->addParticle(i, 22, Particle::c_Unflavored);
2826 
2827  // get their total energy
2828  const Manager::Var* vnonsense = Manager::Instance().getVariable(
2829  "totalPyOfParticlesInList(NONEXISTANTLIST)");
2830  const Manager::Var* vsensible = Manager::Instance().getVariable(
2831  "totalPyOfParticlesInList(testGammaList)");
2832 
2833  // -
2834  EXPECT_B2FATAL(std::get<double>(vnonsense->function(nullptr)));
2835  EXPECT_FLOAT_EQ(std::get<double>(vsensible->function(nullptr)), 1.5);
2836  }
2837  TEST_F(MetaVariableTest, totalPzOfParticlesInList)
2838  {
2839  // we need the particles StoreArray
2840  StoreArray<Particle> particles;
2841  DataStore::EStoreFlags flags = DataStore::c_DontWriteOut;
2842 
2843  // create a photon list for testing
2844  StoreObjPtr<ParticleList> gammalist("testGammaList");
2845  DataStore::Instance().setInitializeActive(true);
2846  gammalist.registerInDataStore(flags);
2847  DataStore::Instance().setInitializeActive(false);
2848  gammalist.create();
2849  gammalist->initialize(22, "testGammaList");
2850 
2851  // create some photons in an stdvector
2852  std::vector<Particle> gammavector = {
2853  Particle({0.5, 0.4, 0.5, 0.8}, 22, Particle::c_Unflavored, Particle::c_Undefined, 0),
2854  Particle({0.5, 0.2, 0.7, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 1),
2855  Particle({0.4, 0.2, 0.7, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 2),
2856  Particle({0.5, 0.4, 0.8, 1.1}, 22, Particle::c_Unflavored, Particle::c_Undefined, 3),
2857  Particle({0.3, 0.3, 0.4, 0.6}, 22, Particle::c_Unflavored, Particle::c_Undefined, 4)
2858  };
2859 
2860  // put the photons in the StoreArray
2861  for (const auto& g : gammavector)
2862  particles.appendNew(g);
2863 
2864  // put the photons in the test list
2865  for (size_t i = 0; i < gammavector.size(); i++)
2866  gammalist->addParticle(i, 22, Particle::c_Unflavored);
2867 
2868  // get their total energy
2869  const Manager::Var* vnonsense = Manager::Instance().getVariable(
2870  "totalPzOfParticlesInList(NONEXISTANTLIST)");
2871  const Manager::Var* vsensible = Manager::Instance().getVariable(
2872  "totalPzOfParticlesInList(testGammaList)");
2873 
2874  // -
2875  EXPECT_B2FATAL(std::get<double>(vnonsense->function(nullptr)));
2876  EXPECT_FLOAT_EQ(std::get<double>(vsensible->function(nullptr)), 3.1);
2877  }
2878  TEST_F(MetaVariableTest, maxPtInList)
2879  {
2880  // we need the particles StoreArray
2881  StoreArray<Particle> particles;
2882  DataStore::EStoreFlags flags = DataStore::c_DontWriteOut;
2883 
2884  // create a photon list for testing
2885  StoreObjPtr<ParticleList> gammalist("testGammaList");
2886  DataStore::Instance().setInitializeActive(true);
2887  gammalist.registerInDataStore(flags);
2888  DataStore::Instance().setInitializeActive(false);
2889  gammalist.create();
2890  gammalist->initialize(22, "testGammaList");
2891 
2892  // create some photons in an stdvector
2893  std::vector<Particle> gammavector = {
2894  Particle({0.5, 0.4, 0.5, 0.8}, 22, Particle::c_Unflavored, Particle::c_Undefined, 0),
2895  Particle({0.5, 0.2, 0.7, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 1),
2896  Particle({0.4, 0.2, 0.7, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 2),
2897  Particle({0.5, 0.4, 0.8, 1.1}, 22, Particle::c_Unflavored, Particle::c_Undefined, 3),
2898  Particle({0.3, 0.3, 0.4, 0.6}, 22, Particle::c_Unflavored, Particle::c_Undefined, 4)
2899  };
2900 
2901  // put the photons in the StoreArray
2902  for (const auto& g : gammavector)
2903  particles.appendNew(g);
2904 
2905  // put the photons in the test list
2906  for (size_t i = 0; i < gammavector.size(); i++)
2907  gammalist->addParticle(i, 22, Particle::c_Unflavored);
2908 
2909  // get their total energy
2910  const Manager::Var* vnonsense = Manager::Instance().getVariable(
2911  "maxPtInList(NONEXISTANTLIST)");
2912  const Manager::Var* vsensible = Manager::Instance().getVariable(
2913  "maxPtInList(testGammaList)");
2914 
2915  // -
2916  EXPECT_B2FATAL(std::get<double>(vnonsense->function(nullptr)));
2917  EXPECT_FLOAT_EQ(std::get<double>(vsensible->function(nullptr)), sqrt(0.5 * 0.5 + 0.4 * 0.4));
2918  }
2919 
2920 
2921  TEST_F(MetaVariableTest, numberOfNonOverlappingParticles)
2922  {
2923  StoreArray<Particle> particles;
2924  DataStore::EStoreFlags flags = DataStore::c_DontWriteOut;
2925 
2926  StoreObjPtr<ParticleList> outputList("pList1");
2927  DataStore::Instance().setInitializeActive(true);
2928  outputList.registerInDataStore(flags);
2929  DataStore::Instance().setInitializeActive(false);
2930  outputList.create();
2931  outputList->initialize(22, "pList1");
2932 
2933  auto* p1 = particles.appendNew(Particle({0.5, 0.4, 0.5, 0.8}, 22, Particle::c_Unflavored, Particle::c_Undefined, 2));
2934  auto* p2 = particles.appendNew(Particle({0.5, 0.2, 0.7, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 3));
2935  auto* p3 = particles.appendNew(Particle({0.5, 0.2, 0.7, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 4));
2936 
2937  outputList->addParticle(0, 22, Particle::c_Unflavored);
2938  outputList->addParticle(1, 22, Particle::c_Unflavored);
2939 
2940  const Manager::Var* var = Manager::Instance().getVariable("numberOfNonOverlappingParticles(pList1)");
2941  ASSERT_NE(var, nullptr);
2942  EXPECT_EQ(std::get<int>(var->function(p1)), 1);
2943  EXPECT_EQ(std::get<int>(var->function(p2)), 1);
2944  EXPECT_EQ(std::get<int>(var->function(p3)), 2);
2945 
2946  }
2947 
2948  TEST_F(MetaVariableTest, veto)
2949  {
2950  StoreArray<Particle> particles;
2951  DataStore::EStoreFlags flags = DataStore::c_DontWriteOut;
2952 
2953  const Particle* p = particles.appendNew(Particle({0.8, 0.8, 1.131370849898476039041351, 1.6}, 22,
2954  Particle::c_Unflavored, Particle::c_Undefined, 1));
2955 
2956  StoreObjPtr<ParticleList> outputList("pList1");
2957  DataStore::Instance().setInitializeActive(true);
2958  outputList.registerInDataStore(flags);
2959  DataStore::Instance().setInitializeActive(false);
2960  outputList.create();
2961  outputList->initialize(22, "pList1");
2962 
2963  particles.appendNew(Particle({0.5, 0.4953406774856531014212777, 0.5609256753154148484773173, 0.9}, 22,
2964  Particle::c_Unflavored, Particle::c_Undefined, 2)); //m=0.135
2965  particles.appendNew(Particle({0.5, 0.2, 0.72111, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 3)); //m=0.3582
2966  particles.appendNew(Particle({0.4, 0.2, 0.78102, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 4)); //m=0.3908
2967  particles.appendNew(Particle({0.5, 0.4, 0.89443, 1.1}, 22, Particle::c_Unflavored, Particle::c_Undefined, 5)); //m=0.2369
2968  particles.appendNew(Particle({0.3, 0.3, 0.42426, 0.6}, 22, Particle::c_Unflavored, Particle::c_Undefined, 6)); //m=0.0036
2969 
2970  outputList->addParticle(1, 22, Particle::c_Unflavored);
2971  outputList->addParticle(2, 22, Particle::c_Unflavored);
2972  outputList->addParticle(3, 22, Particle::c_Unflavored);
2973  outputList->addParticle(4, 22, Particle::c_Unflavored);
2974  outputList->addParticle(5, 22, Particle::c_Unflavored);
2975 
2976  StoreObjPtr<ParticleList> outputList2("pList2");
2977  DataStore::Instance().setInitializeActive(true);
2978  outputList2.registerInDataStore(flags);
2979  DataStore::Instance().setInitializeActive(false);
2980  outputList2.create();
2981  outputList2->initialize(22, "pList2");
2982 
2983  particles.appendNew(Particle({0.5, -0.4, 0.63246, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 7)); //m=1.1353
2984  particles.appendNew(Particle({0.5, 0.2, 0.72111, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 8)); //m=0.3582
2985  particles.appendNew(Particle({0.4, 0.2, 0.78102, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 9)); //m=0.3908
2986  particles.appendNew(Particle({0.5, 0.4, 0.89443, 1.1}, 22, Particle::c_Unflavored, Particle::c_Undefined, 10)); //m=0.2369
2987  particles.appendNew(Particle({0.3, 0.3, 0.42426, 0.6}, 22, Particle::c_Unflavored, Particle::c_Undefined, 11)); //m=0.0036
2988 
2989  outputList2->addParticle(6, 22, Particle::c_Unflavored);
2990  outputList2->addParticle(7, 22, Particle::c_Unflavored);
2991  outputList2->addParticle(8, 22, Particle::c_Unflavored);
2992  outputList2->addParticle(9, 22, Particle::c_Unflavored);
2993  outputList2->addParticle(10, 22, Particle::c_Unflavored);
2994 
2995  const Manager::Var* var = Manager::Instance().getVariable("veto(pList1, 0.130 < M < 0.140, 22)");
2996  ASSERT_NE(var, nullptr);
2997  EXPECT_TRUE(std::get<bool>(var->function(p)));
2998 
2999  var = Manager::Instance().getVariable("veto(pList2, 0.130 < M < 0.140, 22)");
3000  ASSERT_NE(var, nullptr);
3001  EXPECT_FALSE(std::get<bool>(var->function(p)));
3002 
3003  }
3004 
3005  TEST_F(MetaVariableTest, averageValueInList)
3006  {
3007  // we need the particles StoreArray
3008  StoreArray<Particle> particles;
3009  DataStore::EStoreFlags flags = DataStore::c_DontWriteOut;
3010 
3011  // create a photon list for testing
3012  StoreObjPtr<ParticleList> gammalist("testGammaList");
3013  DataStore::Instance().setInitializeActive(true);
3014  gammalist.registerInDataStore(flags);
3015  DataStore::Instance().setInitializeActive(false);
3016  gammalist.create();
3017  gammalist->initialize(22, "testGammaList");
3018 
3019  // create some photons in an stdvector
3020  std::vector<Particle> gammavector = {
3021  Particle({0.5, 0.4, 0.4, 0.8}, 22, Particle::c_Unflavored, Particle::c_Undefined, 0),
3022  Particle({0.5, 0.2, 0.7, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 1),
3023  Particle({0.4, 0.2, 0.7, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 2),
3024  Particle({0.5, 0.4, 0.8, 1.1}, 22, Particle::c_Unflavored, Particle::c_Undefined, 3),
3025  Particle({0.3, 0.3, 0.4, 0.6}, 22, Particle::c_Unflavored, Particle::c_Undefined, 4)
3026  };
3027 
3028  // put the photons in the StoreArray
3029  for (const auto& g : gammavector)
3030  particles.appendNew(g);
3031 
3032  // put the photons in the test list
3033  for (size_t i = 0; i < gammavector.size(); i++)
3034  gammalist->addParticle(i, 22, Particle::c_Unflavored);
3035 
3036  // get the average px, py, pz, E of the gammas in the list
3037  const Manager::Var* vmeanpx = Manager::Instance().getVariable(
3038  "averageValueInList(testGammaList, px)");
3039  const Manager::Var* vmeanpy = Manager::Instance().getVariable(
3040  "averageValueInList(testGammaList, py)");
3041  const Manager::Var* vmeanpz = Manager::Instance().getVariable(
3042  "averageValueInList(testGammaList, pz)");
3043  const Manager::Var* vmeanE = Manager::Instance().getVariable(
3044  "averageValueInList(testGammaList, E)");
3045 
3046  EXPECT_FLOAT_EQ(std::get<double>(vmeanpx->function(nullptr)), 0.44);
3047  EXPECT_FLOAT_EQ(std::get<double>(vmeanpy->function(nullptr)), 0.3);
3048  EXPECT_FLOAT_EQ(std::get<double>(vmeanpz->function(nullptr)), 0.6);
3049  EXPECT_FLOAT_EQ(std::get<double>(vmeanE->function(nullptr)), 0.86);
3050 
3051  // wrong number of arguments (no variable provided)
3052  EXPECT_B2FATAL(Manager::Instance().getVariable("averageValueInList(testGammaList)"));
3053 
3054  // non-existing variable
3055  EXPECT_B2FATAL(Manager::Instance().getVariable("averageValueInList(testGammaList, NONEXISTANTVARIABLE)"));
3056 
3057  // non-existing list
3058  const Manager::Var* vnolist = Manager::Instance().getVariable(
3059  "averageValueInList(NONEXISTANTLIST, px)");
3060 
3061  EXPECT_B2FATAL(std::get<double>(vnolist->function(nullptr)));
3062  }
3063 
3064  TEST_F(MetaVariableTest, medianValueInList)
3065  {
3066  // we need the particles StoreArray
3067  StoreArray<Particle> particles;
3068  DataStore::EStoreFlags flags = DataStore::c_DontWriteOut;
3069 
3070  // create two photon lists for testing (one with odd and one with even number of particles)
3071  StoreObjPtr<ParticleList> oddgammalist("oddGammaList");
3072  DataStore::Instance().setInitializeActive(true);
3073  oddgammalist.registerInDataStore(flags);
3074  DataStore::Instance().setInitializeActive(false);
3075  oddgammalist.create();
3076  oddgammalist->initialize(22, "oddGammaList");
3077  StoreObjPtr<ParticleList> evengammalist("evenGammaList");
3078  DataStore::Instance().setInitializeActive(true);
3079  evengammalist.registerInDataStore(flags);
3080  DataStore::Instance().setInitializeActive(false);
3081  evengammalist.create();
3082  evengammalist->initialize(22, "evenGammaList");
3083 
3084  // create some photons in an stdvector
3085  std::vector<Particle> gammavector = {
3086  Particle({0.5, 0.4, 0.4, 0.8}, 22, Particle::c_Unflavored, Particle::c_Undefined, 0),
3087  Particle({0.5, 0.2, 0.7, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 1),
3088  Particle({0.4, 0.2, 0.7, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 2),
3089  Particle({0.5, 0.4, 0.8, 1.1}, 22, Particle::c_Unflavored, Particle::c_Undefined, 3),
3090  Particle({0.3, 0.3, 0.4, 0.6}, 22, Particle::c_Unflavored, Particle::c_Undefined, 4)
3091  };
3092 
3093  // put the photons in the StoreArray
3094  for (const auto& g : gammavector)
3095  particles.appendNew(g);
3096 
3097  // put the photons in the test lists
3098  oddgammalist->addParticle(0, 22, Particle::c_Unflavored);
3099  for (size_t i = 1; i < gammavector.size(); i++) {
3100  oddgammalist->addParticle(i, 22, Particle::c_Unflavored);
3101  evengammalist->addParticle(i, 22, Particle::c_Unflavored);
3102  }
3103 
3104  // get the median px, py, pz, E of the gammas in the list with odd number of particles
3105  const Manager::Var* voddmedianpx = Manager::Instance().getVariable(
3106  "medianValueInList(oddGammaList, px)");
3107  const Manager::Var* voddmedianpy = Manager::Instance().getVariable(
3108  "medianValueInList(oddGammaList, py)");
3109  const Manager::Var* voddmedianpz = Manager::Instance().getVariable(
3110  "medianValueInList(oddGammaList, pz)");
3111  const Manager::Var* voddmedianE = Manager::Instance().getVariable(
3112  "medianValueInList(oddGammaList, E)");
3113 
3114  EXPECT_FLOAT_EQ(std::get<double>(voddmedianpx->function(nullptr)), 0.5);
3115  EXPECT_FLOAT_EQ(std::get<double>(voddmedianpy->function(nullptr)), 0.3);
3116  EXPECT_FLOAT_EQ(std::get<double>(voddmedianpz->function(nullptr)), 0.7);
3117  EXPECT_FLOAT_EQ(std::get<double>(voddmedianE->function(nullptr)), 0.9);
3118 
3119  // get the median px, py, pz, E of the gammas in the list with odd number of particles
3120  const Manager::Var* vevenmedianpx = Manager::Instance().getVariable(
3121  "medianValueInList(evenGammaList, px)");
3122  const Manager::Var* vevenmedianpy = Manager::Instance().getVariable(
3123  "medianValueInList(evenGammaList, py)");
3124  const Manager::Var* vevenmedianpz = Manager::Instance().getVariable(
3125  "medianValueInList(evenGammaList, pz)");
3126  const Manager::Var* vevenmedianE = Manager::Instance().getVariable(
3127  "medianValueInList(evenGammaList, E)");
3128 
3129  EXPECT_FLOAT_EQ(std::get<double>(vevenmedianpx->function(nullptr)), 0.45);
3130  EXPECT_FLOAT_EQ(std::get<double>(vevenmedianpy->function(nullptr)), 0.25);
3131  EXPECT_FLOAT_EQ(std::get<double>(vevenmedianpz->function(nullptr)), 0.7);
3132  EXPECT_FLOAT_EQ(std::get<double>(vevenmedianE->function(nullptr)), 0.9);
3133 
3134  // wrong number of arguments (no variable provided)
3135  EXPECT_B2FATAL(Manager::Instance().getVariable("medianValueInList(oddGammaList)"));
3136 
3137  // non-existing variable
3138  EXPECT_B2FATAL(Manager::Instance().getVariable("medianValueInList(oddGammaList, NONEXISTANTVARIABLE)"));
3139 
3140  // non-existing list
3141  const Manager::Var* vnolist = Manager::Instance().getVariable(
3142  "medianValueInList(NONEXISTANTLIST, px)");
3143 
3144  EXPECT_B2FATAL(std::get<double>(vnolist->function(nullptr)));
3145  }
3146 
3147  TEST_F(MetaVariableTest, pValueCombination)
3148  {
3149  PxPyPzEVector momentum;
3150  StoreArray<Particle> particles;
3151  std::vector<int> daughterIndices;
3152  Particle KS(PxPyPzEVector(1.164, 1.55200, 0, 2), 310, Particle::c_Unflavored, Particle::c_Composite, 0);
3153  KS.setPValue(0.1);
3154  momentum += KS.get4Vector();
3155  Particle* newDaughters = particles.appendNew(KS);
3156  daughterIndices.push_back(newDaughters->getArrayIndex());
3157  Particle Jpsi(PxPyPzEVector(-1, 1, 1, 3.548), 443, Particle::c_Unflavored, Particle::c_Composite, 1);
3158  Jpsi.setPValue(0.9);
3159  momentum += Jpsi.get4Vector();
3160  newDaughters = particles.appendNew(Jpsi);
3161  daughterIndices.push_back(newDaughters->getArrayIndex());
3162  Particle* B = particles.appendNew(momentum, 521, Particle::c_Flavored, daughterIndices);
3163  B->setPValue(0.5);
3164 
3165  const Manager::Var* singlePvalue = Manager::Instance().getVariable("pValueCombination(chiProb)");
3166  ASSERT_NE(singlePvalue, nullptr);
3167  EXPECT_FLOAT_EQ(std::get<double>(singlePvalue->function(B)), 0.5);
3168 
3169  const Manager::Var* twoPvalues = Manager::Instance().getVariable("pValueCombination(chiProb, daughter(0, chiProb))");
3170  ASSERT_NE(twoPvalues, nullptr);
3171  EXPECT_FLOAT_EQ(std::get<double>(twoPvalues->function(B)), 0.05 * (1 - log(0.05)));
3172 
3173  const Manager::Var* threePvalues =
3174  Manager::Instance().getVariable("pValueCombination(chiProb, daughter(0, chiProb), daughter(1, chiProb))");
3175  ASSERT_NE(threePvalues, nullptr);
3176  EXPECT_FLOAT_EQ(std::get<double>(threePvalues->function(B)), 0.045 * (1 - log(0.045) + 0.5 * log(0.045) * log(0.045)));
3177 
3178  // wrong number of arguments
3179  EXPECT_B2FATAL(Manager::Instance().getVariable("pValueCombination()"));
3180 
3181  // non-existing variable
3182  EXPECT_B2FATAL(Manager::Instance().getVariable("pValueCombination(chiProb, NONEXISTANTVARIABLE)"));
3183  }
3184 
3185 
3186  TEST_F(MetaVariableTest, daughterCombinationOneGeneration)
3187  {
3188  const int nDaughters = 5;
3189  PxPyPzEVector momentum(0, 0, 0, 0);
3190  StoreArray<Particle> particles;
3191  std::vector<int> daughterIndices;
3192  std::vector<PxPyPzEVector> daughterMomenta;
3193 
3194  for (int i = 0; i < nDaughters; i++) {
3195  PxPyPzEVector mom(1, i * 0.5, 1, i * 1.0 + 2.0);
3196  Particle d(mom, (i % 2) ? 111 : 113);
3197  Particle* newDaughters = particles.appendNew(d);
3198  daughterIndices.push_back(newDaughters->getArrayIndex());
3199  daughterMomenta.push_back(mom);
3200  momentum = momentum + mom;
3201  }
3202  const Particle* p = particles.appendNew(momentum, 411, Particle::c_Flavored, daughterIndices);
3203 
3204  // Test the invariant mass of several combinations
3205  const Manager::Var* var = Manager::Instance().getVariable("daughterCombination(M, 0,1,2)");
3206  double M_test = (daughterMomenta[0] + daughterMomenta[1] + daughterMomenta[2]).mag();
3207  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), M_test);
3208 
3209  var = Manager::Instance().getVariable("daughterCombination(M, 0,4)");
3210  M_test = (daughterMomenta[0] + daughterMomenta[4]).mag();
3211  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), M_test);
3212 
3213 
3214  // Try with a non-lorentz invariant quantity
3215  var = Manager::Instance().getVariable("daughterCombination(p, 1, 0, 4)");
3216  double p_test = (daughterMomenta[0] + daughterMomenta[1] + daughterMomenta[4]).P();
3217  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), p_test);
3218 
3219 
3220  // errors and bad stuff
3221  EXPECT_B2FATAL(Manager::Instance().getVariable("daughterCombination(aVeryNonExistingVariableSillyName, 1, 0, 4)"));
3222 
3223  var = Manager::Instance().getVariable("daughterCombination(M, 1, 0, 100)");
3224  EXPECT_B2WARNING(std::get<double>(var->function(p)));
3225  EXPECT_TRUE(std::isnan(std::get<double>(var->function(p))));
3226 
3227 
3228  var = Manager::Instance().getVariable("daughterCombination(M, 1, -1)");
3229  EXPECT_B2WARNING(std::get<double>(var->function(p)));
3230  EXPECT_TRUE(std::isnan(std::get<double>(var->function(p))));
3231 
3232 
3233  var = Manager::Instance().getVariable("daughterCombination(M, 1, 0:1:0:0:1)");
3234  EXPECT_B2WARNING(std::get<double>(var->function(p)));
3235  EXPECT_TRUE(std::isnan(std::get<double>(var->function(p))));
3236 
3237  }
3238 
3239 
3240  TEST_F(MetaVariableTest, daughterCombinationTwoGenerations)
3241  {
3242  StoreArray<Particle> particles;
3243 
3244  // make a 1 -> 3 particle
3245 
3246  PxPyPzEVector momentum_1(0, 0, 0, 0);
3247  std::vector<PxPyPzEVector> daughterMomenta_1;
3248  std::vector<int> daughterIndices_1;
3249 
3250  for (int i = 0; i < 3; i++) {
3251  PxPyPzEVector mom(i * 0.2, 1, 1, i * 1.0 + 2.0);
3252  Particle d(mom, (i % 2) ? 111 : 113);
3253  Particle* newDaughters = particles.appendNew(d);
3254  daughterIndices_1.push_back(newDaughters->getArrayIndex());
3255  daughterMomenta_1.push_back(mom);
3256  momentum_1 = momentum_1 + mom;
3257  }
3258 
3259  const Particle* compositeDau_1 = particles.appendNew(momentum_1, 411, Particle::c_Flavored, daughterIndices_1);
3260 
3261 
3262  // make a 1 -> 2 particle
3263 
3264  PxPyPzEVector momentum_2(0, 0, 0, 0);
3265  std::vector<PxPyPzEVector> daughterMomenta_2;
3266  std::vector<int> daughterIndices_2;
3267 
3268  for (int i = 0; i < 2; i++) {
3269  PxPyPzEVector mom(1, 1, i * 0.3, i * 1.0 + 2.0);
3270  Particle d(mom, (i % 2) ? 111 : 113);
3271  Particle* newDaughters = particles.appendNew(d);
3272  daughterIndices_2.push_back(newDaughters->getArrayIndex());
3273  daughterMomenta_2.push_back(mom);
3274  momentum_2 = momentum_2 + mom;
3275  }
3276 
3277  const Particle* compositeDau_2 = particles.appendNew(momentum_2, 411, Particle::c_Flavored, daughterIndices_2);
3278 
3279 
3280  // make the composite particle
3281  std::vector<int> daughterIndices = {compositeDau_1->getArrayIndex(), compositeDau_2->getArrayIndex()};
3282  const Particle* p = particles.appendNew(momentum_2 + momentum_1, 111, Particle::c_Unflavored, daughterIndices);
3283 
3284 
3285  // Test the invariant mass of several combinations
3286  const Manager::Var* var = Manager::Instance().getVariable("daughterCombination(M, 0,1)");
3287 
3288  double M_test = (momentum_1 + momentum_2).mag();
3289  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), M_test);
3290 
3291  // this should be the mass of the first daughter
3292  var = Manager::Instance().getVariable("daughterCombination(M, 0:0, 0:1, 0:2)");
3293  M_test = (momentum_1).mag();
3294  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), M_test);
3295 
3296  // this should be a generic combinations
3297  var = Manager::Instance().getVariable("daughterCombination(M, 0:0, 0:1, 1:0)");
3298  M_test = (daughterMomenta_1[0] + daughterMomenta_1[1] + daughterMomenta_2[0]).mag();
3299  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), M_test);
3300 
3301  }
3302 
3303 
3304  TEST_F(MetaVariableTest, useAlternativeDaughterHypothesis)
3305  {
3306  const int nDaughters = 5;
3307  StoreArray<Particle> particles;
3308 
3309  // Build a first Particle
3310  PxPyPzEVector momentum(0, 0, 0, 0);
3311  std::vector<int> daughterIndices;
3312  for (int i = 0; i < nDaughters; i++) {
3313  double px = i * 0.1;
3314  double py = i * 0.3;
3315  double pz = -i * 0.1 - 0.2;
3316 
3317  PxPyPzEVector mom(px, py, pz, 1);
3318  // all pions
3319  int pdgCode = Const::pion.getPDGCode();
3320  Particle d(mom, pdgCode);
3321  d.updateMass(pdgCode);
3322  mom = d.get4Vector();
3323 
3324  Particle* daughters = particles.appendNew(d);
3325  daughterIndices.push_back(daughters->getArrayIndex());
3326  momentum = momentum + mom;
3327  }
3328  const Particle* p = particles.appendNew(momentum, 411, Particle::c_Flavored, daughterIndices);
3329 
3330 
3331  // Build a second Particle with same momenta, but different mass hyp.
3332  PxPyPzEVector momentumAlt(0, 0, 0, 0);
3333  std::vector<int> daughterIndicesAlt;
3334  for (int i = 0; i < nDaughters; i++) {
3335  double px = i * 0.1;
3336  double py = i * 0.3;
3337  double pz = -i * 0.1 - 0.2;
3338 
3339  PxPyPzEVector mom(px, py, pz, 1);
3340  // all pions but the first two
3341  int pdgCode = Const::pion.getPDGCode();
3342  if (i == 0)
3343  pdgCode = Const::proton.getPDGCode(); // a proton
3344  if (i == 1)
3345  pdgCode = Const::kaon.getPDGCode(); // a K
3346  Particle d(mom, pdgCode);
3347  d.updateMass(pdgCode);
3348  mom = d.get4Vector();
3349 
3350  Particle* daughters = particles.appendNew(d);
3351  daughterIndicesAlt.push_back(daughters->getArrayIndex());
3352  momentumAlt = momentumAlt + mom;
3353  }
3354  const Particle* pAlt = particles.appendNew(momentumAlt, 411, Particle::c_Flavored, daughterIndicesAlt);
3355 
3356 
3357  // Test the invariant mass under the alternative hypothesis
3358  std::cout << "mass test" << std::endl;
3359  const Manager::Var* var = Manager::Instance().getVariable("useAlternativeDaughterHypothesis(M, 0:p+,1:K+)");
3360  const Manager::Var* varAlt = Manager::Instance().getVariable("M");
3361  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), std::get<double>(varAlt->function(pAlt)));
3362 
3363  // check it's really charge-insensitive...
3364  std::cout << "charge test" << std::endl;
3365  var = Manager::Instance().getVariable("useAlternativeDaughterHypothesis(M, 0:p+,1:K-)");
3366  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), std::get<double>(varAlt->function(pAlt)));
3367 
3368  // check the variable is not changing the 3-momentum
3369  std::cout << "momentum test" << std::endl;
3370  var = Manager::Instance().getVariable("useAlternativeDaughterHypothesis(p, 0:p+,1:K-)");
3371  varAlt = Manager::Instance().getVariable("p");
3372  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), std::get<double>(varAlt->function(pAlt)));
3373  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), std::get<double>(varAlt->function(p)));
3374  EXPECT_FLOAT_EQ(std::get<double>(var->function(pAlt)), std::get<double>(varAlt->function(pAlt)));
3375  }
3376 
3377 
3378 
3379 
3380  TEST_F(MetaVariableTest, daughterAngle)
3381  {
3382  StoreArray<Particle> particles;
3383 
3384  // make a 1 -> 3 particle
3385 
3386  PxPyPzEVector momentum_1(0, 0, 0, 0);
3387  std::vector<PxPyPzEVector> daughterMomenta_1;
3388  std::vector<int> daughterIndices_1;
3389 
3390  for (int i = 0; i < 3; i++) {
3391  PxPyPzEVector mom(i * 0.2, 1, 1, i * 1.0 + 2.0);
3392  Particle d(mom, (i % 2) ? -11 : 211);
3393  Particle* newDaughters = particles.appendNew(d);
3394  daughterIndices_1.push_back(newDaughters->getArrayIndex());
3395  daughterMomenta_1.push_back(mom);
3396  momentum_1 = momentum_1 + mom;
3397  }
3398 
3399  const Particle* compositeDau_1 = particles.appendNew(momentum_1, 411, Particle::c_Flavored, daughterIndices_1);
3400 
3401 
3402  // make a 1 -> 2 particle
3403 
3404  PxPyPzEVector momentum_2(0, 0, 0, 0);
3405  std::vector<PxPyPzEVector> daughterMomenta_2;
3406  std::vector<int> daughterIndices_2;
3407 
3408  for (int i = 0; i < 2; i++) {
3409  PxPyPzEVector mom(1, 1, i * 0.3, i * 1.0 + 2.0);
3410  Particle d(mom, (i % 2) ? -11 : 211);
3411  Particle* newDaughters = particles.appendNew(d);
3412  daughterIndices_2.push_back(newDaughters->getArrayIndex());
3413  daughterMomenta_2.push_back(mom);
3414  momentum_2 = momentum_2 + mom;
3415  }
3416 
3417  const Particle* compositeDau_2 = particles.appendNew(momentum_2, 411, Particle::c_Flavored, daughterIndices_2);
3418 
3419 
3420  // make the composite particle
3421  std::vector<int> daughterIndices = {compositeDau_1->getArrayIndex(), compositeDau_2->getArrayIndex()};
3422  const Particle* p = particles.appendNew(momentum_2 + momentum_1, 111, Particle::c_Unflavored, daughterIndices);
3423 
3424 
3425  // Test the invariant mass of several combinations
3426  const Manager::Var* var = Manager::Instance().getVariable("daughterAngle(0, 1)");
3427  double v_test = acos(momentum_1.Vect().Unit().Dot(momentum_2.Vect().Unit()));
3428 
3429  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), v_test);
3430 
3431  // this should be a generic combinations
3432  var = Manager::Instance().getVariable("daughterAngle(0:0, 1:0)");
3433  v_test = acos(daughterMomenta_1[0].Vect().Unit().Dot(daughterMomenta_2[0].Vect().Unit()));
3434  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), v_test);
3435 
3436  var = Manager::Instance().getVariable("daughterAngle( 1, -1)");
3437  EXPECT_B2WARNING(std::get<double>(var->function(p)));
3438  EXPECT_TRUE(std::isnan(std::get<double>(var->function(p))));
3439 
3440  var = Manager::Instance().getVariable("daughterAngle(1, 0:1:0:0:1)");
3441  EXPECT_B2WARNING(std::get<double>(var->function(p)));
3442  EXPECT_TRUE(std::isnan(std::get<double>(var->function(p))));
3443 
3444  }
3445 
3446  TEST_F(MetaVariableTest, mcDaughterVariables)
3447  {
3448 
3449  DataStore::Instance().setInitializeActive(true);
3450  StoreArray<Particle> particles;
3451  StoreArray<MCParticle> mcParticles;
3452  particles.registerRelationTo(mcParticles);
3453  DataStore::Instance().setInitializeActive(true);
3454  // make a 1 -> 3 particle
3455 
3456  PxPyPzEVector momentum_1(0, 0, 0, 0);
3457  std::vector<PxPyPzEVector> daughterMomenta_1;
3458  std::vector<int> daughterIndices_1;
3459 
3460  for (int i = 0; i < 3; i++) {
3461  PxPyPzEVector mom(i * 0.2, 1, 1, i * 1.0 + 2.0);
3462  Particle d(mom, (i % 2) ? -11 : 211);
3463  Particle* newDaughters = particles.appendNew(d);
3464  daughterIndices_1.push_back(newDaughters->getArrayIndex());
3465  daughterMomenta_1.push_back(mom);
3466  momentum_1 = momentum_1 + mom;
3467 
3468  auto* mcParticle = mcParticles.appendNew();
3469  mcParticle->setPDG((i % 2) ? -Const::electron.getPDGCode() : Const::pion.getPDGCode());
3470  mcParticle->setStatus(MCParticle::c_PrimaryParticle);
3471  mcParticle->set4Vector(mom);
3472  newDaughters->addRelationTo(mcParticle);
3473  }
3474 
3475  const Particle* compositeDau_1 = particles.appendNew(momentum_1, 411, Particle::c_Flavored, daughterIndices_1);
3476  auto* mcCompositeDau_1 = mcParticles.appendNew();
3477  mcCompositeDau_1->setPDG(411);
3478  mcCompositeDau_1->setStatus(MCParticle::c_PrimaryParticle);
3479  mcCompositeDau_1->set4Vector(momentum_1);
3480  compositeDau_1->addRelationTo(mcCompositeDau_1);
3481 
3482  // make a 1 -> 2 particle
3483 
3484  PxPyPzEVector momentum_2(0, 0, 0, 0);
3485  std::vector<PxPyPzEVector> daughterMomenta_2;
3486  std::vector<int> daughterIndices_2;
3487 
3488  for (int i = 0; i < 2; i++) {
3489  PxPyPzEVector mom(1, 1, i * 0.3, i * 1.0 + 2.0);
3490  Particle d(mom, (i % 2) ? -11 : 211);
3491  Particle* newDaughters = particles.appendNew(d);
3492  daughterIndices_2.push_back(newDaughters->getArrayIndex());
3493  daughterMomenta_2.push_back(mom);
3494  momentum_2 = momentum_2 + mom;
3495 
3496  auto* mcParticle = mcParticles.appendNew();
3497  mcParticle->setPDG((i % 2) ? -Const::electron.getPDGCode() : Const::pion.getPDGCode());
3498  mcParticle->setStatus(MCParticle::c_PrimaryParticle);
3499  mcParticle->set4Vector(mom);
3500  newDaughters->addRelationTo(mcParticle);
3501  }
3502 
3503  const Particle* compositeDau_2 = particles.appendNew(momentum_2, 411, Particle::c_Flavored, daughterIndices_2);
3504  auto* mcCompositeDau_2 = mcParticles.appendNew();
3505  mcCompositeDau_2->setPDG(411);
3506  mcCompositeDau_2->setStatus(MCParticle::c_PrimaryParticle);
3507  mcCompositeDau_2->set4Vector(momentum_2);
3508  compositeDau_2->addRelationTo(mcCompositeDau_2);
3509 
3510  // make the composite particle
3511  std::vector<int> daughterIndices = {compositeDau_1->getArrayIndex(), compositeDau_2->getArrayIndex()};
3512  const Particle* p = particles.appendNew(momentum_2 + momentum_1, 111, Particle::c_Unflavored, daughterIndices);
3513 
3514 
3515  // Test mcDaughterAngle
3516  const Manager::Var* var = Manager::Instance().getVariable("mcDaughterAngle(0, 1)");
3517  double v_test = acos(momentum_1.Vect().Unit().Dot(momentum_2.Vect().Unit()));
3518  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), v_test);
3519 
3520  var = Manager::Instance().getVariable("mcDaughterAngle(0:0, 1:0)");
3521  v_test = acos(daughterMomenta_1[0].Vect().Unit().Dot(daughterMomenta_2[0].Vect().Unit()));
3522  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), v_test);
3523 
3524  var = Manager::Instance().getVariable("mcDaughterAngle( 1, -1)");
3525  EXPECT_B2WARNING(std::get<double>(var->function(p)));
3526  EXPECT_TRUE(std::isnan(std::get<double>(var->function(p))));
3527 
3528  var = Manager::Instance().getVariable("mcDaughterAngle(1, 0:1:0:0:1)");
3529  EXPECT_B2WARNING(std::get<double>(var->function(p)));
3530  EXPECT_TRUE(std::isnan(std::get<double>(var->function(p))));
3531 
3532  // Test mcDaughterDiffOf
3533  var = Manager::Instance().getVariable("mcDaughterDiffOf(0, 1, PDG)");
3534  ASSERT_NE(var, nullptr);
3535  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), 0);
3536 
3537  EXPECT_B2FATAL(Manager::Instance().getVariable("mcDaughterDiffOf(0, NOTINT, PDG)"));
3538 
3539  // Test azimuthal angle as well
3540  var = Manager::Instance().getVariable("mcDaughterDiffOf(0, 1, phi)");
3541  ASSERT_NE(var, nullptr);
3542  v_test = momentum_2.Phi() - momentum_1.Phi();
3543  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), v_test);
3544 
3545  }
3546 
3547  TEST_F(MetaVariableTest, varForFirstMCAncestorOfType)
3548  {
3549  DataStore::Instance().setInitializeActive(true);
3550  StoreArray<MCParticle> mcParticles;
3551  StoreArray<Particle> particles;
3552  particles.registerInDataStore();
3553  mcParticles.registerInDataStore();
3554  particles.registerRelationTo(mcParticles);
3555  StoreObjPtr<ParticleList> DList("D0:vartest");
3556  DList.registerInDataStore();
3557  DList.create();
3558  DList->initialize(421, "D0:vartest");
3559  DataStore::Instance().setInitializeActive(false);
3560  PxPyPzEVector momentum;
3561  PxPyPzEVector momentum_0;
3562  PxPyPzEVector momentum_1;
3563  std::vector<int> D_daughterIndices;
3564  std::vector<int> D_grandDaughterIndices_0;
3565  std::vector<int> D_grandDaughterIndices_1;
3566 
3567 
3568  // Create MC graph for D -> (K0s -> pi+ + pi-) (K0s -> pi+ + pi-)
3569  MCParticleGraph mcGraph;
3570 
3571  MCParticleGraph::GraphParticle& mcg_m = mcGraph.addParticle();
3572  MCParticleGraph::GraphParticle& mcg_d_0 = mcGraph.addParticle();
3573  MCParticleGraph::GraphParticle& mcg_d_1 = mcGraph.addParticle();
3574  MCParticleGraph::GraphParticle& mcg_gd_0_0 = mcGraph.addParticle();
3575  MCParticleGraph::GraphParticle& mcg_gd_0_1 = mcGraph.addParticle();
3576  MCParticleGraph::GraphParticle& mcg_gd_1_0 = mcGraph.addParticle();
3577  MCParticleGraph::GraphParticle& mcg_gd_1_1 = mcGraph.addParticle();
3578  MCParticleGraph::GraphParticle& mcg_not_child = mcGraph.addParticle();
3579 
3580  mcg_m.setPDG(421);
3581  mcg_m.set4Vector(PxPyPzEVector(7, 7, 7, 7));
3582  mcg_d_0.setPDG(-Const::Kshort.getPDGCode());
3583  mcg_d_0.set4Vector(PxPyPzEVector(6, 6, 6, 6));
3584  mcg_d_1.setPDG(Const::Kshort.getPDGCode());
3585  mcg_d_1.set4Vector(PxPyPzEVector(5, 5, 5, 5));
3586  mcg_gd_0_0.setPDG(Const::pion.getPDGCode());
3587  mcg_gd_0_0.set4Vector(PxPyPzEVector(4, 4, 4, 4));
3588  mcg_gd_0_1.setPDG(-Const::pion.getPDGCode());
3589  mcg_gd_0_1.set4Vector(PxPyPzEVector(3, 3, 3, 3));
3590  mcg_gd_1_0.setPDG(Const::pion.getPDGCode());
3591  mcg_gd_1_0.set4Vector(PxPyPzEVector(2, 1, 2, 2));
3592  mcg_gd_1_1.setPDG(-Const::pion.getPDGCode());
3593  mcg_gd_1_1.set4Vector(PxPyPzEVector(1, 1, 1, 1));
3594  mcg_not_child.setPDG(Const::pion.getPDGCode());
3595  mcg_not_child.set4Vector(PxPyPzEVector(10, 10, 10, 10));
3596 
3597  mcg_d_0.comesFrom(mcg_m);
3598  mcg_d_1.comesFrom(mcg_m);
3599  mcg_gd_0_0.comesFrom(mcg_d_0);
3600  mcg_gd_0_1.comesFrom(mcg_d_0);
3601  mcg_gd_1_0.comesFrom(mcg_d_1);
3602  mcg_gd_1_1.comesFrom(mcg_d_1);
3603 
3604  mcGraph.generateList();
3605 
3606  // Get MC Particles from StoreArray
3607  auto* mc_not_child = mcParticles[0];
3608  auto* mc_m = mcParticles[1];
3609  auto* mc_d_0 = mcParticles[2];
3610  auto* mc_d_1 = mcParticles[3];
3611  auto* mc_gd_0_0 = mcParticles[4];
3612  auto* mc_gd_0_1 = mcParticles[5];
3613  auto* mc_gd_1_0 = mcParticles[6];
3614  auto* mc_gd_1_1 = mcParticles[7];
3615 
3616 
3617  mc_m->setStatus(MCParticle::c_PrimaryParticle);
3618  mc_d_0->setStatus(MCParticle::c_PrimaryParticle);
3619  mc_d_1->setStatus(MCParticle::c_PrimaryParticle);
3620  mc_gd_0_0->setStatus(MCParticle::c_PrimaryParticle);
3621  mc_gd_0_1->setStatus(MCParticle::c_PrimaryParticle);
3622  mc_gd_1_0->setStatus(MCParticle::c_PrimaryParticle);
3623  mc_gd_1_1->setStatus(MCParticle::c_PrimaryParticle);
3624  mc_not_child->setStatus(MCParticle::c_PrimaryParticle);
3625 
3626  // Creation of D decay: D->K0s(->pi pi) K0s(->pi pi)
3627 
3628  const Particle* D_gd_0_0 = particles.appendNew(PxPyPzEVector(0.0, 1, 1, 1), 211);
3629  const Particle* D_gd_0_1 = particles.appendNew(PxPyPzEVector(1.0, 1, 1, 1), -211);
3630  const Particle* D_gd_1_0 = particles.appendNew(PxPyPzEVector(2.0, 1, 1, 1), 211);
3631  const Particle* D_gd_1_1 = particles.appendNew(PxPyPzEVector(3.0, 1, 1, 1), -211);
3632 
3633  D_grandDaughterIndices_0.push_back(D_gd_0_0->getArrayIndex());
3634  D_grandDaughterIndices_0.push_back(D_gd_0_1->getArrayIndex());
3635  D_grandDaughterIndices_1.push_back(D_gd_1_0->getArrayIndex());
3636  D_grandDaughterIndices_1.push_back(D_gd_1_1->getArrayIndex());
3637  momentum_0 = D_gd_0_0->get4Vector() + D_gd_0_1->get4Vector();
3638  momentum_1 = D_gd_1_0->get4Vector() + D_gd_1_1->get4Vector();
3639 
3640 
3641  const Particle* D_d_0 = particles.appendNew(momentum_0, 310, Particle::c_Unflavored, D_grandDaughterIndices_0);
3642  const Particle* D_d_1 = particles.appendNew(momentum_1, 310, Particle::c_Unflavored, D_grandDaughterIndices_1);
3643 
3644 
3645  momentum = D_d_0->get4Vector() + D_d_1->get4Vector();
3646  D_daughterIndices.push_back(D_d_0->getArrayIndex());
3647  D_daughterIndices.push_back(D_d_1->getArrayIndex());
3648 
3649  const Particle* D_m = particles.appendNew(momentum, 421, Particle::c_Unflavored, D_daughterIndices);
3650  DList->addParticle(D_m);
3651 
3652  // Particle that is not an child
3653  const Particle* not_child = particles.appendNew(PxPyPzEVector(5.0, 1, 1, 1), 211);
3654 
3655  // Particle that is not an child and doesn't have MC particle
3656  const Particle* not_child_2 = particles.appendNew(PxPyPzEVector(6.0, 1, 1, 1), 211);
3657 
3658  // MC matching
3659  D_gd_0_0->addRelationTo(mc_gd_0_0);
3660  D_gd_0_1->addRelationTo(mc_gd_0_1);
3661  D_gd_1_0->addRelationTo(mc_gd_1_0);
3662  D_gd_1_1->addRelationTo(mc_gd_1_1);
3663  D_d_0->addRelationTo(mc_d_0);
3664  D_d_1->addRelationTo(mc_d_1);
3665  D_m->addRelationTo(mc_m);
3666  not_child->addRelationTo(mc_not_child);
3667 
3668  // All pions should have common D mother
3669  const Manager::Var* var_d = Manager::Instance().getVariable("varForFirstMCAncestorOfType(D0, mdstIndex)");
3670  ASSERT_NE(var_d, nullptr);
3671  EXPECT_TRUE(std::get<double>(var_d->function(D_gd_0_0)) >= 0);
3672  EXPECT_FLOAT_EQ(std::get<double>(var_d->function(D_gd_0_0)), std::get<double>(var_d->function(D_gd_0_1)));
3673  EXPECT_FLOAT_EQ(std::get<double>(var_d->function(D_gd_1_0)), std::get<double>(var_d->function(D_gd_1_1)));
3674  EXPECT_FLOAT_EQ(std::get<double>(var_d->function(D_gd_0_0)), std::get<double>(var_d->function(D_gd_1_0)));
3675  EXPECT_FLOAT_EQ(std::get<double>(var_d->function(D_gd_0_1)), std::get<double>(var_d->function(D_gd_1_1)));
3676  EXPECT_TRUE(std::isnan(std::get<double>(var_d->function(not_child))));
3677  EXPECT_TRUE(std::isnan(std::get<double>(var_d->function(not_child_2))));
3678 
3679 
3680  // // All but they have different K0s mothers
3681  const Manager::Var* var_310 = Manager::Instance().getVariable("varForFirstMCAncestorOfType(310, mdstIndex)");
3682  ASSERT_NE(var_310, nullptr);
3683  EXPECT_FLOAT_EQ(std::get<double>(var_310->function(D_gd_0_0)), std::get<double>(var_310->function(D_gd_0_1)));
3684  EXPECT_FLOAT_EQ(std::get<double>(var_310->function(D_gd_1_0)), std::get<double>(var_310->function(D_gd_1_1)));
3685  EXPECT_NE(std::get<double>(var_310->function(D_gd_0_0)), std::get<double>(var_310->function(D_gd_1_0)));
3686  EXPECT_NE(std::get<double>(var_310->function(D_gd_0_1)), std::get<double>(var_310->function(D_gd_1_1)));
3687  EXPECT_TRUE(std::isnan(std::get<double>(var_310->function(not_child))));
3688  EXPECT_TRUE(std::isnan(std::get<double>(var_310->function(not_child_2))));
3689  EXPECT_FLOAT_EQ(int(std::get<double>(Manager::Instance().getVariable("varForFirstMCAncestorOfType(310, E)")->function(D_gd_0_0))),
3690  10);
3691  }
3692 
3693  TEST_F(MetaVariableTest, isDescendantOfList)
3694  {
3695  DataStore::Instance().setInitializeActive(true);
3696  StoreObjPtr<ParticleList> DList("D0:vartest");
3697  DList.registerInDataStore();
3698  DList.create();
3699  DList->initialize(421, "D0:vartest");
3700  StoreObjPtr<ParticleList> BList("B:vartest");
3701  BList.registerInDataStore();
3702  BList.create();
3703  BList->initialize(521, "B:vartest");
3704  DataStore::Instance().setInitializeActive(false);
3705 
3706  PxPyPzEVector momentum;
3707  PxPyPzEVector momentum_0;
3708  PxPyPzEVector momentum_1;
3709  StoreArray<Particle> particles;
3710  std::vector<int> D_daughterIndices;
3711  std::vector<int> D_grandDaughterIndices_0;
3712  std::vector<int> D_grandDaughterIndices_1;
3713  std::vector<int> B_daughterIndices;
3714  std::vector<int> B_grandDaughterIndices;
3715  std::vector<int> B_grandGrandDaughterIndices;
3716 
3717  // Creation of D decay: D->K0s(->pi pi) K0s(->pi pi)
3718 
3719  const Particle* D_gd_0_0 = particles.appendNew(PxPyPzEVector(0.0, 1, 1, 1), 211, Particle::c_Flavored, Particle::c_Track, 0);
3720  const Particle* D_gd_0_1 = particles.appendNew(PxPyPzEVector(1.0, 1, 1, 1), -211, Particle::c_Flavored, Particle::c_Track, 1);
3721  const Particle* D_gd_1_0 = particles.appendNew(PxPyPzEVector(2.0, 1, 1, 1), 211, Particle::c_Flavored, Particle::c_Track, 2);
3722  const Particle* D_gd_1_1 = particles.appendNew(PxPyPzEVector(3.0, 1, 1, 1), -211, Particle::c_Flavored, Particle::c_Track, 3);
3723 
3724  D_grandDaughterIndices_0.push_back(D_gd_0_0->getArrayIndex());
3725  D_grandDaughterIndices_0.push_back(D_gd_0_1->getArrayIndex());
3726  D_grandDaughterIndices_1.push_back(D_gd_1_0->getArrayIndex());
3727  D_grandDaughterIndices_1.push_back(D_gd_1_1->getArrayIndex());
3728  momentum_0 = D_gd_0_0->get4Vector() + D_gd_0_1->get4Vector();
3729  momentum_1 = D_gd_1_0->get4Vector() + D_gd_1_1->get4Vector();
3730 
3731 
3732  const Particle* D_d_0 = particles.appendNew(momentum_0, 310, Particle::c_Unflavored, D_grandDaughterIndices_0);
3733  const Particle* D_d_1 = particles.appendNew(momentum_1, 310, Particle::c_Unflavored, D_grandDaughterIndices_1);
3734 
3735 
3736  momentum = D_d_0->get4Vector() + D_d_1->get4Vector();
3737  D_daughterIndices.push_back(D_d_0->getArrayIndex());
3738  D_daughterIndices.push_back(D_d_1->getArrayIndex());
3739 
3740  const Particle* D_m = particles.appendNew(momentum, 421, Particle::c_Unflavored, D_daughterIndices);
3741  DList->addParticle(D_m);
3742 
3743  // Creation of B decay B -> D(->K0s(->pi pi) pi) pi
3744 
3745  const Particle* B_d_1 = particles.appendNew(PxPyPzEVector(0.0, 1, 1, 1), 211, Particle::c_Flavored, Particle::c_Track, 4);
3746  const Particle* B_gd_0_1 = particles.appendNew(PxPyPzEVector(1.0, 1, 1, 1), -211, Particle::c_Flavored, Particle::c_Track, 5);
3747  const Particle* B_ggd_0_0_0 = particles.appendNew(PxPyPzEVector(2.0, 1, 1, 1), 211, Particle::c_Flavored, Particle::c_Track, 6);
3748  const Particle* B_ggd_0_0_1 = particles.appendNew(PxPyPzEVector(3.0, 1, 1, 1), -211, Particle::c_Flavored, Particle::c_Track, 7);
3749 
3750  B_grandGrandDaughterIndices.push_back(B_ggd_0_0_0->getArrayIndex());
3751  B_grandGrandDaughterIndices.push_back(B_ggd_0_0_1->getArrayIndex());
3752  momentum_0 = B_ggd_0_0_0->get4Vector() + B_ggd_0_0_1->get4Vector();
3753  const Particle* B_gd_0_0 = particles.appendNew(momentum_0, 310, Particle::c_Unflavored, B_grandGrandDaughterIndices);
3754 
3755  B_grandDaughterIndices.push_back(B_gd_0_0->getArrayIndex());
3756  B_grandDaughterIndices.push_back(B_gd_0_1->getArrayIndex());
3757  momentum_1 = B_gd_0_0->get4Vector() + B_gd_0_1->get4Vector();
3758  const Particle* B_d_0 = particles.appendNew(momentum_1, -411, Particle::c_Unflavored, B_grandDaughterIndices);
3759 
3760  B_daughterIndices.push_back(B_d_0->getArrayIndex());
3761  B_daughterIndices.push_back(B_d_1->getArrayIndex());
3762  momentum = B_d_0->get4Vector() + B_d_1->get4Vector();
3763  const Particle* B_m = particles.appendNew(momentum, 521, Particle::c_Unflavored, B_daughterIndices);
3764  BList->addParticle(B_m);
3765 
3766  // Particle that is not an child
3767  const Particle* not_child = particles.appendNew(PxPyPzEVector(5.0, 1, 1, 1), 211, Particle::c_Flavored, Particle::c_Track, 8);
3768 
3769 
3770  const Manager::Var* var_0 = Manager::Instance().getVariable("isDescendantOfList(D0:vartest)");
3771  ASSERT_NE(var_0, nullptr);
3772  EXPECT_TRUE(std::get<bool>(var_0->function(D_gd_0_0)));
3773  EXPECT_TRUE(std::get<bool>(var_0->function(D_gd_0_1)));
3774  EXPECT_TRUE(std::get<bool>(var_0->function(D_gd_1_0)));
3775  EXPECT_TRUE(std::get<bool>(var_0->function(D_gd_1_1)));
3776  EXPECT_TRUE(std::get<bool>(var_0->function(D_d_0)));
3777  EXPECT_TRUE(std::get<bool>(var_0->function(D_d_1)));
3778  EXPECT_FALSE(std::get<bool>(var_0->function(B_ggd_0_0_0)));
3779  EXPECT_FALSE(std::get<bool>(var_0->function(B_ggd_0_0_1)));
3780  EXPECT_FALSE(std::get<bool>(var_0->function(B_gd_0_0)));
3781  EXPECT_FALSE(std::get<bool>(var_0->function(B_gd_0_1)));
3782  EXPECT_FALSE(std::get<bool>(var_0->function(B_d_0)));
3783  EXPECT_FALSE(std::get<bool>(var_0->function(B_d_1)));
3784  EXPECT_FALSE(std::get<bool>(var_0->function(not_child)));
3785 
3786  const Manager::Var* var_0a = Manager::Instance().getVariable("isDaughterOfList(D0:vartest)");
3787  ASSERT_NE(var_0a, nullptr);
3788  EXPECT_FALSE(std::get<bool>(var_0a->function(D_gd_0_0)));
3789  EXPECT_FALSE(std::get<bool>(var_0a->function(D_gd_0_1)));
3790  EXPECT_FALSE(std::get<bool>(var_0a->function(D_gd_1_0)));
3791  EXPECT_FALSE(std::get<bool>(var_0a->function(D_gd_1_1)));
3792  EXPECT_TRUE(std::get<bool>(var_0a->function(D_d_0)));
3793  EXPECT_TRUE(std::get<bool>(var_0a->function(D_d_1)));
3794  EXPECT_FALSE(std::get<bool>(var_0a->function(B_ggd_0_0_0)));
3795  EXPECT_FALSE(std::get<bool>(var_0a->function(B_ggd_0_0_1)));
3796  EXPECT_FALSE(std::get<bool>(var_0a->function(B_gd_0_0)));
3797  EXPECT_FALSE(std::get<bool>(var_0a->function(B_gd_0_1)));
3798  EXPECT_FALSE(std::get<bool>(var_0a->function(B_d_0)));
3799  EXPECT_FALSE(std::get<bool>(var_0a->function(B_d_1)));
3800  EXPECT_FALSE(std::get<bool>(var_0a->function(not_child)));
3801 
3802  const Manager::Var* var_0b = Manager::Instance().getVariable("isGrandDaughterOfList(D0:vartest)");
3803  ASSERT_NE(var_0b, nullptr);
3804  EXPECT_TRUE(std::get<bool>(var_0b->function(D_gd_0_0)));
3805  EXPECT_TRUE(std::get<bool>(var_0b->function(D_gd_0_1)));
3806  EXPECT_TRUE(std::get<bool>(var_0b->function(D_gd_1_0)));
3807  EXPECT_TRUE(std::get<bool>(var_0b->function(D_gd_1_1)));
3808  EXPECT_FALSE(std::get<bool>(var_0b->function(D_d_0)));
3809  EXPECT_FALSE(std::get<bool>(var_0b->function(D_d_1)));
3810  EXPECT_FALSE(std::get<bool>(var_0b->function(B_ggd_0_0_0)));
3811  EXPECT_FALSE(std::get<bool>(var_0b->function(B_ggd_0_0_1)));
3812  EXPECT_FALSE(std::get<bool>(var_0b->function(B_gd_0_0)));
3813  EXPECT_FALSE(std::get<bool>(var_0b->function(B_gd_0_1)));
3814  EXPECT_FALSE(std::get<bool>(var_0b->function(B_d_0)));
3815  EXPECT_FALSE(std::get<bool>(var_0b->function(B_d_1)));
3816  EXPECT_FALSE(std::get<bool>(var_0b->function(not_child)));
3817 
3818  const Manager::Var* var_1 = Manager::Instance().getVariable("isDescendantOfList(D0:vartest, 1)");
3819  ASSERT_NE(var_1, nullptr);
3820  EXPECT_FALSE(std::get<bool>(var_1->function(D_gd_0_0)));
3821  EXPECT_FALSE(std::get<bool>(var_1->function(D_gd_0_1)));
3822  EXPECT_FALSE(std::get<bool>(var_1->function(D_gd_1_0)));
3823  EXPECT_FALSE(std::get<bool>(var_1->function(D_gd_1_1)));
3824  EXPECT_TRUE(std::get<bool>(var_1->function(D_d_0)));
3825  EXPECT_TRUE(std::get<bool>(var_1->function(D_d_1)));
3826  EXPECT_FALSE(std::get<bool>(var_1->function(B_ggd_0_0_0)));
3827  EXPECT_FALSE(std::get<bool>(var_1->function(B_ggd_0_0_1)));
3828  EXPECT_FALSE(std::get<bool>(var_1->function(B_gd_0_0)));
3829  EXPECT_FALSE(std::get<bool>(var_1->function(B_gd_0_1)));
3830  EXPECT_FALSE(std::get<bool>(var_1->function(B_d_0)));
3831  EXPECT_FALSE(std::get<bool>(var_1->function(B_d_1)));
3832  EXPECT_FALSE(std::get<bool>(var_1->function(not_child)));
3833 
3834  const Manager::Var* var_2 = Manager::Instance().getVariable("isDescendantOfList(D0:vartest, 2)");
3835  ASSERT_NE(var_2, nullptr);
3836  EXPECT_TRUE(std::get<bool>(var_2->function(D_gd_0_0)));
3837  EXPECT_TRUE(std::get<bool>(var_2->function(D_gd_0_1)));
3838  EXPECT_TRUE(std::get<bool>(var_2->function(D_gd_1_0)));
3839  EXPECT_TRUE(std::get<bool>(var_2->function(D_gd_1_1)));
3840  EXPECT_FALSE(std::get<bool>(var_2->function(D_d_0)));
3841  EXPECT_FALSE(std::get<bool>(var_2->function(D_d_1)));
3842  EXPECT_FALSE(std::get<bool>(var_2->function(B_ggd_0_0_0)));
3843  EXPECT_FALSE(std::get<bool>(var_2->function(B_ggd_0_0_1)));
3844  EXPECT_FALSE(std::get<bool>(var_2->function(B_gd_0_0)));
3845  EXPECT_FALSE(std::get<bool>(var_2->function(B_gd_0_1)));
3846  EXPECT_FALSE(std::get<bool>(var_2->function(B_d_0)));
3847  EXPECT_FALSE(std::get<bool>(var_2->function(B_d_1)));
3848  EXPECT_FALSE(std::get<bool>(var_2->function(not_child)));
3849 
3850  const Manager::Var* var_3 = Manager::Instance().getVariable("isDescendantOfList(D0:vartest, B:vartest)");
3851  ASSERT_NE(var_3, nullptr);
3852  EXPECT_TRUE(std::get<bool>(var_3->function(D_gd_0_0)));
3853  EXPECT_TRUE(std::get<bool>(var_3->function(D_gd_0_1)));
3854  EXPECT_TRUE(std::get<bool>(var_3->function(D_gd_1_0)));
3855  EXPECT_TRUE(std::get<bool>(var_3->function(D_gd_1_1)));
3856  EXPECT_TRUE(std::get<bool>(var_3->function(D_d_0)));
3857  EXPECT_TRUE(std::get<bool>(var_3->function(D_d_1)));
3858  EXPECT_TRUE(std::get<bool>(var_3->function(B_ggd_0_0_0)));
3859  EXPECT_TRUE(std::get<bool>(var_3->function(B_ggd_0_0_1)));
3860  EXPECT_TRUE(std::get<bool>(var_3->function(B_gd_0_0)));
3861  EXPECT_TRUE(std::get<bool>(var_3->function(B_gd_0_1)));
3862  EXPECT_TRUE(std::get<bool>(var_3->function(B_d_0)));
3863  EXPECT_TRUE(std::get<bool>(var_3->function(B_d_1)));
3864  EXPECT_FALSE(std::get<bool>(var_3->function(not_child)));
3865 
3866  const Manager::Var* var_4 = Manager::Instance().getVariable("isDescendantOfList(D0:vartest, B:vartest, -1)");
3867  ASSERT_NE(var_4, nullptr);
3868  EXPECT_TRUE(std::get<bool>(var_4->function(D_gd_0_0)));
3869  EXPECT_TRUE(std::get<bool>(var_4->function(D_gd_0_1)));
3870  EXPECT_TRUE(std::get<bool>(var_4->function(D_gd_1_0)));
3871  EXPECT_TRUE(std::get<bool>(var_4->function(D_gd_1_1)));
3872  EXPECT_TRUE(std::get<bool>(var_4->function(D_d_0)));
3873  EXPECT_TRUE(std::get<bool>(var_4->function(D_d_1)));
3874  EXPECT_TRUE(std::get<bool>(var_4->function(B_ggd_0_0_0)));
3875  EXPECT_TRUE(std::get<bool>(var_4->function(B_ggd_0_0_1)));
3876  EXPECT_TRUE(std::get<bool>(var_4->function(B_gd_0_0)));
3877  EXPECT_TRUE(std::get<bool>(var_4->function(B_gd_0_1)));
3878  EXPECT_TRUE(std::get<bool>(var_4->function(B_d_0)));
3879  EXPECT_TRUE(std::get<bool>(var_4->function(B_d_1)));
3880  EXPECT_FALSE(std::get<bool>(var_4->function(not_child)));
3881 
3882  const Manager::Var* var_5 = Manager::Instance().getVariable("isDescendantOfList(D0:vartest, B:vartest, 1)");
3883  ASSERT_NE(var_5, nullptr);
3884  EXPECT_FALSE(std::get<bool>(var_5->function(D_gd_0_0)));
3885  EXPECT_FALSE(std::get<bool>(var_5->function(D_gd_0_1)));
3886  EXPECT_FALSE(std::get<bool>(var_5->function(D_gd_1_0)));
3887  EXPECT_FALSE(std::get<bool>(var_5->function(D_gd_1_1)));
3888  EXPECT_TRUE(std::get<bool>(var_5->function(D_d_0)));
3889  EXPECT_TRUE(std::get<bool>(var_5->function(D_d_1)));
3890  EXPECT_FALSE(std::get<bool>(var_5->function(B_ggd_0_0_0)));
3891  EXPECT_FALSE(std::get<bool>(var_5->function(B_ggd_0_0_1)));
3892  EXPECT_FALSE(std::get<bool>(var_5->function(B_gd_0_0)));
3893  EXPECT_FALSE(std::get<bool>(var_5->function(B_gd_0_1)));
3894  EXPECT_TRUE(std::get<bool>(var_5->function(B_d_0)));
3895  EXPECT_TRUE(std::get<bool>(var_5->function(B_d_1)));
3896  EXPECT_FALSE(std::get<bool>(var_5->function(not_child)));
3897 
3898  const Manager::Var* var_6 = Manager::Instance().getVariable("isDescendantOfList(D0:vartest, B:vartest, 2)");
3899  ASSERT_NE(var_6, nullptr);
3900  EXPECT_TRUE(std::get<bool>(var_6->function(D_gd_0_0)));
3901  EXPECT_TRUE(std::get<bool>(var_6->function(D_gd_0_1)));
3902  EXPECT_TRUE(std::get<bool>(var_6->function(D_gd_1_0)));
3903  EXPECT_TRUE(std::get<bool>(var_6->function(D_gd_1_1)));
3904  EXPECT_FALSE(std::get<bool>(var_6->function(D_d_0)));
3905  EXPECT_FALSE(std::get<bool>(var_6->function(D_d_1)));
3906  EXPECT_FALSE(std::get<bool>(var_6->function(B_ggd_0_0_0)));
3907  EXPECT_FALSE(std::get<bool>(var_6->function(B_ggd_0_0_1)));
3908  EXPECT_TRUE(std::get<bool>(var_6->function(B_gd_0_0)));
3909  EXPECT_TRUE(std::get<bool>(var_6->function(B_gd_0_1)));
3910  EXPECT_FALSE(std::get<bool>(var_6->function(B_d_0)));
3911  EXPECT_FALSE(std::get<bool>(var_6->function(B_d_1)));
3912  EXPECT_FALSE(std::get<bool>(var_6->function(not_child)));
3913 
3914  const Manager::Var* var_7 = Manager::Instance().getVariable("isDescendantOfList(D0:vartest, B:vartest, 3)");
3915  ASSERT_NE(var_7, nullptr);
3916  EXPECT_FALSE(std::get<bool>(var_7->function(D_gd_0_0)));
3917  EXPECT_FALSE(std::get<bool>(var_7->function(D_gd_0_1)));
3918  EXPECT_FALSE(std::get<bool>(var_7->function(D_gd_1_0)));
3919  EXPECT_FALSE(std::get<bool>(var_7->function(D_gd_1_1)));
3920  EXPECT_FALSE(std::get<bool>(var_7->function(D_d_0)));
3921  EXPECT_FALSE(std::get<bool>(var_7->function(D_d_1)));
3922  EXPECT_TRUE(std::get<bool>(var_7->function(B_ggd_0_0_0)));
3923  EXPECT_TRUE(std::get<bool>(var_7->function(B_ggd_0_0_1)));
3924  EXPECT_FALSE(std::get<bool>(var_7->function(B_gd_0_0)));
3925  EXPECT_FALSE(std::get<bool>(var_7->function(B_gd_0_1)));
3926  EXPECT_FALSE(std::get<bool>(var_7->function(B_d_0)));
3927  EXPECT_FALSE(std::get<bool>(var_7->function(B_d_1)));
3928  EXPECT_FALSE(std::get<bool>(var_7->function(not_child)));
3929  }
3930 
3931 
3932  TEST_F(MetaVariableTest, isMCDescendantOfList)
3933  {
3934  DataStore::Instance().setInitializeActive(true);
3935  StoreArray<MCParticle> mcParticles;
3936  StoreArray<Particle> particles;
3937  particles.registerInDataStore();
3938  mcParticles.registerInDataStore();
3939  particles.registerRelationTo(mcParticles);
3940  StoreObjPtr<ParticleList> BList("B:vartest");
3941  BList.registerInDataStore();
3942  BList.create();
3943  BList->initialize(521, "B:vartest");
3944  StoreObjPtr<ParticleList> DList("D0:vartest");
3945  DList.registerInDataStore();
3946  DList.create();
3947  DList->initialize(421, "D0:vartest");
3948  DataStore::Instance().setInitializeActive(false);
3949  PxPyPzEVector momentum;
3950  PxPyPzEVector momentum_0;
3951  PxPyPzEVector momentum_1;
3952  std::vector<int> daughterIndices;
3953  std::vector<int> grandDaughterIndices;
3954  std::vector<int> grandGrandDaughterIndices;
3955  std::vector<int> D_daughterIndices;
3956  std::vector<int> D_grandDaughterIndices_0;
3957  std::vector<int> D_grandDaughterIndices_1;
3958 
3959 
3960  // Create MC graph for B+ -> (D -> (K0s -> pi+ + pi-) pi-) + pi+
3961  MCParticleGraph mcGraph;
3962 
3963  MCParticleGraph::GraphParticle& mcg_m = mcGraph.addParticle();
3964  MCParticleGraph::GraphParticle& mcg_d_0 = mcGraph.addParticle();
3965  MCParticleGraph::GraphParticle& mcg_d_1 = mcGraph.addParticle();
3966  MCParticleGraph::GraphParticle& mcg_gd_0_0 = mcGraph.addParticle();
3967  MCParticleGraph::GraphParticle& mcg_gd_0_1 = mcGraph.addParticle();
3968  MCParticleGraph::GraphParticle& mcg_ggd_0_0_0 = mcGraph.addParticle();
3969  MCParticleGraph::GraphParticle& mcg_ggd_0_0_1 = mcGraph.addParticle();
3970  MCParticleGraph::GraphParticle& mcg_not_child = mcGraph.addParticle();
3971 
3972  mcg_m.setPDG(521);
3973  mcg_d_0.setPDG(-411);
3974  mcg_d_1.setPDG(Const::pion.getPDGCode());
3975  mcg_gd_0_0.setPDG(Const::Kshort.getPDGCode());
3976  mcg_gd_0_1.setPDG(-Const::pion.getPDGCode());
3977  mcg_ggd_0_0_0.setPDG(Const::pion.getPDGCode());
3978  mcg_ggd_0_0_1.setPDG(-Const::pion.getPDGCode());
3979  mcg_not_child.setPDG(Const::pion.getPDGCode());
3980 
3981  mcg_d_0.comesFrom(mcg_m);
3982  mcg_d_1.comesFrom(mcg_m);
3983  mcg_gd_0_0.comesFrom(mcg_d_0);
3984  mcg_gd_0_1.comesFrom(mcg_d_0);
3985  mcg_ggd_0_0_0.comesFrom(mcg_gd_0_1);
3986  mcg_ggd_0_0_1.comesFrom(mcg_gd_0_1);
3987 
3988  mcGraph.generateList();
3989 
3990  // Get MC Particles from StoreArray
3991  auto* mc_m = mcParticles[0];
3992  auto* mc_d_0 = mcParticles[1];
3993  auto* mc_d_1 = mcParticles[2];
3994  auto* mc_gd_0_0 = mcParticles[3];
3995  auto* mc_gd_0_1 = mcParticles[4];
3996  auto* mc_ggd_0_0_0 = mcParticles[5];
3997  auto* mc_ggd_0_0_1 = mcParticles[6];
3998  auto* mc_not_child = mcParticles[7];
3999 
4000  mc_m->setStatus(MCParticle::c_PrimaryParticle);
4001  mc_d_0->setStatus(MCParticle::c_PrimaryParticle);
4002  mc_d_1->setStatus(MCParticle::c_PrimaryParticle);
4003  mc_gd_0_0->setStatus(MCParticle::c_PrimaryParticle);
4004  mc_gd_0_1->setStatus(MCParticle::c_PrimaryParticle);
4005  mc_ggd_0_0_0->setStatus(MCParticle::c_PrimaryParticle);
4006  mc_ggd_0_0_1->setStatus(MCParticle::c_PrimaryParticle);
4007  mc_not_child->setStatus(MCParticle::c_PrimaryParticle);
4008 
4009  // Creation of D decay: D->K0s(->pi pi) K0s(->pi pi) (not matched)
4010 
4011  const Particle* D_gd_0_0 = particles.appendNew(PxPyPzEVector(0.0, 1, 1, 1), 211);
4012  const Particle* D_gd_0_1 = particles.appendNew(PxPyPzEVector(1.0, 1, 1, 1), -211);
4013  const Particle* D_gd_1_0 = particles.appendNew(PxPyPzEVector(2.0, 1, 1, 1), 211);
4014  const Particle* D_gd_1_1 = particles.appendNew(PxPyPzEVector(3.0, 1, 1, 1), -211);
4015 
4016  D_grandDaughterIndices_0.push_back(D_gd_0_0->getArrayIndex());
4017  D_grandDaughterIndices_0.push_back(D_gd_0_1->getArrayIndex());
4018  D_grandDaughterIndices_1.push_back(D_gd_1_0->getArrayIndex());
4019  D_grandDaughterIndices_1.push_back(D_gd_1_1->getArrayIndex());
4020  momentum_0 = D_gd_0_0->get4Vector() + D_gd_0_1->get4Vector();
4021  momentum_1 = D_gd_1_0->get4Vector() + D_gd_1_1->get4Vector();
4022 
4023 
4024  const Particle* D_d_0 = particles.appendNew(momentum_0, 310, Particle::c_Unflavored, D_grandDaughterIndices_0);
4025  const Particle* D_d_1 = particles.appendNew(momentum_1, 310, Particle::c_Unflavored, D_grandDaughterIndices_1);
4026 
4027 
4028  momentum = D_d_0->get4Vector() + D_d_1->get4Vector();
4029  D_daughterIndices.push_back(D_d_0->getArrayIndex());
4030  D_daughterIndices.push_back(D_d_1->getArrayIndex());
4031 
4032  const Particle* D_m = particles.appendNew(momentum, 421, Particle::c_Unflavored, D_daughterIndices);
4033  DList->addParticle(D_m);
4034 
4035  // Creating B decay
4036  const Particle* d_1 = particles.appendNew(PxPyPzEVector(0.0, 1, 1, 1), 211);
4037  const Particle* gd_0_1 = particles.appendNew(PxPyPzEVector(1.0, 1, 1, 1), -211);
4038  const Particle* ggd_0_0_0 = particles.appendNew(PxPyPzEVector(2.0, 1, 1, 1), 211);
4039  const Particle* ggd_0_0_1 = particles.appendNew(PxPyPzEVector(3.0, 1, 1, 1), -211);
4040 
4041  grandGrandDaughterIndices.push_back(ggd_0_0_0->getArrayIndex());
4042  grandGrandDaughterIndices.push_back(ggd_0_0_1->getArrayIndex());
4043  momentum_0 = ggd_0_0_0->get4Vector() + ggd_0_0_1->get4Vector();
4044  const Particle* gd_0_0 = particles.appendNew(momentum_0, 310, Particle::c_Unflavored, grandGrandDaughterIndices);
4045 
4046  grandDaughterIndices.push_back(gd_0_0->getArrayIndex());
4047  grandDaughterIndices.push_back(gd_0_1->getArrayIndex());
4048  momentum_1 = gd_0_0->get4Vector() + gd_0_1->get4Vector();
4049  const Particle* d_0 = particles.appendNew(momentum_1, -411, Particle::c_Unflavored, grandDaughterIndices);
4050 
4051  daughterIndices.push_back(d_0->getArrayIndex());
4052  daughterIndices.push_back(d_1->getArrayIndex());
4053  momentum = d_0->get4Vector() + d_1->get4Vector();
4054  const Particle* m = particles.appendNew(momentum, 521, Particle::c_Unflavored, daughterIndices);
4055  BList->addParticle(m);
4056 
4057  // Particle that is not an child
4058  const Particle* not_child = particles.appendNew(PxPyPzEVector(5.0, 1, 1, 1), 211);
4059 
4060  // Particle that is not an child and doesn't have MC particle
4061  const Particle* not_child_2 = particles.appendNew(PxPyPzEVector(6.0, 1, 1, 1), 211);
4062 
4063  gd_0_0->addRelationTo(mc_gd_0_0);
4064  gd_0_1->addRelationTo(mc_gd_0_1);
4065  ggd_0_0_0->addRelationTo(mc_ggd_0_0_0);
4066  ggd_0_0_1->addRelationTo(mc_ggd_0_0_1);
4067  d_0->addRelationTo(mc_d_0);
4068  d_1->addRelationTo(mc_d_1);
4069  m->addRelationTo(mc_m);
4070  not_child->addRelationTo(mc_not_child);
4071 
4072  const Manager::Var* var_0 = Manager::Instance().getVariable("isMCDescendantOfList(B:vartest)");
4073  ASSERT_NE(var_0, nullptr);
4074  EXPECT_FALSE(std::get<bool>(var_0->function(D_gd_0_0)));
4075  EXPECT_FALSE(std::get<bool>(var_0->function(D_gd_0_1)));
4076  EXPECT_FALSE(std::get<bool>(var_0->function(D_gd_1_0)));
4077  EXPECT_FALSE(std::get<bool>(var_0->function(D_gd_1_1)));
4078  EXPECT_FALSE(std::get<bool>(var_0->function(D_d_0)));
4079  EXPECT_FALSE(std::get<bool>(var_0->function(D_d_1)));
4080  EXPECT_TRUE(std::get<bool>(var_0->function(ggd_0_0_0)));
4081  EXPECT_TRUE(std::get<bool>(var_0->function(ggd_0_0_1)));
4082  EXPECT_TRUE(std::get<bool>(var_0->function(gd_0_0)));
4083  EXPECT_TRUE(std::get<bool>(var_0->function(gd_0_1)));
4084  EXPECT_TRUE(std::get<bool>(var_0->function(d_0)));
4085  EXPECT_TRUE(std::get<bool>(var_0->function(d_1)));
4086  EXPECT_FALSE(std::get<bool>(var_0->function(not_child)));
4087  EXPECT_FALSE(std::get<bool>(var_0->function(not_child_2)));
4088 
4089  const Manager::Var* var_1 = Manager::Instance().getVariable("isMCDescendantOfList(B:vartest, D0:vartest)");
4090  ASSERT_NE(var_1, nullptr);
4091  EXPECT_FALSE(std::get<bool>(var_1->function(D_gd_0_0)));
4092  EXPECT_FALSE(std::get<bool>(var_1->function(D_gd_0_1)));
4093  EXPECT_FALSE(std::get<bool>(var_1->function(D_gd_1_0)));
4094  EXPECT_FALSE(std::get<bool>(var_1->function(D_gd_1_1)));
4095  EXPECT_FALSE(std::get<bool>(var_1->function(D_d_0)));
4096  EXPECT_FALSE(std::get<bool>(var_1->function(D_d_1)));
4097  EXPECT_TRUE(std::get<bool>(var_1->function(ggd_0_0_0)));
4098  EXPECT_TRUE(std::get<bool>(var_1->function(ggd_0_0_1)));
4099  EXPECT_TRUE(std::get<bool>(var_1->function(gd_0_0)));
4100  EXPECT_TRUE(std::get<bool>(var_1->function(gd_0_1)));
4101  EXPECT_TRUE(std::get<bool>(var_1->function(d_0)));
4102  EXPECT_TRUE(std::get<bool>(var_1->function(d_1)));
4103  EXPECT_FALSE(std::get<bool>(var_1->function(not_child)));
4104  EXPECT_FALSE(std::get<bool>(var_1->function(not_child_2)));
4105 
4106  const Manager::Var* var_2 = Manager::Instance().getVariable("isMCDescendantOfList(B:vartest, -1)");
4107  ASSERT_NE(var_2, nullptr);
4108  EXPECT_FALSE(std::get<bool>(var_2->function(D_gd_0_0)));
4109  EXPECT_FALSE(std::get<bool>(var_2->function(D_gd_0_1)));
4110  EXPECT_FALSE(std::get<bool>(var_2->function(D_gd_1_0)));
4111  EXPECT_FALSE(std::get<bool>(var_2->function(D_gd_1_1)));
4112  EXPECT_FALSE(std::get<bool>(var_2->function(D_d_0)));
4113  EXPECT_FALSE(std::get<bool>(var_2->function(D_d_1)));
4114  EXPECT_TRUE(std::get<bool>(var_2->function(ggd_0_0_0)));
4115  EXPECT_TRUE(std::get<bool>(var_2->function(ggd_0_0_1)));
4116  EXPECT_TRUE(std::get<bool>(var_2->function(gd_0_0)));
4117  EXPECT_TRUE(std::get<bool>(var_2->function(gd_0_1)));
4118  EXPECT_TRUE(std::get<bool>(var_2->function(d_0)));
4119  EXPECT_TRUE(std::get<bool>(var_2->function(d_1)));
4120  EXPECT_FALSE(std::get<bool>(var_2->function(not_child)));
4121  EXPECT_FALSE(std::get<bool>(var_2->function(not_child_2)));
4122 
4123  const Manager::Var* var_3 = Manager::Instance().getVariable("isMCDescendantOfList(B:vartest, 1)");
4124  ASSERT_NE(var_3, nullptr);
4125  EXPECT_FALSE(std::get<bool>(var_3->function(D_gd_0_0)));
4126  EXPECT_FALSE(std::get<bool>(var_3->function(D_gd_0_1)));
4127  EXPECT_FALSE(std::get<bool>(var_3->function(D_gd_1_0)));
4128  EXPECT_FALSE(std::get<bool>(var_3->function(D_gd_1_1)));
4129  EXPECT_FALSE(std::get<bool>(var_3->function(D_d_0)));
4130  EXPECT_FALSE(std::get<bool>(var_3->function(D_d_1)));
4131  EXPECT_FALSE(std::get<bool>(var_3->function(ggd_0_0_0)));
4132  EXPECT_FALSE(std::get<bool>(var_3->function(ggd_0_0_1)));
4133  EXPECT_FALSE(std::get<bool>(var_3->function(gd_0_0)));
4134  EXPECT_FALSE(std::get<bool>(var_3->function(gd_0_1)));
4135  EXPECT_TRUE(std::get<bool>(var_3->function(d_0)));
4136  EXPECT_TRUE(std::get<bool>(var_3->function(d_1)));
4137  EXPECT_FALSE(std::get<bool>(var_3->function(not_child)));
4138  EXPECT_FALSE(std::get<bool>(var_3->function(not_child_2)));
4139 
4140  const Manager::Var* var_4 = Manager::Instance().getVariable("isMCDescendantOfList(B:vartest, 2)");
4141  ASSERT_NE(var_4, nullptr);
4142  EXPECT_FALSE(std::get<bool>(var_4->function(D_gd_0_0)));
4143  EXPECT_FALSE(std::get<bool>(var_4->function(D_gd_0_1)));
4144  EXPECT_FALSE(std::get<bool>(var_4->function(D_gd_1_0)));
4145  EXPECT_FALSE(std::get<bool>(var_4->function(D_gd_1_1)));
4146  EXPECT_FALSE(std::get<bool>(var_4->function(D_d_0)));
4147  EXPECT_FALSE(std::get<bool>(var_4->function(D_d_1)));
4148  EXPECT_FALSE(std::get<bool>(var_4->function(ggd_0_0_0)));
4149  EXPECT_FALSE(std::get<bool>(var_4->function(ggd_0_0_1)));
4150  EXPECT_TRUE(std::get<bool>(var_4->function(gd_0_0)));
4151  EXPECT_TRUE(std::get<bool>(var_4->function(gd_0_1)));
4152  EXPECT_FALSE(std::get<bool>(var_4->function(d_0)));
4153  EXPECT_FALSE(std::get<bool>(var_4->function(d_1)));
4154  EXPECT_FALSE(std::get<bool>(var_4->function(not_child)));
4155  EXPECT_FALSE(std::get<bool>(var_4->function(not_child_2)));
4156 
4157 
4158  const Manager::Var* var_5 = Manager::Instance().getVariable("isMCDescendantOfList(B:vartest, 3)");
4159  ASSERT_NE(var_5, nullptr);
4160  EXPECT_FALSE(std::get<bool>(var_5->function(D_gd_0_0)));
4161  EXPECT_FALSE(std::get<bool>(var_5->function(D_gd_0_1)));
4162  EXPECT_FALSE(std::get<bool>(var_5->function(D_gd_1_0)));
4163  EXPECT_FALSE(std::get<bool>(var_5->function(D_gd_1_1)));
4164  EXPECT_FALSE(std::get<bool>(var_5->function(D_d_0)));
4165  EXPECT_FALSE(std::get<bool>(var_5->function(D_d_1)));
4166  EXPECT_TRUE(std::get<bool>(var_5->function(ggd_0_0_0)));
4167  EXPECT_TRUE(std::get<bool>(var_5->function(ggd_0_0_1)));
4168  EXPECT_FALSE(std::get<bool>(var_5->function(gd_0_0)));
4169  EXPECT_FALSE(std::get<bool>(var_5->function(gd_0_1)));
4170  EXPECT_FALSE(std::get<bool>(var_5->function(d_0)));
4171  EXPECT_FALSE(std::get<bool>(var_5->function(d_1)));
4172  EXPECT_FALSE(std::get<bool>(var_5->function(not_child)));
4173  EXPECT_FALSE(std::get<bool>(var_5->function(not_child_2)));
4174  }
4175 
4176 
4177 
4178 
4179 
4180  class PIDVariableTest : public ::testing::Test {
4181  protected:
4183  void SetUp() override
4184  {
4185  DataStore::Instance().setInitializeActive(true);
4188  StoreArray<MCParticle> mcparticles;
4189  StoreArray<PIDLikelihood> likelihood;
4190  StoreArray<Particle> particles;
4191  StoreArray<Track> tracks;
4192  peim.registerInDataStore();
4193  tfrs.registerInDataStore();
4194  mcparticles.registerInDataStore();
4195  likelihood.registerInDataStore();
4196  particles.registerInDataStore();
4197  tracks.registerInDataStore();
4198  particles.registerRelationTo(likelihood);
4199  tracks.registerRelationTo(likelihood);
4200  DataStore::Instance().setInitializeActive(false);
4201  }
4202 
4204  void TearDown() override
4205  {
4206  DataStore::Instance().reset();
4207  }
4208  };
4209 
4210  TEST_F(PIDVariableTest, LogLikelihood)
4211  {
4212  StoreArray<PIDLikelihood> likelihood;
4213  StoreArray<Particle> particles;
4214  StoreArray<Track> tracks;
4216 
4217  // create tracks and trackFitResutls
4218  TRandom3 generator;
4219  const float pValue = 0.5;
4220  const float bField = 1.5;
4221  const int charge = 1;
4222  TMatrixDSym cov6(6);
4223  // Generate a random put orthogonal pair of vectors in the r-phi plane
4224  ROOT::Math::Cartesian2D d(generator.Uniform(-1, 1), generator.Uniform(-1, 1));
4225  ROOT::Math::Cartesian2D pt(generator.Uniform(-1, 1), generator.Uniform(-1, 1));
4226  d.SetXY(d.X(), -(d.X()*pt.X()) / pt.Y());
4227  // Add a random z component
4228  ROOT::Math::XYZVector position(d.X(), d.Y(), generator.Uniform(-1, 1));
4229  ROOT::Math::XYZVector momentum(pt.X(), pt.Y(), generator.Uniform(-1, 1));
4230 
4231  auto CDCValue = static_cast<unsigned long long int>(0x300000000000000);
4232  tfrs.appendNew(position, momentum, cov6, charge, Const::electron, pValue, bField, CDCValue, 16777215, 0);
4233  Track mytrack;
4234  mytrack.setTrackFitResultIndex(Const::electron, 0);
4235  Track* allTrack = tracks.appendNew(mytrack);
4236  Track* noSVDTrack = tracks.appendNew(mytrack);
4237  Track* noPIDTrack = tracks.appendNew(mytrack);
4238  Track* dEdxTrack = tracks.appendNew(mytrack);
4239 
4240  // Fill by hand likelihood values for all the detectors and hypothesis
4241  // This is clearly not a physical case, since a particle cannot leave good
4242  // signals in both TOP and ARICH
4243  auto* lAll = likelihood.appendNew();
4244  lAll->setLogLikelihood(Const::TOP, Const::electron, 0.18);
4245  lAll->setLogLikelihood(Const::ARICH, Const::electron, 0.16);
4246  lAll->setLogLikelihood(Const::ECL, Const::electron, 0.14);
4247  lAll->setLogLikelihood(Const::CDC, Const::electron, 0.12);
4248  lAll->setLogLikelihood(Const::SVD, Const::electron, 0.1);
4249  lAll->setLogLikelihood(Const::KLM, Const::electron, 0.01);
4250 
4251  lAll->setLogLikelihood(Const::TOP, Const::muon, 0.5);
4252  lAll->setLogLikelihood(Const::ARICH, Const::muon, 0.52);
4253  lAll->setLogLikelihood(Const::ECL, Const::muon, 0.54);
4254  lAll->setLogLikelihood(Const::CDC, Const::muon, 0.56);
4255  lAll->setLogLikelihood(Const::SVD, Const::muon, 0.58);
4256  lAll->setLogLikelihood(Const::KLM, Const::muon, 0.8);
4257 
4258  lAll->setLogLikelihood(Const::TOP, Const::pion, 0.2);
4259  lAll->setLogLikelihood(Const::ARICH, Const::pion, 0.22);
4260  lAll->setLogLikelihood(Const::ECL, Const::pion, 0.24);
4261  lAll->setLogLikelihood(Const::CDC, Const::pion, 0.26);
4262  lAll->setLogLikelihood(Const::SVD, Const::pion, 0.28);
4263  lAll->setLogLikelihood(Const::KLM, Const::pion, 0.2);
4264 
4265  lAll->setLogLikelihood(Const::TOP, Const::kaon, 0.3);
4266  lAll->setLogLikelihood(Const::ARICH, Const::kaon, 0.32);
4267  lAll->setLogLikelihood(Const::ECL, Const::kaon, 0.34);
4268  lAll->setLogLikelihood(Const::CDC, Const::kaon, 0.36);
4269  lAll->setLogLikelihood(Const::SVD, Const::kaon, 0.38);
4270  lAll->setLogLikelihood(Const::KLM, Const::kaon, 0.2);
4271 
4272  lAll->setLogLikelihood(Const::TOP, Const::proton, 0.4);
4273  lAll->setLogLikelihood(Const::ARICH, Const::proton, 0.42);
4274  lAll->setLogLikelihood(Const::ECL, Const::proton, 0.44);
4275  lAll->setLogLikelihood(Const::CDC, Const::proton, 0.46);
4276  lAll->setLogLikelihood(Const::SVD, Const::proton, 0.48);
4277  lAll->setLogLikelihood(Const::KLM, Const::proton, 0.02);
4278 
4279  lAll->setLogLikelihood(Const::TOP, Const::deuteron, 0.6);
4280  lAll->setLogLikelihood(Const::ARICH, Const::deuteron, 0.62);
4281  lAll->setLogLikelihood(Const::ECL, Const::deuteron, 0.64);
4282  lAll->setLogLikelihood(Const::CDC, Const::deuteron, 0.66);
4283  lAll->setLogLikelihood(Const::SVD, Const::deuteron, 0.68);
4284  lAll->setLogLikelihood(Const::KLM, Const::deuteron, 0.02);
4285 
4286  // Likelihoods for all detectors but SVD
4287  auto* lAllNoSVD = likelihood.appendNew();
4288 
4289  for (const auto& det : Const::PIDDetectorSet::set()) {
4290  for (const auto& hypo : Const::chargedStableSet) {
4291  if (det != Const::SVD) {
4292  lAllNoSVD->setLogLikelihood(det, hypo, lAll->getLogL(hypo, det));
4293  }
4294  }
4295  }
4296 
4297  // Likelihoods for a dEdx only case
4298  auto* ldEdx = likelihood.appendNew();
4299  ldEdx->setLogLikelihood(Const::CDC, Const::electron, 0.12);
4300  ldEdx->setLogLikelihood(Const::SVD, Const::electron, 0.1);
4301 
4302  ldEdx->setLogLikelihood(Const::CDC, Const::pion, 0.26);
4303  ldEdx->setLogLikelihood(Const::SVD, Const::pion, 0.28);
4304 
4305  ldEdx->setLogLikelihood(Const::CDC, Const::kaon, 0.36);
4306  ldEdx->setLogLikelihood(Const::SVD, Const::kaon, 0.38);
4307 
4308  ldEdx->setLogLikelihood(Const::CDC, Const::proton, 0.46);
4309  ldEdx->setLogLikelihood(Const::SVD, Const::proton, 0.48);
4310 
4311  ldEdx->setLogLikelihood(Const::CDC, Const::muon, 0.56);
4312  ldEdx->setLogLikelihood(Const::SVD, Const::muon, 0.58);
4313 
4314  ldEdx->setLogLikelihood(Const::CDC, Const::deuteron, 0.66);
4315  ldEdx->setLogLikelihood(Const::SVD, Const::deuteron, 0.68);
4316 
4317  allTrack->addRelationTo(lAll);
4318  noSVDTrack->addRelationTo(lAllNoSVD);
4319  dEdxTrack->addRelationTo(ldEdx);
4320 
4321  // Table with the sum(LogL) for several cases
4322  // All dEdx AllNoSVD
4323  // e 0.71 0.22 0.61
4324  // mu 3.5 1.14 2.92
4325  // pi 1.4 0.54 1.12
4326  // k 1.9 0.74 1.52
4327  // p 2.22 0.94 1.74
4328  // d 3.22 1.34 2.54
4329 
4330  auto* particleAll = particles.appendNew(allTrack, Const::pion);
4331  auto* particleNoSVD = particles.appendNew(noSVDTrack, Const::pion);
4332  auto* particledEdx = particles.appendNew(dEdxTrack, Const::pion);
4333  auto* particleNoID = particles.appendNew(noPIDTrack, Const::pion);
4334 
4335  double numsumexp = std::exp(0.71) + std::exp(3.5) + std::exp(1.4) + std::exp(1.9) + std::exp(2.22) + std::exp(3.22);
4336  double numsumexp_noSVD = std::exp(0.61) + std::exp(2.92) + std::exp(1.12) + std::exp(1.52) + std::exp(1.74) + std::exp(2.54);
4337 
4338  // Basic PID quantities. Currently just wrappers for global probability.
4339  EXPECT_FLOAT_EQ(electronID(particleAll), std::exp(0.71) / numsumexp);
4340  EXPECT_FLOAT_EQ(muonID(particleAll), std::exp(3.5) / numsumexp);
4341  EXPECT_FLOAT_EQ(pionID(particleAll), std::exp(1.4) / numsumexp);
4342  EXPECT_FLOAT_EQ(kaonID(particleAll), std::exp(1.9) / numsumexp);
4343  EXPECT_FLOAT_EQ(protonID(particleAll), std::exp(2.22) / numsumexp);
4344  EXPECT_FLOAT_EQ(deuteronID(particleAll), std::exp(3.22) / numsumexp);
4345 
4346  // smart PID that takes the hypothesis into account
4347  auto* particleElectron = particles.appendNew(allTrack, Const::electron);
4348  auto* particleMuon = particles.appendNew(allTrack, Const::muon);
4349  auto* particleKaon = particles.appendNew(allTrack, Const::kaon);
4350  auto* particleProton = particles.appendNew(allTrack, Const::proton);
4351  auto* particleDeuteron = particles.appendNew(allTrack, Const::deuteron);
4352 
4353  EXPECT_FLOAT_EQ(particleID(particleAll), std::exp(1.4) / numsumexp); // there's already a pion
4354  EXPECT_FLOAT_EQ(particleID(particleElectron), std::exp(0.71) / numsumexp);
4355  EXPECT_FLOAT_EQ(particleID(particleMuon), std::exp(3.5) / numsumexp);
4356  EXPECT_FLOAT_EQ(particleID(particleKaon), std::exp(1.9) / numsumexp);
4357  EXPECT_FLOAT_EQ(particleID(particleProton), std::exp(2.22) / numsumexp);
4358  EXPECT_FLOAT_EQ(particleID(particleDeuteron), std::exp(3.22) / numsumexp);
4359 
4360  // TEMP: PID w/o the SVD.
4361  EXPECT_FLOAT_EQ(electronID_noSVD(particleNoSVD), std::exp(0.61) / numsumexp_noSVD);
4362  EXPECT_FLOAT_EQ(muonID_noSVD(particleNoSVD), std::exp(2.92) / numsumexp_noSVD);
4363  EXPECT_FLOAT_EQ(pionID_noSVD(particleNoSVD), std::exp(1.12) / numsumexp_noSVD);
4364  EXPECT_FLOAT_EQ(kaonID_noSVD(particleNoSVD), std::exp(1.52) / numsumexp_noSVD);
4365  EXPECT_FLOAT_EQ(protonID_noSVD(particleNoSVD), std::exp(1.74) / numsumexp_noSVD);
4366  EXPECT_FLOAT_EQ(deuteronID_noSVD(particleNoSVD), std::exp(2.54) / numsumexp_noSVD);
4367 
4368  // Binary PID
4369  std::vector<double> v_pi_K {211., 321.};
4370  std::vector<double> v_pi_p {211., 2212.};
4371  std::vector<double> v_K_p {321., 2212.};
4372  EXPECT_FLOAT_EQ(binaryPID(particleAll, v_pi_K), std::exp(1.4) / (std::exp(1.4) + std::exp(1.9)));
4373  EXPECT_FLOAT_EQ(binaryPID(particleAll, v_pi_p), std::exp(1.4) / (std::exp(1.4) + std::exp(2.22)));
4374  EXPECT_FLOAT_EQ(binaryPID(particleAll, v_K_p), std::exp(1.9) / (std::exp(1.9) + std::exp(2.22)));
4375 
4376  // Check what happens if no Likelihood is available
4377  EXPECT_TRUE(std::isnan(electronID(particleNoID)));
4378  EXPECT_TRUE(std::isnan(muonID(particleNoID)));
4379  EXPECT_TRUE(std::isnan(pionID(particleNoID)));
4380  EXPECT_TRUE(std::isnan(kaonID(particleNoID)));
4381  EXPECT_TRUE(std::isnan(protonID(particleNoID)));
4382  EXPECT_TRUE(std::isnan(deuteronID(particleNoID)));
4383 
4384  //expert stuff: LogL values
4385  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidLogLikelihoodValueExpert(11, TOP)")->function(particleAll)),
4386  0.18);
4387  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidLogLikelihoodValueExpert(11, ALL)")->function(particleAll)),
4388  0.71);
4389  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidLogLikelihoodValueExpert(2212, TOP, CDC)")->function(
4390  particleAll)), 0.86);
4391 
4392  // global probability
4393  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidProbabilityExpert(1000010020, ALL)")->function(particleAll)),
4394  std::exp(3.22) / numsumexp);
4395  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidProbabilityExpert(2212, ALL)")->function(particleAll)),
4396  std::exp(2.22) / numsumexp);
4397  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidProbabilityExpert(211, ALL)")->function(particleAll)),
4398  std::exp(1.4) / numsumexp);
4399  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidProbabilityExpert(321, ALL)")->function(particleAll)),
4400  std::exp(1.9) / numsumexp);
4401  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidProbabilityExpert(13, ALL)")->function(particleAll)),
4402  std::exp(3.5) / numsumexp);
4403  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidProbabilityExpert(11, ALL)")->function(particleAll)),
4404  std::exp(0.71) / numsumexp);
4405  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidProbabilityExpert(211, ALL)")->function(particledEdx)),
4406  std::exp(0.54) / (std::exp(0.22) + std::exp(1.14) + std::exp(0.54) + std::exp(0.74) + std::exp(0.94) + std::exp(1.34)));
4407  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidProbabilityExpert(211, ALL)")->function(particledEdx)),
4408  std::get<double>(Manager::Instance().getVariable("pidProbabilityExpert(211, CDC, SVD)")->function(particleAll)));
4409  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidProbabilityExpert(211, CDC)")->function(particledEdx)),
4410  std::get<double>(Manager::Instance().getVariable("pidProbabilityExpert(211, CDC)")->function(particleAll)));
4411  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidProbabilityExpert(321, CDC)")->function(particleAll)),
4412  std::exp(0.36) / (std::exp(0.12) + std::exp(0.26) + std::exp(0.36) + std::exp(0.46) + std::exp(0.56) + std::exp(0.66)));
4413 
4414  // binary probability
4415  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidPairProbabilityExpert(321, 2212, ALL)")->function(
4416  particleAll)),
4417  1.0 / (1.0 + std::exp(2.22 - 1.9)));
4418  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidPairProbabilityExpert(321, 2212, ALL)")->function(
4419  particledEdx)),
4420  1.0 / (1.0 + std::exp(0.94 - 0.74)));
4421  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidPairProbabilityExpert(321, 2212, CDC, SVD)")->function(
4422  particleAll)),
4423  1.0 / (1.0 + std::exp(0.94 - 0.74)));
4424 
4425  // No likelihood available
4426  EXPECT_TRUE(std::isnan(std::get<double>(Manager::Instance().getVariable("pidPairProbabilityExpert(321, 2212, KLM)")->function(
4427  particledEdx))));
4428  EXPECT_TRUE(std::isnan(std::get<double>(Manager::Instance().getVariable("pidLogLikelihoodValueExpert(11, TOP, CDC, SVD)")->function(
4429  particleNoID))));
4430  EXPECT_TRUE(std::isnan(std::get<double>(Manager::Instance().getVariable("pidLogLikelihoodValueExpert(11, TOP)")->function(
4431  particledEdx))));
4432  EXPECT_TRUE(std::isnan(std::get<double>(Manager::Instance().getVariable("pidPairProbabilityExpert(321, 2212, KLM)")->function(
4433  particledEdx))));
4434  EXPECT_TRUE(std::isnan(std::get<double>
4435  (Manager::Instance().getVariable("pidPairProbabilityExpert(321, 2212, ECL, TOP, ARICH)")->function(
4436  particledEdx))));
4437  EXPECT_FALSE(std::isnan(std::get<double>
4438  (Manager::Instance().getVariable("pidPairProbabilityExpert(321, 2212, ECL, TOP, ARICH, SVD)")->function(
4439  particledEdx))));
4440  //Mostlikely PDG tests:
4441  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidMostLikelyPDG()")->function(particledEdx)), 1.00001e+09);
4442  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidMostLikelyPDG(0.5, 0.1, 0.1, 0.1, 0.1, 0.1)")->function(
4443  particledEdx)),
4444  Const::electron.getPDGCode());
4445  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidMostLikelyPDG(0.1, 0.5, 0.1, 0.1, 0.1, 0.1)")->function(
4446  particledEdx)),
4447  Const::muon.getPDGCode());
4448  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidMostLikelyPDG(0.1, 0.1, 0.5, 0.1, 0.1, 0.1)")->function(
4449  particledEdx)),
4450  Const::pion.getPDGCode());
4451  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidMostLikelyPDG(0.1, 0.1, 0.1, 0.5, 0.1, 0.1)")->function(
4452  particledEdx)),
4453  Const::kaon.getPDGCode());
4454  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidMostLikelyPDG(0.1, 0.1, 0.1, 0.1, 0.5, 0.1)")->function(
4455  particledEdx)),
4456  Const::proton.getPDGCode());
4457  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidMostLikelyPDG(0, 1., 0, 0, 0, 0)")->function(particledEdx)),
4458  Const::muon.getPDGCode());
4459  }
4460 
4461  TEST_F(PIDVariableTest, MissingLikelihood)
4462  {
4463  StoreArray<PIDLikelihood> likelihood;
4464  StoreArray<Particle> particles;
4465  StoreArray<Track> tracks;
4467 
4468  // create tracks and trackFitResutls
4469  TRandom3 generator;
4470  const float pValue = 0.5;
4471  const float bField = 1.5;
4472  const int charge = 1;
4473  TMatrixDSym cov6(6);
4474  // Generate a random put orthogonal pair of vectors in the r-phi plane
4475  ROOT::Math::Cartesian2D d(generator.Uniform(-1, 1), generator.Uniform(-1, 1));
4476  ROOT::Math::Cartesian2D pt(generator.Uniform(-1, 1), generator.Uniform(-1, 1));
4477  d.SetXY(d.X(), -(d.X()*pt.X()) / pt.Y());
4478  // Add a random z component
4479  ROOT::Math::XYZVector position(d.X(), d.Y(), generator.Uniform(-1, 1));
4480  ROOT::Math::XYZVector momentum(pt.X(), pt.Y(), generator.Uniform(-1, 1));
4481 
4482  auto CDCValue = static_cast<unsigned long long int>(0x300000000000000);
4483  tfrs.appendNew(position, momentum, cov6, charge, Const::electron, pValue, bField, CDCValue, 16777215, 0);
4484  Track mytrack;
4485  mytrack.setTrackFitResultIndex(Const::electron, 0);
4486  Track* savedTrack1 = tracks.appendNew(mytrack);
4487  Track* savedTrack2 = tracks.appendNew(mytrack);
4488  Track* savedTrack3 = tracks.appendNew(mytrack);
4489  Track* savedTrack4 = tracks.appendNew(mytrack);
4490 
4491  auto* l1 = likelihood.appendNew();
4492  l1->setLogLikelihood(Const::TOP, Const::electron, 0.18);
4493  l1->setLogLikelihood(Const::ECL, Const::electron, 0.14);
4494  savedTrack1->addRelationTo(l1);
4495 
4496  auto* electron = particles.appendNew(savedTrack1, Const::electron);
4497 
4498  auto* l2 = likelihood.appendNew();
4499  l2->setLogLikelihood(Const::TOP, Const::pion, 0.2);
4500  l2->setLogLikelihood(Const::ARICH, Const::pion, 0.22);
4501  l2->setLogLikelihood(Const::ECL, Const::pion, 0.24);
4502  l2->setLogLikelihood(Const::CDC, Const::pion, 0.26);
4503  l2->setLogLikelihood(Const::SVD, Const::pion, 0.28);
4504  savedTrack2->addRelationTo(l2);
4505 
4506  auto* pion = particles.appendNew(savedTrack2, Const::pion);
4507 
4508  auto* l3 = likelihood.appendNew();
4509  l3->setLogLikelihood(Const::TOP, Const::kaon, 0.3);
4510  l3->setLogLikelihood(Const::ARICH, Const::kaon, 0.32);
4511  savedTrack3->addRelationTo(l3);
4512 
4513  auto* kaon = particles.appendNew(savedTrack3, Const::kaon);
4514 
4515  auto* l4 = likelihood.appendNew();
4516  l4->setLogLikelihood(Const::ARICH, Const::proton, 0.42);
4517  l4->setLogLikelihood(Const::ECL, Const::proton, 0.44);
4518  l4->setLogLikelihood(Const::CDC, Const::proton, 0.46);
4519  l4->setLogLikelihood(Const::SVD, Const::proton, 0.48);
4520  savedTrack4->addRelationTo(l4);
4521 
4522  auto* proton = particles.appendNew(savedTrack4, Const::proton);
4523 
4524  const Manager::Var* varMissECL = Manager::Instance().getVariable("pidMissingProbabilityExpert(ECL)");
4525  const Manager::Var* varMissTOP = Manager::Instance().getVariable("pidMissingProbabilityExpert(TOP)");
4526  const Manager::Var* varMissARICH = Manager::Instance().getVariable("pidMissingProbabilityExpert(ARICH)");
4527 
4528 
4529  EXPECT_FLOAT_EQ(std::get<double>(varMissTOP->function(electron)), 0.0);
4530  EXPECT_FLOAT_EQ(std::get<double>(varMissTOP->function(pion)), 0.0);
4531  EXPECT_FLOAT_EQ(std::get<double>(varMissTOP->function(kaon)), 0.0);
4532  EXPECT_FLOAT_EQ(std::get<double>(varMissTOP->function(proton)), 1.0);
4533 
4534  EXPECT_FLOAT_EQ(std::get<double>(varMissARICH->function(electron)), 1.0);
4535  EXPECT_FLOAT_EQ(std::get<double>(varMissARICH->function(pion)), 0.0);
4536  EXPECT_FLOAT_EQ(std::get<double>(varMissARICH->function(kaon)), 0.0);
4537  EXPECT_FLOAT_EQ(std::get<double>(varMissARICH->function(proton)), 0.0);
4538 
4539  EXPECT_FLOAT_EQ(std::get<double>(varMissECL->function(electron)), 0.0);
4540  EXPECT_FLOAT_EQ(std::get<double>(varMissECL->function(pion)), 0.0);
4541  EXPECT_FLOAT_EQ(std::get<double>(varMissECL->function(kaon)), 1.0);
4542  EXPECT_FLOAT_EQ(std::get<double>(varMissECL->function(proton)), 0.0);
4543  }
4544 
4545  class FlightInfoTest : public ::testing::Test {
4546  protected:
4548  void SetUp() override
4549  {
4550  DataStore::Instance().setInitializeActive(true);
4553  StoreArray<MCParticle> mcParticles;
4554  StoreArray<Particle> particles;
4555  particles.registerRelationTo(mcParticles);
4557  DataStore::Instance().setInitializeActive(false);
4558 
4559 
4560  // Insert MC particle logic here
4561  MCParticle mcKs;
4562  mcKs.setPDG(Const::Kshort.getPDGCode());
4563  mcKs.setProductionVertex(1.0, 1.0, 0.0);
4564  mcKs.setDecayVertex(4.0, 5.0, 0.0);
4565  mcKs.setProductionTime(0);
4566  mcKs.setMassFromPDG();
4567  mcKs.setMomentum(1.164, 1.55200, 0);
4568  float decayTime = 5 * mcKs.getMass() / mcKs.getEnergy();
4569  mcKs.setDecayTime(decayTime);
4570  mcKs.setStatus(MCParticle::c_PrimaryParticle);
4571  MCParticle* newMCKs = mcParticles.appendNew(mcKs);
4572 
4573 
4574 
4575  MCParticle mcDp;
4576  mcDp.setPDG(411);
4577  mcDp.setDecayVertex(1.0, 1.0, 0.0);
4578  mcDp.setMassFromPDG();
4579  mcDp.setStatus(MCParticle::c_PrimaryParticle);
4580  MCParticle* newMCDp = mcParticles.appendNew(mcDp);
4581 
4582  // Insert Reco particle logic here
4583  PxPyPzEVector momentum;
4584  TMatrixFSym error(7);
4585  error.Zero();
4586  error(0, 0) = 0.05;
4587  error(1, 1) = 0.2;
4588  error(2, 2) = 0.4;
4589  error(3, 3) = 0.01;
4590  error(4, 4) = 0.04;
4591  error(5, 5) = 0.00875;
4592  error(6, 6) = 0.01;
4593  Particle pi(PxPyPzEVector(1.59607, 1.19705, 0, 2), 211);
4594  momentum += pi.get4Vector();
4595  Particle* newpi = particles.appendNew(pi);
4596 
4597 
4598  Particle Ks(PxPyPzEVector(1.164, 1.55200, 0, 2), 310, Particle::c_Unflavored, Particle::c_Composite, 0);
4599  Ks.setVertex(XYZVector(4.0, 5.0, 0.0));
4600  Ks.setMomentumVertexErrorMatrix(error); // (order: px,py,pz,E,x,y,z)
4601  momentum += Ks.get4Vector();
4602  Ks.addExtraInfo("prodVertX", 1.0);
4603  Ks.addExtraInfo("prodVertY", 1.0);
4604  Ks.addExtraInfo("prodVertZ", 0.0);
4605  Ks.addExtraInfo("prodVertSxx", 0.04);
4606  Ks.addExtraInfo("prodVertSxy", 0.0);
4607  Ks.addExtraInfo("prodVertSxz", 0.0);
4608  Ks.addExtraInfo("prodVertSyx", 0.0);
4609  Ks.addExtraInfo("prodVertSyy", 0.00875);
4610  Ks.addExtraInfo("prodVertSyz", 0.0);
4611  Ks.addExtraInfo("prodVertSzx", 0.0);
4612  Ks.addExtraInfo("prodVertSzy", 0.0);
4613  Ks.addExtraInfo("prodVertSzz", 0.01);
4614  Particle* newKs = particles.appendNew(Ks);
4615  newKs->addRelationTo(newMCKs);
4616 
4617 
4618  Particle Dp(momentum, 411, Particle::c_Flavored, Particle::c_Composite, 0);
4619  Dp.appendDaughter(newpi);
4620  Dp.appendDaughter(newKs);
4621  XYZVector motherVtx(1.0, 1.0, 0.0);
4622  Dp.setVertex(motherVtx);
4623  Dp.setMomentumVertexErrorMatrix(error); // (order: px,py,pz,E,x,y,z)
4624  Dp.addExtraInfo("prodVertX", 0.0);
4625  Dp.addExtraInfo("prodVertY", 1.0);
4626  Dp.addExtraInfo("prodVertZ", -2.0);
4627  Dp.addExtraInfo("prodVertSxx", 0.04);
4628  Dp.addExtraInfo("prodVertSxy", 0.0);
4629  Dp.addExtraInfo("prodVertSxz", 0.0);
4630  Dp.addExtraInfo("prodVertSyx", 0.0);
4631  Dp.addExtraInfo("prodVertSyy", 0.01);
4632  Dp.addExtraInfo("prodVertSyz", 0.0);
4633  Dp.addExtraInfo("prodVertSzx", 0.0);
4634  Dp.addExtraInfo("prodVertSzy", 0.0);
4635  Dp.addExtraInfo("prodVertSzz", 0.1575);
4636  Particle* newDp = particles.appendNew(Dp);
4637  newDp->addRelationTo(newMCDp);
4638 
4639  }
4640 
4642  void TearDown() override
4643  {
4644  DataStore::Instance().reset();
4645  }
4646  };
4647  TEST_F(FlightInfoTest, flightDistance)
4648  {
4649  StoreArray<Particle> particles{};
4650  const Particle* newKs = particles[1]; // Ks had flight distance of 5 cm
4651 
4652  const Manager::Var* var = Manager::Instance().getVariable("flightDistance");
4653  ASSERT_NE(var, nullptr);
4654  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), 5.0);
4655  }
4656  TEST_F(FlightInfoTest, flightDistanceErr)
4657  {
4658  StoreArray<Particle> particles{};
4659  const Particle* newKs = particles[1]; // Ks had flight distance of 5 cm
4660 
4661  const Manager::Var* var = Manager::Instance().getVariable("flightDistanceErr");
4662  ASSERT_NE(var, nullptr);
4663  EXPECT_GT(std::get<double>(var->function(newKs)), 0.0);
4664  }
4665  TEST_F(FlightInfoTest, flightTime)
4666  {
4667  StoreArray<Particle> particles{};
4668  const Particle* newKs = particles[1]; // Ks had flight time of 0.0427 us (t = d/c * m/p)
4669 
4670  const Manager::Var* var = Manager::Instance().getVariable("flightTime");
4671  ASSERT_NE(var, nullptr);
4672  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), 5.0 / Const::speedOfLight * newKs->getPDGMass() / newKs->getP());
4673  }
4674 
4675  TEST_F(FlightInfoTest, flightTimeErr)
4676  {
4677  StoreArray<Particle> particles{};
4678  const Particle* newKs = particles[1]; // Ks should have positive flight distance uncertainty
4679 
4680  const Manager::Var* var = Manager::Instance().getVariable("flightTimeErr");
4681  ASSERT_NE(var, nullptr);
4682  EXPECT_GT(std::get<double>(var->function(newKs)), 0.0);
4683  }
4684 
4685  TEST_F(FlightInfoTest, flightDistanceOfDaughter)
4686  {
4687  StoreArray<Particle> particles{};
4688  const Particle* newDp = particles[2]; // Get D+, its daughter Ks had flight distance of 5 cm
4689 
4690  const Manager::Var* var = Manager::Instance().getVariable("flightDistanceOfDaughter(1)");
4691  ASSERT_NE(var, nullptr);
4692  EXPECT_FLOAT_EQ(std::get<double>(var->function(newDp)), 5.0);
4693 
4694  var = Manager::Instance().getVariable("flightDistanceOfDaughter(3)");
4695  ASSERT_NE(var, nullptr);
4696  EXPECT_TRUE(std::isnan(std::get<double>(var->function(newDp))));
4697  }
4698  TEST_F(FlightInfoTest, flightDistanceOfDaughterErr)
4699  {
4700  StoreArray<Particle> particles{};
4701  const Particle* newDp = particles[2]; // Get D+, its daughter Ks should have positive flight distance uncertainty
4702 
4703  const Manager::Var* var = Manager::Instance().getVariable("flightDistanceOfDaughterErr(1)");
4704  ASSERT_NE(var, nullptr);
4705  EXPECT_GT(std::get<double>(var->function(newDp)), 0.0);
4706 
4707  var = Manager::Instance().getVariable("flightDistanceOfDaughterErr(3)");
4708  ASSERT_NE(var, nullptr);
4709  EXPECT_TRUE(std::isnan(std::get<double>(var->function(newDp))));
4710  }
4711  TEST_F(FlightInfoTest, flightTimeOfDaughter)
4712  {
4713  StoreArray<Particle> particles{};
4714  const Particle* newDp = particles[2]; // Get D+, its daughter Ks had flight time of 0.0427 us (t = d/c * m/p)
4715 
4716  const Manager::Var* var = Manager::Instance().getVariable("flightTimeOfDaughter(1)");
4717  ASSERT_NE(var, nullptr);
4718  const Particle* Ks = newDp->getDaughter(1);
4719 
4720  EXPECT_FLOAT_EQ(std::get<double>(var->function(newDp)), 5.0 / Const::speedOfLight * Ks->getPDGMass() / Ks->getP());
4721 
4722  var = Manager::Instance().getVariable("flightTimeOfDaughter(3)");
4723  ASSERT_NE(var, nullptr);
4724  EXPECT_TRUE(std::isnan(std::get<double>(var->function(newDp))));
4725  }
4726  TEST_F(FlightInfoTest, flightTimeOfDaughterErr)
4727  {
4728  StoreArray<Particle> particles{};
4729  const Particle* newDp = particles[2]; // Get D+, its daughter Ks should have positive flight time uncertainty
4730 
4731  const Manager::Var* var = Manager::Instance().getVariable("flightTimeOfDaughterErr(1)");
4732  ASSERT_NE(var, nullptr);
4733  EXPECT_GT(std::get<double>(var->function(newDp)), 0.0);
4734 
4735  var = Manager::Instance().getVariable("flightTimeOfDaughterErr(3)");
4736  ASSERT_NE(var, nullptr);
4737  EXPECT_TRUE(std::isnan(std::get<double>(var->function(newDp))));
4738  }
4739  TEST_F(FlightInfoTest, mcFlightDistanceOfDaughter)
4740  {
4741  StoreArray<Particle> particles{};
4742  const Particle* newDp = particles[2]; // Get D+, its daughter Ks had flight distance of 5 cm
4743 
4744  const Manager::Var* var = Manager::Instance().getVariable("mcFlightDistanceOfDaughter(1)");
4745  ASSERT_NE(var, nullptr);
4746 
4747  EXPECT_FLOAT_EQ(std::get<double>(var->function(newDp)), 5.0);
4748 
4749  var = Manager::Instance().getVariable("mcFlightDistanceOfDaughter(3)");
4750  ASSERT_NE(var, nullptr);
4751  EXPECT_TRUE(std::isnan(std::get<double>(var->function(newDp))));
4752  }
4753  TEST_F(FlightInfoTest, mcFlightTimeOfDaughter)
4754  {
4755  StoreArray<Particle> particles{};
4756  const Particle* newDp = particles[2]; // Get D+, its daughter Ks had flight time of 0.0427 us (t = d/c * m/p)
4757 
4758  const Manager::Var* var = Manager::Instance().getVariable("mcFlightTimeOfDaughter(1)");
4759  ASSERT_NE(var, nullptr);
4760  auto* Ks = newDp->getDaughter(1)->getRelatedTo<MCParticle>();
4761  // double p = Ks->getMomentum().Mag();
4762  // EXPECT_FLOAT_EQ(std::get<double>(var->function(newDp)), 5.0 / Const::speedOfLight * Ks->getMass() / p);
4763 
4764  EXPECT_FLOAT_EQ(std::get<double>(var->function(newDp)), Ks->getLifetime() / Ks->getEnergy()*Ks->getMass());
4765 
4766  var = Manager::Instance().getVariable("mcFlightTimeOfDaughter(3)");
4767  ASSERT_NE(var, nullptr);
4768  EXPECT_TRUE(std::isnan(std::get<double>(var->function(newDp))));
4769  }
4770 
4771  TEST_F(FlightInfoTest, vertexDistance)
4772  {
4773  StoreArray<Particle> particles{};
4774  const Particle* newKS = particles[1]; // Get KS, as it has both a production and decay vertex
4775 
4776  const Manager::Var* var = Manager::Instance().getVariable("vertexDistance");
4777  ASSERT_NE(var, nullptr);
4778  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKS)), 5.0);
4779  }
4780 
4781  TEST_F(FlightInfoTest, vertexDistanceError)
4782  {
4783  StoreArray<Particle> particles{};
4784  const Particle* newKS = particles[1]; // Get KS, as it has both a production and decay vertex
4785 
4786  const Manager::Var* var = Manager::Instance().getVariable("vertexDistanceErr");
4787  ASSERT_NE(var, nullptr);
4788  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKS)), 0.2);
4789  }
4790 
4791  TEST_F(FlightInfoTest, vertexDistanceSignificance)
4792  {
4793  StoreArray<Particle> particles{};
4794  const Particle* newKS = particles[1]; // Get KS, as it has both a production and decay vertex
4795 
4796  const Manager::Var* var = Manager::Instance().getVariable("vertexDistanceSignificance");
4797  ASSERT_NE(var, nullptr);
4798  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKS)), 25);
4799  }
4800 
4801  TEST_F(FlightInfoTest, vertexDistanceOfDaughter)
4802  {
4803  StoreArray<Particle> particles{};
4804  const Particle* newDp = particles[2]; // Get D+, its daughter KS has both a production and decay vertex
4805 
4806  const Manager::Var* var = Manager::Instance().getVariable("vertexDistanceOfDaughter(1, 0)");
4807  ASSERT_NE(var, nullptr);
4808  EXPECT_FLOAT_EQ(std::get<double>(var->function(newDp)), 5.0);
4809 
4810  var = Manager::Instance().getVariable("vertexDistanceOfDaughter(1)");
4811  ASSERT_NE(var, nullptr);
4812  EXPECT_FLOAT_EQ(std::get<double>(var->function(newDp)), 6.0);
4813 
4814  var = Manager::Instance().getVariable("vertexDistanceOfDaughter(2)");
4815  ASSERT_NE(var, nullptr);
4816  EXPECT_TRUE(std::isnan(std::get<double>(var->function(newDp))));
4817  }
4818 
4819  TEST_F(FlightInfoTest, vertexDistanceOfDaughterError)
4820  {
4821  StoreArray<Particle> particles{};
4822  const Particle* newDp = particles[2]; // Get D+, its daughter KS has both a production and decay vertex
4823 
4824  const Manager::Var* var = Manager::Instance().getVariable("vertexDistanceOfDaughterErr(1, 0)");
4825  ASSERT_NE(var, nullptr);
4826  EXPECT_FLOAT_EQ(std::get<double>(var->function(newDp)), 0.2);
4827 
4828  var = Manager::Instance().getVariable("vertexDistanceOfDaughterErr(1)");
4829  ASSERT_NE(var, nullptr);
4830  EXPECT_FLOAT_EQ(std::get<double>(var->function(newDp)), 0.25);
4831  }
4832 
4833  TEST_F(FlightInfoTest, vertexDistanceOfDaughterSignificance)
4834  {
4835  StoreArray<Particle> particles{};
4836  const Particle* newDp = particles[2]; // Get D+, its daughter KS has both a production and decay vertex
4837 
4838  const Manager::Var* var = Manager::Instance().getVariable("vertexDistanceOfDaughterSignificance(1, 0)");
4839  ASSERT_NE(var, nullptr);
4840  EXPECT_FLOAT_EQ(std::get<double>(var->function(newDp)), 25);
4841 
4842  var = Manager::Instance().getVariable("vertexDistanceOfDaughterSignificance(1)");
4843  ASSERT_NE(var, nullptr);
4844  EXPECT_FLOAT_EQ(std::get<double>(var->function(newDp)), 24);
4845  }
4846 
4847  class VertexVariablesTest : public ::testing::Test {
4848  protected:
4850  void SetUp() override
4851  {
4852  DataStore::Instance().setInitializeActive(true);
4855  StoreArray<MCParticle> mcParticles;
4856  StoreArray<Particle> particles;
4857  particles.registerRelationTo(mcParticles);
4859  DataStore::Instance().setInitializeActive(false);
4860 
4861 
4862  // Insert MC particle logic here
4863  MCParticle mcKs;
4864  mcKs.setPDG(Const::Kshort.getPDGCode());
4865  mcKs.setDecayVertex(4.0, 5.0, 0.0);
4866  mcKs.setProductionVertex(1.0, 2.0, 3.0);
4867  mcKs.setMassFromPDG();
4868  mcKs.setMomentum(1.164, 1.55200, 0);
4869  mcKs.setStatus(MCParticle::c_PrimaryParticle);
4870  MCParticle* newMCKs = mcParticles.appendNew(mcKs);
4871 
4872  Particle Ks(PxPyPzEVector(1.164, 1.55200, 0, 2), 310);
4873  Ks.setVertex(XYZVector(4.0, 5.0, 0.0));
4874  Ks.addExtraInfo("prodVertX", 1.0);
4875  Ks.addExtraInfo("prodVertY", 2.0);
4876  Ks.addExtraInfo("prodVertZ", 3.0);
4877  Ks.addExtraInfo("prodVertSxx", 0.1);
4878  Ks.addExtraInfo("prodVertSxy", 0.2);
4879  Ks.addExtraInfo("prodVertSxz", 0.3);
4880  Ks.addExtraInfo("prodVertSyx", 0.4);
4881  Ks.addExtraInfo("prodVertSyy", 0.5);
4882  Ks.addExtraInfo("prodVertSyz", 0.6);
4883  Ks.addExtraInfo("prodVertSzx", 0.7);
4884  Ks.addExtraInfo("prodVertSzy", 0.8);
4885  Ks.addExtraInfo("prodVertSzz", 0.9);
4886  Particle* newKs = particles.appendNew(Ks);
4887  newKs->addRelationTo(newMCKs);
4888  }
4889 
4891  void TearDown() override
4892  {
4893  DataStore::Instance().reset();
4894  }
4895  };
4896 
4897  // MC vertex tests
4898  TEST_F(VertexVariablesTest, mcDecayVertexX)
4899  {
4900  StoreArray<Particle> particles{};
4901  const Particle* newKs = particles[0]; // Ks had truth decay x is 4.0
4902 
4903  const Manager::Var* var = Manager::Instance().getVariable("mcDecayVertexX");
4904  ASSERT_NE(var, nullptr);
4905  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), 4.0);
4906  }
4907 
4908  TEST_F(VertexVariablesTest, mcDecayVertexY)
4909  {
4910  StoreArray<Particle> particles{};
4911  const Particle* newKs = particles[0]; // Ks had truth decay y is 5.0
4912 
4913  const Manager::Var* var = Manager::Instance().getVariable("mcDecayVertexY");
4914  ASSERT_NE(var, nullptr);
4915  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), 5.0);
4916  }
4917 
4918  TEST_F(VertexVariablesTest, mcDecayVertexZ)
4919  {
4920  StoreArray<Particle> particles{};
4921  const Particle* newKs = particles[0]; // Ks had truth decay z is 0.0
4922 
4923  const Manager::Var* var = Manager::Instance().getVariable("mcDecayVertexZ");
4924  ASSERT_NE(var, nullptr);
4925  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), 0.0);
4926  }
4927 
4928 
4929  TEST_F(VertexVariablesTest, mcDecayVertexFromIPDistance)
4930  {
4931  StoreArray<Particle> particles{};
4932  const Particle* newKs = particles[0]; // Ks had truth distance of sqrt(41)
4933 
4934  const Manager::Var* var = Manager::Instance().getVariable("mcDecayVertexFromIPDistance");
4935  ASSERT_NE(var, nullptr);
4936  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), sqrt(4.0 * 4.0 + 5.0 * 5.0));
4937  }
4938 
4939  TEST_F(VertexVariablesTest, mcDecayVertexRho)
4940  {
4941  StoreArray<Particle> particles{};
4942  const Particle* newKs = particles[0]; // Ks had truth rho of sqrt(41)
4943 
4944  const Manager::Var* var = Manager::Instance().getVariable("mcDecayVertexRho");
4945  ASSERT_NE(var, nullptr);
4946  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), sqrt(4.0 * 4.0 + 5.0 * 5.0));
4947  }
4948 
4949  TEST_F(VertexVariablesTest, mcProductionVertexX)
4950  {
4951  StoreArray<Particle> particles{};
4952  const Particle* newKs = particles[0]; // Ks had production vertex x of 1.0 cm
4953 
4954  const Manager::Var* var = Manager::Instance().getVariable("mcProductionVertexX");
4955  ASSERT_NE(var, nullptr);
4956  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), 1.0);
4957  }
4958 
4959  TEST_F(VertexVariablesTest, mcProductionVertexY)
4960  {
4961  StoreArray<Particle> particles{};
4962  const Particle* newKs = particles[0]; // Ks had production vertex y of 2.0 cm
4963 
4964  const Manager::Var* var = Manager::Instance().getVariable("mcProductionVertexY");
4965  ASSERT_NE(var, nullptr);
4966  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), 2.0);
4967  }
4968 
4969  TEST_F(VertexVariablesTest, mcProductionVertexZ)
4970  {
4971  StoreArray<Particle> particles{};
4972  const Particle* newKs = particles[0]; // Ks had production vertex z of 3.0 cm
4973 
4974  const Manager::Var* var = Manager::Instance().getVariable("mcProductionVertexZ");
4975  ASSERT_NE(var, nullptr);
4976  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), 3.0);
4977  }
4978 
4979  // Production position tests
4980 
4981  TEST_F(VertexVariablesTest, prodVertexX)
4982  {
4983  StoreArray<Particle> particles{};
4984  const Particle* newKs = particles[0]; // Ks had production vertex x of 1.0 cm
4985 
4986  const Manager::Var* var = Manager::Instance().getVariable("prodVertexX");
4987  ASSERT_NE(var, nullptr);
4988  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), 1.0);
4989  }
4990  TEST_F(VertexVariablesTest, prodVertexY)
4991  {
4992  StoreArray<Particle> particles{};
4993  const Particle* newKs = particles[0]; // Ks had production vertex y of 2.0 cm
4994 
4995  const Manager::Var* var = Manager::Instance().getVariable("prodVertexY");
4996  ASSERT_NE(var, nullptr);
4997  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), 2.0);
4998  }
4999  TEST_F(VertexVariablesTest, prodVertexZ)
5000  {
5001  StoreArray<Particle> particles{};
5002  const Particle* newKs = particles[0]; // Ks had production vertex z of 3.0 cm
5003 
5004  const Manager::Var* var = Manager::Instance().getVariable("prodVertexZ");
5005  ASSERT_NE(var, nullptr);
5006  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), 3.0);
5007  }
5008 
5009  // Production Covariance tests
5010 
5011  TEST_F(VertexVariablesTest, prodVertexCov)
5012  {
5013  StoreArray<Particle> particles{};
5014  const Particle* newKs = particles[0]; // Ks had production vertex covariance xx of .1 cm
5015 
5016  //const Manager::Var* var = Manager::Instance().getVariable("prodVertexCovXX");
5017  const Manager::Var* var = Manager::Instance().getVariable("prodVertexCov(0,0)");
5018  ASSERT_NE(var, nullptr);
5019  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), 0.1);
5020  var = Manager::Instance().getVariable("prodVertexCov(0,1)");
5021  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), 0.2);
5022  var = Manager::Instance().getVariable("prodVertexCov(0,2)");
5023  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), 0.3);
5024  var = Manager::Instance().getVariable("prodVertexCov(1,0)");
5025  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), 0.4);
5026  var = Manager::Instance().getVariable("prodVertexCov(1,1)");
5027  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), 0.5);
5028  var = Manager::Instance().getVariable("prodVertexCov(1,2)");
5029  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), 0.6);
5030  var = Manager::Instance().getVariable("prodVertexCov(2,0)");
5031  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), 0.7);
5032  var = Manager::Instance().getVariable("prodVertexCov(2,1)");
5033  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), 0.8);
5034  var = Manager::Instance().getVariable("prodVertexCov(2,2)");
5035  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), 0.9);
5036  var = Manager::Instance().getVariable("prodVertexXErr");
5037  ASSERT_NE(var, nullptr);
5038  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), sqrt(0.1));
5039  var = Manager::Instance().getVariable("prodVertexYErr");
5040  ASSERT_NE(var, nullptr);
5041  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), sqrt(0.5));
5042  var = Manager::Instance().getVariable("prodVertexZErr");
5043  ASSERT_NE(var, nullptr);
5044  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), sqrt(0.9));
5045  }
5046 
5047  // Tests of ContinuumSuppressionVariables
5048 
5049  TEST_F(MetaVariableTest, KSFWVariables)
5050  {
5051  // simple tests that do not require the ROE builder nor the CS builder
5052 
5053  // check that garbage input throws helpful B2FATAL
5054  EXPECT_B2FATAL(Manager::Instance().getVariable("KSFWVariables(NONSENSE)"));
5055 
5056  // check for NaN if we don't have a CS object for this particle
5057  StoreArray<Particle> myParticles;
5058  const Particle* particle_with_no_cs = myParticles.appendNew();
5059  const Manager::Var* var = Manager::Instance().getVariable("KSFWVariables(mm2)");
5060  EXPECT_TRUE(std::isnan(std::get<double>(var->function(particle_with_no_cs))));
5061 
5062  // check that FS1 set as third argument, throws a B2ERROR
5063  EXPECT_B2ERROR(Manager::Instance().getVariable("KSFWVariables(et, mask, FS1)"));
5064  }
5065 
5066  TEST_F(MetaVariableTest, CleoConeCS)
5067  {
5068  // simple tests that do not require the ROE builder nor the CS builder
5069 
5070  // check that garbage input throws helpful B2FATAL
5071  EXPECT_B2FATAL(Manager::Instance().getVariable("CleoConeCS(NONSENSE)"));
5072 
5073  // check for NaN if we don't have a CS object for this particle
5074  StoreArray<Particle> myParticles;
5075  const Particle* particle_with_no_cs = myParticles.appendNew();
5076  const Manager::Var* var = Manager::Instance().getVariable("CleoConeCS(0)");
5077  EXPECT_TRUE(std::isnan(std::get<double>(var->function(particle_with_no_cs))));
5078 
5079  // check that string other than ROE as second argument, which is interpreted as mask name, returns NaN
5080  var = Manager::Instance().getVariable("CleoConeCS(0, NOTROE)");
5081  EXPECT_TRUE(std::isnan(std::get<double>(var->function(particle_with_no_cs))));
5082 
5083  // check that ROE set as third argument, throws a B2ERROR
5084  EXPECT_B2ERROR(Manager::Instance().getVariable("CleoConeCS(0, mask, ROE)"));
5085  }
5086 
5087  TEST_F(MetaVariableTest, TransformedNetworkOutput)
5088  {
5089  // check that garbage input throws helpful B2FATAL
5090  EXPECT_B2FATAL(Manager::Instance().getVariable("transformedNetworkOutput(NONSENSE)"));
5091 
5092  // check that helpful B2FATAL is thrown if second or third argument is not a double
5093  EXPECT_B2FATAL(Manager::Instance().getVariable("transformedNetworkOutput(NONEXISTENT, 0, NOTDOUBLE)"));
5094  EXPECT_B2FATAL(Manager::Instance().getVariable("transformedNetworkOutput(NONEXISTENT, NOTDOUBLE, 1)"));
5095 
5096  // check for NaN if network output variable does not exist (no matter whether particle is provided or not)
5097  StoreArray<Particle> myParticles;
5098  const Particle* particle = myParticles.appendNew();
5099  const Manager::Var* var = Manager::Instance().getVariable("transformedNetworkOutput(NONEXISTENT, 0, 1)");
5100  EXPECT_TRUE(std::isnan(std::get<double>(var->function(particle))));
5101  StoreObjPtr<EventExtraInfo> eventExtraInfo;
5102  if (not eventExtraInfo.isValid())
5103  eventExtraInfo.create();
5104  var = Manager::Instance().getVariable("transformedNetworkOutput(NONEXISTENT, 0, 1)");
5105  EXPECT_TRUE(std::isnan(std::get<double>(var->function(nullptr))));
5106  }
5107 }
Provides a type-safe way to pass members of the chargedStableSet set.
Definition: Const.h:580
EStoreFlags
Flags describing behaviours of objects etc.
Definition: DataStore.h:69
ECL cluster data.
Definition: ECLCluster.h:27
void setTheta(double theta)
Set Theta of Shower (radian).
Definition: ECLCluster.h:217
void setPhi(double phi)
Set Phi of Shower (radian).
Definition: ECLCluster.h:220
void setClusterId(int clusterid)
Set cluster id.
Definition: ECLCluster.h:145
void setHypothesis(EHypothesisBit hypothesis)
Set hypotheses.
Definition: ECLCluster.h:123
void setEnergy(double energy)
Set Corrected Energy (GeV).
Definition: ECLCluster.h:226
void setR(double r)
Set R (in cm).
Definition: ECLCluster.h:223
Singleton class responsible for loading detector parameters from an XML file.
Definition: Gearbox.h:34
Class to represent Particle data in graph.
void comesFrom(GraphParticle &mother)
Tells the graph that this particle is a decay product of mother.
Class to build, validate and sort a particle decay chain.
void generateList(const std::string &name="", int options=c_setNothing)
Generates the MCParticle list and stores it in the StoreArray with the given name.
A Class to store the Monte Carlo particle information.
Definition: MCParticle.h:32
float getEnergy() const
Return particle energy in GeV.
Definition: MCParticle.h:147
void setDecayTime(float time)
Set decay time.
Definition: MCParticle.h:390
void setMass(float mass)
Set particle mass.
Definition: MCParticle.h:366
void setDecayVertex(const ROOT::Math::XYZVector &vertex)
Set decay vertex.
Definition: MCParticle.h:447
float getMass() const
Return the particle mass in GeV.
Definition: MCParticle.h:135
void setProductionVertex(const ROOT::Math::XYZVector &vertex)
Set production vertex position.
Definition: MCParticle.h:396
ROOT::Math::PxPyPzEVector get4Vector() const
Return 4Vector of particle.
Definition: MCParticle.h:207
void setPDG(int pdg)
Set PDG code of the particle.
Definition: MCParticle.h:335
void set4Vector(const ROOT::Math::PxPyPzEVector &p4)
Sets the 4Vector of particle.
Definition: MCParticle.h:438
void setMomentum(const ROOT::Math::XYZVector &momentum)
Set particle momentum.
Definition: MCParticle.h:417
void setStatus(unsigned short int status)
Set Status code for the particle.
Definition: MCParticle.h:346
void setProductionTime(float time)
Set production time.
Definition: MCParticle.h:384
void setMassFromPDG()
Sets the mass for the particle from the particle's PDG code.
Definition: MCParticle.cc:28
Class to hold Lorentz transformations from/to CMS and boost vector.
double getCMSEnergy() const
Returns CMS energy of e+e- (aka.
const ROOT::Math::LorentzRotation rotateLabToCms() const
Returns Lorentz transformation from Lab to CMS.
const ROOT::Math::LorentzRotation rotateCmsToLab() const
Returns Lorentz transformation from CMS to Lab.
Class to store reconstructed particles.
Definition: Particle.h:75
void appendDaughter(const Particle *daughter, const bool updateType=true, const int daughterProperty=c_Ordinary)
Appends index of daughter to daughters index array.
Definition: Particle.cc:680
void setVertex(const ROOT::Math::XYZVector &vertex)
Sets position (decay vertex)
Definition: Particle.h:295
double getEnergy() const
Returns total energy.
Definition: Particle.h:507
double getPDGMass(void) const
Returns uncertainty on the invariant mass (requires valid momentum error matrix)
Definition: Particle.cc:608
ROOT::Math::PxPyPzEVector get4Vector() const
Returns Lorentz vector.
Definition: Particle.h:517
void addExtraInfo(const std::string &name, double value)
Sets the user-defined data of given name to the given value.
Definition: Particle.cc:1337
double getP() const
Returns momentum magnitude (same as getMomentumMagnitude but with shorter name)
Definition: Particle.h:544
const Particle * getDaughter(unsigned i) const
Returns a pointer to the i-th daughter particle.
Definition: Particle.cc:635
double getMass() const
Returns invariant mass (= nominal for FS particles)
Definition: Particle.h:479
void addRelationTo(const RelationsInterface< BASE > *object, float weight=1.0, const std::string &namedRelation="") const
Add a relation from this object to another object (with caching).
int getArrayIndex() const
Returns this object's array index (in StoreArray), or -1 if not found.
TO * getRelatedTo(const std::string &name="", const std::string &namedRelation="") const
Get the object to which this object has a relation.
bool registerInDataStore(DataStore::EStoreFlags storeFlags=DataStore::c_WriteOut)
Register the object/array in the DataStore.
bool create(bool replace=false)
Create a default object in the data store.
Accessor to arrays stored in the data store.
Definition: StoreArray.h:113
T * appendNew()
Construct a new T object at the end of the array.
Definition: StoreArray.h:246
void clear() override
Delete all entries in this array.
Definition: StoreArray.h:207
bool registerRelationTo(const StoreArray< TO > &toArray, DataStore::EDurability durability=DataStore::c_Event, DataStore::EStoreFlags storeFlags=DataStore::c_WriteOut, const std::string &namedRelation="") const
Register a relation to the given StoreArray.
Definition: StoreArray.h:140
Type-safe access to single objects in the data store.
Definition: StoreObjPtr.h:96
Class that bundles various TrackFitResults.
Definition: Track.h:25
void setTrackFitResultIndex(const Const::ChargedStable &chargedStable, short index)
Set an index (for positive values) or unavailability-code (index = -1) for a specific mass hypothesis...
Definition: Track.h:174
The Unit class.
Definition: Unit.h:40
A template class to apply the reference frame.
Object holding information for V0s.
Definition: V0.h:34
TEST_F(GlobalLabelTest, LargeNumberOfTimeDependentParameters)
Test large number of time-dep params for registration and retrieval.
Definition: globalLabel.cc:72
TEST(TestgetDetectorRegion, TestgetDetectorRegion)
Test Constructors.
double sqrt(double a)
sqrt for double
Definition: beamHelpers.h:28
GraphParticle & addParticle()
Add new particle to the graph.
double charge(int pdgCode)
Returns electric charge of a particle with given pdg code.
Definition: EvtPDLUtil.cc:44
Abstract base class for different kinds of events.
A variable returning a floating-point value for a given Particle.
Definition: Manager.h:146
FunctionPtr function
Pointer to function.
Definition: Manager.h:147