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