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