Belle II Software  release-08-00-10
variables.cc
1 /**************************************************************************
2  * basf2 (Belle II Analysis Software Framework) *
3  * Author: The Belle II Collaboration *
4  * *
5  * See git log for contributors and copyright holders. *
6  * This file is licensed under LGPL-3.0, see LICENSE.md. *
7  **************************************************************************/
8 
9 #include <analysis/variables/Variables.h>
10 #include <analysis/variables/BasicParticleInformation.h>
11 #include <analysis/variables/VertexVariables.h>
12 #include <analysis/variables/PIDVariables.h>
13 #include <analysis/variables/TrackVariables.h>
14 
15 #include <analysis/VariableManager/Manager.h>
16 #include <analysis/VariableManager/Utility.h>
17 
18 #include <analysis/dataobjects/Particle.h>
19 #include <analysis/dataobjects/ParticleExtraInfoMap.h>
20 #include <analysis/dataobjects/ParticleList.h>
21 #include <framework/dataobjects/EventExtraInfo.h>
22 #include <analysis/dataobjects/RestOfEvent.h>
23 #include <analysis/utility/ReferenceFrame.h>
24 
25 #include <framework/datastore/StoreArray.h>
26 #include <framework/datastore/StoreObjPtr.h>
27 #include <framework/utilities/TestHelpers.h>
28 #include <framework/logging/Logger.h>
29 #include <framework/gearbox/Gearbox.h>
30 #include <framework/gearbox/Const.h>
31 
32 #include <mdst/dataobjects/MCParticle.h>
33 #include <mdst/dataobjects/MCParticleGraph.h>
34 #include <mdst/dataobjects/PIDLikelihood.h>
35 #include <mdst/dataobjects/Track.h>
36 #include <mdst/dataobjects/V0.h>
37 #include <mdst/dataobjects/ECLCluster.h>
38 #include <mdst/dataobjects/KLMCluster.h>
39 
40 #include <gtest/gtest.h>
41 
42 #include <TMatrixFSym.h>
43 #include <TRandom3.h>
44 #include <Math/Cartesian2D.h>
45 #include <Math/Vector3D.h>
46 #include <Math/Vector4D.h>
47 #include <TMath.h>
48 #include <utility>
49 
50 using namespace std;
51 using namespace Belle2;
52 using namespace Belle2::Variable;
53 using namespace ROOT::Math;
54 
55 namespace {
56 
58  TEST(KinematicVariableTest, Variable)
59  {
60 
61  // Connect gearbox for CMS variables
62 
63  Gearbox& gearbox = Gearbox::getInstance();
64  gearbox.setBackends({std::string("file:")});
65  gearbox.close();
66  gearbox.open("geometry/Belle2.xml", false);
67 
68  {
69  Particle pH({0.290582573157898, 0, 6.99796952744559, 7.004}, 11);
70  Particle pL({0.166035330010433, 0, -3.99855423973071, 4.002}, -11);
71 
72  Particle p({0, 0.999999869440028, 0, 1.0}, -11);
73  const double eps = 1e-15;
75 
76  EXPECT_NEAR(0.0, particlePx(&pH), eps);
77  EXPECT_NEAR(0.0, particlePy(&pH), eps);
78  EXPECT_NEAR(0.0, particlePx(&pL), eps);
79  EXPECT_NEAR(0.0, particlePy(&pL), eps);
80  EXPECT_FLOAT_EQ(5.289778893721573, particlePz(&pH));
81  EXPECT_FLOAT_EQ(-5.289778893721573, particlePz(&pL));
82  EXPECT_FLOAT_EQ(10.579557836806245064 / 2, particleE(&pH));
83  EXPECT_FLOAT_EQ(10.579557836806245064 / 2, particleE(&pL));
84 
85  EXPECT_FLOAT_EQ(0.999999869440028, particlePy(&p));
86  }
87 
88  {
89  Particle p({ 0.1, -0.4, 0.8, 1.0 }, 411);
90 
91  TMatrixFSym error(7);
92  error.Zero();
93  error(0, 0) = 0.05;
94  error(1, 1) = 0.2;
95  error(2, 2) = 0.4;
96  error(0, 1) = -0.1;
97  error(0, 2) = 0.9;
98  p.setMomentumVertexErrorMatrix(error);
99 
100  EXPECT_FLOAT_EQ(0.9, particleP(&p));
101  EXPECT_FLOAT_EQ(1.0, particleE(&p));
102  EXPECT_FLOAT_EQ(0.1, particlePx(&p));
103  EXPECT_FLOAT_EQ(-0.4, particlePy(&p));
104  EXPECT_FLOAT_EQ(0.8, particlePz(&p));
105  EXPECT_FLOAT_EQ(0.412310562, particlePt(&p));
106  EXPECT_FLOAT_EQ(0.8 / 0.9, particleCosTheta(&p));
107  EXPECT_FLOAT_EQ(-1.325817664, particlePhi(&p));
108 
109  EXPECT_FLOAT_EQ(0.737446378, particlePErr(&p));
110  EXPECT_FLOAT_EQ(sqrt(0.05), particlePxErr(&p));
111  EXPECT_FLOAT_EQ(sqrt(0.2), particlePyErr(&p));
112  EXPECT_FLOAT_EQ(sqrt(0.4), particlePzErr(&p));
113  EXPECT_FLOAT_EQ(0.488093530, particlePtErr(&p));
114  EXPECT_FLOAT_EQ(0.156402664, particleCosThetaErr(&p));
115  EXPECT_FLOAT_EQ(0.263066820, particlePhiErr(&p));
116 
117 
118  {
120  EXPECT_FLOAT_EQ(0.68174648, particleP(&p));
121  EXPECT_FLOAT_EQ(0.80918372, particleE(&p));
122  EXPECT_FLOAT_EQ(0.058562335, particlePx(&p));
123  EXPECT_FLOAT_EQ(-0.40000001, particlePy(&p));
124  EXPECT_FLOAT_EQ(0.5489524, particlePz(&p));
125  EXPECT_FLOAT_EQ(0.40426421, particlePt(&p));
126  EXPECT_FLOAT_EQ(0.80521482, particleCosTheta(&p));
127  EXPECT_FLOAT_EQ(-1.4254233, particlePhi(&p));
128 
129  EXPECT_FLOAT_EQ(sqrt(0.2), particlePyErr(&p));
130  }
131 
132  {
134  EXPECT_ALL_NEAR(particleP(&p), 0.0, 1e-9);
135  EXPECT_FLOAT_EQ(0.4358899, particleE(&p));
136  EXPECT_ALL_NEAR(0.0, particlePx(&p), 1e-9);
137  EXPECT_ALL_NEAR(0.0, particlePy(&p), 1e-9);
138  EXPECT_ALL_NEAR(0.0, particlePz(&p), 1e-9);
139  EXPECT_ALL_NEAR(0.0, particlePt(&p), 1e-9);
140 
141  }
142 
143  {
145  EXPECT_FLOAT_EQ(0.9, particleP(&p));
146  EXPECT_FLOAT_EQ(1.0, particleE(&p));
147  EXPECT_FLOAT_EQ(0.1, particlePx(&p));
148  EXPECT_FLOAT_EQ(-0.4, particlePy(&p));
149  EXPECT_FLOAT_EQ(0.8, particlePz(&p));
150  EXPECT_FLOAT_EQ(0.412310562, particlePt(&p));
151  EXPECT_FLOAT_EQ(0.8 / 0.9, particleCosTheta(&p));
152  EXPECT_FLOAT_EQ(-1.325817664, particlePhi(&p));
153 
154  EXPECT_FLOAT_EQ(0.737446378, particlePErr(&p));
155  EXPECT_FLOAT_EQ(sqrt(0.05), particlePxErr(&p));
156  EXPECT_FLOAT_EQ(sqrt(0.2), particlePyErr(&p));
157  EXPECT_FLOAT_EQ(sqrt(0.4), particlePzErr(&p));
158  EXPECT_FLOAT_EQ(0.488093530, particlePtErr(&p));
159  EXPECT_FLOAT_EQ(0.156402664, particleCosThetaErr(&p));
160  EXPECT_FLOAT_EQ(0.263066820, particlePhiErr(&p));
161  }
162 
163  {
164  UseReferenceFrame<RotationFrame> dummy(XYZVector(1, 0, 0), XYZVector(0, 1, 0), XYZVector(0, 0, 1));
165  EXPECT_FLOAT_EQ(0.9, particleP(&p));
166  EXPECT_FLOAT_EQ(1.0, particleE(&p));
167  EXPECT_FLOAT_EQ(0.1, particlePx(&p));
168  EXPECT_FLOAT_EQ(-0.4, particlePy(&p));
169  EXPECT_FLOAT_EQ(0.8, particlePz(&p));
170  EXPECT_FLOAT_EQ(0.412310562, particlePt(&p));
171  EXPECT_FLOAT_EQ(0.8 / 0.9, particleCosTheta(&p));
172  EXPECT_FLOAT_EQ(-1.325817664, particlePhi(&p));
173 
174  EXPECT_FLOAT_EQ(0.737446378, particlePErr(&p));
175  EXPECT_FLOAT_EQ(sqrt(0.05), particlePxErr(&p));
176  EXPECT_FLOAT_EQ(sqrt(0.2), particlePyErr(&p));
177  EXPECT_FLOAT_EQ(sqrt(0.4), particlePzErr(&p));
178  EXPECT_FLOAT_EQ(0.488093530, particlePtErr(&p));
179  EXPECT_FLOAT_EQ(0.156402664, particleCosThetaErr(&p));
180  EXPECT_FLOAT_EQ(0.263066820, particlePhiErr(&p));
181 
182  const auto& frame = ReferenceFrame::GetCurrent();
183  EXPECT_FLOAT_EQ(-0.1, frame.getMomentumErrorMatrix(&p)(0, 1));
184  EXPECT_FLOAT_EQ(0.9, frame.getMomentumErrorMatrix(&p)(0, 2));
185  }
186 
187  {
188  UseReferenceFrame<RotationFrame> dummy(XYZVector(1, 0, 0), XYZVector(0, 0, -1), XYZVector(0, 1, 0));
189  EXPECT_FLOAT_EQ(0.9, particleP(&p));
190  EXPECT_FLOAT_EQ(1.0, particleE(&p));
191  EXPECT_FLOAT_EQ(0.1, particlePx(&p));
192  EXPECT_FLOAT_EQ(-0.8, particlePy(&p));
193  EXPECT_FLOAT_EQ(-0.4, particlePz(&p));
194 
195  EXPECT_FLOAT_EQ(0.737446378, particlePErr(&p));
196  EXPECT_FLOAT_EQ(sqrt(0.05), particlePxErr(&p));
197  EXPECT_FLOAT_EQ(sqrt(0.4), particlePyErr(&p));
198  EXPECT_FLOAT_EQ(sqrt(0.2), particlePzErr(&p));
199 
200  const auto& frame = ReferenceFrame::GetCurrent();
201  EXPECT_FLOAT_EQ(-0.9, frame.getMomentumErrorMatrix(&p)(0, 1));
202  EXPECT_FLOAT_EQ(-0.1, frame.getMomentumErrorMatrix(&p)(0, 2));
203  }
204 
205  {
206  UseReferenceFrame<CMSRotationFrame> dummy(XYZVector(1, 0, 0), XYZVector(0, 1, 0), XYZVector(0, 0, 1));
207  EXPECT_FLOAT_EQ(0.68174648, particleP(&p));
208  EXPECT_FLOAT_EQ(0.80918372, particleE(&p));
209  EXPECT_FLOAT_EQ(0.058562335, particlePx(&p));
210  EXPECT_FLOAT_EQ(-0.40000001, particlePy(&p));
211  EXPECT_FLOAT_EQ(0.5489524, particlePz(&p));
212  EXPECT_FLOAT_EQ(0.40426421, particlePt(&p));
213  EXPECT_FLOAT_EQ(0.80521482, particleCosTheta(&p));
214  EXPECT_FLOAT_EQ(-1.4254233, particlePhi(&p));
215 
216  EXPECT_FLOAT_EQ(sqrt(0.2), particlePyErr(&p));
217  }
218 
219  {
220  Particle pinv({ -0.1, 0.4, -0.8, 1.0 }, 411);
221  UseReferenceFrame<RestFrame> dummy(&pinv);
222  Particle p2({ 0.0, 0.0, 0.0, 0.4358899}, 411);
223  EXPECT_FLOAT_EQ(0.9, particleP(&p2));
224  EXPECT_FLOAT_EQ(1.0, particleE(&p2));
225  EXPECT_FLOAT_EQ(0.1, particlePx(&p2));
226  EXPECT_FLOAT_EQ(-0.4, particlePy(&p2));
227  EXPECT_FLOAT_EQ(0.8, particlePz(&p2));
228  EXPECT_FLOAT_EQ(0.412310562, particlePt(&p2));
229  EXPECT_FLOAT_EQ(0.8 / 0.9, particleCosTheta(&p2));
230  EXPECT_FLOAT_EQ(-1.325817664, particlePhi(&p2));
231  }
232  }
233 
234  {
235  Particle p({ 0.0, 0.0, 0.0, 0.0 }, 411);
236  EXPECT_FLOAT_EQ(0.0, particleP(&p));
237  EXPECT_FLOAT_EQ(0.0, particleE(&p));
238  EXPECT_FLOAT_EQ(0.0, particlePx(&p));
239  EXPECT_FLOAT_EQ(0.0, particlePy(&p));
240  EXPECT_FLOAT_EQ(0.0, particlePz(&p));
241  EXPECT_FLOAT_EQ(0.0, particlePt(&p));
242  EXPECT_FLOAT_EQ(1.0, particleCosTheta(&p));
243  EXPECT_FLOAT_EQ(0.0, particlePhi(&p));
244 
246  EXPECT_FLOAT_EQ(0.0, particleP(&p));
247  EXPECT_FLOAT_EQ(0.0, particleE(&p));
248  EXPECT_FLOAT_EQ(0.0, particlePx(&p));
249  EXPECT_FLOAT_EQ(0.0, particlePy(&p));
250  EXPECT_FLOAT_EQ(0.0, particlePz(&p));
251  EXPECT_FLOAT_EQ(0.0, particlePt(&p));
252  EXPECT_FLOAT_EQ(1.0, particleCosTheta(&p));
253  EXPECT_FLOAT_EQ(0.0, particlePhi(&p));
254  }
255 
256  {
257  DataStore::Instance().setInitializeActive(true);
258  StoreArray<Particle> particles;
259  particles.registerInDataStore();
260  DataStore::Instance().setInitializeActive(false);
262  PxPyPzEVector vec0 = {0.0, 0.0, 0.0, T.getCMSEnergy()};
263  PxPyPzEVector vec1 = {0.0, +0.332174566, 0.0, T.getCMSEnergy() / 2.};
264  PxPyPzEVector vec2 = {0.0, -0.332174566, 0.0, T.getCMSEnergy() / 2.};
265  Particle* p0 = particles.appendNew(Particle(T.rotateCmsToLab() * vec0, 22));
266  Particle* p1 = particles.appendNew(Particle(T.rotateCmsToLab() * vec1, 22, Particle::c_Unflavored, Particle::c_Undefined, 1));
267  Particle* p2 = particles.appendNew(Particle(T.rotateCmsToLab() * vec2, 22, Particle::c_Unflavored, Particle::c_Undefined, 2));
268 
269  p0->appendDaughter(p1->getArrayIndex());
270  p0->appendDaughter(p2->getArrayIndex());
271 
272  EXPECT_ALL_NEAR(m2RecoilSignalSide(p0), 0.0, 1e-7);
273  }
274 
275 
276  }
277 
278 
279  TEST(VertexVariableTest, Variable)
280  {
281 
282  // Connect gearbox for CMS variables
283 
284  Gearbox& gearbox = Gearbox::getInstance();
285  gearbox.setBackends({std::string("file:")});
286  gearbox.close();
287  gearbox.open("geometry/Belle2.xml", false);
288 
289  Particle p({ 0.1, -0.4, 0.8, 1.0 }, 11);
290  p.setPValue(0.5);
291  p.setVertex(XYZVector(1.0, 2.0, 2.0));
292 
293  EXPECT_FLOAT_EQ(1.0, particleDX(&p));
294  EXPECT_FLOAT_EQ(2.0, particleDY(&p));
295  EXPECT_FLOAT_EQ(2.0, particleDZ(&p));
296  EXPECT_FLOAT_EQ(std::sqrt(5.0), particleDRho(&p));
297  EXPECT_FLOAT_EQ(3.0, particleDistance(&p));
298  EXPECT_FLOAT_EQ(0.5, particlePvalue(&p));
299 
300  {
302  EXPECT_FLOAT_EQ(1.026177, particleDX(&p));
303  EXPECT_FLOAT_EQ(2.0, particleDY(&p));
304  EXPECT_FLOAT_EQ(2.2568872, particleDZ(&p));
305  EXPECT_FLOAT_EQ(hypot(2.0, 1.026177), particleDRho(&p));
306  EXPECT_FLOAT_EQ(3.1853695, particleDistance(&p));
307  EXPECT_FLOAT_EQ(0.5, particlePvalue(&p));
308  }
309 
310  {
311  Particle p2({ 0.1, -0.4, 0.8, 1.0 }, 11);
312  p2.setPValue(0.5);
313  p2.setVertex(XYZVector(1.0, 2.0, 2.0));
314 
315  UseReferenceFrame<RestFrame> dummy(&p2);
316  EXPECT_FLOAT_EQ(0.0, particleDX(&p));
317  EXPECT_FLOAT_EQ(0.0, particleDY(&p));
318  EXPECT_FLOAT_EQ(0.0, particleDZ(&p));
319  EXPECT_FLOAT_EQ(0.0, particleDRho(&p));
320  EXPECT_FLOAT_EQ(0.0, particleDistance(&p));
321  EXPECT_FLOAT_EQ(0.5, particlePvalue(&p));
322  }
323 
324  /* Test with a distance between mother and daughter vertex. One
325  * has to calculate the result by hand to test the code....
326 
327  {
328  Particle p2({ 0.0 , 1.0, 0.0, 1.0 }, 11);
329  p2.setPValue(0.5);
330  p2.setVertex(XYZVector(1.0, 0.0, 2.0));
331 
332  UseReferenceFrame<RestFrame> dummy(&p2);
333  EXPECT_FLOAT_EQ(0.0, particleDX(&p));
334  EXPECT_FLOAT_EQ(2.0, particleDY(&p));
335  EXPECT_FLOAT_EQ(0.0, particleDZ(&p));
336  EXPECT_FLOAT_EQ(2.0, particleDRho(&p));
337  EXPECT_FLOAT_EQ(2.0, particleDistance(&p));
338  EXPECT_FLOAT_EQ(0.5, particlePvalue(&p));
339  }
340  */
341 
342  }
343 
344  TEST(TrackVariablesTest, Variable)
345  {
346  DataStore::Instance().setInitializeActive(true);
347  StoreArray<TrackFitResult> myResults;
348  StoreArray<Track> myTracks;
349  StoreArray<V0> myV0s;
350  StoreArray<Particle> myParticles;
351  myResults.registerInDataStore();
352  myTracks.registerInDataStore();
353  myV0s.registerInDataStore();
354  myParticles.registerInDataStore();
355  DataStore::Instance().setInitializeActive(false);
356 
357  TRandom3 generator;
358 
359  const float pValue = 0.5;
360  const float bField = 1.5;
361  const int charge = 1;
362  TMatrixDSym cov6(6);
363 
364  // Generate a random put orthogonal pair of vectors in the r-phi plane
365  ROOT::Math::Cartesian2D d(generator.Uniform(-1, 1), generator.Uniform(-1, 1));
366  ROOT::Math::Cartesian2D pt(generator.Uniform(-1, 1), generator.Uniform(-1, 1));
367  d.SetXY(d.X(), -(d.X()*pt.X()) / pt.Y());
368 
369  // Add a random z component
370  ROOT::Math::XYZVector position(d.X(), d.Y(), generator.Uniform(-1, 1));
371  ROOT::Math::XYZVector momentum(pt.X(), pt.Y(), generator.Uniform(-1, 1));
372 
373  auto CDCValue = static_cast<unsigned long long int>(0x300000000000000);
374 
375  myResults.appendNew(position, momentum, cov6, charge, Const::electron, pValue, bField, CDCValue, 16777215, 0);
376  Track mytrack;
377  mytrack.setTrackFitResultIndex(Const::electron, 0);
378  Track* savedTrack = myTracks.appendNew(mytrack);
379 
380  Particle* part = myParticles.appendNew(savedTrack, Const::ChargedStable(11));
381 
382  const Manager::Var* vIsFromECL = Manager::Instance().getVariable("isFromECL");
383  const Manager::Var* vIsFromKLM = Manager::Instance().getVariable("isFromKLM");
384  const Manager::Var* vIsFromTrack = Manager::Instance().getVariable("isFromTrack");
385  const Manager::Var* vIsFromV0 = Manager::Instance().getVariable("isFromV0");
386 
387  EXPECT_TRUE(std::get<bool>(vIsFromTrack->function(part)));
388  EXPECT_FALSE(std::get<bool>(vIsFromECL->function(part)));
389  EXPECT_FALSE(std::get<bool>(vIsFromKLM->function(part)));
390  EXPECT_FALSE(std::get<bool>(vIsFromV0->function(part)));
391  EXPECT_FLOAT_EQ(0.5, trackPValue(part));
392  EXPECT_FLOAT_EQ(position.Z(), trackZ0(part));
393  EXPECT_FLOAT_EQ(position.Rho(), trackD0(part));
394  EXPECT_FLOAT_EQ(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  EXPECT_B2FATAL(Manager::Instance().getVariable("daughterDiffOf(0, NOTINT, PDG)"));
1895  }
1896 
1897 
1898  TEST_F(MetaVariableTest, mcDaughterDiffOf)
1899  {
1900  DataStore::Instance().setInitializeActive(true);
1901  PxPyPzEVector momentum;
1902  const int nDaughters = 4;
1903  StoreArray<Particle> particles;
1904  StoreArray<MCParticle> mcParticles;
1905  particles.registerRelationTo(mcParticles);
1906  std::vector<int> daughterIndices;
1907  DataStore::Instance().setInitializeActive(false);
1908 
1909  for (int i = 0; i < nDaughters; i++) {
1910  Particle d(PxPyPzEVector(1, 1, 1, i * 1.0 + 1.0), (i % 2) ? -11 : 211);
1911  momentum += d.get4Vector();
1912  Particle* newDaughters = particles.appendNew(d);
1913  daughterIndices.push_back(newDaughters->getArrayIndex());
1914  auto* mcParticle = mcParticles.appendNew();
1915  mcParticle->setPDG((i % 2) ? -Const::electron.getPDGCode() : Const::pion.getPDGCode());
1916  mcParticle->setStatus(MCParticle::c_PrimaryParticle);
1917  newDaughters->addRelationTo(mcParticle);
1918  }
1919  const Particle* p = particles.appendNew(momentum, 411, Particle::c_Unflavored, daughterIndices);
1920 
1921  const Manager::Var* var = Manager::Instance().getVariable("mcDaughterDiffOf(0, 1, PDG)");
1922  ASSERT_NE(var, nullptr);
1923  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), -222);
1924 
1925  var = Manager::Instance().getVariable("mcDaughterDiffOf(1, 0, PDG)");
1926  ASSERT_NE(var, nullptr);
1927  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), 222);
1928 
1929  var = Manager::Instance().getVariable("mcDaughterDiffOf(0, 1, abs(PDG))");
1930  ASSERT_NE(var, nullptr);
1931  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), -200);
1932 
1933  var = Manager::Instance().getVariable("mcDaughterDiffOf(1, 1, PDG)");
1934  ASSERT_NE(var, nullptr);
1935  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), 0);
1936 
1937  var = Manager::Instance().getVariable("mcDaughterDiffOf(1, 3, abs(PDG))");
1938  ASSERT_NE(var, nullptr);
1939  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), 0);
1940 
1941  var = Manager::Instance().getVariable("mcDaughterDiffOf(0, 2, PDG)");
1942  ASSERT_NE(var, nullptr);
1943  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), 0);
1944 
1945  EXPECT_B2FATAL(Manager::Instance().getVariable("mcDaughterDiffOf(0, NOTINT, PDG)"));
1946  }
1947 
1948 
1949 
1950  TEST_F(MetaVariableTest, daughterClusterAngleInBetween)
1951  {
1952  // declare all the array we need
1953  StoreArray<Particle> particles;
1954  std::vector<int> daughterIndices, daughterIndices_noclst;
1955 
1956  //proxy initialize where to declare the needed array
1957  DataStore::Instance().setInitializeActive(true);
1958  StoreArray<ECLCluster> eclclusters;
1959  eclclusters.registerInDataStore();
1960  particles.registerRelationTo(eclclusters);
1961  DataStore::Instance().setInitializeActive(false);
1962 
1963  // create two Lorentz vectors that are back to back in the CMS and boost them to the Lab frame
1964  const float px_CM = 2.;
1965  const float py_CM = 1.;
1966  const float pz_CM = 3.;
1967  float E_CM;
1968  E_CM = sqrt(pow(px_CM, 2) + pow(py_CM, 2) + pow(pz_CM, 2));
1969  PxPyPzEVector momentum, momentum_noclst;
1970  PxPyPzEVector dau0_4vec_CM(px_CM, py_CM, pz_CM, E_CM), dau1_4vec_CM(-px_CM, -py_CM, -pz_CM, E_CM);
1971  PxPyPzEVector dau0_4vec_Lab, dau1_4vec_Lab;
1972  dau0_4vec_Lab = PCmsLabTransform::cmsToLab(
1973  dau0_4vec_CM); //why is everybody using the extended method when there are the functions that do all the steps for us?
1974  dau1_4vec_Lab = PCmsLabTransform::cmsToLab(dau1_4vec_CM);
1975 
1976  // add the two photons (now in the Lab frame) as the two daughters of some particle and create the latter
1977  Particle dau0_noclst(dau0_4vec_Lab, 22);
1978  momentum += dau0_noclst.get4Vector();
1979  Particle* newDaughter0_noclst = particles.appendNew(dau0_noclst);
1980  daughterIndices_noclst.push_back(newDaughter0_noclst->getArrayIndex());
1981  Particle dau1_noclst(dau1_4vec_Lab, 22);
1982  momentum += dau1_noclst.get4Vector();
1983  Particle* newDaughter1_noclst = particles.appendNew(dau1_noclst);
1984  daughterIndices_noclst.push_back(newDaughter1_noclst->getArrayIndex());
1985  const Particle* par_noclst = particles.appendNew(momentum, 111, Particle::c_Unflavored, daughterIndices_noclst);
1986 
1987  // grab variables
1988  const Manager::Var* var = Manager::Instance().getVariable("daughterClusterAngleInBetween(0, 1)");
1989  const Manager::Var* varCMS = Manager::Instance().getVariable("useCMSFrame(daughterClusterAngleInBetween(0, 1))");
1990 
1991  // when no relations are set between the particles and the eclClusters, nan is expected to be returned
1992  ASSERT_NE(var, nullptr);
1993  EXPECT_TRUE(std::isnan(std::get<double>(var->function(par_noclst))));
1994 
1995  // set relations between particles and eclClusters
1996  ECLCluster* eclst0 = eclclusters.appendNew(ECLCluster());
1997  eclst0->setEnergy(dau0_4vec_Lab.E());
1998  eclst0->setHypothesis(ECLCluster::EHypothesisBit::c_nPhotons);
1999  eclst0->setClusterId(1);
2000  eclst0->setTheta(dau0_4vec_Lab.Theta());
2001  eclst0->setPhi(dau0_4vec_Lab.Phi());
2002  eclst0->setR(148.4);
2003  ECLCluster* eclst1 = eclclusters.appendNew(ECLCluster());
2004  eclst1->setEnergy(dau1_4vec_Lab.E());
2005  eclst1->setHypothesis(ECLCluster::EHypothesisBit::c_nPhotons);
2006  eclst1->setClusterId(2);
2007  eclst1->setTheta(dau1_4vec_Lab.Theta());
2008  eclst1->setPhi(dau1_4vec_Lab.Phi());
2009  eclst1->setR(148.5);
2010 
2011  const Particle* newDaughter0 = particles.appendNew(Particle(eclclusters[0]));
2012  daughterIndices.push_back(newDaughter0->getArrayIndex());
2013  const Particle* newDaughter1 = particles.appendNew(Particle(eclclusters[1]));
2014  daughterIndices.push_back(newDaughter1->getArrayIndex());
2015 
2016  const Particle* par = particles.appendNew(momentum, 111, Particle::c_Unflavored, daughterIndices);
2017 
2018  //now we expect non-nan results
2019  EXPECT_FLOAT_EQ(std::get<double>(var->function(par)), 2.8613892);
2020  EXPECT_FLOAT_EQ(std::get<double>(varCMS->function(par)), M_PI);
2021  }
2022 
2023  TEST_F(MetaVariableTest, grandDaughterDiffOfs)
2024  {
2025  // declare all the array we need
2026  StoreArray<Particle> particles;
2027  std::vector<int> daughterIndices0_noclst, daughterIndices1_noclst, daughterIndices2_noclst;
2028  std::vector<int> daughterIndices0, daughterIndices1, daughterIndices2;
2029 
2030  //proxy initialize where to declare the needed array
2031  DataStore::Instance().setInitializeActive(true);
2032  StoreArray<ECLCluster> eclclusters;
2033  eclclusters.registerInDataStore();
2034  particles.registerRelationTo(eclclusters);
2035  DataStore::Instance().setInitializeActive(false);
2036 
2037  // create two Lorentz vectors
2038  const float px_0 = 2.;
2039  const float py_0 = 1.;
2040  const float pz_0 = 3.;
2041  const float px_1 = 1.5;
2042  const float py_1 = 1.5;
2043  const float pz_1 = 2.5;
2044  float E_0, E_1;
2045  E_0 = sqrt(pow(px_0, 2) + pow(py_0, 2) + pow(pz_0, 2));
2046  E_1 = sqrt(pow(px_1, 2) + pow(py_1, 2) + pow(pz_1, 2));
2047  PxPyPzEVector momentum_0, momentum_1, momentum;
2048  PxPyPzEVector dau0_4vec(px_0, py_0, pz_0, E_0), dau1_4vec(px_1, py_1, pz_1, E_1);
2049 
2050  // add the two photons as the two daughters of some particle and create the latter
2051  // Particle dau0_noclst(dau0_4vec, 22);
2052  // momentum += dau0_noclst.get4Vector();
2053  // Particle* newDaughter0_noclst = particles.appendNew(dau0_noclst);
2054  // daughterIndices_noclst.push_back(newDaughter0_noclst->getArrayIndex());
2055  // Particle dau1_noclst(dau1_4vec, 22);
2056  // momentum += dau1_noclst.get4Vector();
2057  // Particle* newDaughter1_noclst = particles.appendNew(dau1_noclst);
2058  // daughterIndices_noclst.push_back(newDaughter1_noclst->getArrayIndex());
2059  // const Particle* par_noclst = particles.appendNew(momentum, 111, Particle::c_Unflavored, daughterIndices_noclst);
2060 
2061  Particle dau0_noclst(dau0_4vec, 22);
2062  momentum_0 = dau0_4vec;
2063  Particle* newDaughter0_noclst = particles.appendNew(dau0_noclst);
2064  daughterIndices0_noclst.push_back(newDaughter0_noclst->getArrayIndex());
2065  const Particle* par0_noclst = particles.appendNew(momentum_0, 111, Particle::c_Unflavored, daughterIndices0_noclst);
2066  Particle dau1_noclst(dau1_4vec, 22);
2067  momentum_1 = dau1_4vec;
2068  Particle* newDaughter1_noclst = particles.appendNew(dau1_noclst);
2069  daughterIndices1_noclst.push_back(newDaughter1_noclst->getArrayIndex());
2070  const Particle* par1_noclst = particles.appendNew(momentum_1, 111, Particle::c_Unflavored, daughterIndices1_noclst);
2071 
2072  momentum = momentum_0 + momentum_1;
2073  daughterIndices2_noclst.push_back(par0_noclst->getArrayIndex());
2074  daughterIndices2_noclst.push_back(par1_noclst->getArrayIndex());
2075  const Particle* parGranny_noclst = particles.appendNew(momentum, 111, Particle::c_Unflavored, daughterIndices2_noclst);
2076 
2077  // grab variables
2078  const Manager::Var* var_Theta = Manager::Instance().getVariable("grandDaughterDiffOf(0,1,0,0,theta)");
2079  const Manager::Var* var_ClusterTheta = Manager::Instance().getVariable("grandDaughterDiffOf(0,1,0,0,clusterTheta)");
2080  const Manager::Var* var_E = Manager::Instance().getVariable("grandDaughterDiffOf(0,1,0,0,E)");
2081  const Manager::Var* var_ClusterE = Manager::Instance().getVariable("grandDaughterDiffOf(0,1,0,0,clusterE)");
2082  const Manager::Var* var_E_wrongIndexes = Manager::Instance().getVariable("grandDaughterDiffOf(0,1,2,3,E)");
2083  const Manager::Var* var_ClusterE_wrongIndexes = Manager::Instance().getVariable("grandDaughterDiffOf(0,1,2,3,clusterE)");
2084 
2085  const Manager::Var* var_ClusterPhi = Manager::Instance().getVariable("grandDaughterDiffOf(0,1,0,0,clusterPhi)");
2086  const Manager::Var* var_Phi = Manager::Instance().getVariable("grandDaughterDiffOf(0,1,0,0,phi)");
2087  const Manager::Var* var_ClusterPhi_wrongIndexes = Manager::Instance().getVariable("grandDaughterDiffOf(0,1,2,3,clusterPhi)");
2088  const Manager::Var* var_Phi_wrongIndexes = Manager::Instance().getVariable("grandDaughterDiffOf(0,1,2,3,phi)");
2089 
2090  // when no relations are set between the particles and the eclClusters, nan is expected to be returned for the Cluster- vars
2091  // no problems are supposed to happen for non-Cluster- vars
2092  // also, we expect NaN when we pass wrong indexes
2093  ASSERT_NE(var_ClusterPhi, nullptr);
2094  EXPECT_TRUE(std::isnan(std::get<double>(var_ClusterPhi->function(parGranny_noclst))));
2095  EXPECT_TRUE(std::isnan(std::get<double>(var_ClusterTheta->function(parGranny_noclst))));
2096  EXPECT_TRUE(std::isnan(std::get<double>(var_ClusterE->function(parGranny_noclst))));
2097  EXPECT_FLOAT_EQ(std::get<double>(var_Phi->function(parGranny_noclst)), 0.32175055);
2098  EXPECT_FLOAT_EQ(std::get<double>(var_Theta->function(parGranny_noclst)), 0.06311664);
2099  EXPECT_FLOAT_EQ(std::get<double>(var_E->function(parGranny_noclst)), -0.46293807);
2100  EXPECT_TRUE(std::isnan(std::get<double>(var_ClusterPhi_wrongIndexes->function(parGranny_noclst))));
2101  EXPECT_TRUE(std::isnan(std::get<double>(var_Phi_wrongIndexes->function(parGranny_noclst))));
2102  EXPECT_TRUE(std::isnan(std::get<double>(var_ClusterE_wrongIndexes->function(parGranny_noclst))));
2103  EXPECT_TRUE(std::isnan(std::get<double>(var_E_wrongIndexes->function(parGranny_noclst))));
2104 
2105  // set relations between particles and eclClusters
2106  ECLCluster* eclst0 = eclclusters.appendNew(ECLCluster());
2107  eclst0->setEnergy(dau0_4vec.E());
2108  eclst0->setHypothesis(ECLCluster::EHypothesisBit::c_nPhotons);
2109  eclst0->setClusterId(1);
2110  eclst0->setTheta(dau0_4vec.Theta());
2111  eclst0->setPhi(dau0_4vec.Phi());
2112  eclst0->setR(148.4);
2113  ECLCluster* eclst1 = eclclusters.appendNew(ECLCluster());
2114  eclst1->setEnergy(dau1_4vec.E());
2115  eclst1->setHypothesis(ECLCluster::EHypothesisBit::c_nPhotons);
2116  eclst1->setClusterId(2);
2117  eclst1->setTheta(dau1_4vec.Theta());
2118  eclst1->setPhi(dau1_4vec.Phi());
2119  eclst1->setR(148.5);
2120 
2121  const Particle* newDaughter0 = particles.appendNew(Particle(eclclusters[0]));
2122  daughterIndices0.push_back(newDaughter0->getArrayIndex());
2123  const Particle* par0 = particles.appendNew(momentum_0, 111, Particle::c_Unflavored, daughterIndices0);
2124 
2125  const Particle* newDaughter1 = particles.appendNew(Particle(eclclusters[1]));
2126  daughterIndices1.push_back(newDaughter1->getArrayIndex());
2127  const Particle* par1 = particles.appendNew(momentum_1, 111, Particle::c_Unflavored, daughterIndices1);
2128 
2129  daughterIndices2.push_back(par0->getArrayIndex());
2130  daughterIndices2.push_back(par1->getArrayIndex());
2131  const Particle* parGranny = particles.appendNew(momentum, 111, Particle::c_Unflavored, daughterIndices2);
2132  //const Particle* par = particles.appendNew(momentum, 111, Particle::c_Unflavored, daughterIndices);
2133 
2134  //now we expect non-nan results
2135  EXPECT_FLOAT_EQ(std::get<double>(var_ClusterPhi->function(parGranny)), 0.32175055);
2136  EXPECT_FLOAT_EQ(std::get<double>(var_Phi->function(parGranny)), 0.32175055);
2137  EXPECT_FLOAT_EQ(std::get<double>(var_ClusterTheta->function(parGranny)), 0.06311664);
2138  EXPECT_FLOAT_EQ(std::get<double>(var_Theta->function(parGranny)), 0.06311664);
2139  EXPECT_FLOAT_EQ(std::get<double>(var_ClusterE->function(parGranny)), -0.46293831);
2140  EXPECT_FLOAT_EQ(std::get<double>(var_E->function(parGranny)), -0.46293831);
2141  }
2142 
2143  TEST_F(MetaVariableTest, daughterNormDiffOf)
2144  {
2145  PxPyPzEVector momentum;
2146  const int nDaughters = 4;
2147  StoreArray<Particle> particles;
2148  std::vector<int> daughterIndices;
2149  for (int i = 0; i < nDaughters; i++) {
2150  Particle d(PxPyPzEVector(1, 1, 1, i * 1.0 + 1.0), (i % 2) ? -11 : 211);
2151  momentum += d.get4Vector();
2152  Particle* newDaughters = particles.appendNew(d);
2153  daughterIndices.push_back(newDaughters->getArrayIndex());
2154  }
2155  const Particle* p = particles.appendNew(momentum, 411, Particle::c_Unflavored, daughterIndices);
2156 
2157  const Manager::Var* var = Manager::Instance().getVariable("daughterNormDiffOf(0, 1, PDG)");
2158  ASSERT_NE(var, nullptr);
2159  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), -222 / 200.);
2160 
2161  var = Manager::Instance().getVariable("daughterNormDiffOf(1, 0, 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(0, 1, abs(PDG))");
2166  ASSERT_NE(var, nullptr);
2167  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), -200 / 222.);
2168 
2169  var = Manager::Instance().getVariable("daughterNormDiffOf(1, 1, PDG)");
2170  ASSERT_NE(var, nullptr);
2171  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), -0 / 22.);
2172 
2173  var = Manager::Instance().getVariable("daughterNormDiffOf(1, 3, abs(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(0, 2, PDG)");
2178  ASSERT_NE(var, nullptr);
2179  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), 0 / 422.);
2180 
2181  }
2182 
2183  TEST_F(MetaVariableTest, daughterMotherDiffOf)
2184  {
2185  PxPyPzEVector momentum;
2186  const int nDaughters = 4;
2187  StoreArray<Particle> particles;
2188  std::vector<int> daughterIndices;
2189  for (int i = 0; i < nDaughters; i++) {
2190  Particle d(PxPyPzEVector(1, 1, 1, i * 1.0 + 1.0), (i % 2) ? -11 : 211);
2191  momentum += d.get4Vector();
2192  Particle* newDaughters = particles.appendNew(d);
2193  daughterIndices.push_back(newDaughters->getArrayIndex());
2194  }
2195  const Particle* p = particles.appendNew(momentum, 411, Particle::c_Unflavored, daughterIndices);
2196 
2197  const Manager::Var* var = Manager::Instance().getVariable("daughterMotherDiffOf(1, PDG)");
2198  ASSERT_NE(var, nullptr);
2199  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), 422);
2200 
2201  var = Manager::Instance().getVariable("daughterMotherDiffOf(1, abs(PDG))");
2202  ASSERT_NE(var, nullptr);
2203  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), 400);
2204 
2205  var = Manager::Instance().getVariable("daughterMotherDiffOf(0, PDG)");
2206  ASSERT_NE(var, nullptr);
2207  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), 200);
2208 
2209  }
2210 
2211  TEST_F(MetaVariableTest, daughterMotherNormDiffOf)
2212  {
2213  PxPyPzEVector momentum;
2214  const int nDaughters = 4;
2215  StoreArray<Particle> particles;
2216  std::vector<int> daughterIndices;
2217  for (int i = 0; i < nDaughters; i++) {
2218  Particle d(PxPyPzEVector(1, 1, 1, i * 1.0 + 1.0), (i % 2) ? -11 : 211);
2219  momentum += d.get4Vector();
2220  Particle* newDaughters = particles.appendNew(d);
2221  daughterIndices.push_back(newDaughters->getArrayIndex());
2222  }
2223  const Particle* p = particles.appendNew(momentum, 411, Particle::c_Unflavored, daughterIndices);
2224 
2225  const Manager::Var* var = Manager::Instance().getVariable("daughterMotherNormDiffOf(1, PDG)");
2226  ASSERT_NE(var, nullptr);
2227  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), 422 / 400.);
2228 
2229  var = Manager::Instance().getVariable("daughterMotherNormDiffOf(1, abs(PDG))");
2230  ASSERT_NE(var, nullptr);
2231  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), 400 / 422.);
2232 
2233  var = Manager::Instance().getVariable("daughterMotherNormDiffOf(0, PDG)");
2234  ASSERT_NE(var, nullptr);
2235  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), 200 / 622.);
2236 
2237  }
2238 
2239  TEST_F(MetaVariableTest, constant)
2240  {
2241 
2242  const Manager::Var* var = Manager::Instance().getVariable("constant(1)");
2243  ASSERT_NE(var, nullptr);
2244  EXPECT_FLOAT_EQ(std::get<double>(var->function(nullptr)), 1.0);
2245 
2246  var = Manager::Instance().getVariable("constant(0)");
2247  ASSERT_NE(var, nullptr);
2248  EXPECT_FLOAT_EQ(std::get<double>(var->function(nullptr)), 0.0);
2249 
2250  }
2251 
2252  TEST_F(MetaVariableTest, abs)
2253  {
2254  Particle p({ 0.1, -0.4, 0.8, 2.0 }, 11);
2255  Particle p2({ -0.1, -0.4, 0.8, 4.0 }, -11);
2256 
2257  const Manager::Var* var = Manager::Instance().getVariable("abs(px)");
2258  ASSERT_NE(var, nullptr);
2259  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), 0.1);
2260  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p2)), 0.1);
2261 
2262  }
2263 
2264  TEST_F(MetaVariableTest, sin)
2265  {
2266  Particle p({ 3.14159265359 / 2.0, -0.4, 0.8, 1.0}, 11);
2267  Particle p2({ 0.0, -0.4, 0.8, 1.0 }, -11);
2268 
2269  const Manager::Var* var = Manager::Instance().getVariable("sin(px)");
2270  ASSERT_NE(var, nullptr);
2271  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p)), 1.0);
2272  EXPECT_NEAR(std::get<double>(var->function(&p2)), 0.0, 1e-6);
2273 
2274  }
2275 
2276  TEST_F(MetaVariableTest, cos)
2277  {
2278  Particle p({ 3.14159265359 / 2.0, -0.4, 0.8, 1.0}, 11);
2279  Particle p2({ 0.0, -0.4, 0.8, 1.0 }, -11);
2280 
2281  const Manager::Var* var = Manager::Instance().getVariable("cos(px)");
2282  ASSERT_NE(var, nullptr);
2283  EXPECT_NEAR(std::get<double>(var->function(&p)), 0.0, 1e-6);
2284  EXPECT_FLOAT_EQ(std::get<double>(var->function(&p2)), 1.0);
2285 
2286  }
2287 
2288  TEST_F(MetaVariableTest, NBDeltaIfMissingDeathTest)
2289  {
2290  //Variable got removed, test for absence
2291  EXPECT_B2FATAL(Manager::Instance().getVariable("NBDeltaIfMissing(TOP, 11)"));
2292  EXPECT_B2FATAL(Manager::Instance().getVariable("NBDeltaIfMissing(ARICH, 11)"));
2293  }
2294 
2295  TEST_F(MetaVariableTest, matchedMC)
2296  {
2297  DataStore::Instance().setInitializeActive(true);
2298  StoreArray<MCParticle> mcParticles;
2299  StoreArray<Particle> particles;
2300  particles.registerRelationTo(mcParticles);
2301  DataStore::Instance().setInitializeActive(false);
2302 
2303  auto* mcParticle = mcParticles.appendNew();
2304  mcParticle->setPDG(Const::electron.getPDGCode());
2305  mcParticle->setStatus(MCParticle::c_PrimaryParticle);
2306  auto* p1 = particles.appendNew(PxPyPzEVector({ 0.0, -0.4, 0.8, 1.0}), 11);
2307  p1->addRelationTo(mcParticle);
2308 
2309  mcParticle = mcParticles.appendNew();
2310  mcParticle->setPDG(-Const::electron.getPDGCode());
2311  mcParticle->setStatus(MCParticle::c_PrimaryParticle);
2312  auto* p2 = particles.appendNew(PxPyPzEVector({ 0.0, -0.4, 0.8, 1.0}), 11);
2313  p2->addRelationTo(mcParticle);
2314 
2315  mcParticle = mcParticles.appendNew();
2316  mcParticle->setPDG(Const::photon.getPDGCode());
2317  mcParticle->setStatus(MCParticle::c_PrimaryParticle);
2318  auto* p3 = particles.appendNew(PxPyPzEVector({ 0.0, -0.4, 0.8, 1.0}), 11);
2319  p3->addRelationTo(mcParticle);
2320 
2321  // Test if matchedMC also works for particle which already is an MCParticle.
2322  auto* p4 = particles.appendNew(mcParticle);
2323 
2324  const Manager::Var* var = Manager::Instance().getVariable("matchedMC(charge)");
2325  ASSERT_NE(var, nullptr);
2326  EXPECT_FLOAT_EQ(std::get<double>(var->function(p1)), -1);
2327  EXPECT_FLOAT_EQ(std::get<double>(var->function(p2)), 1);
2328  EXPECT_FLOAT_EQ(std::get<double>(var->function(p3)), 0);
2329  EXPECT_FLOAT_EQ(std::get<double>(var->function(p4)), 0);
2330  }
2331 
2332  TEST_F(MetaVariableTest, countInList)
2333  {
2334  StoreArray<Particle> particles;
2335  DataStore::EStoreFlags flags = DataStore::c_DontWriteOut;
2336 
2337  StoreObjPtr<ParticleList> outputList("pList1");
2338  DataStore::Instance().setInitializeActive(true);
2339  outputList.registerInDataStore(flags);
2340  DataStore::Instance().setInitializeActive(false);
2341  outputList.create();
2342  outputList->initialize(22, "pList1");
2343 
2344  particles.appendNew(Particle({0.5, 0.4, 0.5, 0.8}, 22, Particle::c_Unflavored, Particle::c_Undefined, 2));
2345  particles.appendNew(Particle({0.5, 0.2, 0.7, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 3));
2346  particles.appendNew(Particle({0.4, 0.2, 0.7, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 4));
2347  particles.appendNew(Particle({0.5, 0.4, 0.8, 1.1}, 22, Particle::c_Unflavored, Particle::c_Undefined, 5));
2348  particles.appendNew(Particle({0.3, 0.3, 0.4, 0.6}, 22, Particle::c_Unflavored, Particle::c_Undefined, 6));
2349 
2350  outputList->addParticle(0, 22, Particle::c_Unflavored);
2351  outputList->addParticle(1, 22, Particle::c_Unflavored);
2352  outputList->addParticle(2, 22, Particle::c_Unflavored);
2353  outputList->addParticle(3, 22, Particle::c_Unflavored);
2354  outputList->addParticle(4, 22, Particle::c_Unflavored);
2355 
2356  const Manager::Var* var = Manager::Instance().getVariable("countInList(pList1, E < 0.85)");
2357  ASSERT_NE(var, nullptr);
2358  EXPECT_EQ(std::get<int>(var->function(nullptr)), 2);
2359 
2360  var = Manager::Instance().getVariable("countInList(pList1)");
2361  ASSERT_NE(var, nullptr);
2362  EXPECT_EQ(std::get<int>(var->function(nullptr)), 5);
2363 
2364  var = Manager::Instance().getVariable("countInList(pList1, E > 5)");
2365  ASSERT_NE(var, nullptr);
2366  EXPECT_EQ(std::get<int>(var->function(nullptr)), 0);
2367 
2368  var = Manager::Instance().getVariable("countInList(pList1, E < 5)");
2369  ASSERT_NE(var, nullptr);
2370  EXPECT_EQ(std::get<int>(var->function(nullptr)), 5);
2371  }
2372 
2373  TEST_F(MetaVariableTest, isInList)
2374  {
2375  // we need the particles StoreArray
2376  StoreArray<Particle> particles;
2377  DataStore::EStoreFlags flags = DataStore::c_DontWriteOut;
2378 
2379  // create a photon list for testing
2380  StoreObjPtr<ParticleList> gammalist("testGammaList");
2381  DataStore::Instance().setInitializeActive(true);
2382  gammalist.registerInDataStore(flags);
2383  DataStore::Instance().setInitializeActive(false);
2384  gammalist.create();
2385  gammalist->initialize(22, "testGammaList");
2386 
2387  // mock up two photons
2388  Particle goingin({0.5, 0.4, 0.5, 0.8}, 22, Particle::c_Unflavored, Particle::c_Undefined, 0);
2389  Particle notgoingin({0.3, 0.3, 0.4, 0.6}, 22, Particle::c_Unflavored, Particle::c_Undefined, 1);
2390  auto* inthelist = particles.appendNew(goingin);
2391  auto* notinthelist = particles.appendNew(notgoingin);
2392 
2393  // put the the zeroth one in the list the first on not in the list
2394  gammalist->addParticle(0, 22, Particle::c_Unflavored);
2395 
2396  // get the variables
2397  const Manager::Var* vnonsense = Manager::Instance().getVariable("isInList(NONEXISTANTLIST)");
2398  const Manager::Var* vsensible = Manager::Instance().getVariable("isInList(testGammaList)");
2399 
2400  // -
2401  EXPECT_B2FATAL(std::get<bool>(vnonsense->function(notinthelist)));
2402  EXPECT_TRUE(std::get<bool>(vsensible->function(inthelist)));
2403  EXPECT_FALSE(std::get<bool>(vsensible->function(notinthelist)));
2404  }
2405 
2407  TEST_F(MetaVariableTest, cutIsInList)
2408  {
2409  StoreArray<Particle> particles;
2410  DataStore::EStoreFlags flags = DataStore::c_DontWriteOut;
2411 
2412  // create a photon list for testing
2413  const std::string listname {"wil/d(-+)'':l*"};
2414 
2415  StoreObjPtr<ParticleList> particlelist(listname);
2416  DataStore::Instance().setInitializeActive(true);
2417  particlelist.registerInDataStore(flags);
2418  DataStore::Instance().setInitializeActive(false);
2419  particlelist.create();
2420  particlelist->initialize(22, listname);
2421 
2422  Particle goingin({0.5, 0.4, 0.5, 0.8}, 22, Particle::c_Unflavored, Particle::c_Undefined, 0);
2423  Particle notgoingin({0.3, 0.3, 0.4, 0.6}, 22, Particle::c_Unflavored, Particle::c_Undefined, 1);
2424  auto* inthelist = particles.appendNew(goingin);
2425  auto* notinthelist = particles.appendNew(notgoingin);
2426 
2427  // put the the zeroth one in the list the first on not in the list
2428  particlelist->addParticle(0, 22, Particle::c_Unflavored);
2429 
2430  // use passesCut metavariable to check cut parsing of isInList particle list.
2431  const Manager::Var* nonexistlist = Manager::Instance().getVariable("passesCut(isInList(NONEXISTANTLIST))");
2432  const Manager::Var* existlist = Manager::Instance().getVariable("passesCut(isInList(" + listname + "))");
2433 
2434  EXPECT_B2FATAL(std::get<bool>(nonexistlist->function(inthelist)));
2435  EXPECT_FALSE(std::get<bool>(existlist->function(notinthelist)));
2436  EXPECT_TRUE(std::get<bool>(existlist->function(inthelist)));
2437  }
2438 
2439  TEST_F(MetaVariableTest, sourceObjectIsInList)
2440  {
2441  // datastore things
2442  DataStore::Instance().reset();
2443  DataStore::Instance().setInitializeActive(true);
2444 
2445  // needed to mock up
2446  StoreArray<ECLCluster> clusters;
2447  StoreArray<Particle> particles;
2448  StoreObjPtr<ParticleList> gammalist("testGammaList");
2449 
2450  clusters.registerInDataStore();
2451  particles.registerInDataStore();
2452  DataStore::EStoreFlags flags = DataStore::c_DontWriteOut;
2453  gammalist.registerInDataStore(flags);
2454 
2455  // end datastore things
2456  DataStore::Instance().setInitializeActive(false);
2457 
2458  // of course we have to create the list...
2459  gammalist.create();
2460  gammalist->initialize(22, "testGammaList");
2461 
2462  // mock up two clusters from the ECL let's say they both came from true Klongs
2463  // but one looked a little bit photon-like
2464  auto* cl0 = clusters.appendNew(ECLCluster());
2465  cl0->setEnergy(1.0);
2466  cl0->setHypothesis(ECLCluster::EHypothesisBit::c_nPhotons);
2467  cl0->addHypothesis(ECLCluster::EHypothesisBit::c_neutralHadron);
2468  cl0->setClusterId(0);
2469  auto* cl1 = clusters.appendNew(ECLCluster());
2470  cl1->setEnergy(1.0);
2471  cl1->setHypothesis(ECLCluster::EHypothesisBit::c_neutralHadron);
2472  cl1->setClusterId(1);
2473 
2474  // create particles from the clusters
2475  Particle myphoton(cl0, Const::photon);
2476  Particle iscopiedin(cl0, Const::Klong);
2477  Particle notcopiedin(cl1, Const::Klong);
2478 
2479  // add the particle created from cluster zero to the gamma list
2480  auto* myphoton_ = particles.appendNew(myphoton);
2481  gammalist->addParticle(myphoton_);
2482 
2483  auto* iscopied = particles.appendNew(iscopiedin); // a clone of this guy is now in the gamma list
2484  auto* notcopied = particles.appendNew(notcopiedin);
2485 
2486  // get the variables
2487  const Manager::Var* vnonsense = Manager::Instance().getVariable("sourceObjectIsInList(NONEXISTANTLIST)");
2488  const Manager::Var* vsensible = Manager::Instance().getVariable("sourceObjectIsInList(testGammaList)");
2489 
2490  // -
2491  EXPECT_B2FATAL(std::get<int>(vnonsense->function(iscopied)));
2492  EXPECT_EQ(std::get<int>(vsensible->function(iscopied)), 1);
2493  EXPECT_EQ(std::get<int>(vsensible->function(notcopied)), 0);
2494 
2495  // now mock up some other type particles
2496  Particle composite({0.5, 0.4, 0.5, 0.8}, 512, Particle::c_Unflavored, Particle::c_Composite, 0);
2497  Particle undefined({0.3, 0.3, 0.4, 0.6}, 22, Particle::c_Unflavored, Particle::c_Undefined, 1);
2498  auto* composite_ = particles.appendNew(undefined);
2499  auto* undefined_ = particles.appendNew(composite);
2500  EXPECT_EQ(std::get<int>(vsensible->function(composite_)), -1);
2501  EXPECT_EQ(std::get<int>(vsensible->function(undefined_)), -1);
2502  }
2503 
2504  TEST_F(MetaVariableTest, mcParticleIsInMCList)
2505  {
2506  // datastore things
2507  DataStore::Instance().reset();
2508  DataStore::Instance().setInitializeActive(true);
2509 
2510  // needed to mock up
2511  StoreArray<MCParticle> mcparticles;
2512  StoreArray<Particle> particles;
2513  StoreObjPtr<ParticleList> list("testList");
2514  StoreObjPtr<ParticleList> anotherlist("supplimentaryList");
2515 
2516  mcparticles.registerInDataStore();
2517  particles.registerInDataStore();
2518  particles.registerRelationTo(mcparticles);
2519  DataStore::EStoreFlags flags = DataStore::c_DontWriteOut;
2520  list.registerInDataStore(flags);
2521  anotherlist.registerInDataStore(flags);
2522 
2523  DataStore::Instance().setInitializeActive(false);
2524  // end datastore setup
2525 
2526  list.create();
2527  list->initialize(22, "testList");
2528 
2529  anotherlist.create();
2530  anotherlist->initialize(22, "supplimentaryList");
2531 
2532  // MCParticles
2533  auto* mcphoton = mcparticles.appendNew();
2534  mcphoton->setPDG(Const::photon.getPDGCode());
2535  mcphoton->setStatus(MCParticle::c_PrimaryParticle);
2536 
2537  auto* mcelectron = mcparticles.appendNew();
2538  mcelectron->setPDG(Const::electron.getPDGCode());
2539  mcelectron->setStatus(MCParticle::c_PrimaryParticle);
2540 
2541  auto* mcanotherelectron = mcparticles.appendNew();
2542  mcanotherelectron->setPDG(Const::photon.getPDGCode());
2543  mcanotherelectron->setStatus(MCParticle::c_PrimaryParticle);
2544 
2545  auto* mcyetanotherelectron = mcparticles.appendNew();
2546  mcyetanotherelectron->setPDG(Const::photon.getPDGCode());
2547  mcyetanotherelectron->setStatus(MCParticle::c_PrimaryParticle);
2548 
2549  // particles
2550  auto* photon = particles.appendNew(PxPyPzEVector({ 0.0, -0.4, 0.8, 1.0}), 22);
2551  photon->addRelationTo(mcphoton);
2552  list->addParticle(photon);
2553 
2554  auto* electron = particles.appendNew(PxPyPzEVector({ 0.0, -0.4, 0.8, 1.0}), 22);
2555  electron->addRelationTo(mcelectron);
2556  list->addParticle(electron);
2557 
2558  auto* other = particles.appendNew(PxPyPzEVector({ 0.0, -0.4, 0.8, 1.0}), 22);
2559  other->addRelationTo(mcanotherelectron);
2560 
2561  auto* yetanotherelectron = particles.appendNew(PxPyPzEVector({ 0.0, -0.4, 0.8, 1.0}), 22);
2562  yetanotherelectron->addRelationTo(mcyetanotherelectron);
2563  anotherlist->addParticle(yetanotherelectron);
2564  // not in the list
2565 
2566  // get the variable
2567  const Manager::Var* vnonsense = Manager::Instance().getVariable("mcParticleIsInMCList(NONEXISTANTLIST)");
2568  const Manager::Var* vsensible = Manager::Instance().getVariable("mcParticleIsInMCList(testList)");
2569 
2570  // -
2571  EXPECT_B2FATAL(std::get<bool>(vnonsense->function(photon)));
2572  EXPECT_TRUE(std::get<bool>(vsensible->function(photon)));
2573  EXPECT_TRUE(std::get<bool>(vsensible->function(electron)));
2574  EXPECT_FALSE(std::get<bool>(vsensible->function(other)));
2575  EXPECT_FALSE(std::get<bool>(vsensible->function(yetanotherelectron)));
2576 
2577  // now mock up some other type particles
2578  Particle composite({0.5, 0.4, 0.5, 0.8}, 512, Particle::c_Unflavored, Particle::c_Composite, 0);
2579  Particle undefined({0.3, 0.3, 0.4, 0.6}, 22, Particle::c_Unflavored, Particle::c_Undefined, 1);
2580  auto* composite_ = particles.appendNew(undefined);
2581  auto* undefined_ = particles.appendNew(composite);
2582  EXPECT_FALSE(std::get<bool>(vsensible->function(composite_)));
2583  EXPECT_FALSE(std::get<bool>(vsensible->function(undefined_)));
2584  }
2585 
2586  TEST_F(MetaVariableTest, mostB2BAndClosestParticles)
2587  {
2588  /* Mock up an event with a "photon" and an "electron" which are nearly back to
2589  * back, and second "photon" which is close-ish to the "electron".
2590  *
2591  * Other test of non-existent / empty lists and variables also included.
2592  */
2593 
2594  // Connect gearbox for CMS variables
2595  Gearbox& gearbox = Gearbox::getInstance();
2596  gearbox.setBackends({std::string("file:")});
2597  gearbox.close();
2598  gearbox.open("geometry/Belle2.xml", false);
2599 
2600  // we need the particles StoreArray
2601  StoreArray<Particle> particles;
2602  DataStore::EStoreFlags flags = DataStore::c_DontWriteOut;
2603 
2604  // create a photon list for testing
2605  StoreObjPtr<ParticleList> gammalist("testGammaList");
2606  StoreObjPtr<ParticleList> emptylist("testEmptyList");
2607  DataStore::Instance().setInitializeActive(true);
2608  gammalist.registerInDataStore(flags);
2609  emptylist.registerInDataStore(flags);
2610  DataStore::Instance().setInitializeActive(false);
2611  gammalist.create();
2612  gammalist->initialize(22, "testGammaList");
2613  emptylist.create();
2614  emptylist->initialize(22, "testEmptyList");
2615 
2616  // create some photons in an stdvector
2617  std::vector<Particle> gammavector = {
2618  Particle({ -1.0, -1.0, 0.8, 1.7}, // this should be the most b2b to our reference particle
2619  22, Particle::c_Unflavored, Particle::c_Undefined, 0),
2620  Particle({0.2, 0.7, 0.9, 3.4}, // should be the closest
2621  22, Particle::c_Unflavored, Particle::c_Undefined, 1),
2622  };
2623  // put the photons in the StoreArray
2624  for (const auto& g : gammavector)
2625  particles.appendNew(g);
2626 
2627  // put the photons in the test list
2628  for (size_t i = 0; i < gammavector.size(); i++)
2629  gammalist->addParticle(i, 22, Particle::c_Unflavored);
2630 
2631  // add the reference particle (electron) to the StoreArray
2632  const auto* electron = particles.appendNew(
2633  Particle({1.0, 1.0, 0.5, 1.6}, // somewhere in the +ve quarter of the detector
2634  11, Particle::c_Unflavored, Particle::c_Undefined, 2) // needs to be incremented if we add to gamma vector
2635  );
2636 
2637  {
2638  EXPECT_B2FATAL(Manager::Instance().getVariable("angleToClosestInList"));
2639  EXPECT_B2FATAL(Manager::Instance().getVariable("angleToClosestInList(A, B)"));
2640 
2641  const auto* nonexistent = Manager::Instance().getVariable("angleToClosestInList(NONEXISTANTLIST)");
2642  EXPECT_B2FATAL(std::get<double>(nonexistent->function(electron)));
2643 
2644  const auto* empty = Manager::Instance().getVariable("angleToClosestInList(testEmptyList)");
2645  EXPECT_TRUE(std::isnan(std::get<double>(empty->function(electron))));
2646 
2647  const auto* closest = Manager::Instance().getVariable("angleToClosestInList(testGammaList)");
2648  EXPECT_FLOAT_EQ(std::get<double>(closest->function(electron)), 0.68014491);
2649 
2650  const auto* closestCMS = Manager::Instance().getVariable("useCMSFrame(angleToClosestInList(testGammaList))");
2651  EXPECT_FLOAT_EQ(std::get<double>(closestCMS->function(electron)), 0.67901474);
2652  }
2653 
2654  {
2655  EXPECT_B2FATAL(Manager::Instance().getVariable("closestInList"));
2656  EXPECT_B2FATAL(Manager::Instance().getVariable("closestInList(A, B, C)"));
2657 
2658  const auto* nonexistent = Manager::Instance().getVariable("closestInList(NONEXISTANTLIST, E)");
2659  EXPECT_B2FATAL(std::get<double>(nonexistent->function(electron)));
2660 
2661  const auto* empty = Manager::Instance().getVariable("closestInList(testEmptyList, E)");
2662  EXPECT_TRUE(std::isnan(std::get<double>(empty->function(electron))));
2663 
2664  const auto* closest = Manager::Instance().getVariable("closestInList(testGammaList, E)");
2665  EXPECT_FLOAT_EQ(std::get<double>(closest->function(electron)), 3.4);
2666 
2667  const auto* closestCMS = Manager::Instance().getVariable("useCMSFrame(closestInList(testGammaList, E))");
2668  EXPECT_FLOAT_EQ(std::get<double>(closestCMS->function(electron)), 3.2732551); // the energy gets smeared because of boost
2669 
2670  const auto* closestCMSLabE = Manager::Instance().getVariable("useCMSFrame(closestInList(testGammaList, useLabFrame(E)))");
2671  EXPECT_FLOAT_EQ(std::get<double>(closestCMSLabE->function(electron)), 3.4); // aaand should be back to the lab frame value
2672  }
2673 
2674  {
2675  EXPECT_B2FATAL(Manager::Instance().getVariable("angleToMostB2BInList"));
2676  EXPECT_B2FATAL(Manager::Instance().getVariable("angleToMostB2BInList(A, B)"));
2677 
2678  const auto* nonexistent = Manager::Instance().getVariable("angleToMostB2BInList(NONEXISTANTLIST)");
2679  EXPECT_B2FATAL(std::get<double>(nonexistent->function(electron)));
2680 
2681  const auto* empty = Manager::Instance().getVariable("angleToMostB2BInList(testEmptyList)");
2682  EXPECT_TRUE(std::isnan(std::get<double>(empty->function(electron))));
2683 
2684  const auto* mostB2B = Manager::Instance().getVariable("angleToMostB2BInList(testGammaList)");
2685  EXPECT_FLOAT_EQ(std::get<double>(mostB2B->function(electron)), 2.2869499);
2686 
2687  const auto* mostB2BCMS = Manager::Instance().getVariable("useCMSFrame(angleToMostB2BInList(testGammaList))");
2688  EXPECT_FLOAT_EQ(std::get<double>(mostB2BCMS->function(electron)), 2.8312778);
2689  }
2690 
2691  {
2692  EXPECT_B2FATAL(Manager::Instance().getVariable("mostB2BInList"));
2693  EXPECT_B2FATAL(Manager::Instance().getVariable("mostB2BInList(A, B, C)"));
2694 
2695  const auto* nonexistent = Manager::Instance().getVariable("mostB2BInList(NONEXISTANTLIST, E)");
2696  EXPECT_B2FATAL(std::get<double>(nonexistent->function(electron)));
2697 
2698  const auto* empty = Manager::Instance().getVariable("mostB2BInList(testEmptyList, E)");
2699  EXPECT_TRUE(std::isnan(std::get<double>(empty->function(electron))));
2700 
2701  const auto* mostB2B = Manager::Instance().getVariable("mostB2BInList(testGammaList, E)");
2702  EXPECT_FLOAT_EQ(std::get<double>(mostB2B->function(electron)), 1.7);
2703 
2704  const auto* mostB2BCMS = Manager::Instance().getVariable("useCMSFrame(mostB2BInList(testGammaList, E))");
2705  EXPECT_FLOAT_EQ(std::get<double>(mostB2BCMS->function(electron)), 1.5848758); // the energy gets smeared because of boost
2706 
2707  const auto* mostB2BCMSLabE = Manager::Instance().getVariable("useCMSFrame(mostB2BInList(testGammaList, useLabFrame(E)))");
2708  EXPECT_FLOAT_EQ(std::get<double>(mostB2BCMSLabE->function(electron)), 1.7); // aaand should be back to the lab frame value
2709  }
2710  }
2711 
2712  TEST_F(MetaVariableTest, totalEnergyOfParticlesInList)
2713  {
2714  // we need the particles StoreArray
2715  StoreArray<Particle> particles;
2716  DataStore::EStoreFlags flags = DataStore::c_DontWriteOut;
2717 
2718  // create a photon list for testing
2719  StoreObjPtr<ParticleList> gammalist("testGammaList");
2720  DataStore::Instance().setInitializeActive(true);
2721  gammalist.registerInDataStore(flags);
2722  DataStore::Instance().setInitializeActive(false);
2723  gammalist.create();
2724  gammalist->initialize(22, "testGammaList");
2725 
2726  // create some photons in an stdvector
2727  std::vector<Particle> gammavector = {
2728  Particle({0.5, 0.4, 0.4, 0.8}, 22, Particle::c_Unflavored, Particle::c_Undefined, 0),
2729  Particle({0.5, 0.2, 0.7, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 1),
2730  Particle({0.4, 0.2, 0.7, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 2),
2731  Particle({0.5, 0.4, 0.8, 1.1}, 22, Particle::c_Unflavored, Particle::c_Undefined, 3),
2732  Particle({0.3, 0.3, 0.4, 0.6}, 22, Particle::c_Unflavored, Particle::c_Undefined, 4)
2733  };
2734 
2735  // put the photons in the StoreArray
2736  for (const auto& g : gammavector)
2737  particles.appendNew(g);
2738 
2739  // put the photons in the test list
2740  for (size_t i = 0; i < gammavector.size(); i++)
2741  gammalist->addParticle(i, 22, Particle::c_Unflavored);
2742 
2743  // get their total energy
2744  const Manager::Var* vnonsense = Manager::Instance().getVariable(
2745  "totalEnergyOfParticlesInList(NONEXISTANTLIST)");
2746  const Manager::Var* vsensible = Manager::Instance().getVariable(
2747  "totalEnergyOfParticlesInList(testGammaList)");
2748 
2749  // -
2750  EXPECT_B2FATAL(std::get<double>(vnonsense->function(nullptr)));
2751  EXPECT_FLOAT_EQ(std::get<double>(vsensible->function(nullptr)), 4.3);
2752  }
2753  TEST_F(MetaVariableTest, totalPxOfParticlesInList)
2754  {
2755  // we need the particles StoreArray
2756  StoreArray<Particle> particles;
2757  DataStore::EStoreFlags flags = DataStore::c_DontWriteOut;
2758 
2759  // create a photon list for testing
2760  StoreObjPtr<ParticleList> gammalist("testGammaList");
2761  DataStore::Instance().setInitializeActive(true);
2762  gammalist.registerInDataStore(flags);
2763  DataStore::Instance().setInitializeActive(false);
2764  gammalist.create();
2765  gammalist->initialize(22, "testGammaList");
2766 
2767  // create some photons in an stdvector
2768  std::vector<Particle> gammavector = {
2769  Particle({0.5, 0.4, 0.5, 0.8}, 22, Particle::c_Unflavored, Particle::c_Undefined, 0),
2770  Particle({0.5, 0.2, 0.7, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 1),
2771  Particle({0.4, 0.2, 0.7, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 2),
2772  Particle({0.5, 0.4, 0.8, 1.1}, 22, Particle::c_Unflavored, Particle::c_Undefined, 3),
2773  Particle({0.3, 0.3, 0.4, 0.6}, 22, Particle::c_Unflavored, Particle::c_Undefined, 4)
2774  };
2775 
2776  // put the photons in the StoreArray
2777  for (const auto& g : gammavector)
2778  particles.appendNew(g);
2779 
2780  // put the photons in the test list
2781  for (size_t i = 0; i < gammavector.size(); i++)
2782  gammalist->addParticle(i, 22, Particle::c_Unflavored);
2783 
2784  // get their total energy
2785  const Manager::Var* vnonsense = Manager::Instance().getVariable(
2786  "totalPxOfParticlesInList(NONEXISTANTLIST)");
2787  const Manager::Var* vsensible = Manager::Instance().getVariable(
2788  "totalPxOfParticlesInList(testGammaList)");
2789 
2790  // -
2791  EXPECT_B2FATAL(std::get<double>(vnonsense->function(nullptr)));
2792  EXPECT_FLOAT_EQ(std::get<double>(vsensible->function(nullptr)), 2.2);
2793  }
2794  TEST_F(MetaVariableTest, totalPyOfParticlesInList)
2795  {
2796  // we need the particles StoreArray
2797  StoreArray<Particle> particles;
2798  DataStore::EStoreFlags flags = DataStore::c_DontWriteOut;
2799 
2800  // create a photon list for testing
2801  StoreObjPtr<ParticleList> gammalist("testGammaList");
2802  DataStore::Instance().setInitializeActive(true);
2803  gammalist.registerInDataStore(flags);
2804  DataStore::Instance().setInitializeActive(false);
2805  gammalist.create();
2806  gammalist->initialize(22, "testGammaList");
2807 
2808  // create some photons in an stdvector
2809  std::vector<Particle> gammavector = {
2810  Particle({0.5, 0.4, 0.5, 0.8}, 22, Particle::c_Unflavored, Particle::c_Undefined, 0),
2811  Particle({0.5, 0.2, 0.7, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 1),
2812  Particle({0.4, 0.2, 0.7, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 2),
2813  Particle({0.5, 0.4, 0.8, 1.1}, 22, Particle::c_Unflavored, Particle::c_Undefined, 3),
2814  Particle({0.3, 0.3, 0.4, 0.6}, 22, Particle::c_Unflavored, Particle::c_Undefined, 4)
2815  };
2816 
2817  // put the photons in the StoreArray
2818  for (const auto& g : gammavector)
2819  particles.appendNew(g);
2820 
2821  // put the photons in the test list
2822  for (size_t i = 0; i < gammavector.size(); i++)
2823  gammalist->addParticle(i, 22, Particle::c_Unflavored);
2824 
2825  // get their total energy
2826  const Manager::Var* vnonsense = Manager::Instance().getVariable(
2827  "totalPyOfParticlesInList(NONEXISTANTLIST)");
2828  const Manager::Var* vsensible = Manager::Instance().getVariable(
2829  "totalPyOfParticlesInList(testGammaList)");
2830 
2831  // -
2832  EXPECT_B2FATAL(std::get<double>(vnonsense->function(nullptr)));
2833  EXPECT_FLOAT_EQ(std::get<double>(vsensible->function(nullptr)), 1.5);
2834  }
2835  TEST_F(MetaVariableTest, totalPzOfParticlesInList)
2836  {
2837  // we need the particles StoreArray
2838  StoreArray<Particle> particles;
2839  DataStore::EStoreFlags flags = DataStore::c_DontWriteOut;
2840 
2841  // create a photon list for testing
2842  StoreObjPtr<ParticleList> gammalist("testGammaList");
2843  DataStore::Instance().setInitializeActive(true);
2844  gammalist.registerInDataStore(flags);
2845  DataStore::Instance().setInitializeActive(false);
2846  gammalist.create();
2847  gammalist->initialize(22, "testGammaList");
2848 
2849  // create some photons in an stdvector
2850  std::vector<Particle> gammavector = {
2851  Particle({0.5, 0.4, 0.5, 0.8}, 22, Particle::c_Unflavored, Particle::c_Undefined, 0),
2852  Particle({0.5, 0.2, 0.7, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 1),
2853  Particle({0.4, 0.2, 0.7, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 2),
2854  Particle({0.5, 0.4, 0.8, 1.1}, 22, Particle::c_Unflavored, Particle::c_Undefined, 3),
2855  Particle({0.3, 0.3, 0.4, 0.6}, 22, Particle::c_Unflavored, Particle::c_Undefined, 4)
2856  };
2857 
2858  // put the photons in the StoreArray
2859  for (const auto& g : gammavector)
2860  particles.appendNew(g);
2861 
2862  // put the photons in the test list
2863  for (size_t i = 0; i < gammavector.size(); i++)
2864  gammalist->addParticle(i, 22, Particle::c_Unflavored);
2865 
2866  // get their total energy
2867  const Manager::Var* vnonsense = Manager::Instance().getVariable(
2868  "totalPzOfParticlesInList(NONEXISTANTLIST)");
2869  const Manager::Var* vsensible = Manager::Instance().getVariable(
2870  "totalPzOfParticlesInList(testGammaList)");
2871 
2872  // -
2873  EXPECT_B2FATAL(std::get<double>(vnonsense->function(nullptr)));
2874  EXPECT_FLOAT_EQ(std::get<double>(vsensible->function(nullptr)), 3.1);
2875  }
2876  TEST_F(MetaVariableTest, maxPtInList)
2877  {
2878  // we need the particles StoreArray
2879  StoreArray<Particle> particles;
2880  DataStore::EStoreFlags flags = DataStore::c_DontWriteOut;
2881 
2882  // create a photon list for testing
2883  StoreObjPtr<ParticleList> gammalist("testGammaList");
2884  DataStore::Instance().setInitializeActive(true);
2885  gammalist.registerInDataStore(flags);
2886  DataStore::Instance().setInitializeActive(false);
2887  gammalist.create();
2888  gammalist->initialize(22, "testGammaList");
2889 
2890  // create some photons in an stdvector
2891  std::vector<Particle> gammavector = {
2892  Particle({0.5, 0.4, 0.5, 0.8}, 22, Particle::c_Unflavored, Particle::c_Undefined, 0),
2893  Particle({0.5, 0.2, 0.7, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 1),
2894  Particle({0.4, 0.2, 0.7, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 2),
2895  Particle({0.5, 0.4, 0.8, 1.1}, 22, Particle::c_Unflavored, Particle::c_Undefined, 3),
2896  Particle({0.3, 0.3, 0.4, 0.6}, 22, Particle::c_Unflavored, Particle::c_Undefined, 4)
2897  };
2898 
2899  // put the photons in the StoreArray
2900  for (const auto& g : gammavector)
2901  particles.appendNew(g);
2902 
2903  // put the photons in the test list
2904  for (size_t i = 0; i < gammavector.size(); i++)
2905  gammalist->addParticle(i, 22, Particle::c_Unflavored);
2906 
2907  // get their total energy
2908  const Manager::Var* vnonsense = Manager::Instance().getVariable(
2909  "maxPtInList(NONEXISTANTLIST)");
2910  const Manager::Var* vsensible = Manager::Instance().getVariable(
2911  "maxPtInList(testGammaList)");
2912 
2913  // -
2914  EXPECT_B2FATAL(std::get<double>(vnonsense->function(nullptr)));
2915  EXPECT_FLOAT_EQ(std::get<double>(vsensible->function(nullptr)), sqrt(0.5 * 0.5 + 0.4 * 0.4));
2916  }
2917 
2918 
2919  TEST_F(MetaVariableTest, numberOfNonOverlappingParticles)
2920  {
2921  StoreArray<Particle> particles;
2922  DataStore::EStoreFlags flags = DataStore::c_DontWriteOut;
2923 
2924  StoreObjPtr<ParticleList> outputList("pList1");
2925  DataStore::Instance().setInitializeActive(true);
2926  outputList.registerInDataStore(flags);
2927  DataStore::Instance().setInitializeActive(false);
2928  outputList.create();
2929  outputList->initialize(22, "pList1");
2930 
2931  auto* p1 = particles.appendNew(Particle({0.5, 0.4, 0.5, 0.8}, 22, Particle::c_Unflavored, Particle::c_Undefined, 2));
2932  auto* p2 = particles.appendNew(Particle({0.5, 0.2, 0.7, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 3));
2933  auto* p3 = particles.appendNew(Particle({0.5, 0.2, 0.7, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 4));
2934 
2935  outputList->addParticle(0, 22, Particle::c_Unflavored);
2936  outputList->addParticle(1, 22, Particle::c_Unflavored);
2937 
2938  const Manager::Var* var = Manager::Instance().getVariable("numberOfNonOverlappingParticles(pList1)");
2939  ASSERT_NE(var, nullptr);
2940  EXPECT_EQ(std::get<int>(var->function(p1)), 1);
2941  EXPECT_EQ(std::get<int>(var->function(p2)), 1);
2942  EXPECT_EQ(std::get<int>(var->function(p3)), 2);
2943 
2944  }
2945 
2946  TEST_F(MetaVariableTest, veto)
2947  {
2948  StoreArray<Particle> particles;
2949  DataStore::EStoreFlags flags = DataStore::c_DontWriteOut;
2950 
2951  const Particle* p = particles.appendNew(Particle({0.8, 0.8, 1.131370849898476039041351, 1.6}, 22,
2952  Particle::c_Unflavored, Particle::c_Undefined, 1));
2953 
2954  StoreObjPtr<ParticleList> outputList("pList1");
2955  DataStore::Instance().setInitializeActive(true);
2956  outputList.registerInDataStore(flags);
2957  DataStore::Instance().setInitializeActive(false);
2958  outputList.create();
2959  outputList->initialize(22, "pList1");
2960 
2961  particles.appendNew(Particle({0.5, 0.4953406774856531014212777, 0.5609256753154148484773173, 0.9}, 22,
2962  Particle::c_Unflavored, Particle::c_Undefined, 2)); //m=0.135
2963  particles.appendNew(Particle({0.5, 0.2, 0.72111, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 3)); //m=0.3582
2964  particles.appendNew(Particle({0.4, 0.2, 0.78102, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 4)); //m=0.3908
2965  particles.appendNew(Particle({0.5, 0.4, 0.89443, 1.1}, 22, Particle::c_Unflavored, Particle::c_Undefined, 5)); //m=0.2369
2966  particles.appendNew(Particle({0.3, 0.3, 0.42426, 0.6}, 22, Particle::c_Unflavored, Particle::c_Undefined, 6)); //m=0.0036
2967 
2968  outputList->addParticle(1, 22, Particle::c_Unflavored);
2969  outputList->addParticle(2, 22, Particle::c_Unflavored);
2970  outputList->addParticle(3, 22, Particle::c_Unflavored);
2971  outputList->addParticle(4, 22, Particle::c_Unflavored);
2972  outputList->addParticle(5, 22, Particle::c_Unflavored);
2973 
2974  StoreObjPtr<ParticleList> outputList2("pList2");
2975  DataStore::Instance().setInitializeActive(true);
2976  outputList2.registerInDataStore(flags);
2977  DataStore::Instance().setInitializeActive(false);
2978  outputList2.create();
2979  outputList2->initialize(22, "pList2");
2980 
2981  particles.appendNew(Particle({0.5, -0.4, 0.63246, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 7)); //m=1.1353
2982  particles.appendNew(Particle({0.5, 0.2, 0.72111, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 8)); //m=0.3582
2983  particles.appendNew(Particle({0.4, 0.2, 0.78102, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 9)); //m=0.3908
2984  particles.appendNew(Particle({0.5, 0.4, 0.89443, 1.1}, 22, Particle::c_Unflavored, Particle::c_Undefined, 10)); //m=0.2369
2985  particles.appendNew(Particle({0.3, 0.3, 0.42426, 0.6}, 22, Particle::c_Unflavored, Particle::c_Undefined, 11)); //m=0.0036
2986 
2987  outputList2->addParticle(6, 22, Particle::c_Unflavored);
2988  outputList2->addParticle(7, 22, Particle::c_Unflavored);
2989  outputList2->addParticle(8, 22, Particle::c_Unflavored);
2990  outputList2->addParticle(9, 22, Particle::c_Unflavored);
2991  outputList2->addParticle(10, 22, Particle::c_Unflavored);
2992 
2993  const Manager::Var* var = Manager::Instance().getVariable("veto(pList1, 0.130 < M < 0.140, 22)");
2994  ASSERT_NE(var, nullptr);
2995  EXPECT_TRUE(std::get<bool>(var->function(p)));
2996 
2997  var = Manager::Instance().getVariable("veto(pList2, 0.130 < M < 0.140, 22)");
2998  ASSERT_NE(var, nullptr);
2999  EXPECT_FALSE(std::get<bool>(var->function(p)));
3000 
3001  }
3002 
3003  TEST_F(MetaVariableTest, averageValueInList)
3004  {
3005  // we need the particles StoreArray
3006  StoreArray<Particle> particles;
3007  DataStore::EStoreFlags flags = DataStore::c_DontWriteOut;
3008 
3009  // create a photon list for testing
3010  StoreObjPtr<ParticleList> gammalist("testGammaList");
3011  DataStore::Instance().setInitializeActive(true);
3012  gammalist.registerInDataStore(flags);
3013  DataStore::Instance().setInitializeActive(false);
3014  gammalist.create();
3015  gammalist->initialize(22, "testGammaList");
3016 
3017  // create some photons in an stdvector
3018  std::vector<Particle> gammavector = {
3019  Particle({0.5, 0.4, 0.4, 0.8}, 22, Particle::c_Unflavored, Particle::c_Undefined, 0),
3020  Particle({0.5, 0.2, 0.7, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 1),
3021  Particle({0.4, 0.2, 0.7, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 2),
3022  Particle({0.5, 0.4, 0.8, 1.1}, 22, Particle::c_Unflavored, Particle::c_Undefined, 3),
3023  Particle({0.3, 0.3, 0.4, 0.6}, 22, Particle::c_Unflavored, Particle::c_Undefined, 4)
3024  };
3025 
3026  // put the photons in the StoreArray
3027  for (const auto& g : gammavector)
3028  particles.appendNew(g);
3029 
3030  // put the photons in the test list
3031  for (size_t i = 0; i < gammavector.size(); i++)
3032  gammalist->addParticle(i, 22, Particle::c_Unflavored);
3033 
3034  // get the average px, py, pz, E of the gammas in the list
3035  const Manager::Var* vmeanpx = Manager::Instance().getVariable(
3036  "averageValueInList(testGammaList, px)");
3037  const Manager::Var* vmeanpy = Manager::Instance().getVariable(
3038  "averageValueInList(testGammaList, py)");
3039  const Manager::Var* vmeanpz = Manager::Instance().getVariable(
3040  "averageValueInList(testGammaList, pz)");
3041  const Manager::Var* vmeanE = Manager::Instance().getVariable(
3042  "averageValueInList(testGammaList, E)");
3043 
3044  EXPECT_FLOAT_EQ(std::get<double>(vmeanpx->function(nullptr)), 0.44);
3045  EXPECT_FLOAT_EQ(std::get<double>(vmeanpy->function(nullptr)), 0.3);
3046  EXPECT_FLOAT_EQ(std::get<double>(vmeanpz->function(nullptr)), 0.6);
3047  EXPECT_FLOAT_EQ(std::get<double>(vmeanE->function(nullptr)), 0.86);
3048 
3049  // wrong number of arguments (no variable provided)
3050  EXPECT_B2FATAL(Manager::Instance().getVariable("averageValueInList(testGammaList)"));
3051 
3052  // non-existing variable
3053  EXPECT_B2FATAL(Manager::Instance().getVariable("averageValueInList(testGammaList, NONEXISTANTVARIABLE)"));
3054 
3055  // non-existing list
3056  const Manager::Var* vnolist = Manager::Instance().getVariable(
3057  "averageValueInList(NONEXISTANTLIST, px)");
3058 
3059  EXPECT_B2FATAL(std::get<double>(vnolist->function(nullptr)));
3060  }
3061 
3062  TEST_F(MetaVariableTest, medianValueInList)
3063  {
3064  // we need the particles StoreArray
3065  StoreArray<Particle> particles;
3066  DataStore::EStoreFlags flags = DataStore::c_DontWriteOut;
3067 
3068  // create two photon lists for testing (one with odd and one with even number of particles)
3069  StoreObjPtr<ParticleList> oddgammalist("oddGammaList");
3070  DataStore::Instance().setInitializeActive(true);
3071  oddgammalist.registerInDataStore(flags);
3072  DataStore::Instance().setInitializeActive(false);
3073  oddgammalist.create();
3074  oddgammalist->initialize(22, "oddGammaList");
3075  StoreObjPtr<ParticleList> evengammalist("evenGammaList");
3076  DataStore::Instance().setInitializeActive(true);
3077  evengammalist.registerInDataStore(flags);
3078  DataStore::Instance().setInitializeActive(false);
3079  evengammalist.create();
3080  evengammalist->initialize(22, "evenGammaList");
3081 
3082  // create some photons in an stdvector
3083  std::vector<Particle> gammavector = {
3084  Particle({0.5, 0.4, 0.4, 0.8}, 22, Particle::c_Unflavored, Particle::c_Undefined, 0),
3085  Particle({0.5, 0.2, 0.7, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 1),
3086  Particle({0.4, 0.2, 0.7, 0.9}, 22, Particle::c_Unflavored, Particle::c_Undefined, 2),
3087  Particle({0.5, 0.4, 0.8, 1.1}, 22, Particle::c_Unflavored, Particle::c_Undefined, 3),
3088  Particle({0.3, 0.3, 0.4, 0.6}, 22, Particle::c_Unflavored, Particle::c_Undefined, 4)
3089  };
3090 
3091  // put the photons in the StoreArray
3092  for (const auto& g : gammavector)
3093  particles.appendNew(g);
3094 
3095  // put the photons in the test lists
3096  oddgammalist->addParticle(0, 22, Particle::c_Unflavored);
3097  for (size_t i = 1; i < gammavector.size(); i++) {
3098  oddgammalist->addParticle(i, 22, Particle::c_Unflavored);
3099  evengammalist->addParticle(i, 22, Particle::c_Unflavored);
3100  }
3101 
3102  // get the median px, py, pz, E of the gammas in the list with odd number of particles
3103  const Manager::Var* voddmedianpx = Manager::Instance().getVariable(
3104  "medianValueInList(oddGammaList, px)");
3105  const Manager::Var* voddmedianpy = Manager::Instance().getVariable(
3106  "medianValueInList(oddGammaList, py)");
3107  const Manager::Var* voddmedianpz = Manager::Instance().getVariable(
3108  "medianValueInList(oddGammaList, pz)");
3109  const Manager::Var* voddmedianE = Manager::Instance().getVariable(
3110  "medianValueInList(oddGammaList, E)");
3111 
3112  EXPECT_FLOAT_EQ(std::get<double>(voddmedianpx->function(nullptr)), 0.5);
3113  EXPECT_FLOAT_EQ(std::get<double>(voddmedianpy->function(nullptr)), 0.3);
3114  EXPECT_FLOAT_EQ(std::get<double>(voddmedianpz->function(nullptr)), 0.7);
3115  EXPECT_FLOAT_EQ(std::get<double>(voddmedianE->function(nullptr)), 0.9);
3116 
3117  // get the median px, py, pz, E of the gammas in the list with odd number of particles
3118  const Manager::Var* vevenmedianpx = Manager::Instance().getVariable(
3119  "medianValueInList(evenGammaList, px)");
3120  const Manager::Var* vevenmedianpy = Manager::Instance().getVariable(
3121  "medianValueInList(evenGammaList, py)");
3122  const Manager::Var* vevenmedianpz = Manager::Instance().getVariable(
3123  "medianValueInList(evenGammaList, pz)");
3124  const Manager::Var* vevenmedianE = Manager::Instance().getVariable(
3125  "medianValueInList(evenGammaList, E)");
3126 
3127  EXPECT_FLOAT_EQ(std::get<double>(vevenmedianpx->function(nullptr)), 0.45);
3128  EXPECT_FLOAT_EQ(std::get<double>(vevenmedianpy->function(nullptr)), 0.25);
3129  EXPECT_FLOAT_EQ(std::get<double>(vevenmedianpz->function(nullptr)), 0.7);
3130  EXPECT_FLOAT_EQ(std::get<double>(vevenmedianE->function(nullptr)), 0.9);
3131 
3132  // wrong number of arguments (no variable provided)
3133  EXPECT_B2FATAL(Manager::Instance().getVariable("medianValueInList(oddGammaList)"));
3134 
3135  // non-existing variable
3136  EXPECT_B2FATAL(Manager::Instance().getVariable("medianValueInList(oddGammaList, NONEXISTANTVARIABLE)"));
3137 
3138  // non-existing list
3139  const Manager::Var* vnolist = Manager::Instance().getVariable(
3140  "medianValueInList(NONEXISTANTLIST, px)");
3141 
3142  EXPECT_B2FATAL(std::get<double>(vnolist->function(nullptr)));
3143  }
3144 
3145  TEST_F(MetaVariableTest, pValueCombination)
3146  {
3147  PxPyPzEVector momentum;
3148  StoreArray<Particle> particles;
3149  std::vector<int> daughterIndices;
3150  Particle KS(PxPyPzEVector(1.164, 1.55200, 0, 2), 310, Particle::c_Unflavored, Particle::c_Composite, 0);
3151  KS.setPValue(0.1);
3152  momentum += KS.get4Vector();
3153  Particle* newDaughters = particles.appendNew(KS);
3154  daughterIndices.push_back(newDaughters->getArrayIndex());
3155  Particle Jpsi(PxPyPzEVector(-1, 1, 1, 3.548), 443, Particle::c_Unflavored, Particle::c_Composite, 1);
3156  Jpsi.setPValue(0.9);
3157  momentum += Jpsi.get4Vector();
3158  newDaughters = particles.appendNew(Jpsi);
3159  daughterIndices.push_back(newDaughters->getArrayIndex());
3160  Particle* B = particles.appendNew(momentum, 521, Particle::c_Flavored, daughterIndices);
3161  B->setPValue(0.5);
3162 
3163  const Manager::Var* singlePvalue = Manager::Instance().getVariable("pValueCombination(chiProb)");
3164  ASSERT_NE(singlePvalue, nullptr);
3165  EXPECT_FLOAT_EQ(std::get<double>(singlePvalue->function(B)), 0.5);
3166 
3167  const Manager::Var* twoPvalues = Manager::Instance().getVariable("pValueCombination(chiProb, daughter(0, chiProb))");
3168  ASSERT_NE(twoPvalues, nullptr);
3169  EXPECT_FLOAT_EQ(std::get<double>(twoPvalues->function(B)), 0.05 * (1 - log(0.05)));
3170 
3171  const Manager::Var* threePvalues =
3172  Manager::Instance().getVariable("pValueCombination(chiProb, daughter(0, chiProb), daughter(1, chiProb))");
3173  ASSERT_NE(threePvalues, nullptr);
3174  EXPECT_FLOAT_EQ(std::get<double>(threePvalues->function(B)), 0.045 * (1 - log(0.045) + 0.5 * log(0.045) * log(0.045)));
3175 
3176  // wrong number of arguments
3177  EXPECT_B2FATAL(Manager::Instance().getVariable("pValueCombination()"));
3178 
3179  // non-existing variable
3180  EXPECT_B2FATAL(Manager::Instance().getVariable("pValueCombination(chiProb, NONEXISTANTVARIABLE)"));
3181  }
3182 
3183 
3184  TEST_F(MetaVariableTest, daughterCombinationOneGeneration)
3185  {
3186  const int nDaughters = 5;
3187  PxPyPzEVector momentum(0, 0, 0, 0);
3188  StoreArray<Particle> particles;
3189  std::vector<int> daughterIndices;
3190  std::vector<PxPyPzEVector> daughterMomenta;
3191 
3192  for (int i = 0; i < nDaughters; i++) {
3193  PxPyPzEVector mom(1, i * 0.5, 1, i * 1.0 + 2.0);
3194  Particle d(mom, (i % 2) ? 111 : 113);
3195  Particle* newDaughters = particles.appendNew(d);
3196  daughterIndices.push_back(newDaughters->getArrayIndex());
3197  daughterMomenta.push_back(mom);
3198  momentum = momentum + mom;
3199  }
3200  const Particle* p = particles.appendNew(momentum, 411, Particle::c_Flavored, daughterIndices);
3201 
3202  // Test the invariant mass of several combinations
3203  const Manager::Var* var = Manager::Instance().getVariable("daughterCombination(M, 0,1,2)");
3204  double M_test = (daughterMomenta[0] + daughterMomenta[1] + daughterMomenta[2]).mag();
3205  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), M_test);
3206 
3207  var = Manager::Instance().getVariable("daughterCombination(M, 0,4)");
3208  M_test = (daughterMomenta[0] + daughterMomenta[4]).mag();
3209  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), M_test);
3210 
3211 
3212  // Try with a non-lorentz invariant quantity
3213  var = Manager::Instance().getVariable("daughterCombination(p, 1, 0, 4)");
3214  double p_test = (daughterMomenta[0] + daughterMomenta[1] + daughterMomenta[4]).P();
3215  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), p_test);
3216 
3217 
3218  // errors and bad stuff
3219  EXPECT_B2FATAL(Manager::Instance().getVariable("daughterCombination(aVeryNonExistingVariableSillyName, 1, 0, 4)"));
3220 
3221  var = Manager::Instance().getVariable("daughterCombination(M, 1, 0, 100)");
3222  EXPECT_B2WARNING(std::get<double>(var->function(p)));
3223  EXPECT_TRUE(std::isnan(std::get<double>(var->function(p))));
3224 
3225 
3226  var = Manager::Instance().getVariable("daughterCombination(M, 1, -1)");
3227  EXPECT_B2WARNING(std::get<double>(var->function(p)));
3228  EXPECT_TRUE(std::isnan(std::get<double>(var->function(p))));
3229 
3230 
3231  var = Manager::Instance().getVariable("daughterCombination(M, 1, 0:1:0:0:1)");
3232  EXPECT_B2WARNING(std::get<double>(var->function(p)));
3233  EXPECT_TRUE(std::isnan(std::get<double>(var->function(p))));
3234 
3235  }
3236 
3237 
3238  TEST_F(MetaVariableTest, daughterCombinationTwoGenerations)
3239  {
3240  StoreArray<Particle> particles;
3241 
3242  // make a 1 -> 3 particle
3243 
3244  PxPyPzEVector momentum_1(0, 0, 0, 0);
3245  std::vector<PxPyPzEVector> daughterMomenta_1;
3246  std::vector<int> daughterIndices_1;
3247 
3248  for (int i = 0; i < 3; i++) {
3249  PxPyPzEVector mom(i * 0.2, 1, 1, i * 1.0 + 2.0);
3250  Particle d(mom, (i % 2) ? 111 : 113);
3251  Particle* newDaughters = particles.appendNew(d);
3252  daughterIndices_1.push_back(newDaughters->getArrayIndex());
3253  daughterMomenta_1.push_back(mom);
3254  momentum_1 = momentum_1 + mom;
3255  }
3256 
3257  const Particle* compositeDau_1 = particles.appendNew(momentum_1, 411, Particle::c_Flavored, daughterIndices_1);
3258 
3259 
3260  // make a 1 -> 2 particle
3261 
3262  PxPyPzEVector momentum_2(0, 0, 0, 0);
3263  std::vector<PxPyPzEVector> daughterMomenta_2;
3264  std::vector<int> daughterIndices_2;
3265 
3266  for (int i = 0; i < 2; i++) {
3267  PxPyPzEVector mom(1, 1, i * 0.3, i * 1.0 + 2.0);
3268  Particle d(mom, (i % 2) ? 111 : 113);
3269  Particle* newDaughters = particles.appendNew(d);
3270  daughterIndices_2.push_back(newDaughters->getArrayIndex());
3271  daughterMomenta_2.push_back(mom);
3272  momentum_2 = momentum_2 + mom;
3273  }
3274 
3275  const Particle* compositeDau_2 = particles.appendNew(momentum_2, 411, Particle::c_Flavored, daughterIndices_2);
3276 
3277 
3278  // make the composite particle
3279  std::vector<int> daughterIndices = {compositeDau_1->getArrayIndex(), compositeDau_2->getArrayIndex()};
3280  const Particle* p = particles.appendNew(momentum_2 + momentum_1, 111, Particle::c_Unflavored, daughterIndices);
3281 
3282 
3283  // Test the invariant mass of several combinations
3284  const Manager::Var* var = Manager::Instance().getVariable("daughterCombination(M, 0,1)");
3285 
3286  double M_test = (momentum_1 + momentum_2).mag();
3287  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), M_test);
3288 
3289  // this should be the mass of the first daughter
3290  var = Manager::Instance().getVariable("daughterCombination(M, 0:0, 0:1, 0:2)");
3291  M_test = (momentum_1).mag();
3292  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), M_test);
3293 
3294  // this should be a generic combinations
3295  var = Manager::Instance().getVariable("daughterCombination(M, 0:0, 0:1, 1:0)");
3296  M_test = (daughterMomenta_1[0] + daughterMomenta_1[1] + daughterMomenta_2[0]).mag();
3297  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), M_test);
3298 
3299  }
3300 
3301 
3302  TEST_F(MetaVariableTest, useAlternativeDaughterHypothesis)
3303  {
3304  const int nDaughters = 5;
3305  StoreArray<Particle> particles;
3306 
3307  // Build a first Particle
3308  PxPyPzEVector momentum(0, 0, 0, 0);
3309  std::vector<int> daughterIndices;
3310  for (int i = 0; i < nDaughters; i++) {
3311  double px = i * 0.1;
3312  double py = i * 0.3;
3313  double pz = -i * 0.1 - 0.2;
3314 
3315  PxPyPzEVector mom(px, py, pz, 1);
3316  // all pions
3317  int pdgCode = Const::pion.getPDGCode();
3318  Particle d(mom, pdgCode);
3319  d.updateMass(pdgCode);
3320  mom = d.get4Vector();
3321 
3322  Particle* daughters = particles.appendNew(d);
3323  daughterIndices.push_back(daughters->getArrayIndex());
3324  momentum = momentum + mom;
3325  }
3326  const Particle* p = particles.appendNew(momentum, 411, Particle::c_Flavored, daughterIndices);
3327 
3328 
3329  // Build a second Particle with same momenta, but different mass hyp.
3330  PxPyPzEVector momentumAlt(0, 0, 0, 0);
3331  std::vector<int> daughterIndicesAlt;
3332  for (int i = 0; i < nDaughters; i++) {
3333  double px = i * 0.1;
3334  double py = i * 0.3;
3335  double pz = -i * 0.1 - 0.2;
3336 
3337  PxPyPzEVector mom(px, py, pz, 1);
3338  // all pions but the first two
3339  int pdgCode = Const::pion.getPDGCode();
3340  if (i == 0)
3341  pdgCode = Const::proton.getPDGCode(); // a proton
3342  if (i == 1)
3343  pdgCode = Const::kaon.getPDGCode(); // a K
3344  Particle d(mom, pdgCode);
3345  d.updateMass(pdgCode);
3346  mom = d.get4Vector();
3347 
3348  Particle* daughters = particles.appendNew(d);
3349  daughterIndicesAlt.push_back(daughters->getArrayIndex());
3350  momentumAlt = momentumAlt + mom;
3351  }
3352  const Particle* pAlt = particles.appendNew(momentumAlt, 411, Particle::c_Flavored, daughterIndicesAlt);
3353 
3354 
3355  // Test the invariant mass under the alternative hypothesis
3356  std::cout << "mass test" << std::endl;
3357  const Manager::Var* var = Manager::Instance().getVariable("useAlternativeDaughterHypothesis(M, 0:p+,1:K+)");
3358  const Manager::Var* varAlt = Manager::Instance().getVariable("M");
3359  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), std::get<double>(varAlt->function(pAlt)));
3360 
3361  // check it's really charge-insensitive...
3362  std::cout << "charge test" << std::endl;
3363  var = Manager::Instance().getVariable("useAlternativeDaughterHypothesis(M, 0:p+,1:K-)");
3364  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), std::get<double>(varAlt->function(pAlt)));
3365 
3366  // check the variable is not changing the 3-momentum
3367  std::cout << "momentum test" << std::endl;
3368  var = Manager::Instance().getVariable("useAlternativeDaughterHypothesis(p, 0:p+,1:K-)");
3369  varAlt = Manager::Instance().getVariable("p");
3370  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), std::get<double>(varAlt->function(pAlt)));
3371  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), std::get<double>(varAlt->function(p)));
3372  EXPECT_FLOAT_EQ(std::get<double>(var->function(pAlt)), std::get<double>(varAlt->function(pAlt)));
3373  }
3374 
3375 
3376 
3377 
3378  TEST_F(MetaVariableTest, daughterAngle)
3379  {
3380  StoreArray<Particle> particles;
3381 
3382  // make a 1 -> 3 particle
3383 
3384  PxPyPzEVector momentum_1(0, 0, 0, 0);
3385  std::vector<PxPyPzEVector> daughterMomenta_1;
3386  std::vector<int> daughterIndices_1;
3387 
3388  for (int i = 0; i < 3; i++) {
3389  PxPyPzEVector mom(i * 0.2, 1, 1, i * 1.0 + 2.0);
3390  Particle d(mom, (i % 2) ? -11 : 211);
3391  Particle* newDaughters = particles.appendNew(d);
3392  daughterIndices_1.push_back(newDaughters->getArrayIndex());
3393  daughterMomenta_1.push_back(mom);
3394  momentum_1 = momentum_1 + mom;
3395  }
3396 
3397  const Particle* compositeDau_1 = particles.appendNew(momentum_1, 411, Particle::c_Flavored, daughterIndices_1);
3398 
3399 
3400  // make a 1 -> 2 particle
3401 
3402  PxPyPzEVector momentum_2(0, 0, 0, 0);
3403  std::vector<PxPyPzEVector> daughterMomenta_2;
3404  std::vector<int> daughterIndices_2;
3405 
3406  for (int i = 0; i < 2; i++) {
3407  PxPyPzEVector mom(1, 1, i * 0.3, i * 1.0 + 2.0);
3408  Particle d(mom, (i % 2) ? -11 : 211);
3409  Particle* newDaughters = particles.appendNew(d);
3410  daughterIndices_2.push_back(newDaughters->getArrayIndex());
3411  daughterMomenta_2.push_back(mom);
3412  momentum_2 = momentum_2 + mom;
3413  }
3414 
3415  const Particle* compositeDau_2 = particles.appendNew(momentum_2, 411, Particle::c_Flavored, daughterIndices_2);
3416 
3417 
3418  // make the composite particle
3419  std::vector<int> daughterIndices = {compositeDau_1->getArrayIndex(), compositeDau_2->getArrayIndex()};
3420  const Particle* p = particles.appendNew(momentum_2 + momentum_1, 111, Particle::c_Unflavored, daughterIndices);
3421 
3422 
3423  // Test the invariant mass of several combinations
3424  const Manager::Var* var = Manager::Instance().getVariable("daughterAngle(0, 1)");
3425  double v_test = acos(momentum_1.Vect().Unit().Dot(momentum_2.Vect().Unit()));
3426 
3427  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), v_test);
3428 
3429  // this should be a generic combinations
3430  var = Manager::Instance().getVariable("daughterAngle(0:0, 1:0)");
3431  v_test = acos(daughterMomenta_1[0].Vect().Unit().Dot(daughterMomenta_2[0].Vect().Unit()));
3432  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), v_test);
3433 
3434  var = Manager::Instance().getVariable("daughterAngle( 1, -1)");
3435  EXPECT_B2WARNING(std::get<double>(var->function(p)));
3436  EXPECT_TRUE(std::isnan(std::get<double>(var->function(p))));
3437 
3438  var = Manager::Instance().getVariable("daughterAngle(1, 0:1:0:0:1)");
3439  EXPECT_B2WARNING(std::get<double>(var->function(p)));
3440  EXPECT_TRUE(std::isnan(std::get<double>(var->function(p))));
3441 
3442  }
3443 
3444  TEST_F(MetaVariableTest, mcDaughterVariables)
3445  {
3446 
3447  DataStore::Instance().setInitializeActive(true);
3448  StoreArray<Particle> particles;
3449  StoreArray<MCParticle> mcParticles;
3450  particles.registerRelationTo(mcParticles);
3451  DataStore::Instance().setInitializeActive(true);
3452  // make a 1 -> 3 particle
3453 
3454  PxPyPzEVector momentum_1(0, 0, 0, 0);
3455  std::vector<PxPyPzEVector> daughterMomenta_1;
3456  std::vector<int> daughterIndices_1;
3457 
3458  for (int i = 0; i < 3; i++) {
3459  PxPyPzEVector mom(i * 0.2, 1, 1, i * 1.0 + 2.0);
3460  Particle d(mom, (i % 2) ? -11 : 211);
3461  Particle* newDaughters = particles.appendNew(d);
3462  daughterIndices_1.push_back(newDaughters->getArrayIndex());
3463  daughterMomenta_1.push_back(mom);
3464  momentum_1 = momentum_1 + mom;
3465 
3466  auto* mcParticle = mcParticles.appendNew();
3467  mcParticle->setPDG((i % 2) ? -Const::electron.getPDGCode() : Const::pion.getPDGCode());
3468  mcParticle->setStatus(MCParticle::c_PrimaryParticle);
3469  mcParticle->set4Vector(mom);
3470  newDaughters->addRelationTo(mcParticle);
3471  }
3472 
3473  const Particle* compositeDau_1 = particles.appendNew(momentum_1, 411, Particle::c_Flavored, daughterIndices_1);
3474  auto* mcCompositeDau_1 = mcParticles.appendNew();
3475  mcCompositeDau_1->setPDG(411);
3476  mcCompositeDau_1->setStatus(MCParticle::c_PrimaryParticle);
3477  mcCompositeDau_1->set4Vector(momentum_1);
3478  compositeDau_1->addRelationTo(mcCompositeDau_1);
3479 
3480  // make a 1 -> 2 particle
3481 
3482  PxPyPzEVector momentum_2(0, 0, 0, 0);
3483  std::vector<PxPyPzEVector> daughterMomenta_2;
3484  std::vector<int> daughterIndices_2;
3485 
3486  for (int i = 0; i < 2; i++) {
3487  PxPyPzEVector mom(1, 1, i * 0.3, i * 1.0 + 2.0);
3488  Particle d(mom, (i % 2) ? -11 : 211);
3489  Particle* newDaughters = particles.appendNew(d);
3490  daughterIndices_2.push_back(newDaughters->getArrayIndex());
3491  daughterMomenta_2.push_back(mom);
3492  momentum_2 = momentum_2 + mom;
3493 
3494  auto* mcParticle = mcParticles.appendNew();
3495  mcParticle->setPDG((i % 2) ? -Const::electron.getPDGCode() : Const::pion.getPDGCode());
3496  mcParticle->setStatus(MCParticle::c_PrimaryParticle);
3497  mcParticle->set4Vector(mom);
3498  newDaughters->addRelationTo(mcParticle);
3499  }
3500 
3501  const Particle* compositeDau_2 = particles.appendNew(momentum_2, 411, Particle::c_Flavored, daughterIndices_2);
3502  auto* mcCompositeDau_2 = mcParticles.appendNew();
3503  mcCompositeDau_2->setPDG(411);
3504  mcCompositeDau_2->setStatus(MCParticle::c_PrimaryParticle);
3505  mcCompositeDau_2->set4Vector(momentum_2);
3506  compositeDau_2->addRelationTo(mcCompositeDau_2);
3507 
3508  // make the composite particle
3509  std::vector<int> daughterIndices = {compositeDau_1->getArrayIndex(), compositeDau_2->getArrayIndex()};
3510  const Particle* p = particles.appendNew(momentum_2 + momentum_1, 111, Particle::c_Unflavored, daughterIndices);
3511 
3512 
3513  // Test mcDaughterAngle
3514  const Manager::Var* var = Manager::Instance().getVariable("mcDaughterAngle(0, 1)");
3515  double v_test = acos(momentum_1.Vect().Unit().Dot(momentum_2.Vect().Unit()));
3516  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), v_test);
3517 
3518  var = Manager::Instance().getVariable("mcDaughterAngle(0:0, 1:0)");
3519  v_test = acos(daughterMomenta_1[0].Vect().Unit().Dot(daughterMomenta_2[0].Vect().Unit()));
3520  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), v_test);
3521 
3522  var = Manager::Instance().getVariable("mcDaughterAngle( 1, -1)");
3523  EXPECT_B2WARNING(std::get<double>(var->function(p)));
3524  EXPECT_TRUE(std::isnan(std::get<double>(var->function(p))));
3525 
3526  var = Manager::Instance().getVariable("mcDaughterAngle(1, 0:1:0:0:1)");
3527  EXPECT_B2WARNING(std::get<double>(var->function(p)));
3528  EXPECT_TRUE(std::isnan(std::get<double>(var->function(p))));
3529 
3530  // Test mcDaughterDiffOf
3531  var = Manager::Instance().getVariable("mcDaughterDiffOf(0, 1, PDG)");
3532  ASSERT_NE(var, nullptr);
3533  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), 0);
3534 
3535  EXPECT_B2FATAL(Manager::Instance().getVariable("mcDaughterDiffOf(0, NOTINT, PDG)"));
3536 
3537  // Test azimuthal angle as well
3538  var = Manager::Instance().getVariable("mcDaughterDiffOf(0, 1, phi)");
3539  ASSERT_NE(var, nullptr);
3540  v_test = momentum_2.Phi() - momentum_1.Phi();
3541  EXPECT_FLOAT_EQ(std::get<double>(var->function(p)), v_test);
3542 
3543  }
3544 
3545  TEST_F(MetaVariableTest, varForFirstMCAncestorOfType)
3546  {
3547  DataStore::Instance().setInitializeActive(true);
3548  StoreArray<MCParticle> mcParticles;
3549  StoreArray<Particle> particles;
3550  particles.registerInDataStore();
3551  mcParticles.registerInDataStore();
3552  particles.registerRelationTo(mcParticles);
3553  StoreObjPtr<ParticleList> DList("D0:vartest");
3554  DList.registerInDataStore();
3555  DList.create();
3556  DList->initialize(421, "D0:vartest");
3557  DataStore::Instance().setInitializeActive(false);
3558  PxPyPzEVector momentum;
3559  PxPyPzEVector momentum_0;
3560  PxPyPzEVector momentum_1;
3561  std::vector<int> D_daughterIndices;
3562  std::vector<int> D_grandDaughterIndices_0;
3563  std::vector<int> D_grandDaughterIndices_1;
3564 
3565 
3566  // Create MC graph for D -> (K0s -> pi+ + pi-) (K0s -> pi+ + pi-)
3567  MCParticleGraph mcGraph;
3568 
3569  MCParticleGraph::GraphParticle& mcg_m = mcGraph.addParticle();
3570  MCParticleGraph::GraphParticle& mcg_d_0 = mcGraph.addParticle();
3571  MCParticleGraph::GraphParticle& mcg_d_1 = mcGraph.addParticle();
3572  MCParticleGraph::GraphParticle& mcg_gd_0_0 = mcGraph.addParticle();
3573  MCParticleGraph::GraphParticle& mcg_gd_0_1 = mcGraph.addParticle();
3574  MCParticleGraph::GraphParticle& mcg_gd_1_0 = mcGraph.addParticle();
3575  MCParticleGraph::GraphParticle& mcg_gd_1_1 = mcGraph.addParticle();
3576  MCParticleGraph::GraphParticle& mcg_not_child = mcGraph.addParticle();
3577 
3578  mcg_m.setPDG(421);
3579  mcg_m.set4Vector(PxPyPzEVector(7, 7, 7, 7));
3580  mcg_d_0.setPDG(-Const::Kshort.getPDGCode());
3581  mcg_d_0.set4Vector(PxPyPzEVector(6, 6, 6, 6));
3582  mcg_d_1.setPDG(Const::Kshort.getPDGCode());
3583  mcg_d_1.set4Vector(PxPyPzEVector(5, 5, 5, 5));
3584  mcg_gd_0_0.setPDG(Const::pion.getPDGCode());
3585  mcg_gd_0_0.set4Vector(PxPyPzEVector(4, 4, 4, 4));
3586  mcg_gd_0_1.setPDG(-Const::pion.getPDGCode());
3587  mcg_gd_0_1.set4Vector(PxPyPzEVector(3, 3, 3, 3));
3588  mcg_gd_1_0.setPDG(Const::pion.getPDGCode());
3589  mcg_gd_1_0.set4Vector(PxPyPzEVector(2, 1, 2, 2));
3590  mcg_gd_1_1.setPDG(-Const::pion.getPDGCode());
3591  mcg_gd_1_1.set4Vector(PxPyPzEVector(1, 1, 1, 1));
3592  mcg_not_child.setPDG(Const::pion.getPDGCode());
3593  mcg_not_child.set4Vector(PxPyPzEVector(10, 10, 10, 10));
3594 
3595  mcg_d_0.comesFrom(mcg_m);
3596  mcg_d_1.comesFrom(mcg_m);
3597  mcg_gd_0_0.comesFrom(mcg_d_0);
3598  mcg_gd_0_1.comesFrom(mcg_d_0);
3599  mcg_gd_1_0.comesFrom(mcg_d_1);
3600  mcg_gd_1_1.comesFrom(mcg_d_1);
3601 
3602  mcGraph.generateList();
3603 
3604  // Get MC Particles from StoreArray
3605  auto* mc_not_child = mcParticles[0];
3606  auto* mc_m = mcParticles[1];
3607  auto* mc_d_0 = mcParticles[2];
3608  auto* mc_d_1 = mcParticles[3];
3609  auto* mc_gd_0_0 = mcParticles[4];
3610  auto* mc_gd_0_1 = mcParticles[5];
3611  auto* mc_gd_1_0 = mcParticles[6];
3612  auto* mc_gd_1_1 = mcParticles[7];
3613 
3614 
3615  mc_m->setStatus(MCParticle::c_PrimaryParticle);
3616  mc_d_0->setStatus(MCParticle::c_PrimaryParticle);
3617  mc_d_1->setStatus(MCParticle::c_PrimaryParticle);
3618  mc_gd_0_0->setStatus(MCParticle::c_PrimaryParticle);
3619  mc_gd_0_1->setStatus(MCParticle::c_PrimaryParticle);
3620  mc_gd_1_0->setStatus(MCParticle::c_PrimaryParticle);
3621  mc_gd_1_1->setStatus(MCParticle::c_PrimaryParticle);
3622  mc_not_child->setStatus(MCParticle::c_PrimaryParticle);
3623 
3624  // Creation of D decay: D->K0s(->pi pi) K0s(->pi pi)
3625 
3626  const Particle* D_gd_0_0 = particles.appendNew(PxPyPzEVector(0.0, 1, 1, 1), 211);
3627  const Particle* D_gd_0_1 = particles.appendNew(PxPyPzEVector(1.0, 1, 1, 1), -211);
3628  const Particle* D_gd_1_0 = particles.appendNew(PxPyPzEVector(2.0, 1, 1, 1), 211);
3629  const Particle* D_gd_1_1 = particles.appendNew(PxPyPzEVector(3.0, 1, 1, 1), -211);
3630 
3631  D_grandDaughterIndices_0.push_back(D_gd_0_0->getArrayIndex());
3632  D_grandDaughterIndices_0.push_back(D_gd_0_1->getArrayIndex());
3633  D_grandDaughterIndices_1.push_back(D_gd_1_0->getArrayIndex());
3634  D_grandDaughterIndices_1.push_back(D_gd_1_1->getArrayIndex());
3635  momentum_0 = D_gd_0_0->get4Vector() + D_gd_0_1->get4Vector();
3636  momentum_1 = D_gd_1_0->get4Vector() + D_gd_1_1->get4Vector();
3637 
3638 
3639  const Particle* D_d_0 = particles.appendNew(momentum_0, 310, Particle::c_Unflavored, D_grandDaughterIndices_0);
3640  const Particle* D_d_1 = particles.appendNew(momentum_1, 310, Particle::c_Unflavored, D_grandDaughterIndices_1);
3641 
3642 
3643  momentum = D_d_0->get4Vector() + D_d_1->get4Vector();
3644  D_daughterIndices.push_back(D_d_0->getArrayIndex());
3645  D_daughterIndices.push_back(D_d_1->getArrayIndex());
3646 
3647  const Particle* D_m = particles.appendNew(momentum, 421, Particle::c_Unflavored, D_daughterIndices);
3648  DList->addParticle(D_m);
3649 
3650  // Particle that is not an child
3651  const Particle* not_child = particles.appendNew(PxPyPzEVector(5.0, 1, 1, 1), 211);
3652 
3653  // Particle that is not an child and doesn't have MC particle
3654  const Particle* not_child_2 = particles.appendNew(PxPyPzEVector(6.0, 1, 1, 1), 211);
3655 
3656  // MC matching
3657  D_gd_0_0->addRelationTo(mc_gd_0_0);
3658  D_gd_0_1->addRelationTo(mc_gd_0_1);
3659  D_gd_1_0->addRelationTo(mc_gd_1_0);
3660  D_gd_1_1->addRelationTo(mc_gd_1_1);
3661  D_d_0->addRelationTo(mc_d_0);
3662  D_d_1->addRelationTo(mc_d_1);
3663  D_m->addRelationTo(mc_m);
3664  not_child->addRelationTo(mc_not_child);
3665 
3666  // All pions should have common D mother
3667  const Manager::Var* var_d = Manager::Instance().getVariable("varForFirstMCAncestorOfType(D0, mdstIndex)");
3668  ASSERT_NE(var_d, nullptr);
3669  EXPECT_TRUE(std::get<double>(var_d->function(D_gd_0_0)) >= 0);
3670  EXPECT_FLOAT_EQ(std::get<double>(var_d->function(D_gd_0_0)), std::get<double>(var_d->function(D_gd_0_1)));
3671  EXPECT_FLOAT_EQ(std::get<double>(var_d->function(D_gd_1_0)), std::get<double>(var_d->function(D_gd_1_1)));
3672  EXPECT_FLOAT_EQ(std::get<double>(var_d->function(D_gd_0_0)), std::get<double>(var_d->function(D_gd_1_0)));
3673  EXPECT_FLOAT_EQ(std::get<double>(var_d->function(D_gd_0_1)), std::get<double>(var_d->function(D_gd_1_1)));
3674  EXPECT_TRUE(std::isnan(std::get<double>(var_d->function(not_child))));
3675  EXPECT_TRUE(std::isnan(std::get<double>(var_d->function(not_child_2))));
3676 
3677 
3678  // // All but they have different K0s mothers
3679  const Manager::Var* var_310 = Manager::Instance().getVariable("varForFirstMCAncestorOfType(310, mdstIndex)");
3680  ASSERT_NE(var_310, nullptr);
3681  EXPECT_FLOAT_EQ(std::get<double>(var_310->function(D_gd_0_0)), std::get<double>(var_310->function(D_gd_0_1)));
3682  EXPECT_FLOAT_EQ(std::get<double>(var_310->function(D_gd_1_0)), std::get<double>(var_310->function(D_gd_1_1)));
3683  EXPECT_NE(std::get<double>(var_310->function(D_gd_0_0)), std::get<double>(var_310->function(D_gd_1_0)));
3684  EXPECT_NE(std::get<double>(var_310->function(D_gd_0_1)), std::get<double>(var_310->function(D_gd_1_1)));
3685  EXPECT_TRUE(std::isnan(std::get<double>(var_310->function(not_child))));
3686  EXPECT_TRUE(std::isnan(std::get<double>(var_310->function(not_child_2))));
3687  EXPECT_FLOAT_EQ(int(std::get<double>(Manager::Instance().getVariable("varForFirstMCAncestorOfType(310, E)")->function(D_gd_0_0))),
3688  10);
3689  }
3690 
3691  TEST_F(MetaVariableTest, isDescendantOfList)
3692  {
3693  DataStore::Instance().setInitializeActive(true);
3694  StoreObjPtr<ParticleList> DList("D0:vartest");
3695  DList.registerInDataStore();
3696  DList.create();
3697  DList->initialize(421, "D0:vartest");
3698  StoreObjPtr<ParticleList> BList("B:vartest");
3699  BList.registerInDataStore();
3700  BList.create();
3701  BList->initialize(521, "B:vartest");
3702  DataStore::Instance().setInitializeActive(false);
3703 
3704  PxPyPzEVector momentum;
3705  PxPyPzEVector momentum_0;
3706  PxPyPzEVector momentum_1;
3707  StoreArray<Particle> particles;
3708  std::vector<int> D_daughterIndices;
3709  std::vector<int> D_grandDaughterIndices_0;
3710  std::vector<int> D_grandDaughterIndices_1;
3711  std::vector<int> B_daughterIndices;
3712  std::vector<int> B_grandDaughterIndices;
3713  std::vector<int> B_grandGrandDaughterIndices;
3714 
3715  // Creation of D decay: D->K0s(->pi pi) K0s(->pi pi)
3716 
3717  const Particle* D_gd_0_0 = particles.appendNew(PxPyPzEVector(0.0, 1, 1, 1), 211, Particle::c_Flavored, Particle::c_Track, 0);
3718  const Particle* D_gd_0_1 = particles.appendNew(PxPyPzEVector(1.0, 1, 1, 1), -211, Particle::c_Flavored, Particle::c_Track, 1);
3719  const Particle* D_gd_1_0 = particles.appendNew(PxPyPzEVector(2.0, 1, 1, 1), 211, Particle::c_Flavored, Particle::c_Track, 2);
3720  const Particle* D_gd_1_1 = particles.appendNew(PxPyPzEVector(3.0, 1, 1, 1), -211, Particle::c_Flavored, Particle::c_Track, 3);
3721 
3722  D_grandDaughterIndices_0.push_back(D_gd_0_0->getArrayIndex());
3723  D_grandDaughterIndices_0.push_back(D_gd_0_1->getArrayIndex());
3724  D_grandDaughterIndices_1.push_back(D_gd_1_0->getArrayIndex());
3725  D_grandDaughterIndices_1.push_back(D_gd_1_1->getArrayIndex());
3726  momentum_0 = D_gd_0_0->get4Vector() + D_gd_0_1->get4Vector();
3727  momentum_1 = D_gd_1_0->get4Vector() + D_gd_1_1->get4Vector();
3728 
3729 
3730  const Particle* D_d_0 = particles.appendNew(momentum_0, 310, Particle::c_Unflavored, D_grandDaughterIndices_0);
3731  const Particle* D_d_1 = particles.appendNew(momentum_1, 310, Particle::c_Unflavored, D_grandDaughterIndices_1);
3732 
3733 
3734  momentum = D_d_0->get4Vector() + D_d_1->get4Vector();
3735  D_daughterIndices.push_back(D_d_0->getArrayIndex());
3736  D_daughterIndices.push_back(D_d_1->getArrayIndex());
3737 
3738  const Particle* D_m = particles.appendNew(momentum, 421, Particle::c_Unflavored, D_daughterIndices);
3739  DList->addParticle(D_m);
3740 
3741  // Creation of B decay B -> D(->K0s(->pi pi) pi) pi
3742 
3743  const Particle* B_d_1 = particles.appendNew(PxPyPzEVector(0.0, 1, 1, 1), 211, Particle::c_Flavored, Particle::c_Track, 4);
3744  const Particle* B_gd_0_1 = particles.appendNew(PxPyPzEVector(1.0, 1, 1, 1), -211, Particle::c_Flavored, Particle::c_Track, 5);
3745  const Particle* B_ggd_0_0_0 = particles.appendNew(PxPyPzEVector(2.0, 1, 1, 1), 211, Particle::c_Flavored, Particle::c_Track, 6);
3746  const Particle* B_ggd_0_0_1 = particles.appendNew(PxPyPzEVector(3.0, 1, 1, 1), -211, Particle::c_Flavored, Particle::c_Track, 7);
3747 
3748  B_grandGrandDaughterIndices.push_back(B_ggd_0_0_0->getArrayIndex());
3749  B_grandGrandDaughterIndices.push_back(B_ggd_0_0_1->getArrayIndex());
3750  momentum_0 = B_ggd_0_0_0->get4Vector() + B_ggd_0_0_1->get4Vector();
3751  const Particle* B_gd_0_0 = particles.appendNew(momentum_0, 310, Particle::c_Unflavored, B_grandGrandDaughterIndices);
3752 
3753  B_grandDaughterIndices.push_back(B_gd_0_0->getArrayIndex());
3754  B_grandDaughterIndices.push_back(B_gd_0_1->getArrayIndex());
3755  momentum_1 = B_gd_0_0->get4Vector() + B_gd_0_1->get4Vector();
3756  const Particle* B_d_0 = particles.appendNew(momentum_1, -411, Particle::c_Unflavored, B_grandDaughterIndices);
3757 
3758  B_daughterIndices.push_back(B_d_0->getArrayIndex());
3759  B_daughterIndices.push_back(B_d_1->getArrayIndex());
3760  momentum = B_d_0->get4Vector() + B_d_1->get4Vector();
3761  const Particle* B_m = particles.appendNew(momentum, 521, Particle::c_Unflavored, B_daughterIndices);
3762  BList->addParticle(B_m);
3763 
3764  // Particle that is not an child
3765  const Particle* not_child = particles.appendNew(PxPyPzEVector(5.0, 1, 1, 1), 211, Particle::c_Flavored, Particle::c_Track, 8);
3766 
3767 
3768  const Manager::Var* var_0 = Manager::Instance().getVariable("isDescendantOfList(D0:vartest)");
3769  ASSERT_NE(var_0, nullptr);
3770  EXPECT_TRUE(std::get<bool>(var_0->function(D_gd_0_0)));
3771  EXPECT_TRUE(std::get<bool>(var_0->function(D_gd_0_1)));
3772  EXPECT_TRUE(std::get<bool>(var_0->function(D_gd_1_0)));
3773  EXPECT_TRUE(std::get<bool>(var_0->function(D_gd_1_1)));
3774  EXPECT_TRUE(std::get<bool>(var_0->function(D_d_0)));
3775  EXPECT_TRUE(std::get<bool>(var_0->function(D_d_1)));
3776  EXPECT_FALSE(std::get<bool>(var_0->function(B_ggd_0_0_0)));
3777  EXPECT_FALSE(std::get<bool>(var_0->function(B_ggd_0_0_1)));
3778  EXPECT_FALSE(std::get<bool>(var_0->function(B_gd_0_0)));
3779  EXPECT_FALSE(std::get<bool>(var_0->function(B_gd_0_1)));
3780  EXPECT_FALSE(std::get<bool>(var_0->function(B_d_0)));
3781  EXPECT_FALSE(std::get<bool>(var_0->function(B_d_1)));
3782  EXPECT_FALSE(std::get<bool>(var_0->function(not_child)));
3783 
3784  const Manager::Var* var_0a = Manager::Instance().getVariable("isDaughterOfList(D0:vartest)");
3785  ASSERT_NE(var_0a, nullptr);
3786  EXPECT_FALSE(std::get<bool>(var_0a->function(D_gd_0_0)));
3787  EXPECT_FALSE(std::get<bool>(var_0a->function(D_gd_0_1)));
3788  EXPECT_FALSE(std::get<bool>(var_0a->function(D_gd_1_0)));
3789  EXPECT_FALSE(std::get<bool>(var_0a->function(D_gd_1_1)));
3790  EXPECT_TRUE(std::get<bool>(var_0a->function(D_d_0)));
3791  EXPECT_TRUE(std::get<bool>(var_0a->function(D_d_1)));
3792  EXPECT_FALSE(std::get<bool>(var_0a->function(B_ggd_0_0_0)));
3793  EXPECT_FALSE(std::get<bool>(var_0a->function(B_ggd_0_0_1)));
3794  EXPECT_FALSE(std::get<bool>(var_0a->function(B_gd_0_0)));
3795  EXPECT_FALSE(std::get<bool>(var_0a->function(B_gd_0_1)));
3796  EXPECT_FALSE(std::get<bool>(var_0a->function(B_d_0)));
3797  EXPECT_FALSE(std::get<bool>(var_0a->function(B_d_1)));
3798  EXPECT_FALSE(std::get<bool>(var_0a->function(not_child)));
3799 
3800  const Manager::Var* var_0b = Manager::Instance().getVariable("isGrandDaughterOfList(D0:vartest)");
3801  ASSERT_NE(var_0b, nullptr);
3802  EXPECT_TRUE(std::get<bool>(var_0b->function(D_gd_0_0)));
3803  EXPECT_TRUE(std::get<bool>(var_0b->function(D_gd_0_1)));
3804  EXPECT_TRUE(std::get<bool>(var_0b->function(D_gd_1_0)));
3805  EXPECT_TRUE(std::get<bool>(var_0b->function(D_gd_1_1)));
3806  EXPECT_FALSE(std::get<bool>(var_0b->function(D_d_0)));
3807  EXPECT_FALSE(std::get<bool>(var_0b->function(D_d_1)));
3808  EXPECT_FALSE(std::get<bool>(var_0b->function(B_ggd_0_0_0)));
3809  EXPECT_FALSE(std::get<bool>(var_0b->function(B_ggd_0_0_1)));
3810  EXPECT_FALSE(std::get<bool>(var_0b->function(B_gd_0_0)));
3811  EXPECT_FALSE(std::get<bool>(var_0b->function(B_gd_0_1)));
3812  EXPECT_FALSE(std::get<bool>(var_0b->function(B_d_0)));
3813  EXPECT_FALSE(std::get<bool>(var_0b->function(B_d_1)));
3814  EXPECT_FALSE(std::get<bool>(var_0b->function(not_child)));
3815 
3816  const Manager::Var* var_1 = Manager::Instance().getVariable("isDescendantOfList(D0:vartest, 1)");
3817  ASSERT_NE(var_1, nullptr);
3818  EXPECT_FALSE(std::get<bool>(var_1->function(D_gd_0_0)));
3819  EXPECT_FALSE(std::get<bool>(var_1->function(D_gd_0_1)));
3820  EXPECT_FALSE(std::get<bool>(var_1->function(D_gd_1_0)));
3821  EXPECT_FALSE(std::get<bool>(var_1->function(D_gd_1_1)));
3822  EXPECT_TRUE(std::get<bool>(var_1->function(D_d_0)));
3823  EXPECT_TRUE(std::get<bool>(var_1->function(D_d_1)));
3824  EXPECT_FALSE(std::get<bool>(var_1->function(B_ggd_0_0_0)));
3825  EXPECT_FALSE(std::get<bool>(var_1->function(B_ggd_0_0_1)));
3826  EXPECT_FALSE(std::get<bool>(var_1->function(B_gd_0_0)));
3827  EXPECT_FALSE(std::get<bool>(var_1->function(B_gd_0_1)));
3828  EXPECT_FALSE(std::get<bool>(var_1->function(B_d_0)));
3829  EXPECT_FALSE(std::get<bool>(var_1->function(B_d_1)));
3830  EXPECT_FALSE(std::get<bool>(var_1->function(not_child)));
3831 
3832  const Manager::Var* var_2 = Manager::Instance().getVariable("isDescendantOfList(D0:vartest, 2)");
3833  ASSERT_NE(var_2, nullptr);
3834  EXPECT_TRUE(std::get<bool>(var_2->function(D_gd_0_0)));
3835  EXPECT_TRUE(std::get<bool>(var_2->function(D_gd_0_1)));
3836  EXPECT_TRUE(std::get<bool>(var_2->function(D_gd_1_0)));
3837  EXPECT_TRUE(std::get<bool>(var_2->function(D_gd_1_1)));
3838  EXPECT_FALSE(std::get<bool>(var_2->function(D_d_0)));
3839  EXPECT_FALSE(std::get<bool>(var_2->function(D_d_1)));
3840  EXPECT_FALSE(std::get<bool>(var_2->function(B_ggd_0_0_0)));
3841  EXPECT_FALSE(std::get<bool>(var_2->function(B_ggd_0_0_1)));
3842  EXPECT_FALSE(std::get<bool>(var_2->function(B_gd_0_0)));
3843  EXPECT_FALSE(std::get<bool>(var_2->function(B_gd_0_1)));
3844  EXPECT_FALSE(std::get<bool>(var_2->function(B_d_0)));
3845  EXPECT_FALSE(std::get<bool>(var_2->function(B_d_1)));
3846  EXPECT_FALSE(std::get<bool>(var_2->function(not_child)));
3847 
3848  const Manager::Var* var_3 = Manager::Instance().getVariable("isDescendantOfList(D0:vartest, B:vartest)");
3849  ASSERT_NE(var_3, nullptr);
3850  EXPECT_TRUE(std::get<bool>(var_3->function(D_gd_0_0)));
3851  EXPECT_TRUE(std::get<bool>(var_3->function(D_gd_0_1)));
3852  EXPECT_TRUE(std::get<bool>(var_3->function(D_gd_1_0)));
3853  EXPECT_TRUE(std::get<bool>(var_3->function(D_gd_1_1)));
3854  EXPECT_TRUE(std::get<bool>(var_3->function(D_d_0)));
3855  EXPECT_TRUE(std::get<bool>(var_3->function(D_d_1)));
3856  EXPECT_TRUE(std::get<bool>(var_3->function(B_ggd_0_0_0)));
3857  EXPECT_TRUE(std::get<bool>(var_3->function(B_ggd_0_0_1)));
3858  EXPECT_TRUE(std::get<bool>(var_3->function(B_gd_0_0)));
3859  EXPECT_TRUE(std::get<bool>(var_3->function(B_gd_0_1)));
3860  EXPECT_TRUE(std::get<bool>(var_3->function(B_d_0)));
3861  EXPECT_TRUE(std::get<bool>(var_3->function(B_d_1)));
3862  EXPECT_FALSE(std::get<bool>(var_3->function(not_child)));
3863 
3864  const Manager::Var* var_4 = Manager::Instance().getVariable("isDescendantOfList(D0:vartest, B:vartest, -1)");
3865  ASSERT_NE(var_4, nullptr);
3866  EXPECT_TRUE(std::get<bool>(var_4->function(D_gd_0_0)));
3867  EXPECT_TRUE(std::get<bool>(var_4->function(D_gd_0_1)));
3868  EXPECT_TRUE(std::get<bool>(var_4->function(D_gd_1_0)));
3869  EXPECT_TRUE(std::get<bool>(var_4->function(D_gd_1_1)));
3870  EXPECT_TRUE(std::get<bool>(var_4->function(D_d_0)));
3871  EXPECT_TRUE(std::get<bool>(var_4->function(D_d_1)));
3872  EXPECT_TRUE(std::get<bool>(var_4->function(B_ggd_0_0_0)));
3873  EXPECT_TRUE(std::get<bool>(var_4->function(B_ggd_0_0_1)));
3874  EXPECT_TRUE(std::get<bool>(var_4->function(B_gd_0_0)));
3875  EXPECT_TRUE(std::get<bool>(var_4->function(B_gd_0_1)));
3876  EXPECT_TRUE(std::get<bool>(var_4->function(B_d_0)));
3877  EXPECT_TRUE(std::get<bool>(var_4->function(B_d_1)));
3878  EXPECT_FALSE(std::get<bool>(var_4->function(not_child)));
3879 
3880  const Manager::Var* var_5 = Manager::Instance().getVariable("isDescendantOfList(D0:vartest, B:vartest, 1)");
3881  ASSERT_NE(var_5, nullptr);
3882  EXPECT_FALSE(std::get<bool>(var_5->function(D_gd_0_0)));
3883  EXPECT_FALSE(std::get<bool>(var_5->function(D_gd_0_1)));
3884  EXPECT_FALSE(std::get<bool>(var_5->function(D_gd_1_0)));
3885  EXPECT_FALSE(std::get<bool>(var_5->function(D_gd_1_1)));
3886  EXPECT_TRUE(std::get<bool>(var_5->function(D_d_0)));
3887  EXPECT_TRUE(std::get<bool>(var_5->function(D_d_1)));
3888  EXPECT_FALSE(std::get<bool>(var_5->function(B_ggd_0_0_0)));
3889  EXPECT_FALSE(std::get<bool>(var_5->function(B_ggd_0_0_1)));
3890  EXPECT_FALSE(std::get<bool>(var_5->function(B_gd_0_0)));
3891  EXPECT_FALSE(std::get<bool>(var_5->function(B_gd_0_1)));
3892  EXPECT_TRUE(std::get<bool>(var_5->function(B_d_0)));
3893  EXPECT_TRUE(std::get<bool>(var_5->function(B_d_1)));
3894  EXPECT_FALSE(std::get<bool>(var_5->function(not_child)));
3895 
3896  const Manager::Var* var_6 = Manager::Instance().getVariable("isDescendantOfList(D0:vartest, B:vartest, 2)");
3897  ASSERT_NE(var_6, nullptr);
3898  EXPECT_TRUE(std::get<bool>(var_6->function(D_gd_0_0)));
3899  EXPECT_TRUE(std::get<bool>(var_6->function(D_gd_0_1)));
3900  EXPECT_TRUE(std::get<bool>(var_6->function(D_gd_1_0)));
3901  EXPECT_TRUE(std::get<bool>(var_6->function(D_gd_1_1)));
3902  EXPECT_FALSE(std::get<bool>(var_6->function(D_d_0)));
3903  EXPECT_FALSE(std::get<bool>(var_6->function(D_d_1)));
3904  EXPECT_FALSE(std::get<bool>(var_6->function(B_ggd_0_0_0)));
3905  EXPECT_FALSE(std::get<bool>(var_6->function(B_ggd_0_0_1)));
3906  EXPECT_TRUE(std::get<bool>(var_6->function(B_gd_0_0)));
3907  EXPECT_TRUE(std::get<bool>(var_6->function(B_gd_0_1)));
3908  EXPECT_FALSE(std::get<bool>(var_6->function(B_d_0)));
3909  EXPECT_FALSE(std::get<bool>(var_6->function(B_d_1)));
3910  EXPECT_FALSE(std::get<bool>(var_6->function(not_child)));
3911 
3912  const Manager::Var* var_7 = Manager::Instance().getVariable("isDescendantOfList(D0:vartest, B:vartest, 3)");
3913  ASSERT_NE(var_7, nullptr);
3914  EXPECT_FALSE(std::get<bool>(var_7->function(D_gd_0_0)));
3915  EXPECT_FALSE(std::get<bool>(var_7->function(D_gd_0_1)));
3916  EXPECT_FALSE(std::get<bool>(var_7->function(D_gd_1_0)));
3917  EXPECT_FALSE(std::get<bool>(var_7->function(D_gd_1_1)));
3918  EXPECT_FALSE(std::get<bool>(var_7->function(D_d_0)));
3919  EXPECT_FALSE(std::get<bool>(var_7->function(D_d_1)));
3920  EXPECT_TRUE(std::get<bool>(var_7->function(B_ggd_0_0_0)));
3921  EXPECT_TRUE(std::get<bool>(var_7->function(B_ggd_0_0_1)));
3922  EXPECT_FALSE(std::get<bool>(var_7->function(B_gd_0_0)));
3923  EXPECT_FALSE(std::get<bool>(var_7->function(B_gd_0_1)));
3924  EXPECT_FALSE(std::get<bool>(var_7->function(B_d_0)));
3925  EXPECT_FALSE(std::get<bool>(var_7->function(B_d_1)));
3926  EXPECT_FALSE(std::get<bool>(var_7->function(not_child)));
3927  }
3928 
3929 
3930  TEST_F(MetaVariableTest, isMCDescendantOfList)
3931  {
3932  DataStore::Instance().setInitializeActive(true);
3933  StoreArray<MCParticle> mcParticles;
3934  StoreArray<Particle> particles;
3935  particles.registerInDataStore();
3936  mcParticles.registerInDataStore();
3937  particles.registerRelationTo(mcParticles);
3938  StoreObjPtr<ParticleList> BList("B:vartest");
3939  BList.registerInDataStore();
3940  BList.create();
3941  BList->initialize(521, "B:vartest");
3942  StoreObjPtr<ParticleList> DList("D0:vartest");
3943  DList.registerInDataStore();
3944  DList.create();
3945  DList->initialize(421, "D0:vartest");
3946  DataStore::Instance().setInitializeActive(false);
3947  PxPyPzEVector momentum;
3948  PxPyPzEVector momentum_0;
3949  PxPyPzEVector momentum_1;
3950  std::vector<int> daughterIndices;
3951  std::vector<int> grandDaughterIndices;
3952  std::vector<int> grandGrandDaughterIndices;
3953  std::vector<int> D_daughterIndices;
3954  std::vector<int> D_grandDaughterIndices_0;
3955  std::vector<int> D_grandDaughterIndices_1;
3956 
3957 
3958  // Create MC graph for B+ -> (D -> (K0s -> pi+ + pi-) pi-) + pi+
3959  MCParticleGraph mcGraph;
3960 
3961  MCParticleGraph::GraphParticle& mcg_m = mcGraph.addParticle();
3962  MCParticleGraph::GraphParticle& mcg_d_0 = mcGraph.addParticle();
3963  MCParticleGraph::GraphParticle& mcg_d_1 = mcGraph.addParticle();
3964  MCParticleGraph::GraphParticle& mcg_gd_0_0 = mcGraph.addParticle();
3965  MCParticleGraph::GraphParticle& mcg_gd_0_1 = mcGraph.addParticle();
3966  MCParticleGraph::GraphParticle& mcg_ggd_0_0_0 = mcGraph.addParticle();
3967  MCParticleGraph::GraphParticle& mcg_ggd_0_0_1 = mcGraph.addParticle();
3968  MCParticleGraph::GraphParticle& mcg_not_child = mcGraph.addParticle();
3969 
3970  mcg_m.setPDG(521);
3971  mcg_d_0.setPDG(-411);
3972  mcg_d_1.setPDG(Const::pion.getPDGCode());
3973  mcg_gd_0_0.setPDG(Const::Kshort.getPDGCode());
3974  mcg_gd_0_1.setPDG(-Const::pion.getPDGCode());
3975  mcg_ggd_0_0_0.setPDG(Const::pion.getPDGCode());
3976  mcg_ggd_0_0_1.setPDG(-Const::pion.getPDGCode());
3977  mcg_not_child.setPDG(Const::pion.getPDGCode());
3978 
3979  mcg_d_0.comesFrom(mcg_m);
3980  mcg_d_1.comesFrom(mcg_m);
3981  mcg_gd_0_0.comesFrom(mcg_d_0);
3982  mcg_gd_0_1.comesFrom(mcg_d_0);
3983  mcg_ggd_0_0_0.comesFrom(mcg_gd_0_1);
3984  mcg_ggd_0_0_1.comesFrom(mcg_gd_0_1);
3985 
3986  mcGraph.generateList();
3987 
3988  // Get MC Particles from StoreArray
3989  auto* mc_m = mcParticles[0];
3990  auto* mc_d_0 = mcParticles[1];
3991  auto* mc_d_1 = mcParticles[2];
3992  auto* mc_gd_0_0 = mcParticles[3];
3993  auto* mc_gd_0_1 = mcParticles[4];
3994  auto* mc_ggd_0_0_0 = mcParticles[5];
3995  auto* mc_ggd_0_0_1 = mcParticles[6];
3996  auto* mc_not_child = mcParticles[7];
3997 
3998  mc_m->setStatus(MCParticle::c_PrimaryParticle);
3999  mc_d_0->setStatus(MCParticle::c_PrimaryParticle);
4000  mc_d_1->setStatus(MCParticle::c_PrimaryParticle);
4001  mc_gd_0_0->setStatus(MCParticle::c_PrimaryParticle);
4002  mc_gd_0_1->setStatus(MCParticle::c_PrimaryParticle);
4003  mc_ggd_0_0_0->setStatus(MCParticle::c_PrimaryParticle);
4004  mc_ggd_0_0_1->setStatus(MCParticle::c_PrimaryParticle);
4005  mc_not_child->setStatus(MCParticle::c_PrimaryParticle);
4006 
4007  // Creation of D decay: D->K0s(->pi pi) K0s(->pi pi) (not matched)
4008 
4009  const Particle* D_gd_0_0 = particles.appendNew(PxPyPzEVector(0.0, 1, 1, 1), 211);
4010  const Particle* D_gd_0_1 = particles.appendNew(PxPyPzEVector(1.0, 1, 1, 1), -211);
4011  const Particle* D_gd_1_0 = particles.appendNew(PxPyPzEVector(2.0, 1, 1, 1), 211);
4012  const Particle* D_gd_1_1 = particles.appendNew(PxPyPzEVector(3.0, 1, 1, 1), -211);
4013 
4014  D_grandDaughterIndices_0.push_back(D_gd_0_0->getArrayIndex());
4015  D_grandDaughterIndices_0.push_back(D_gd_0_1->getArrayIndex());
4016  D_grandDaughterIndices_1.push_back(D_gd_1_0->getArrayIndex());
4017  D_grandDaughterIndices_1.push_back(D_gd_1_1->getArrayIndex());
4018  momentum_0 = D_gd_0_0->get4Vector() + D_gd_0_1->get4Vector();
4019  momentum_1 = D_gd_1_0->get4Vector() + D_gd_1_1->get4Vector();
4020 
4021 
4022  const Particle* D_d_0 = particles.appendNew(momentum_0, 310, Particle::c_Unflavored, D_grandDaughterIndices_0);
4023  const Particle* D_d_1 = particles.appendNew(momentum_1, 310, Particle::c_Unflavored, D_grandDaughterIndices_1);
4024 
4025 
4026  momentum = D_d_0->get4Vector() + D_d_1->get4Vector();
4027  D_daughterIndices.push_back(D_d_0->getArrayIndex());
4028  D_daughterIndices.push_back(D_d_1->getArrayIndex());
4029 
4030  const Particle* D_m = particles.appendNew(momentum, 421, Particle::c_Unflavored, D_daughterIndices);
4031  DList->addParticle(D_m);
4032 
4033  // Creating B decay
4034  const Particle* d_1 = particles.appendNew(PxPyPzEVector(0.0, 1, 1, 1), 211);
4035  const Particle* gd_0_1 = particles.appendNew(PxPyPzEVector(1.0, 1, 1, 1), -211);
4036  const Particle* ggd_0_0_0 = particles.appendNew(PxPyPzEVector(2.0, 1, 1, 1), 211);
4037  const Particle* ggd_0_0_1 = particles.appendNew(PxPyPzEVector(3.0, 1, 1, 1), -211);
4038 
4039  grandGrandDaughterIndices.push_back(ggd_0_0_0->getArrayIndex());
4040  grandGrandDaughterIndices.push_back(ggd_0_0_1->getArrayIndex());
4041  momentum_0 = ggd_0_0_0->get4Vector() + ggd_0_0_1->get4Vector();
4042  const Particle* gd_0_0 = particles.appendNew(momentum_0, 310, Particle::c_Unflavored, grandGrandDaughterIndices);
4043 
4044  grandDaughterIndices.push_back(gd_0_0->getArrayIndex());
4045  grandDaughterIndices.push_back(gd_0_1->getArrayIndex());
4046  momentum_1 = gd_0_0->get4Vector() + gd_0_1->get4Vector();
4047  const Particle* d_0 = particles.appendNew(momentum_1, -411, Particle::c_Unflavored, grandDaughterIndices);
4048 
4049  daughterIndices.push_back(d_0->getArrayIndex());
4050  daughterIndices.push_back(d_1->getArrayIndex());
4051  momentum = d_0->get4Vector() + d_1->get4Vector();
4052  const Particle* m = particles.appendNew(momentum, 521, Particle::c_Unflavored, daughterIndices);
4053  BList->addParticle(m);
4054 
4055  // Particle that is not an child
4056  const Particle* not_child = particles.appendNew(PxPyPzEVector(5.0, 1, 1, 1), 211);
4057 
4058  // Particle that is not an child and doesn't have MC particle
4059  const Particle* not_child_2 = particles.appendNew(PxPyPzEVector(6.0, 1, 1, 1), 211);
4060 
4061  gd_0_0->addRelationTo(mc_gd_0_0);
4062  gd_0_1->addRelationTo(mc_gd_0_1);
4063  ggd_0_0_0->addRelationTo(mc_ggd_0_0_0);
4064  ggd_0_0_1->addRelationTo(mc_ggd_0_0_1);
4065  d_0->addRelationTo(mc_d_0);
4066  d_1->addRelationTo(mc_d_1);
4067  m->addRelationTo(mc_m);
4068  not_child->addRelationTo(mc_not_child);
4069 
4070  const Manager::Var* var_0 = Manager::Instance().getVariable("isMCDescendantOfList(B:vartest)");
4071  ASSERT_NE(var_0, nullptr);
4072  EXPECT_FALSE(std::get<bool>(var_0->function(D_gd_0_0)));
4073  EXPECT_FALSE(std::get<bool>(var_0->function(D_gd_0_1)));
4074  EXPECT_FALSE(std::get<bool>(var_0->function(D_gd_1_0)));
4075  EXPECT_FALSE(std::get<bool>(var_0->function(D_gd_1_1)));
4076  EXPECT_FALSE(std::get<bool>(var_0->function(D_d_0)));
4077  EXPECT_FALSE(std::get<bool>(var_0->function(D_d_1)));
4078  EXPECT_TRUE(std::get<bool>(var_0->function(ggd_0_0_0)));
4079  EXPECT_TRUE(std::get<bool>(var_0->function(ggd_0_0_1)));
4080  EXPECT_TRUE(std::get<bool>(var_0->function(gd_0_0)));
4081  EXPECT_TRUE(std::get<bool>(var_0->function(gd_0_1)));
4082  EXPECT_TRUE(std::get<bool>(var_0->function(d_0)));
4083  EXPECT_TRUE(std::get<bool>(var_0->function(d_1)));
4084  EXPECT_FALSE(std::get<bool>(var_0->function(not_child)));
4085  EXPECT_FALSE(std::get<bool>(var_0->function(not_child_2)));
4086 
4087  const Manager::Var* var_1 = Manager::Instance().getVariable("isMCDescendantOfList(B:vartest, D0:vartest)");
4088  ASSERT_NE(var_1, nullptr);
4089  EXPECT_FALSE(std::get<bool>(var_1->function(D_gd_0_0)));
4090  EXPECT_FALSE(std::get<bool>(var_1->function(D_gd_0_1)));
4091  EXPECT_FALSE(std::get<bool>(var_1->function(D_gd_1_0)));
4092  EXPECT_FALSE(std::get<bool>(var_1->function(D_gd_1_1)));
4093  EXPECT_FALSE(std::get<bool>(var_1->function(D_d_0)));
4094  EXPECT_FALSE(std::get<bool>(var_1->function(D_d_1)));
4095  EXPECT_TRUE(std::get<bool>(var_1->function(ggd_0_0_0)));
4096  EXPECT_TRUE(std::get<bool>(var_1->function(ggd_0_0_1)));
4097  EXPECT_TRUE(std::get<bool>(var_1->function(gd_0_0)));
4098  EXPECT_TRUE(std::get<bool>(var_1->function(gd_0_1)));
4099  EXPECT_TRUE(std::get<bool>(var_1->function(d_0)));
4100  EXPECT_TRUE(std::get<bool>(var_1->function(d_1)));
4101  EXPECT_FALSE(std::get<bool>(var_1->function(not_child)));
4102  EXPECT_FALSE(std::get<bool>(var_1->function(not_child_2)));
4103 
4104  const Manager::Var* var_2 = Manager::Instance().getVariable("isMCDescendantOfList(B:vartest, -1)");
4105  ASSERT_NE(var_2, nullptr);
4106  EXPECT_FALSE(std::get<bool>(var_2->function(D_gd_0_0)));
4107  EXPECT_FALSE(std::get<bool>(var_2->function(D_gd_0_1)));
4108  EXPECT_FALSE(std::get<bool>(var_2->function(D_gd_1_0)));
4109  EXPECT_FALSE(std::get<bool>(var_2->function(D_gd_1_1)));
4110  EXPECT_FALSE(std::get<bool>(var_2->function(D_d_0)));
4111  EXPECT_FALSE(std::get<bool>(var_2->function(D_d_1)));
4112  EXPECT_TRUE(std::get<bool>(var_2->function(ggd_0_0_0)));
4113  EXPECT_TRUE(std::get<bool>(var_2->function(ggd_0_0_1)));
4114  EXPECT_TRUE(std::get<bool>(var_2->function(gd_0_0)));
4115  EXPECT_TRUE(std::get<bool>(var_2->function(gd_0_1)));
4116  EXPECT_TRUE(std::get<bool>(var_2->function(d_0)));
4117  EXPECT_TRUE(std::get<bool>(var_2->function(d_1)));
4118  EXPECT_FALSE(std::get<bool>(var_2->function(not_child)));
4119  EXPECT_FALSE(std::get<bool>(var_2->function(not_child_2)));
4120 
4121  const Manager::Var* var_3 = Manager::Instance().getVariable("isMCDescendantOfList(B:vartest, 1)");
4122  ASSERT_NE(var_3, nullptr);
4123  EXPECT_FALSE(std::get<bool>(var_3->function(D_gd_0_0)));
4124  EXPECT_FALSE(std::get<bool>(var_3->function(D_gd_0_1)));
4125  EXPECT_FALSE(std::get<bool>(var_3->function(D_gd_1_0)));
4126  EXPECT_FALSE(std::get<bool>(var_3->function(D_gd_1_1)));
4127  EXPECT_FALSE(std::get<bool>(var_3->function(D_d_0)));
4128  EXPECT_FALSE(std::get<bool>(var_3->function(D_d_1)));
4129  EXPECT_FALSE(std::get<bool>(var_3->function(ggd_0_0_0)));
4130  EXPECT_FALSE(std::get<bool>(var_3->function(ggd_0_0_1)));
4131  EXPECT_FALSE(std::get<bool>(var_3->function(gd_0_0)));
4132  EXPECT_FALSE(std::get<bool>(var_3->function(gd_0_1)));
4133  EXPECT_TRUE(std::get<bool>(var_3->function(d_0)));
4134  EXPECT_TRUE(std::get<bool>(var_3->function(d_1)));
4135  EXPECT_FALSE(std::get<bool>(var_3->function(not_child)));
4136  EXPECT_FALSE(std::get<bool>(var_3->function(not_child_2)));
4137 
4138  const Manager::Var* var_4 = Manager::Instance().getVariable("isMCDescendantOfList(B:vartest, 2)");
4139  ASSERT_NE(var_4, nullptr);
4140  EXPECT_FALSE(std::get<bool>(var_4->function(D_gd_0_0)));
4141  EXPECT_FALSE(std::get<bool>(var_4->function(D_gd_0_1)));
4142  EXPECT_FALSE(std::get<bool>(var_4->function(D_gd_1_0)));
4143  EXPECT_FALSE(std::get<bool>(var_4->function(D_gd_1_1)));
4144  EXPECT_FALSE(std::get<bool>(var_4->function(D_d_0)));
4145  EXPECT_FALSE(std::get<bool>(var_4->function(D_d_1)));
4146  EXPECT_FALSE(std::get<bool>(var_4->function(ggd_0_0_0)));
4147  EXPECT_FALSE(std::get<bool>(var_4->function(ggd_0_0_1)));
4148  EXPECT_TRUE(std::get<bool>(var_4->function(gd_0_0)));
4149  EXPECT_TRUE(std::get<bool>(var_4->function(gd_0_1)));
4150  EXPECT_FALSE(std::get<bool>(var_4->function(d_0)));
4151  EXPECT_FALSE(std::get<bool>(var_4->function(d_1)));
4152  EXPECT_FALSE(std::get<bool>(var_4->function(not_child)));
4153  EXPECT_FALSE(std::get<bool>(var_4->function(not_child_2)));
4154 
4155 
4156  const Manager::Var* var_5 = Manager::Instance().getVariable("isMCDescendantOfList(B:vartest, 3)");
4157  ASSERT_NE(var_5, nullptr);
4158  EXPECT_FALSE(std::get<bool>(var_5->function(D_gd_0_0)));
4159  EXPECT_FALSE(std::get<bool>(var_5->function(D_gd_0_1)));
4160  EXPECT_FALSE(std::get<bool>(var_5->function(D_gd_1_0)));
4161  EXPECT_FALSE(std::get<bool>(var_5->function(D_gd_1_1)));
4162  EXPECT_FALSE(std::get<bool>(var_5->function(D_d_0)));
4163  EXPECT_FALSE(std::get<bool>(var_5->function(D_d_1)));
4164  EXPECT_TRUE(std::get<bool>(var_5->function(ggd_0_0_0)));
4165  EXPECT_TRUE(std::get<bool>(var_5->function(ggd_0_0_1)));
4166  EXPECT_FALSE(std::get<bool>(var_5->function(gd_0_0)));
4167  EXPECT_FALSE(std::get<bool>(var_5->function(gd_0_1)));
4168  EXPECT_FALSE(std::get<bool>(var_5->function(d_0)));
4169  EXPECT_FALSE(std::get<bool>(var_5->function(d_1)));
4170  EXPECT_FALSE(std::get<bool>(var_5->function(not_child)));
4171  EXPECT_FALSE(std::get<bool>(var_5->function(not_child_2)));
4172  }
4173 
4174 
4175 
4176 
4177 
4178  class PIDVariableTest : public ::testing::Test {
4179  protected:
4181  void SetUp() override
4182  {
4183  DataStore::Instance().setInitializeActive(true);
4186  StoreArray<MCParticle> mcparticles;
4187  StoreArray<PIDLikelihood> likelihood;
4188  StoreArray<Particle> particles;
4189  StoreArray<Track> tracks;
4190  peim.registerInDataStore();
4191  tfrs.registerInDataStore();
4192  mcparticles.registerInDataStore();
4193  likelihood.registerInDataStore();
4194  particles.registerInDataStore();
4195  tracks.registerInDataStore();
4196  particles.registerRelationTo(likelihood);
4197  tracks.registerRelationTo(likelihood);
4198  DataStore::Instance().setInitializeActive(false);
4199  }
4200 
4202  void TearDown() override
4203  {
4204  DataStore::Instance().reset();
4205  }
4206  };
4207 
4208  TEST_F(PIDVariableTest, LogLikelihood)
4209  {
4210  StoreArray<PIDLikelihood> likelihood;
4211  StoreArray<Particle> particles;
4212  StoreArray<Track> tracks;
4214 
4215  // create tracks and trackFitResutls
4216  TRandom3 generator;
4217  const float pValue = 0.5;
4218  const float bField = 1.5;
4219  const int charge = 1;
4220  TMatrixDSym cov6(6);
4221  // Generate a random put orthogonal pair of vectors in the r-phi plane
4222  ROOT::Math::Cartesian2D d(generator.Uniform(-1, 1), generator.Uniform(-1, 1));
4223  ROOT::Math::Cartesian2D pt(generator.Uniform(-1, 1), generator.Uniform(-1, 1));
4224  d.SetXY(d.X(), -(d.X()*pt.X()) / pt.Y());
4225  // Add a random z component
4226  ROOT::Math::XYZVector position(d.X(), d.Y(), generator.Uniform(-1, 1));
4227  ROOT::Math::XYZVector momentum(pt.X(), pt.Y(), generator.Uniform(-1, 1));
4228 
4229  auto CDCValue = static_cast<unsigned long long int>(0x300000000000000);
4230  tfrs.appendNew(position, momentum, cov6, charge, Const::electron, pValue, bField, CDCValue, 16777215, 0);
4231  Track mytrack;
4232  mytrack.setTrackFitResultIndex(Const::electron, 0);
4233  Track* allTrack = tracks.appendNew(mytrack);
4234  Track* noSVDTrack = tracks.appendNew(mytrack);
4235  Track* noPIDTrack = tracks.appendNew(mytrack);
4236  Track* dEdxTrack = tracks.appendNew(mytrack);
4237 
4238  // Fill by hand likelihood values for all the detectors and hypothesis
4239  // This is clearly not a physical case, since a particle cannot leave good
4240  // signals in both TOP and ARICH
4241  auto* lAll = likelihood.appendNew();
4242  lAll->setLogLikelihood(Const::TOP, Const::electron, 0.18);
4243  lAll->setLogLikelihood(Const::ARICH, Const::electron, 0.16);
4244  lAll->setLogLikelihood(Const::ECL, Const::electron, 0.14);
4245  lAll->setLogLikelihood(Const::CDC, Const::electron, 0.12);
4246  lAll->setLogLikelihood(Const::SVD, Const::electron, 0.1);
4247  lAll->setLogLikelihood(Const::KLM, Const::electron, 0.01);
4248 
4249  lAll->setLogLikelihood(Const::TOP, Const::muon, 0.5);
4250  lAll->setLogLikelihood(Const::ARICH, Const::muon, 0.52);
4251  lAll->setLogLikelihood(Const::ECL, Const::muon, 0.54);
4252  lAll->setLogLikelihood(Const::CDC, Const::muon, 0.56);
4253  lAll->setLogLikelihood(Const::SVD, Const::muon, 0.58);
4254  lAll->setLogLikelihood(Const::KLM, Const::muon, 0.8);
4255 
4256  lAll->setLogLikelihood(Const::TOP, Const::pion, 0.2);
4257  lAll->setLogLikelihood(Const::ARICH, Const::pion, 0.22);
4258  lAll->setLogLikelihood(Const::ECL, Const::pion, 0.24);
4259  lAll->setLogLikelihood(Const::CDC, Const::pion, 0.26);
4260  lAll->setLogLikelihood(Const::SVD, Const::pion, 0.28);
4261  lAll->setLogLikelihood(Const::KLM, Const::pion, 0.2);
4262 
4263  lAll->setLogLikelihood(Const::TOP, Const::kaon, 0.3);
4264  lAll->setLogLikelihood(Const::ARICH, Const::kaon, 0.32);
4265  lAll->setLogLikelihood(Const::ECL, Const::kaon, 0.34);
4266  lAll->setLogLikelihood(Const::CDC, Const::kaon, 0.36);
4267  lAll->setLogLikelihood(Const::SVD, Const::kaon, 0.38);
4268  lAll->setLogLikelihood(Const::KLM, Const::kaon, 0.2);
4269 
4270  lAll->setLogLikelihood(Const::TOP, Const::proton, 0.4);
4271  lAll->setLogLikelihood(Const::ARICH, Const::proton, 0.42);
4272  lAll->setLogLikelihood(Const::ECL, Const::proton, 0.44);
4273  lAll->setLogLikelihood(Const::CDC, Const::proton, 0.46);
4274  lAll->setLogLikelihood(Const::SVD, Const::proton, 0.48);
4275  lAll->setLogLikelihood(Const::KLM, Const::proton, 0.02);
4276 
4277  lAll->setLogLikelihood(Const::TOP, Const::deuteron, 0.6);
4278  lAll->setLogLikelihood(Const::ARICH, Const::deuteron, 0.62);
4279  lAll->setLogLikelihood(Const::ECL, Const::deuteron, 0.64);
4280  lAll->setLogLikelihood(Const::CDC, Const::deuteron, 0.66);
4281  lAll->setLogLikelihood(Const::SVD, Const::deuteron, 0.68);
4282  lAll->setLogLikelihood(Const::KLM, Const::deuteron, 0.02);
4283 
4284  // Likelihoods for all detectors but SVD
4285  auto* lAllNoSVD = likelihood.appendNew();
4286 
4287  for (const auto& det : Const::PIDDetectorSet::set()) {
4288  for (const auto& hypo : Const::chargedStableSet) {
4289  if (det != Const::SVD) {
4290  lAllNoSVD->setLogLikelihood(det, hypo, lAll->getLogL(hypo, det));
4291  }
4292  }
4293  }
4294 
4295  // Likelihoods for a dEdx only case
4296  auto* ldEdx = likelihood.appendNew();
4297  ldEdx->setLogLikelihood(Const::CDC, Const::electron, 0.12);
4298  ldEdx->setLogLikelihood(Const::SVD, Const::electron, 0.1);
4299 
4300  ldEdx->setLogLikelihood(Const::CDC, Const::pion, 0.26);
4301  ldEdx->setLogLikelihood(Const::SVD, Const::pion, 0.28);
4302 
4303  ldEdx->setLogLikelihood(Const::CDC, Const::kaon, 0.36);
4304  ldEdx->setLogLikelihood(Const::SVD, Const::kaon, 0.38);
4305 
4306  ldEdx->setLogLikelihood(Const::CDC, Const::proton, 0.46);
4307  ldEdx->setLogLikelihood(Const::SVD, Const::proton, 0.48);
4308 
4309  ldEdx->setLogLikelihood(Const::CDC, Const::muon, 0.56);
4310  ldEdx->setLogLikelihood(Const::SVD, Const::muon, 0.58);
4311 
4312  ldEdx->setLogLikelihood(Const::CDC, Const::deuteron, 0.66);
4313  ldEdx->setLogLikelihood(Const::SVD, Const::deuteron, 0.68);
4314 
4315  allTrack->addRelationTo(lAll);
4316  noSVDTrack->addRelationTo(lAllNoSVD);
4317  dEdxTrack->addRelationTo(ldEdx);
4318 
4319  // Table with the sum(LogL) for several cases
4320  // All dEdx AllNoSVD
4321  // e 0.71 0.22 0.61
4322  // mu 3.5 1.14 2.92
4323  // pi 1.4 0.54 1.12
4324  // k 1.9 0.74 1.52
4325  // p 2.22 0.94 1.74
4326  // d 3.22 1.34 2.54
4327 
4328  auto* particleAll = particles.appendNew(allTrack, Const::pion);
4329  auto* particleNoSVD = particles.appendNew(noSVDTrack, Const::pion);
4330  auto* particledEdx = particles.appendNew(dEdxTrack, Const::pion);
4331  auto* particleNoID = particles.appendNew(noPIDTrack, Const::pion);
4332 
4333  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);
4334  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);
4335 
4336  // Basic PID quantities. Currently just wrappers for global probability.
4337  EXPECT_FLOAT_EQ(electronID(particleAll), std::exp(0.71) / numsumexp);
4338  EXPECT_FLOAT_EQ(muonID(particleAll), std::exp(3.5) / numsumexp);
4339  EXPECT_FLOAT_EQ(pionID(particleAll), std::exp(1.4) / numsumexp);
4340  EXPECT_FLOAT_EQ(kaonID(particleAll), std::exp(1.9) / numsumexp);
4341  EXPECT_FLOAT_EQ(protonID(particleAll), std::exp(2.22) / numsumexp);
4342  EXPECT_FLOAT_EQ(deuteronID(particleAll), std::exp(3.22) / numsumexp);
4343 
4344  // smart PID that takes the hypothesis into account
4345  auto* particleElectron = particles.appendNew(allTrack, Const::electron);
4346  auto* particleMuon = particles.appendNew(allTrack, Const::muon);
4347  auto* particleKaon = particles.appendNew(allTrack, Const::kaon);
4348  auto* particleProton = particles.appendNew(allTrack, Const::proton);
4349  auto* particleDeuteron = particles.appendNew(allTrack, Const::deuteron);
4350 
4351  EXPECT_FLOAT_EQ(particleID(particleAll), std::exp(1.4) / numsumexp); // there's already a pion
4352  EXPECT_FLOAT_EQ(particleID(particleElectron), std::exp(0.71) / numsumexp);
4353  EXPECT_FLOAT_EQ(particleID(particleMuon), std::exp(3.5) / numsumexp);
4354  EXPECT_FLOAT_EQ(particleID(particleKaon), std::exp(1.9) / numsumexp);
4355  EXPECT_FLOAT_EQ(particleID(particleProton), std::exp(2.22) / numsumexp);
4356  EXPECT_FLOAT_EQ(particleID(particleDeuteron), std::exp(3.22) / numsumexp);
4357 
4358  // TEMP: PID w/o the SVD.
4359  EXPECT_FLOAT_EQ(electronID_noSVD(particleNoSVD), std::exp(0.61) / numsumexp_noSVD);
4360  EXPECT_FLOAT_EQ(muonID_noSVD(particleNoSVD), std::exp(2.92) / numsumexp_noSVD);
4361  EXPECT_FLOAT_EQ(pionID_noSVD(particleNoSVD), std::exp(1.12) / numsumexp_noSVD);
4362  EXPECT_FLOAT_EQ(kaonID_noSVD(particleNoSVD), std::exp(1.52) / numsumexp_noSVD);
4363  EXPECT_FLOAT_EQ(protonID_noSVD(particleNoSVD), std::exp(1.74) / numsumexp_noSVD);
4364  EXPECT_FLOAT_EQ(deuteronID_noSVD(particleNoSVD), std::exp(2.54) / numsumexp_noSVD);
4365 
4366  // Binary PID
4367  std::vector<double> v_pi_K {211., 321.};
4368  std::vector<double> v_pi_p {211., 2212.};
4369  std::vector<double> v_K_p {321., 2212.};
4370  EXPECT_FLOAT_EQ(binaryPID(particleAll, v_pi_K), std::exp(1.4) / (std::exp(1.4) + std::exp(1.9)));
4371  EXPECT_FLOAT_EQ(binaryPID(particleAll, v_pi_p), std::exp(1.4) / (std::exp(1.4) + std::exp(2.22)));
4372  EXPECT_FLOAT_EQ(binaryPID(particleAll, v_K_p), std::exp(1.9) / (std::exp(1.9) + std::exp(2.22)));
4373 
4374  // Check what happens if no Likelihood is available
4375  EXPECT_TRUE(std::isnan(electronID(particleNoID)));
4376  EXPECT_TRUE(std::isnan(muonID(particleNoID)));
4377  EXPECT_TRUE(std::isnan(pionID(particleNoID)));
4378  EXPECT_TRUE(std::isnan(kaonID(particleNoID)));
4379  EXPECT_TRUE(std::isnan(protonID(particleNoID)));
4380  EXPECT_TRUE(std::isnan(deuteronID(particleNoID)));
4381 
4382  //expert stuff: LogL values
4383  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidLogLikelihoodValueExpert(11, TOP)")->function(particleAll)),
4384  0.18);
4385  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidLogLikelihoodValueExpert(11, ALL)")->function(particleAll)),
4386  0.71);
4387  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidLogLikelihoodValueExpert(2212, TOP, CDC)")->function(
4388  particleAll)), 0.86);
4389 
4390  // global probability
4391  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidProbabilityExpert(1000010020, ALL)")->function(particleAll)),
4392  std::exp(3.22) / numsumexp);
4393  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidProbabilityExpert(2212, ALL)")->function(particleAll)),
4394  std::exp(2.22) / numsumexp);
4395  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidProbabilityExpert(211, ALL)")->function(particleAll)),
4396  std::exp(1.4) / numsumexp);
4397  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidProbabilityExpert(321, ALL)")->function(particleAll)),
4398  std::exp(1.9) / numsumexp);
4399  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidProbabilityExpert(13, ALL)")->function(particleAll)),
4400  std::exp(3.5) / numsumexp);
4401  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidProbabilityExpert(11, ALL)")->function(particleAll)),
4402  std::exp(0.71) / numsumexp);
4403  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidProbabilityExpert(211, ALL)")->function(particledEdx)),
4404  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)));
4405  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidProbabilityExpert(211, ALL)")->function(particledEdx)),
4406  std::get<double>(Manager::Instance().getVariable("pidProbabilityExpert(211, CDC, SVD)")->function(particleAll)));
4407  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidProbabilityExpert(211, CDC)")->function(particledEdx)),
4408  std::get<double>(Manager::Instance().getVariable("pidProbabilityExpert(211, CDC)")->function(particleAll)));
4409  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidProbabilityExpert(321, CDC)")->function(particleAll)),
4410  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)));
4411 
4412  // binary probability
4413  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidPairProbabilityExpert(321, 2212, ALL)")->function(
4414  particleAll)),
4415  1.0 / (1.0 + std::exp(2.22 - 1.9)));
4416  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidPairProbabilityExpert(321, 2212, ALL)")->function(
4417  particledEdx)),
4418  1.0 / (1.0 + std::exp(0.94 - 0.74)));
4419  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidPairProbabilityExpert(321, 2212, CDC, SVD)")->function(
4420  particleAll)),
4421  1.0 / (1.0 + std::exp(0.94 - 0.74)));
4422 
4423  // No likelihood available
4424  EXPECT_TRUE(std::isnan(std::get<double>(Manager::Instance().getVariable("pidPairProbabilityExpert(321, 2212, KLM)")->function(
4425  particledEdx))));
4426  EXPECT_TRUE(std::isnan(std::get<double>(Manager::Instance().getVariable("pidLogLikelihoodValueExpert(11, TOP, CDC, SVD)")->function(
4427  particleNoID))));
4428  EXPECT_TRUE(std::isnan(std::get<double>(Manager::Instance().getVariable("pidLogLikelihoodValueExpert(11, TOP)")->function(
4429  particledEdx))));
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>
4433  (Manager::Instance().getVariable("pidPairProbabilityExpert(321, 2212, ECL, TOP, ARICH)")->function(
4434  particledEdx))));
4435  EXPECT_FALSE(std::isnan(std::get<double>
4436  (Manager::Instance().getVariable("pidPairProbabilityExpert(321, 2212, ECL, TOP, ARICH, SVD)")->function(
4437  particledEdx))));
4438  //Mostlikely PDG tests:
4439  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidMostLikelyPDG()")->function(particledEdx)), 1.00001e+09);
4440  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidMostLikelyPDG(0.5, 0.1, 0.1, 0.1, 0.1, 0.1)")->function(
4441  particledEdx)),
4442  Const::electron.getPDGCode());
4443  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidMostLikelyPDG(0.1, 0.5, 0.1, 0.1, 0.1, 0.1)")->function(
4444  particledEdx)),
4445  Const::muon.getPDGCode());
4446  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidMostLikelyPDG(0.1, 0.1, 0.5, 0.1, 0.1, 0.1)")->function(
4447  particledEdx)),
4448  Const::pion.getPDGCode());
4449  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidMostLikelyPDG(0.1, 0.1, 0.1, 0.5, 0.1, 0.1)")->function(
4450  particledEdx)),
4451  Const::kaon.getPDGCode());
4452  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidMostLikelyPDG(0.1, 0.1, 0.1, 0.1, 0.5, 0.1)")->function(
4453  particledEdx)),
4454  Const::proton.getPDGCode());
4455  EXPECT_FLOAT_EQ(std::get<double>(Manager::Instance().getVariable("pidMostLikelyPDG(0, 1., 0, 0, 0, 0)")->function(particledEdx)),
4456  Const::muon.getPDGCode());
4457  }
4458 
4459  TEST_F(PIDVariableTest, MissingLikelihood)
4460  {
4461  StoreArray<PIDLikelihood> likelihood;
4462  StoreArray<Particle> particles;
4463  StoreArray<Track> tracks;
4465 
4466  // create tracks and trackFitResutls
4467  TRandom3 generator;
4468  const float pValue = 0.5;
4469  const float bField = 1.5;
4470  const int charge = 1;
4471  TMatrixDSym cov6(6);
4472  // Generate a random put orthogonal pair of vectors in the r-phi plane
4473  ROOT::Math::Cartesian2D d(generator.Uniform(-1, 1), generator.Uniform(-1, 1));
4474  ROOT::Math::Cartesian2D pt(generator.Uniform(-1, 1), generator.Uniform(-1, 1));
4475  d.SetXY(d.X(), -(d.X()*pt.X()) / pt.Y());
4476  // Add a random z component
4477  ROOT::Math::XYZVector position(d.X(), d.Y(), generator.Uniform(-1, 1));
4478  ROOT::Math::XYZVector momentum(pt.X(), pt.Y(), generator.Uniform(-1, 1));
4479 
4480  auto CDCValue = static_cast<unsigned long long int>(0x300000000000000);
4481  tfrs.appendNew(position, momentum, cov6, charge, Const::electron, pValue, bField, CDCValue, 16777215, 0);
4482  Track mytrack;
4483  mytrack.setTrackFitResultIndex(Const::electron, 0);
4484  Track* savedTrack1 = tracks.appendNew(mytrack);
4485  Track* savedTrack2 = tracks.appendNew(mytrack);
4486  Track* savedTrack3 = tracks.appendNew(mytrack);
4487  Track* savedTrack4 = tracks.appendNew(mytrack);
4488 
4489  auto* l1 = likelihood.appendNew();
4490  l1->setLogLikelihood(Const::TOP, Const::electron, 0.18);
4491  l1->setLogLikelihood(Const::ECL, Const::electron, 0.14);
4492  savedTrack1->addRelationTo(l1);
4493 
4494  auto* electron = particles.appendNew(savedTrack1, Const::electron);
4495 
4496  auto* l2 = likelihood.appendNew();
4497  l2->setLogLikelihood(Const::TOP, Const::pion, 0.2);
4498  l2->setLogLikelihood(Const::ARICH, Const::pion, 0.22);
4499  l2->setLogLikelihood(Const::ECL, Const::pion, 0.24);
4500  l2->setLogLikelihood(Const::CDC, Const::pion, 0.26);
4501  l2->setLogLikelihood(Const::SVD, Const::pion, 0.28);
4502  savedTrack2->addRelationTo(l2);
4503 
4504  auto* pion = particles.appendNew(savedTrack2, Const::pion);
4505 
4506  auto* l3 = likelihood.appendNew();
4507  l3->setLogLikelihood(Const::TOP, Const::kaon, 0.3);
4508  l3->setLogLikelihood(Const::ARICH, Const::kaon, 0.32);
4509  savedTrack3->addRelationTo(l3);
4510 
4511  auto* kaon = particles.appendNew(savedTrack3, Const::kaon);
4512 
4513  auto* l4 = likelihood.appendNew();
4514  l4->setLogLikelihood(Const::ARICH, Const::proton, 0.42);
4515  l4->setLogLikelihood(Const::ECL, Const::proton, 0.44);
4516  l4->setLogLikelihood(Const::CDC, Const::proton, 0.46);
4517  l4->setLogLikelihood(Const::SVD, Const::proton, 0.48);
4518  savedTrack4->addRelationTo(l4);
4519 
4520  auto* proton = particles.appendNew(savedTrack4, Const::proton);
4521 
4522  const Manager::Var* varMissECL = Manager::Instance().getVariable("pidMissingProbabilityExpert(ECL)");
4523  const Manager::Var* varMissTOP = Manager::Instance().getVariable("pidMissingProbabilityExpert(TOP)");
4524  const Manager::Var* varMissARICH = Manager::Instance().getVariable("pidMissingProbabilityExpert(ARICH)");
4525 
4526 
4527  EXPECT_FLOAT_EQ(std::get<double>(varMissTOP->function(electron)), 0.0);
4528  EXPECT_FLOAT_EQ(std::get<double>(varMissTOP->function(pion)), 0.0);
4529  EXPECT_FLOAT_EQ(std::get<double>(varMissTOP->function(kaon)), 0.0);
4530  EXPECT_FLOAT_EQ(std::get<double>(varMissTOP->function(proton)), 1.0);
4531 
4532  EXPECT_FLOAT_EQ(std::get<double>(varMissARICH->function(electron)), 1.0);
4533  EXPECT_FLOAT_EQ(std::get<double>(varMissARICH->function(pion)), 0.0);
4534  EXPECT_FLOAT_EQ(std::get<double>(varMissARICH->function(kaon)), 0.0);
4535  EXPECT_FLOAT_EQ(std::get<double>(varMissARICH->function(proton)), 0.0);
4536 
4537  EXPECT_FLOAT_EQ(std::get<double>(varMissECL->function(electron)), 0.0);
4538  EXPECT_FLOAT_EQ(std::get<double>(varMissECL->function(pion)), 0.0);
4539  EXPECT_FLOAT_EQ(std::get<double>(varMissECL->function(kaon)), 1.0);
4540  EXPECT_FLOAT_EQ(std::get<double>(varMissECL->function(proton)), 0.0);
4541  }
4542 
4543  class FlightInfoTest : public ::testing::Test {
4544  protected:
4546  void SetUp() override
4547  {
4548  DataStore::Instance().setInitializeActive(true);
4551  StoreArray<MCParticle> mcParticles;
4552  StoreArray<Particle> particles;
4553  particles.registerRelationTo(mcParticles);
4555  DataStore::Instance().setInitializeActive(false);
4556 
4557 
4558  // Insert MC particle logic here
4559  MCParticle mcKs;
4560  mcKs.setPDG(Const::Kshort.getPDGCode());
4561  mcKs.setProductionVertex(1.0, 1.0, 0.0);
4562  mcKs.setDecayVertex(4.0, 5.0, 0.0);
4563  mcKs.setProductionTime(0);
4564  mcKs.setMassFromPDG();
4565  mcKs.setMomentum(1.164, 1.55200, 0);
4566  float decayTime = 5 * mcKs.getMass() / mcKs.getEnergy();
4567  mcKs.setDecayTime(decayTime);
4568  mcKs.setStatus(MCParticle::c_PrimaryParticle);
4569  MCParticle* newMCKs = mcParticles.appendNew(mcKs);
4570 
4571 
4572 
4573  MCParticle mcDp;
4574  mcDp.setPDG(411);
4575  mcDp.setDecayVertex(1.0, 1.0, 0.0);
4576  mcDp.setMassFromPDG();
4577  mcDp.setStatus(MCParticle::c_PrimaryParticle);
4578  MCParticle* newMCDp = mcParticles.appendNew(mcDp);
4579 
4580  // Insert Reco particle logic here
4581  PxPyPzEVector momentum;
4582  TMatrixFSym error(7);
4583  error.Zero();
4584  error(0, 0) = 0.05;
4585  error(1, 1) = 0.2;
4586  error(2, 2) = 0.4;
4587  error(3, 3) = 0.01;
4588  error(4, 4) = 0.04;
4589  error(5, 5) = 0.00875;
4590  error(6, 6) = 0.01;
4591  Particle pi(PxPyPzEVector(1.59607, 1.19705, 0, 2), 211);
4592  momentum += pi.get4Vector();
4593  Particle* newpi = particles.appendNew(pi);
4594 
4595 
4596  Particle Ks(PxPyPzEVector(1.164, 1.55200, 0, 2), 310, Particle::c_Unflavored, Particle::c_Composite, 0);
4597  Ks.setVertex(XYZVector(4.0, 5.0, 0.0));
4598  Ks.setMomentumVertexErrorMatrix(error); // (order: px,py,pz,E,x,y,z)
4599  momentum += Ks.get4Vector();
4600  Ks.addExtraInfo("prodVertX", 1.0);
4601  Ks.addExtraInfo("prodVertY", 1.0);
4602  Ks.addExtraInfo("prodVertZ", 0.0);
4603  Ks.addExtraInfo("prodVertSxx", 0.04);
4604  Ks.addExtraInfo("prodVertSxy", 0.0);
4605  Ks.addExtraInfo("prodVertSxz", 0.0);
4606  Ks.addExtraInfo("prodVertSyx", 0.0);
4607  Ks.addExtraInfo("prodVertSyy", 0.00875);
4608  Ks.addExtraInfo("prodVertSyz", 0.0);
4609  Ks.addExtraInfo("prodVertSzx", 0.0);
4610  Ks.addExtraInfo("prodVertSzy", 0.0);
4611  Ks.addExtraInfo("prodVertSzz", 0.01);
4612  Particle* newKs = particles.appendNew(Ks);
4613  newKs->addRelationTo(newMCKs);
4614 
4615 
4616  Particle Dp(momentum, 411, Particle::c_Flavored, Particle::c_Composite, 0);
4617  Dp.appendDaughter(newpi);
4618  Dp.appendDaughter(newKs);
4619  XYZVector motherVtx(1.0, 1.0, 0.0);
4620  Dp.setVertex(motherVtx);
4621  Dp.setMomentumVertexErrorMatrix(error); // (order: px,py,pz,E,x,y,z)
4622  Dp.addExtraInfo("prodVertX", 0.0);
4623  Dp.addExtraInfo("prodVertY", 1.0);
4624  Dp.addExtraInfo("prodVertZ", -2.0);
4625  Dp.addExtraInfo("prodVertSxx", 0.04);
4626  Dp.addExtraInfo("prodVertSxy", 0.0);
4627  Dp.addExtraInfo("prodVertSxz", 0.0);
4628  Dp.addExtraInfo("prodVertSyx", 0.0);
4629  Dp.addExtraInfo("prodVertSyy", 0.01);
4630  Dp.addExtraInfo("prodVertSyz", 0.0);
4631  Dp.addExtraInfo("prodVertSzx", 0.0);
4632  Dp.addExtraInfo("prodVertSzy", 0.0);
4633  Dp.addExtraInfo("prodVertSzz", 0.1575);
4634  Particle* newDp = particles.appendNew(Dp);
4635  newDp->addRelationTo(newMCDp);
4636 
4637  }
4638 
4640  void TearDown() override
4641  {
4642  DataStore::Instance().reset();
4643  }
4644  };
4645  TEST_F(FlightInfoTest, flightDistance)
4646  {
4647  StoreArray<Particle> particles{};
4648  const Particle* newKs = particles[1]; // Ks had flight distance of 5 cm
4649 
4650  const Manager::Var* var = Manager::Instance().getVariable("flightDistance");
4651  ASSERT_NE(var, nullptr);
4652  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), 5.0);
4653  }
4654  TEST_F(FlightInfoTest, flightDistanceErr)
4655  {
4656  StoreArray<Particle> particles{};
4657  const Particle* newKs = particles[1]; // Ks had flight distance of 5 cm
4658 
4659  const Manager::Var* var = Manager::Instance().getVariable("flightDistanceErr");
4660  ASSERT_NE(var, nullptr);
4661  EXPECT_GT(std::get<double>(var->function(newKs)), 0.0);
4662  }
4663  TEST_F(FlightInfoTest, flightTime)
4664  {
4665  StoreArray<Particle> particles{};
4666  const Particle* newKs = particles[1]; // Ks had flight time of 0.0427 us (t = d/c * m/p)
4667 
4668  const Manager::Var* var = Manager::Instance().getVariable("flightTime");
4669  ASSERT_NE(var, nullptr);
4670  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), 5.0 / Const::speedOfLight * newKs->getPDGMass() / newKs->getP());
4671  }
4672 
4673  TEST_F(FlightInfoTest, flightTimeErr)
4674  {
4675  StoreArray<Particle> particles{};
4676  const Particle* newKs = particles[1]; // Ks should have positive flight distance uncertainty
4677 
4678  const Manager::Var* var = Manager::Instance().getVariable("flightTimeErr");
4679  ASSERT_NE(var, nullptr);
4680  EXPECT_GT(std::get<double>(var->function(newKs)), 0.0);
4681  }
4682 
4683  TEST_F(FlightInfoTest, flightDistanceOfDaughter)
4684  {
4685  StoreArray<Particle> particles{};
4686  const Particle* newDp = particles[2]; // Get D+, its daughter Ks had flight distance of 5 cm
4687 
4688  const Manager::Var* var = Manager::Instance().getVariable("flightDistanceOfDaughter(1)");
4689  ASSERT_NE(var, nullptr);
4690  EXPECT_FLOAT_EQ(std::get<double>(var->function(newDp)), 5.0);
4691 
4692  var = Manager::Instance().getVariable("flightDistanceOfDaughter(3)");
4693  ASSERT_NE(var, nullptr);
4694  EXPECT_TRUE(std::isnan(std::get<double>(var->function(newDp))));
4695  }
4696  TEST_F(FlightInfoTest, flightDistanceOfDaughterErr)
4697  {
4698  StoreArray<Particle> particles{};
4699  const Particle* newDp = particles[2]; // Get D+, its daughter Ks should have positive flight distance uncertainty
4700 
4701  const Manager::Var* var = Manager::Instance().getVariable("flightDistanceOfDaughterErr(1)");
4702  ASSERT_NE(var, nullptr);
4703  EXPECT_GT(std::get<double>(var->function(newDp)), 0.0);
4704 
4705  var = Manager::Instance().getVariable("flightDistanceOfDaughterErr(3)");
4706  ASSERT_NE(var, nullptr);
4707  EXPECT_TRUE(std::isnan(std::get<double>(var->function(newDp))));
4708  }
4709  TEST_F(FlightInfoTest, flightTimeOfDaughter)
4710  {
4711  StoreArray<Particle> particles{};
4712  const Particle* newDp = particles[2]; // Get D+, its daughter Ks had flight time of 0.0427 us (t = d/c * m/p)
4713 
4714  const Manager::Var* var = Manager::Instance().getVariable("flightTimeOfDaughter(1)");
4715  ASSERT_NE(var, nullptr);
4716  const Particle* Ks = newDp->getDaughter(1);
4717 
4718  EXPECT_FLOAT_EQ(std::get<double>(var->function(newDp)), 5.0 / Const::speedOfLight * Ks->getPDGMass() / Ks->getP());
4719 
4720  var = Manager::Instance().getVariable("flightTimeOfDaughter(3)");
4721  ASSERT_NE(var, nullptr);
4722  EXPECT_TRUE(std::isnan(std::get<double>(var->function(newDp))));
4723  }
4724  TEST_F(FlightInfoTest, flightTimeOfDaughterErr)
4725  {
4726  StoreArray<Particle> particles{};
4727  const Particle* newDp = particles[2]; // Get D+, its daughter Ks should have positive flight time uncertainty
4728 
4729  const Manager::Var* var = Manager::Instance().getVariable("flightTimeOfDaughterErr(1)");
4730  ASSERT_NE(var, nullptr);
4731  EXPECT_GT(std::get<double>(var->function(newDp)), 0.0);
4732 
4733  var = Manager::Instance().getVariable("flightTimeOfDaughterErr(3)");
4734  ASSERT_NE(var, nullptr);
4735  EXPECT_TRUE(std::isnan(std::get<double>(var->function(newDp))));
4736  }
4737  TEST_F(FlightInfoTest, mcFlightDistanceOfDaughter)
4738  {
4739  StoreArray<Particle> particles{};
4740  const Particle* newDp = particles[2]; // Get D+, its daughter Ks had flight distance of 5 cm
4741 
4742  const Manager::Var* var = Manager::Instance().getVariable("mcFlightDistanceOfDaughter(1)");
4743  ASSERT_NE(var, nullptr);
4744 
4745  EXPECT_FLOAT_EQ(std::get<double>(var->function(newDp)), 5.0);
4746 
4747  var = Manager::Instance().getVariable("mcFlightDistanceOfDaughter(3)");
4748  ASSERT_NE(var, nullptr);
4749  EXPECT_TRUE(std::isnan(std::get<double>(var->function(newDp))));
4750  }
4751  TEST_F(FlightInfoTest, mcFlightTimeOfDaughter)
4752  {
4753  StoreArray<Particle> particles{};
4754  const Particle* newDp = particles[2]; // Get D+, its daughter Ks had flight time of 0.0427 us (t = d/c * m/p)
4755 
4756  const Manager::Var* var = Manager::Instance().getVariable("mcFlightTimeOfDaughter(1)");
4757  ASSERT_NE(var, nullptr);
4758  auto* Ks = newDp->getDaughter(1)->getRelatedTo<MCParticle>();
4759  // double p = Ks->getMomentum().Mag();
4760  // EXPECT_FLOAT_EQ(std::get<double>(var->function(newDp)), 5.0 / Const::speedOfLight * Ks->getMass() / p);
4761 
4762  EXPECT_FLOAT_EQ(std::get<double>(var->function(newDp)), Ks->getLifetime() / Ks->getEnergy()*Ks->getMass());
4763 
4764  var = Manager::Instance().getVariable("mcFlightTimeOfDaughter(3)");
4765  ASSERT_NE(var, nullptr);
4766  EXPECT_TRUE(std::isnan(std::get<double>(var->function(newDp))));
4767  }
4768 
4769  TEST_F(FlightInfoTest, vertexDistance)
4770  {
4771  StoreArray<Particle> particles{};
4772  const Particle* newKS = particles[1]; // Get KS, as it has both a production and decay vertex
4773 
4774  const Manager::Var* var = Manager::Instance().getVariable("vertexDistance");
4775  ASSERT_NE(var, nullptr);
4776  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKS)), 5.0);
4777  }
4778 
4779  TEST_F(FlightInfoTest, vertexDistanceError)
4780  {
4781  StoreArray<Particle> particles{};
4782  const Particle* newKS = particles[1]; // Get KS, as it has both a production and decay vertex
4783 
4784  const Manager::Var* var = Manager::Instance().getVariable("vertexDistanceErr");
4785  ASSERT_NE(var, nullptr);
4786  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKS)), 0.2);
4787  }
4788 
4789  TEST_F(FlightInfoTest, vertexDistanceSignificance)
4790  {
4791  StoreArray<Particle> particles{};
4792  const Particle* newKS = particles[1]; // Get KS, as it has both a production and decay vertex
4793 
4794  const Manager::Var* var = Manager::Instance().getVariable("vertexDistanceSignificance");
4795  ASSERT_NE(var, nullptr);
4796  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKS)), 25);
4797  }
4798 
4799  TEST_F(FlightInfoTest, vertexDistanceOfDaughter)
4800  {
4801  StoreArray<Particle> particles{};
4802  const Particle* newDp = particles[2]; // Get D+, its daughter KS has both a production and decay vertex
4803 
4804  const Manager::Var* var = Manager::Instance().getVariable("vertexDistanceOfDaughter(1, 0)");
4805  ASSERT_NE(var, nullptr);
4806  EXPECT_FLOAT_EQ(std::get<double>(var->function(newDp)), 5.0);
4807 
4808  var = Manager::Instance().getVariable("vertexDistanceOfDaughter(1)");
4809  ASSERT_NE(var, nullptr);
4810  EXPECT_FLOAT_EQ(std::get<double>(var->function(newDp)), 6.0);
4811 
4812  var = Manager::Instance().getVariable("vertexDistanceOfDaughter(2)");
4813  ASSERT_NE(var, nullptr);
4814  EXPECT_TRUE(std::isnan(std::get<double>(var->function(newDp))));
4815  }
4816 
4817  TEST_F(FlightInfoTest, vertexDistanceOfDaughterError)
4818  {
4819  StoreArray<Particle> particles{};
4820  const Particle* newDp = particles[2]; // Get D+, its daughter KS has both a production and decay vertex
4821 
4822  const Manager::Var* var = Manager::Instance().getVariable("vertexDistanceOfDaughterErr(1, 0)");
4823  ASSERT_NE(var, nullptr);
4824  EXPECT_FLOAT_EQ(std::get<double>(var->function(newDp)), 0.2);
4825 
4826  var = Manager::Instance().getVariable("vertexDistanceOfDaughterErr(1)");
4827  ASSERT_NE(var, nullptr);
4828  EXPECT_FLOAT_EQ(std::get<double>(var->function(newDp)), 0.25);
4829  }
4830 
4831  TEST_F(FlightInfoTest, vertexDistanceOfDaughterSignificance)
4832  {
4833  StoreArray<Particle> particles{};
4834  const Particle* newDp = particles[2]; // Get D+, its daughter KS has both a production and decay vertex
4835 
4836  const Manager::Var* var = Manager::Instance().getVariable("vertexDistanceOfDaughterSignificance(1, 0)");
4837  ASSERT_NE(var, nullptr);
4838  EXPECT_FLOAT_EQ(std::get<double>(var->function(newDp)), 25);
4839 
4840  var = Manager::Instance().getVariable("vertexDistanceOfDaughterSignificance(1)");
4841  ASSERT_NE(var, nullptr);
4842  EXPECT_FLOAT_EQ(std::get<double>(var->function(newDp)), 24);
4843  }
4844 
4845  class VertexVariablesTest : public ::testing::Test {
4846  protected:
4848  void SetUp() override
4849  {
4850  DataStore::Instance().setInitializeActive(true);
4853  StoreArray<MCParticle> mcParticles;
4854  StoreArray<Particle> particles;
4855  particles.registerRelationTo(mcParticles);
4857  DataStore::Instance().setInitializeActive(false);
4858 
4859 
4860  // Insert MC particle logic here
4861  MCParticle mcKs;
4862  mcKs.setPDG(Const::Kshort.getPDGCode());
4863  mcKs.setDecayVertex(4.0, 5.0, 0.0);
4864  mcKs.setProductionVertex(1.0, 2.0, 3.0);
4865  mcKs.setMassFromPDG();
4866  mcKs.setMomentum(1.164, 1.55200, 0);
4867  mcKs.setStatus(MCParticle::c_PrimaryParticle);
4868  MCParticle* newMCKs = mcParticles.appendNew(mcKs);
4869 
4870  Particle Ks(PxPyPzEVector(1.164, 1.55200, 0, 2), 310);
4871  Ks.setVertex(XYZVector(4.0, 5.0, 0.0));
4872  Ks.addExtraInfo("prodVertX", 1.0);
4873  Ks.addExtraInfo("prodVertY", 2.0);
4874  Ks.addExtraInfo("prodVertZ", 3.0);
4875  Ks.addExtraInfo("prodVertSxx", 0.1);
4876  Ks.addExtraInfo("prodVertSxy", 0.2);
4877  Ks.addExtraInfo("prodVertSxz", 0.3);
4878  Ks.addExtraInfo("prodVertSyx", 0.4);
4879  Ks.addExtraInfo("prodVertSyy", 0.5);
4880  Ks.addExtraInfo("prodVertSyz", 0.6);
4881  Ks.addExtraInfo("prodVertSzx", 0.7);
4882  Ks.addExtraInfo("prodVertSzy", 0.8);
4883  Ks.addExtraInfo("prodVertSzz", 0.9);
4884  Particle* newKs = particles.appendNew(Ks);
4885  newKs->addRelationTo(newMCKs);
4886  }
4887 
4889  void TearDown() override
4890  {
4891  DataStore::Instance().reset();
4892  }
4893  };
4894 
4895  // MC vertex tests
4896  TEST_F(VertexVariablesTest, mcDecayVertexX)
4897  {
4898  StoreArray<Particle> particles{};
4899  const Particle* newKs = particles[0]; // Ks had truth decay x is 4.0
4900 
4901  const Manager::Var* var = Manager::Instance().getVariable("mcDecayVertexX");
4902  ASSERT_NE(var, nullptr);
4903  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), 4.0);
4904  }
4905 
4906  TEST_F(VertexVariablesTest, mcDecayVertexY)
4907  {
4908  StoreArray<Particle> particles{};
4909  const Particle* newKs = particles[0]; // Ks had truth decay y is 5.0
4910 
4911  const Manager::Var* var = Manager::Instance().getVariable("mcDecayVertexY");
4912  ASSERT_NE(var, nullptr);
4913  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), 5.0);
4914  }
4915 
4916  TEST_F(VertexVariablesTest, mcDecayVertexZ)
4917  {
4918  StoreArray<Particle> particles{};
4919  const Particle* newKs = particles[0]; // Ks had truth decay z is 0.0
4920 
4921  const Manager::Var* var = Manager::Instance().getVariable("mcDecayVertexZ");
4922  ASSERT_NE(var, nullptr);
4923  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), 0.0);
4924  }
4925 
4926 
4927  TEST_F(VertexVariablesTest, mcDecayVertexFromIPDistance)
4928  {
4929  StoreArray<Particle> particles{};
4930  const Particle* newKs = particles[0]; // Ks had truth distance of sqrt(41)
4931 
4932  const Manager::Var* var = Manager::Instance().getVariable("mcDecayVertexFromIPDistance");
4933  ASSERT_NE(var, nullptr);
4934  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), sqrt(4.0 * 4.0 + 5.0 * 5.0));
4935  }
4936 
4937  TEST_F(VertexVariablesTest, mcDecayVertexRho)
4938  {
4939  StoreArray<Particle> particles{};
4940  const Particle* newKs = particles[0]; // Ks had truth rho of sqrt(41)
4941 
4942  const Manager::Var* var = Manager::Instance().getVariable("mcDecayVertexRho");
4943  ASSERT_NE(var, nullptr);
4944  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), sqrt(4.0 * 4.0 + 5.0 * 5.0));
4945  }
4946 
4947  TEST_F(VertexVariablesTest, mcProductionVertexX)
4948  {
4949  StoreArray<Particle> particles{};
4950  const Particle* newKs = particles[0]; // Ks had production vertex x of 1.0 cm
4951 
4952  const Manager::Var* var = Manager::Instance().getVariable("mcProductionVertexX");
4953  ASSERT_NE(var, nullptr);
4954  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), 1.0);
4955  }
4956 
4957  TEST_F(VertexVariablesTest, mcProductionVertexY)
4958  {
4959  StoreArray<Particle> particles{};
4960  const Particle* newKs = particles[0]; // Ks had production vertex y of 2.0 cm
4961 
4962  const Manager::Var* var = Manager::Instance().getVariable("mcProductionVertexY");
4963  ASSERT_NE(var, nullptr);
4964  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), 2.0);
4965  }
4966 
4967  TEST_F(VertexVariablesTest, mcProductionVertexZ)
4968  {
4969  StoreArray<Particle> particles{};
4970  const Particle* newKs = particles[0]; // Ks had production vertex z of 3.0 cm
4971 
4972  const Manager::Var* var = Manager::Instance().getVariable("mcProductionVertexZ");
4973  ASSERT_NE(var, nullptr);
4974  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), 3.0);
4975  }
4976 
4977  // Production position tests
4978 
4979  TEST_F(VertexVariablesTest, prodVertexX)
4980  {
4981  StoreArray<Particle> particles{};
4982  const Particle* newKs = particles[0]; // Ks had production vertex x of 1.0 cm
4983 
4984  const Manager::Var* var = Manager::Instance().getVariable("prodVertexX");
4985  ASSERT_NE(var, nullptr);
4986  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), 1.0);
4987  }
4988  TEST_F(VertexVariablesTest, prodVertexY)
4989  {
4990  StoreArray<Particle> particles{};
4991  const Particle* newKs = particles[0]; // Ks had production vertex y of 2.0 cm
4992 
4993  const Manager::Var* var = Manager::Instance().getVariable("prodVertexY");
4994  ASSERT_NE(var, nullptr);
4995  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), 2.0);
4996  }
4997  TEST_F(VertexVariablesTest, prodVertexZ)
4998  {
4999  StoreArray<Particle> particles{};
5000  const Particle* newKs = particles[0]; // Ks had production vertex z of 3.0 cm
5001 
5002  const Manager::Var* var = Manager::Instance().getVariable("prodVertexZ");
5003  ASSERT_NE(var, nullptr);
5004  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), 3.0);
5005  }
5006 
5007  // Production Covariance tests
5008 
5009  TEST_F(VertexVariablesTest, prodVertexCov)
5010  {
5011  StoreArray<Particle> particles{};
5012  const Particle* newKs = particles[0]; // Ks had production vertex covariance xx of .1 cm
5013 
5014  //const Manager::Var* var = Manager::Instance().getVariable("prodVertexCovXX");
5015  const Manager::Var* var = Manager::Instance().getVariable("prodVertexCov(0,0)");
5016  ASSERT_NE(var, nullptr);
5017  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), 0.1);
5018  var = Manager::Instance().getVariable("prodVertexCov(0,1)");
5019  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), 0.2);
5020  var = Manager::Instance().getVariable("prodVertexCov(0,2)");
5021  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), 0.3);
5022  var = Manager::Instance().getVariable("prodVertexCov(1,0)");
5023  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), 0.4);
5024  var = Manager::Instance().getVariable("prodVertexCov(1,1)");
5025  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), 0.5);
5026  var = Manager::Instance().getVariable("prodVertexCov(1,2)");
5027  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), 0.6);
5028  var = Manager::Instance().getVariable("prodVertexCov(2,0)");
5029  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), 0.7);
5030  var = Manager::Instance().getVariable("prodVertexCov(2,1)");
5031  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), 0.8);
5032  var = Manager::Instance().getVariable("prodVertexCov(2,2)");
5033  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), 0.9);
5034  var = Manager::Instance().getVariable("prodVertexXErr");
5035  ASSERT_NE(var, nullptr);
5036  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), sqrt(0.1));
5037  var = Manager::Instance().getVariable("prodVertexYErr");
5038  ASSERT_NE(var, nullptr);
5039  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), sqrt(0.5));
5040  var = Manager::Instance().getVariable("prodVertexZErr");
5041  ASSERT_NE(var, nullptr);
5042  EXPECT_FLOAT_EQ(std::get<double>(var->function(newKs)), sqrt(0.9));
5043  }
5044 
5045  // Tests of ContinuumSuppressionVariables
5046 
5047  TEST_F(MetaVariableTest, KSFWVariables)
5048  {
5049  // simple tests that do not require the ROE builder nor the CS builder
5050 
5051  // check that garbage input throws helpful B2FATAL
5052  EXPECT_B2FATAL(Manager::Instance().getVariable("KSFWVariables(NONSENSE)"));
5053 
5054  // check for NaN if we don't have a CS object for this particle
5055  StoreArray<Particle> myParticles;
5056  const Particle* particle_with_no_cs = myParticles.appendNew();
5057  const Manager::Var* var = Manager::Instance().getVariable("KSFWVariables(mm2)");
5058  EXPECT_TRUE(std::isnan(std::get<double>(var->function(particle_with_no_cs))));
5059 
5060  // check that FS1 set as third argument, throws a B2ERROR
5061  EXPECT_B2ERROR(Manager::Instance().getVariable("KSFWVariables(et, mask, FS1)"));
5062  }
5063 
5064  TEST_F(MetaVariableTest, CleoConeCS)
5065  {
5066  // simple tests that do not require the ROE builder nor the CS builder
5067 
5068  // check that garbage input throws helpful B2FATAL
5069  EXPECT_B2FATAL(Manager::Instance().getVariable("CleoConeCS(NONSENSE)"));
5070 
5071  // check for NaN if we don't have a CS object for this particle
5072  StoreArray<Particle> myParticles;
5073  const Particle* particle_with_no_cs = myParticles.appendNew();
5074  const Manager::Var* var = Manager::Instance().getVariable("CleoConeCS(0)");
5075  EXPECT_TRUE(std::isnan(std::get<double>(var->function(particle_with_no_cs))));
5076 
5077  // check that string other than ROE as second argument, which is interpreted as mask name, returns NaN
5078  var = Manager::Instance().getVariable("CleoConeCS(0, NOTROE)");
5079  EXPECT_TRUE(std::isnan(std::get<double>(var->function(particle_with_no_cs))));
5080 
5081  // check that ROE set as third argument, throws a B2ERROR
5082  EXPECT_B2ERROR(Manager::Instance().getVariable("CleoConeCS(0, mask, ROE)"));
5083  }
5084 
5085  TEST_F(MetaVariableTest, TransformedNetworkOutput)
5086  {
5087  // check that garbage input throws helpful B2FATAL
5088  EXPECT_B2FATAL(Manager::Instance().getVariable("transformedNetworkOutput(NONSENSE)"));
5089 
5090  // check that helpful B2FATAL is thrown if second or third argument is not a double
5091  EXPECT_B2FATAL(Manager::Instance().getVariable("transformedNetworkOutput(NONEXISTENT, 0, NOTDOUBLE)"));
5092  EXPECT_B2FATAL(Manager::Instance().getVariable("transformedNetworkOutput(NONEXISTENT, NOTDOUBLE, 1)"));
5093 
5094  // check for NaN if network output variable does not exist (no matter whether particle is provided or not)
5095  StoreArray<Particle> myParticles;
5096  const Particle* particle = myParticles.appendNew();
5097  const Manager::Var* var = Manager::Instance().getVariable("transformedNetworkOutput(NONEXISTENT, 0, 1)");
5098  EXPECT_TRUE(std::isnan(std::get<double>(var->function(particle))));
5099  StoreObjPtr<EventExtraInfo> eventExtraInfo;
5100  if (not eventExtraInfo.isValid())
5101  eventExtraInfo.create();
5102  var = Manager::Instance().getVariable("transformedNetworkOutput(NONEXISTENT, 0, 1)");
5103  EXPECT_TRUE(std::isnan(std::get<double>(var->function(nullptr))));
5104  }
5105 }
Provides a type-safe way to pass members of the chargedStableSet set.
Definition: Const.h:580
EStoreFlags
Flags describing behaviours of objects etc.
Definition: DataStore.h:69
ECL cluster data.
Definition: ECLCluster.h:27
void setTheta(double theta)
Set Theta of Shower (radian).
Definition: ECLCluster.h:217
void setPhi(double phi)
Set Phi of Shower (radian).
Definition: ECLCluster.h:220
void setClusterId(int clusterid)
Set cluster id.
Definition: ECLCluster.h:145
void setHypothesis(EHypothesisBit hypothesis)
Set hypotheses.
Definition: ECLCluster.h:123
void setEnergy(double energy)
Set Corrected Energy (GeV).
Definition: ECLCluster.h:226
void setR(double r)
Set R (in cm).
Definition: ECLCluster.h:223
Singleton class responsible for loading detector parameters from an XML file.
Definition: Gearbox.h:34
Class to represent Particle data in graph.
void comesFrom(GraphParticle &mother)
Tells the graph that this particle is a decay product of mother.
Class to build, validate and sort a particle decay chain.
void generateList(const std::string &name="", int options=c_setNothing)
Generates the MCParticle list and stores it in the StoreArray with the given name.
A Class to store the Monte Carlo particle information.
Definition: MCParticle.h:32
float getEnergy() const
Return particle energy in GeV.
Definition: MCParticle.h:147
void setDecayTime(float time)
Set decay time.
Definition: MCParticle.h:390
void setMass(float mass)
Set particle mass.
Definition: MCParticle.h:366
void setDecayVertex(const ROOT::Math::XYZVector &vertex)
Set decay vertex.
Definition: MCParticle.h:447
float getMass() const
Return the particle mass in GeV.
Definition: MCParticle.h:135
void setProductionVertex(const ROOT::Math::XYZVector &vertex)
Set production vertex position.
Definition: MCParticle.h:396
ROOT::Math::PxPyPzEVector get4Vector() const
Return 4Vector of particle.
Definition: MCParticle.h:207
void setPDG(int pdg)
Set PDG code of the particle.
Definition: MCParticle.h:335
void set4Vector(const ROOT::Math::PxPyPzEVector &p4)
Sets the 4Vector of particle.
Definition: MCParticle.h:438
void setMomentum(const ROOT::Math::XYZVector &momentum)
Set particle momentum.
Definition: MCParticle.h:417
void setStatus(unsigned short int status)
Set Status code for the particle.
Definition: MCParticle.h:346
void setProductionTime(float time)
Set production time.
Definition: MCParticle.h:384
void setMassFromPDG()
Sets the mass for the particle from the particle's PDG code.
Definition: MCParticle.cc:28
Class to hold Lorentz transformations from/to CMS and boost vector.
double getCMSEnergy() const
Returns CMS energy of e+e- (aka.
const ROOT::Math::LorentzRotation rotateLabToCms() const
Returns Lorentz transformation from Lab to CMS.
const ROOT::Math::LorentzRotation rotateCmsToLab() const
Returns Lorentz transformation from CMS to Lab.
Class to store reconstructed particles.
Definition: Particle.h:75
void appendDaughter(const Particle *daughter, const bool updateType=true, const int daughterProperty=c_Ordinary)
Appends index of daughter to daughters index array.
Definition: Particle.cc:680
void setVertex(const ROOT::Math::XYZVector &vertex)
Sets position (decay vertex)
Definition: Particle.h:295
double getEnergy() const
Returns total energy.
Definition: Particle.h:507
double getPDGMass(void) const
Returns uncertainty on the invariant mass (requires valid momentum error matrix)
Definition: Particle.cc:608
ROOT::Math::PxPyPzEVector get4Vector() const
Returns Lorentz vector.
Definition: Particle.h:517
void addExtraInfo(const std::string &name, double value)
Sets the user-defined data of given name to the given value.
Definition: Particle.cc:1337
double getP() const
Returns momentum magnitude (same as getMomentumMagnitude but with shorter name)
Definition: Particle.h:544
const Particle * getDaughter(unsigned i) const
Returns a pointer to the i-th daughter particle.
Definition: Particle.cc:635
double getMass() const
Returns invariant mass (= nominal for FS particles)
Definition: Particle.h:479
void addRelationTo(const RelationsInterface< BASE > *object, float weight=1.0, const std::string &namedRelation="") const
Add a relation from this object to another object (with caching).
int getArrayIndex() const
Returns this object's array index (in StoreArray), or -1 if not found.
TO * getRelatedTo(const std::string &name="", const std::string &namedRelation="") const
Get the object to which this object has a relation.
bool registerInDataStore(DataStore::EStoreFlags storeFlags=DataStore::c_WriteOut)
Register the object/array in the DataStore.
bool create(bool replace=false)
Create a default object in the data store.
Accessor to arrays stored in the data store.
Definition: StoreArray.h:113
T * appendNew()
Construct a new T object at the end of the array.
Definition: StoreArray.h:246
void clear() override
Delete all entries in this array.
Definition: StoreArray.h:207
bool registerRelationTo(const StoreArray< TO > &toArray, DataStore::EDurability durability=DataStore::c_Event, DataStore::EStoreFlags storeFlags=DataStore::c_WriteOut, const std::string &namedRelation="") const
Register a relation to the given StoreArray.
Definition: StoreArray.h:140
Type-safe access to single objects in the data store.
Definition: StoreObjPtr.h:96
Class that bundles various TrackFitResults.
Definition: Track.h:25
void setTrackFitResultIndex(const Const::ChargedStable &chargedStable, short index)
Set an index (for positive values) or unavailability-code (index = -1) for a specific mass hypothesis...
Definition: Track.h:174
The Unit class.
Definition: Unit.h:40
A template class to apply the reference frame.
Object holding information for V0s.
Definition: V0.h:34
TEST_F(GlobalLabelTest, LargeNumberOfTimeDependentParameters)
Test large number of time-dep params for registration and retrieval.
Definition: globalLabel.cc:72
TEST(TestgetDetectorRegion, TestgetDetectorRegion)
Test Constructors.
double sqrt(double a)
sqrt for double
Definition: beamHelpers.h:28
GraphParticle & addParticle()
Add new particle to the graph.
double charge(int pdgCode)
Returns electric charge of a particle with given pdg code.
Definition: EvtPDLUtil.cc:44
Abstract base class for different kinds of events.
A variable returning a floating-point value for a given Particle.
Definition: Manager.h:146
FunctionPtr function
Pointer to function.
Definition: Manager.h:147