Belle II Software  release-08-01-10
clusteringVariables.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 // Unit tests for all variables related to clustering and clustering
10 // subdetectors (KLM and ECL variables, track <--> cluster matching etc)
11 
12 #include <gtest/gtest.h>
13 #include <TRandom3.h>
14 #include <Math/Cartesian2D.h>
15 
16 #include <analysis/ParticleCombiner/ParticleCombiner.h>
17 
18 // VariableManager and particle(list)
19 #include <analysis/VariableManager/Manager.h>
20 #include <analysis/dataobjects/Particle.h>
21 #include <analysis/dataobjects/ParticleList.h>
22 
23 // mdst dataobjects
24 #include <mdst/dataobjects/ECLCluster.h>
25 #include <mdst/dataobjects/KLMCluster.h>
26 #include <mdst/dataobjects/Track.h>
27 
28 // framework - set up mock events
29 #include <framework/datastore/StoreArray.h>
30 #include <framework/datastore/StoreObjPtr.h>
31 #include <framework/utilities/TestHelpers.h>
32 #include <framework/gearbox/Const.h>
33 #include <framework/gearbox/Gearbox.h>
34 
35 using namespace Belle2;
36 using namespace Belle2::Variable;
37 
38 namespace {
39 
41  class ECLVariableTest : public ::testing::Test {
42  protected:
44  void SetUp() override
45  {
46  // setup the DataStore
48 
49  // particles (to be filled)
50  StoreArray<Particle> particles;
51  particles.registerInDataStore();
52 
53  // mock up mdst objects
54  StoreArray<Track> tracks;
55  tracks.registerInDataStore();
57  trackFits.registerInDataStore();
58  StoreArray<ECLCluster> eclclusters;
59  eclclusters.registerInDataStore();
60 
61  // tracks can be matched to clusters
62  tracks.registerRelationTo(eclclusters);
63 
64  // we're done setting up the datastore
66 
67  // add some tracks the zeroth one is not going to be matched
68  tracks.appendNew(Track());
69  const Track* t1 = tracks.appendNew(Track());
70  const Track* t2 = tracks.appendNew(Track());
71  const Track* t3 = tracks.appendNew(Track());
72  const Track* t4 = tracks.appendNew(Track());
73  tracks.appendNew(Track());
74  tracks.appendNew(Track());
75 
76  // mock up some TrackFits for them (all pions)
77  TRandom3 generator;
78  TMatrixDSym cov6(6);
79  auto CDCValue = static_cast<unsigned long long int>(0x300000000000000);
80 
81  for (int i = 0; i < tracks.getEntries(); ++i) {
82  int charge = (i % 2 == 0) ? +1 : -1;
83  ROOT::Math::Cartesian2D d(generator.Uniform(-1, 1), generator.Uniform(-1, 1));
84  ROOT::Math::Cartesian2D pt(generator.Uniform(-1, 1), generator.Uniform(-1, 1));
85  d.SetXY(d.X(), -(d.X()*pt.X()) / pt.Y());
86  ROOT::Math::XYZVector position(d.X(), d.Y(), generator.Uniform(-1, 1));
87  ROOT::Math::XYZVector momentum(pt.X(), pt.Y(), generator.Uniform(-1, 1));
88  trackFits.appendNew(position, momentum, cov6, charge, Const::pion, 0.5, 1.5, CDCValue, 16777215, 0);
89  tracks[i]->setTrackFitResultIndex(Const::pion, i);
90  }
91 
92  // add some ECL clusters
93  ECLCluster* e1 = eclclusters.appendNew(ECLCluster());
94  e1->setEnergy(0.3);
96  e1->setClusterId(1);
97  e1->setTime(1);
98  e1->setDeltaTime99(0.1);
99  e1->setConnectedRegionId(1);
100  // leave this guy with default theta and phi
101  ECLCluster* e2 = eclclusters.appendNew(ECLCluster());
102  e2->setEnergy(0.6);
103  e2->setTheta(1.0); // somewhere in the barrel
104  e2->setPhi(2.0);
105  e2->setR(148.5);
107  e2->setClusterId(2);
108  e2->setTime(2);
109  e2->setDeltaTime99(0.2);
110  e2->setConnectedRegionId(2);
111  ECLCluster* e3 = eclclusters.appendNew(ECLCluster());
112  e3->setEnergy(0.15);
113  e3->setTheta(0.2); // somewhere in the fwd endcap
114  e3->setPhi(1.5);
115  e3->setR(200.0);
118  // let's suppose this cluster could also be due to a neutral hadron. In
119  // this case, the c_neutralHadron hypothesis bit would hopefully also have
120  // been set by the reconstruction... arbitrarily choose cluster 3
121  e3->setClusterId(3);
122  e3->setTime(3);
123  e3->setDeltaTime99(0.3);
124  e3->setConnectedRegionId(1); // shares the connected region with cluster 1
125 
126  // aaand add clusters related to the tracks
127  ECLCluster* e4 = eclclusters.appendNew(ECLCluster());
128  e4->setEnergy(0.2);
130  e4->setClusterId(4);
131  t1->addRelationTo(e4);
132  e4->setIsTrack(true);
133 
134  ECLCluster* e5 = eclclusters.appendNew(ECLCluster());
135  e5->setEnergy(0.3);
137  e5->setClusterId(5);
138  t2->addRelationTo(e5);
139  e5->setIsTrack(true);
140 
141  ECLCluster* e6 = eclclusters.appendNew(ECLCluster());
142  e6->setEnergy(0.2);
144  e6->setClusterId(6);
145  t3->addRelationTo(e6);
146  t4->addRelationTo(e6);
147  // two tracks are related to this cluster this can happen due to real
148  // physics and we should be able to cope
149  e6->setIsTrack(true);
150 
151  }
152 
154  void TearDown() override
155  {
157  }
158  };
159 
160  TEST_F(ECLVariableTest, b2bKinematicsTest)
161  {
162  // we need the particles and ECLClusters arrays
163  StoreArray<Particle> particles;
164  StoreArray<ECLCluster> eclclusters;
165  StoreArray<Track> tracks;
166 
167  // connect gearbox for CMS boosting etc
168  Gearbox& gearbox = Gearbox::getInstance();
169  gearbox.setBackends({std::string("file:")});
170  gearbox.close();
171  gearbox.open("geometry/Belle2.xml", false);
172 
173  // register in the datastore
174  StoreObjPtr<ParticleList> gammalist("gamma:testGammaAllList");
176  gammalist.registerInDataStore(DataStore::c_DontWriteOut);
178 
179  // initialise the lists
180  gammalist.create();
181  gammalist->initialize(22, gammalist.getName());
182 
183  // make the photons from clusters
184  for (int i = 0; i < eclclusters.getEntries(); ++i) {
185  if (!eclclusters[i]->isTrack()) {
186  const Particle* p = particles.appendNew(Particle(eclclusters[i]));
187  gammalist->addParticle(p);
188  }
189  }
190 
191  // get the zeroth track in the array (is not associated to a cluster)
192  const Particle* noclustertrack = particles.appendNew(Particle(tracks[0], Const::pion));
193 
194  // grab variables for testing
195  const Manager::Var* b2bClusterTheta = Manager::Instance().getVariable("b2bClusterTheta");
196  const Manager::Var* b2bClusterPhi = Manager::Instance().getVariable("b2bClusterPhi");
197 
198  EXPECT_EQ(gammalist->getListSize(), 3);
199 
200  EXPECT_FLOAT_EQ(std::get<double>(b2bClusterTheta->function(gammalist->getParticle(0))), 3.0276554);
201  EXPECT_FLOAT_EQ(std::get<double>(b2bClusterPhi->function(gammalist->getParticle(0))), 0.0);
202  EXPECT_FLOAT_EQ(std::get<double>(b2bClusterTheta->function(gammalist->getParticle(1))), 1.6035342);
203  EXPECT_FLOAT_EQ(std::get<double>(b2bClusterPhi->function(gammalist->getParticle(1))), -1.0607308);
204  EXPECT_FLOAT_EQ(std::get<double>(b2bClusterTheta->function(gammalist->getParticle(2))), 2.7839828);
205  EXPECT_FLOAT_EQ(std::get<double>(b2bClusterPhi->function(gammalist->getParticle(2))), -1.3155545);
206 
207  // track (or anything without a cluster) should be nan
208  ASSERT_TRUE(std::isnan(std::get<double>(b2bClusterTheta->function(noclustertrack))));
209  ASSERT_TRUE(std::isnan(std::get<double>(b2bClusterPhi->function(noclustertrack))));
210 
211  // the "normal" (not cluster based) variables should be the same for photons
212  // (who have no track information)
213  const Manager::Var* b2bTheta = Manager::Instance().getVariable("b2bTheta");
214  const Manager::Var* b2bPhi = Manager::Instance().getVariable("b2bPhi");
215 
216  EXPECT_FLOAT_EQ(std::get<double>(b2bClusterTheta->function(gammalist->getParticle(0))),
217  std::get<double>(b2bTheta->function(gammalist->getParticle(0))));
218  EXPECT_FLOAT_EQ(std::get<double>(b2bClusterPhi->function(gammalist->getParticle(0))),
219  std::get<double>(b2bPhi->function(gammalist->getParticle(0))));
220  }
221 
222  TEST_F(ECLVariableTest, clusterKinematicsTest)
223  {
224  // we need the particles and ECLClusters arrays
225  StoreArray<Particle> particles;
226  StoreArray<ECLCluster> eclclusters;
227  StoreArray<Track> tracks;
228 
229  // connect gearbox for CMS boosting etc
230  Gearbox& gearbox = Gearbox::getInstance();
231  gearbox.setBackends({std::string("file:")});
232  gearbox.close();
233  gearbox.open("geometry/Belle2.xml", false);
234 
235  // register in the datastore
236  StoreObjPtr<ParticleList> gammalist("gamma:testGammaAllList");
238  gammalist.registerInDataStore(DataStore::c_DontWriteOut);
240 
241  // initialise the lists
242  gammalist.create();
243  gammalist->initialize(22, gammalist.getName());
244 
245  // make the photons from clusters
246  for (int i = 0; i < eclclusters.getEntries(); ++i) {
247  if (!eclclusters[i]->isTrack()) {
248  const Particle* p = particles.appendNew(Particle(eclclusters[i]));
249  gammalist->addParticle(p);
250  }
251  }
252 
253  // grab variables for testing
254  const Manager::Var* clusterPhi = Manager::Instance().getVariable("clusterPhi");
255  const Manager::Var* clusterPhiCMS = Manager::Instance().getVariable("useCMSFrame(clusterPhi)");
256  const Manager::Var* clusterTheta = Manager::Instance().getVariable("clusterTheta");
257  const Manager::Var* clusterThetaCMS = Manager::Instance().getVariable("useCMSFrame(clusterTheta)");
258 
259  EXPECT_FLOAT_EQ(std::get<double>(clusterPhi->function(gammalist->getParticle(1))), 2.0);
260  EXPECT_FLOAT_EQ(std::get<double>(clusterPhiCMS->function(gammalist->getParticle(1))), 2.0442522);
261  EXPECT_FLOAT_EQ(std::get<double>(clusterTheta->function(gammalist->getParticle(1))), 1.0);
262  EXPECT_FLOAT_EQ(std::get<double>(clusterThetaCMS->function(gammalist->getParticle(1))), 1.2625608);
263 
264  // test cluster quantities directly (lab system only)
265  EXPECT_FLOAT_EQ(std::get<double>(clusterPhi->function(gammalist->getParticle(0))), eclclusters[0]->getPhi());
266  EXPECT_FLOAT_EQ(std::get<double>(clusterTheta->function(gammalist->getParticle(0))), eclclusters[0]->getTheta());
267  }
268 
269  TEST_F(ECLVariableTest, HypothesisVariables)
270  {
271  // we need the particles and ECLClusters arrays
272  StoreArray<Particle> particles;
273  StoreArray<ECLCluster> eclclusters;
274 
275  // register in the datastore
276  StoreObjPtr<ParticleList> gammalist("gamma");
278  gammalist.registerInDataStore(DataStore::c_DontWriteOut);
280 
281  // initialise the lists
282  gammalist.create();
283  gammalist->initialize(22, gammalist.getName());
284 
285  // make the photons from clusters
286  for (int i = 0; i < eclclusters.getEntries(); ++i)
287  if (!eclclusters[i]->isTrack()) {
288  const Particle* p = particles.appendNew(Particle(eclclusters[i]));
289  gammalist->addParticle(p);
290  }
291 
292  // grab variables for testing
293  const Manager::Var* vHasNPhotons = Manager::Instance().getVariable("clusterHasNPhotons");
294  const Manager::Var* vHasNeutHadr = Manager::Instance().getVariable("clusterHasNeutralHadron");
295 
296  // check that the hypotheses are correctly propagated to the VM.
297  for (size_t i = 0; i < gammalist->getListSize(); ++i) {
298  EXPECT_FLOAT_EQ(std::get<double>(vHasNPhotons->function(gammalist->getParticle(i))), 1.0);
299  if (i == 2) { // third cluster arbitrarily chosen to test the behaviour of dual hypothesis clusters
300  EXPECT_FLOAT_EQ(std::get<double>(vHasNeutHadr->function(gammalist->getParticle(i))), 1.0);
301  } else {
302  EXPECT_FLOAT_EQ(std::get<double>(vHasNeutHadr->function(gammalist->getParticle(i))), 0.0);
303  }
304  } // end loop over test list
305  }
306 
307  TEST_F(ECLVariableTest, IsFromECL)
308  {
309  StoreArray<Particle> particles;
310  StoreArray<ECLCluster> eclclusters;
311 
312  const Manager::Var* vIsFromECL = Manager::Instance().getVariable("isFromECL");
313  const Manager::Var* vIsFromKLM = Manager::Instance().getVariable("isFromKLM");
314  const Manager::Var* vIsFromTrack = Manager::Instance().getVariable("isFromTrack");
315  const Manager::Var* vIsFromV0 = Manager::Instance().getVariable("isFromV0");
316 
317  for (int i = 0; i < eclclusters.getEntries(); ++i)
318  if (!eclclusters[i]->isTrack()) {
319  const Particle* p = particles.appendNew(Particle(eclclusters[i]));
320  EXPECT_TRUE(std::get<bool>(vIsFromECL->function(p)));
321  EXPECT_FALSE(std::get<bool>(vIsFromKLM->function(p)));
322  EXPECT_FALSE(std::get<bool>(vIsFromTrack->function(p)));
323  EXPECT_FALSE(std::get<bool>(vIsFromV0->function(p)));
324  }
325  }
326 
327  TEST_F(ECLVariableTest, ECLThetaAndPhiId)
328  {
329  StoreArray<Particle> particles;
330  StoreArray<ECLCluster> clusters{};
331  StoreArray<ECLCluster> eclclusters;
332  // make a particle from cluster #1
333  const Particle* p = particles.appendNew(Particle(eclclusters[0]));
334 
335  // get the variables to test
336  const Manager::Var* clusterThetaID = Manager::Instance().getVariable("clusterThetaID");
337  const Manager::Var* clusterPhiID = Manager::Instance().getVariable("clusterPhiID");
338 
339  {
340  clusters[0]->setMaxECellId(1);
341  EXPECT_FLOAT_EQ(std::get<double>(clusterThetaID->function(p)), 0);
342  EXPECT_FLOAT_EQ(std::get<double>(clusterPhiID->function(p)), 0);
343  }
344  {
345  clusters[0]->setMaxECellId(6903);
346  EXPECT_FLOAT_EQ(std::get<double>(clusterThetaID->function(p)), 52);
347  EXPECT_FLOAT_EQ(std::get<double>(clusterPhiID->function(p)), 134);
348  }
349  {
350  clusters[0]->setMaxECellId(8457);
351  EXPECT_FLOAT_EQ(std::get<double>(clusterThetaID->function(p)), 65);
352  EXPECT_FLOAT_EQ(std::get<double>(clusterPhiID->function(p)), 8);
353  }
354  }
355 
356 
357  TEST_F(ECLVariableTest, WholeEventClosure)
358  {
359  // we need the particles, tracks, and ECLClusters StoreArrays
360  StoreArray<Particle> particles;
361  StoreArray<Track> tracks;
362  StoreArray<ECLCluster> eclclusters;
363 
364  // create a photon (clusters) and pion (tracks) lists
365  StoreObjPtr<ParticleList> gammalist("gamma:testGammaAllList");
366  StoreObjPtr<ParticleList> pionslist("pi+:testPionAllList");
367  StoreObjPtr<ParticleList> apionslist("pi-:testPionAllList");
368 
369  // register the lists in the datastore
371  gammalist.registerInDataStore(DataStore::c_DontWriteOut);
372  pionslist.registerInDataStore(DataStore::c_DontWriteOut);
373  apionslist.registerInDataStore(DataStore::c_DontWriteOut);
375 
376  // initialise the lists
377  gammalist.create();
378  gammalist->initialize(22, gammalist.getName());
379  pionslist.create();
380  pionslist->initialize(211, pionslist.getName());
381  apionslist.create();
382  apionslist->initialize(-211, apionslist.getName());
383  apionslist->bindAntiParticleList(*(pionslist));
384 
385  // make the photons from clusters (and sum up the total ecl energy)
386  double eclEnergy = 0.0;
387  for (int i = 0; i < eclclusters.getEntries(); ++i) {
388  eclEnergy += eclclusters[i]->getEnergy(ECLCluster::EHypothesisBit::c_nPhotons);
389  if (!eclclusters[i]->isTrack()) {
390  const Particle* p = particles.appendNew(Particle(eclclusters[i]));
391  gammalist->addParticle(p);
392  }
393  }
394 
395 
396  // make the pions from tracks
397  for (int i = 0; i < tracks.getEntries(); ++i) {
398  const Particle* p = particles.appendNew(Particle(tracks[i], Const::pion));
399  pionslist->addParticle(p);
400  }
401 
402  // grab variables
403  const Manager::Var* vClusterE = Manager::Instance().getVariable("clusterE");
404  const Manager::Var* vClNTrack = Manager::Instance().getVariable("nECLClusterTrackMatches");
405 
406  // calculate the total neutral energy from the particle list --> VM
407  double totalNeutralClusterE = 0.0;
408  for (size_t i = 0; i < gammalist->getListSize(); ++i)
409  totalNeutralClusterE += std::get<double>(vClusterE->function(gammalist->getParticle(i)));
410 
411  // calculate the total track-matched cluster energy from the particle list --> VM
412  double totalTrackClusterE = 0.0;
413  for (size_t i = 0; i < pionslist->getListSize(); ++i) { // includes antiparticles
414  double clusterE = std::get<double>(vClusterE->function(pionslist->getParticle(i)));
415  double nOtherCl = std::get<double>(vClNTrack->function(pionslist->getParticle(i)));
416  if (nOtherCl > 0)
417  totalTrackClusterE += clusterE / nOtherCl;
418  }
419 
420  EXPECT_FLOAT_EQ(totalNeutralClusterE + totalTrackClusterE, eclEnergy);
421  }
422 
423  TEST_F(ECLVariableTest, eclClusterOnlyInvariantMass)
424  {
425  // declare all the array we need
426  StoreArray<Particle> particles;
427  std::vector<int> daughterIndices, daughterIndices_noclst;
428 
429  //proxy initialize where to declare the needed array
431  StoreArray<ECLCluster> eclclusters_new;
432  eclclusters_new.registerInDataStore();
433  particles.registerRelationTo(eclclusters_new);
435 
436  // create two Lorentz vectors
437  const float px_0 = 2.;
438  const float py_0 = 1.;
439  const float pz_0 = 3.;
440  const float px_1 = 1.5;
441  const float py_1 = 1.5;
442  const float pz_1 = 2.5;
443  float E_0, E_1;
444  E_0 = sqrt(pow(px_0, 2) + pow(py_0, 2) + pow(pz_0, 2));
445  E_1 = sqrt(pow(px_1, 2) + pow(py_1, 2) + pow(pz_1, 2));
446  ROOT::Math::PxPyPzEVector momentum;
447  ROOT::Math::PxPyPzEVector dau0_4vec(px_0, py_0, pz_0, E_0), dau1_4vec(px_1, py_1, pz_1, E_1);
448 
449  // add the two photons as the two daughters of some particle and create the latter
450  Particle dau0_noclst(dau0_4vec, 22);
451  momentum += dau0_noclst.get4Vector();
452  Particle* newDaughter0_noclst = particles.appendNew(dau0_noclst);
453  daughterIndices_noclst.push_back(newDaughter0_noclst->getArrayIndex());
454  Particle dau1_noclst(dau1_4vec, 22);
455  momentum += dau1_noclst.get4Vector();
456  Particle* newDaughter1_noclst = particles.appendNew(dau1_noclst);
457  daughterIndices_noclst.push_back(newDaughter1_noclst->getArrayIndex());
458  const Particle* par_noclst = particles.appendNew(momentum, 111, Particle::c_Unflavored, daughterIndices_noclst);
459 
460  // grab variables
461  const Manager::Var* var = Manager::Instance().getVariable("eclClusterOnlyInvariantMass");
462 
463  // when no relations are set between the particles and the eclClusters, nan is expected to be returned
464  ASSERT_NE(var, nullptr);
465  EXPECT_TRUE(std::isnan(std::get<double>(var->function(par_noclst))));
466 
467  // set relations between particles and eclClusters
468  ECLCluster* eclst0 = eclclusters_new.appendNew(ECLCluster());
469  eclst0->setEnergy(dau0_4vec.E());
471  eclst0->setClusterId(1);
472  eclst0->setTheta(dau0_4vec.Theta());
473  eclst0->setPhi(dau0_4vec.Phi());
474  eclst0->setR(148.4);
475  ECLCluster* eclst1 = eclclusters_new.appendNew(ECLCluster());
476  eclst1->setEnergy(dau1_4vec.E());
478  eclst1->setClusterId(2);
479  eclst1->setTheta(dau1_4vec.Theta());
480  eclst1->setPhi(dau1_4vec.Phi());
481  eclst1->setR(148.5);
482 
483  // use these new-created clusters rather than the 6 default ones
484  const Particle* newDaughter0 = particles.appendNew(Particle(eclclusters_new[6]));
485  daughterIndices.push_back(newDaughter0->getArrayIndex());
486  const Particle* newDaughter1 = particles.appendNew(Particle(eclclusters_new[7]));
487  daughterIndices.push_back(newDaughter1->getArrayIndex());
488 
489  const Particle* par = particles.appendNew(momentum, 111, Particle::c_Unflavored, daughterIndices);
490 
491  //now we expect non-nan results
492  EXPECT_FLOAT_EQ(std::get<double>(var->function(par)), 0.73190731);
493  }
494 
495  TEST_F(ECLVariableTest, averageECLTimeQuantities)
496  {
497  // we need the particles, and ECLClusters StoreArrays
498  StoreArray<Particle> particles;
499  StoreArray<ECLCluster> eclclusters;
500 
501  // create a photon (clusters) and a pi0 list
502  StoreObjPtr<ParticleList> gammalist("gamma:testGammaAllList");
503  StoreObjPtr<ParticleList> pionslist("pi0:testPizAllList");
504 
505  // register the lists in the datastore
507  gammalist.registerInDataStore(DataStore::c_DontWriteOut);
508  pionslist.registerInDataStore(DataStore::c_DontWriteOut);
510 
511  // initialise the lists
512  gammalist.create();
513  gammalist->initialize(22, gammalist.getName());
514  pionslist.create();
515  pionslist->initialize(111, pionslist.getName());
516 
517  // make the photons from clusters
518  for (int i = 0; i < eclclusters.getEntries(); ++i) {
519  if (!eclclusters[i]->isTrack()) {
520  const Particle* p = particles.appendNew(Particle(eclclusters[i]));
521  gammalist->addParticle(p);
522  }
523  }
524 
525  // make the pi0s as combinations of photons
526  ParticleGenerator combiner_pi0("pi0:testPizAllList -> gamma:testGammaAllList gamma:testGammaAllList");
527  combiner_pi0.init();
528  while (combiner_pi0.loadNext()) {
529  const Particle& particle = combiner_pi0.getCurrentParticle();
530 
531  particles.appendNew(particle);
532  int iparticle = particles.getEntries() - 1;
533 
534  pionslist->addParticle(iparticle, particle.getPDGCode(), particle.getFlavorType());
535  }
536 
537  // grab variables
538  const Manager::Var* weightedAverageECLTime = Manager::Instance().getVariable("weightedAverageECLTime");
539  const Manager::Var* maxDist = Manager::Instance().getVariable("maxWeightedDistanceFromAverageECLTime");
540 
541  // particles without daughters should have NaN as weighted average
542  EXPECT_TRUE(std::isnan(std::get<double>(weightedAverageECLTime->function(gammalist->getParticle(0)))));
543 
544  // check that weighted average of first pi0 is correct
545  EXPECT_FLOAT_EQ(std::get<double>(weightedAverageECLTime->function(pionslist->getParticle(0))), 1.2);
546 
547  // check that maximal difference to weighted average in units of uncertainty is calculated correctly
548  EXPECT_FLOAT_EQ(std::get<double>(maxDist->function(pionslist->getParticle(0))), 4.0);
549  }
550 
551  TEST_F(ECLVariableTest, photonHasOverlap)
552  {
553  // declare StoreArrays of Particles and ECLClusters
554  StoreArray<Particle> particles;
555  StoreArray<ECLCluster> eclclusters;
556  StoreArray<Track> tracks;
557 
558  // create a photon and the pion lists
559  StoreObjPtr<ParticleList> gammalist("gamma:all");
560  StoreObjPtr<ParticleList> pionlist("pi+:all");
561  StoreObjPtr<ParticleList> piminuslist("pi-:all");
562 
563  // register the particle lists in the datastore
565  gammalist.registerInDataStore(DataStore::c_DontWriteOut);
566  pionlist.registerInDataStore(DataStore::c_DontWriteOut);
567  piminuslist.registerInDataStore(DataStore::c_DontWriteOut);
569 
570  // initialise the photon list
571  gammalist.create();
572  gammalist->initialize(22, gammalist.getName());
573  gammalist->setEditable(true);
574 
575  // make the photons from clusters
576  for (int i = 0; i < eclclusters.getEntries(); ++i) {
577  if (!eclclusters[i]->isTrack()) {
578  const Particle* p = particles.appendNew(Particle(eclclusters[i]));
579  gammalist->addParticle(p);
580  }
581  }
582  gammalist->setEditable(false);
583 
584  // initialize the pion lists
585  pionlist.create();
586  pionlist->initialize(211, pionlist.getName());
587  piminuslist.create();
588  piminuslist->initialize(-211, piminuslist.getName());
589  piminuslist->bindAntiParticleList(*(pionlist));
590  pionlist->setEditable(true);
591 
592  // fill the pion list with the tracks
593  for (int i = 0; i < tracks.getEntries(); ++i) {
594  const Particle* p = particles.appendNew(Particle(tracks[i], Const::pion));
595  pionlist->addParticle(p);
596  }
597  pionlist->setEditable(false);
598 
599  // check overlap without any arguments
600  const Manager::Var* photonHasOverlapNoArgs = Manager::Instance().getVariable("photonHasOverlap()");
601  // the track list e-:all is missing so NaN is returned
602  EXPECT_TRUE(std::isnan(std::get<double>(photonHasOverlapNoArgs->function(particles[0]))));
603 
604  // check overlap without any requirement on other photons
605  const Manager::Var* photonHasOverlapAll = Manager::Instance().getVariable("photonHasOverlap(, gamma:all, pi+:all)");
606  // cluster 3 and cluster 1 share the connected region
607  EXPECT_TRUE(std::get<double>(photonHasOverlapAll->function(particles[0])));
608  // photonHasOverlap is designed for photons, so calling it for a pion returns NaN
609  EXPECT_TRUE(std::isnan(std::get<double>(photonHasOverlapAll->function(particles[3]))));
610 
611  // check overlap with photons in barrel
612  const Manager::Var* photonHasOverlapBarrel = Manager::Instance().getVariable("photonHasOverlap(clusterReg==2, gamma:all, pi+:all)");
613  // cluster 3 is in the forward end cap so it doesn't matter that it has the same connected region like cluster 1
614  EXPECT_FALSE(std::get<double>(photonHasOverlapBarrel->function(particles[0])));
615  }
616 
617  class KLMVariableTest : public ::testing::Test {
618  protected:
620  void SetUp() override
621  {
622  // setup the DataStore
624 
625  // particles (to be filled)
626  StoreArray<Particle> particles;
627  particles.registerInDataStore();
628 
629  // mock up mdst objects
630  StoreArray<Track> tracks;
631  tracks.registerInDataStore();
632  StoreArray<TrackFitResult> trackFits;
633  trackFits.registerInDataStore();
634  StoreArray<KLMCluster> klmClusters;
635  klmClusters.registerInDataStore();
636 
637  // tracks can be matched to clusters
638  tracks.registerRelationTo(klmClusters);
639 
640  // we're done setting up the datastore
642  }
643 
645  void TearDown() override
646  {
648  }
649  };
650 
651  TEST_F(KLMVariableTest, WholeEventClosure)
652  {
653  // we need the Particles, Tracks, TrackFitResults and KLMClusters StoreArrays
654  StoreArray<Particle> particles;
655  StoreArray<Track> tracks;
656  StoreArray<TrackFitResult> trackFits;
657  StoreArray<KLMCluster> klmClusters;
658 
659  // create a KLong (clusters) and muon (tracks) lists
660  StoreObjPtr<ParticleList> kLongList("K0_L:testKLong");
661  StoreObjPtr<ParticleList> muonsList("mu-:testMuons");
662  StoreObjPtr<ParticleList> amuonsList("mu+:testMuons");
663 
664  // register the lists in the datastore
666  kLongList.registerInDataStore(DataStore::c_DontWriteOut);
667  muonsList.registerInDataStore(DataStore::c_DontWriteOut);
668  amuonsList.registerInDataStore(DataStore::c_DontWriteOut);
670 
671  // initialise the lists
672  kLongList.create();
673  kLongList->initialize(Const::Klong.getPDGCode(), kLongList.getName());
674  muonsList.create();
675  muonsList->initialize(Const::muon.getPDGCode(), muonsList.getName());
676  amuonsList.create();
677  amuonsList->initialize(-Const::muon.getPDGCode(), amuonsList.getName());
678  amuonsList->bindAntiParticleList(*(muonsList));
679 
680  // add some tracks
681  const Track* t1 = tracks.appendNew(Track());
682  const Track* t2 = tracks.appendNew(Track());
683  const Track* t3 = tracks.appendNew(Track());
684  tracks.appendNew(Track());
685  tracks.appendNew(Track());
686 
687  // mock up some TrackFits for them (all muons)
688  TRandom3 generator;
689  TMatrixDSym cov6(6);
690  auto CDCValue = static_cast<unsigned long long int>(0x300000000000000);
691 
692  for (int i = 0; i < tracks.getEntries(); ++i) {
693  int charge = (i % 2 == 0) ? +1 : -1;
694  ROOT::Math::Cartesian2D d(generator.Uniform(-1, 1), generator.Uniform(-1, 1));
695  ROOT::Math::Cartesian2D pt(generator.Uniform(-1, 1), generator.Uniform(-1, 1));
696  d.SetXY(d.X(), -(d.X()*pt.X()) / pt.Y());
697  ROOT::Math::XYZVector position(d.X(), d.Y(), generator.Uniform(-1, 1));
698  ROOT::Math::XYZVector momentum(pt.X(), pt.Y(), generator.Uniform(-1, 1));
699  trackFits.appendNew(position, momentum, cov6, charge, Const::muon, 0.5, 1.5, CDCValue, 16777215, 0);
700  tracks[i]->setTrackFitResultIndex(Const::muon, i);
701  }
702 
703  // add some clusters
704  KLMCluster* klm1 = klmClusters.appendNew(KLMCluster());
705  klm1->setTime(1.1);
706  klm1->setClusterPosition(1.1, 1.1, 1.0);
707  klm1->setLayers(1);
708  klm1->setInnermostLayer(1);
709  klm1->setMomentumMag(1.0);
710  KLMCluster* klm2 = klmClusters.appendNew(KLMCluster());
711  klm2->setTime(1.2);
712  klm2->setClusterPosition(1.2, 1.2, 2.0);
713  klm2->setLayers(2);
714  klm2->setInnermostLayer(2);
715  klm2->setMomentumMag(1.0);
716  KLMCluster* klm3 = klmClusters.appendNew(KLMCluster());
717  klm3->setTime(1.3);
718  klm3->setClusterPosition(1.3, 1.3, 3.0);
719  klm3->setLayers(3);
720  klm3->setInnermostLayer(3);
721  klm3->setMomentumMag(1.0);
722 
723  // and add clusters related to the tracks
724  // case 1: 1 track --> 1 cluster
725  KLMCluster* klm4 = klmClusters.appendNew(KLMCluster());
726  klm4->setTime(1.4);
727  klm4->setClusterPosition(-1.4, -1.4, 1.0);
728  klm4->setLayers(4);
729  klm4->setInnermostLayer(4);
730  klm4->setMomentumMag(1.0);
731  t1->addRelationTo(klm4);
732 
733  // case 2: 2 tracks --> 1 cluster
734  KLMCluster* klm5 = klmClusters.appendNew(KLMCluster());
735  klm5->setTime(1.5);
736  klm5->setClusterPosition(-1.5, -1.5, 1.0);
737  klm5->setLayers(5);
738  klm5->setInnermostLayer(5);
739  klm5->setMomentumMag(1.0);
740  t2->addRelationTo(klm5);
741  t3->addRelationTo(klm5);
742 
743  // case 3: 1 track --> 2 clusters
744  // not possible
745 
746  // make the KLong from clusters (and sum up the total KLM momentum magnitude)
747  double klmMomentum = 0.0;
748  for (int i = 0; i < klmClusters.getEntries(); ++i) {
749  klmMomentum += klmClusters[i]->getMomentumMag();
750  if (!klmClusters[i]->getAssociatedTrackFlag()) {
751  const Particle* p = particles.appendNew(Particle(klmClusters[i]));
752  kLongList->addParticle(p);
753  }
754  }
755 
756  // make the muons from tracks
757  for (int i = 0; i < tracks.getEntries(); ++i) {
758  const Particle* p = particles.appendNew(Particle(tracks[i], Const::muon));
759  muonsList->addParticle(p);
760  }
761 
762  // grab variables
763  const Manager::Var* vClusterP = Manager::Instance().getVariable("klmClusterMomentum");
764  const Manager::Var* vClNTrack = Manager::Instance().getVariable("nKLMClusterTrackMatches");
765 
766  // calculate the total KLM momentum from the KLong list --> VM
767  double totalKLongMomentum = 0.0;
768  for (size_t i = 0; i < kLongList->getListSize(); ++i)
769  totalKLongMomentum += std::get<double>(vClusterP->function(kLongList->getParticle(i)));
770 
771  // calculate the total KLM momentum from muon-matched list --> VM
772  double totalMuonMomentum = 0.0;
773  for (size_t i = 0; i < muonsList->getListSize(); ++i) { // includes antiparticles
774  double muonMomentum = std::get<double>(vClusterP->function(muonsList->getParticle(i)));
775  double nOtherCl = std::get<double>(vClNTrack->function(muonsList->getParticle(i)));
776  if (nOtherCl > 0)
777  totalMuonMomentum += muonMomentum / nOtherCl;
778  }
779 
780  EXPECT_FLOAT_EQ(5.0, klmMomentum);
781  EXPECT_FLOAT_EQ(totalKLongMomentum + totalMuonMomentum, klmMomentum);
782  }
783 
784  TEST_F(KLMVariableTest, TrackToKLMClusterMatchingTest)
785  {
786  StoreArray<Particle> particles;
787  StoreArray<Track> tracks;
788  StoreArray<TrackFitResult> trackFits;
789  StoreArray<KLMCluster> klmClusters;
790 
791  // add a TrackFitResult
792  ROOT::Math::XYZVector position(1.0, 0, 0);
793  ROOT::Math::XYZVector momentum(0, 1.0, 0);
794  TMatrixDSym cov6(6);
795  const int charge = 1;
796  const float pValue = 0.5;
797  const float bField = 1.5;
798  auto CDCValue = static_cast<unsigned long long int>(0x300000000000000);
799  trackFits.appendNew(position, momentum, cov6, charge, Const::muon, pValue, bField, CDCValue, 16777215, 0);
800 
801  // add one Track
802  Track myTrack;
804  Track* muonTrack = tracks.appendNew(myTrack);
805 
806  // add two KLMClusters
807  KLMCluster* klm1 = klmClusters.appendNew(KLMCluster());
808  klm1->setTime(1.1);
809  klm1->setClusterPosition(1.1, 1.1, 1.0);
810  klm1->setLayers(1);
811  klm1->setInnermostLayer(1);
812  klm1->setMomentumMag(1.0);
813  KLMCluster* klm2 = klmClusters.appendNew(KLMCluster());
814  klm2->setTime(1.2);
815  klm2->setClusterPosition(1.2, 1.2, 2.0);
816  klm2->setLayers(2);
817  klm2->setInnermostLayer(2);
818  klm2->setMomentumMag(1.0);
819 
820  // and add a weighted relationship between the track and both clusters
821  // only the relation with klm1 must be returned
822  // in reconstruction we set a relation to only one cluster (if any),
823  // so here we test that getKLMCluster() returns the first cluster
824  // stored in the RelationVector
825  float distance1 = 11.1;
826  muonTrack->addRelationTo(klm1, 1. / distance1);
827  float distance2 = 2.2;
828  muonTrack->addRelationTo(klm2, 1. / distance2);
829 
830  // add a Particle
831  const Particle* muon = particles.appendNew(Particle(muonTrack, Const::muon));
832 
833  // grab variables
834  const Manager::Var* vTrNClusters = Manager::Instance().getVariable("nMatchedKLMClusters");
835  const Manager::Var* vClusterInnermostLayer = Manager::Instance().getVariable("klmClusterInnermostLayer");
836  const Manager::Var* vClusterTrackDistance = Manager::Instance().getVariable("klmClusterTrackDistance");
837 
838  EXPECT_POSITIVE(std::get<double>(vTrNClusters->function(muon)));
839  EXPECT_FLOAT_EQ(1.0, std::get<double>(vClusterInnermostLayer->function(muon)));
840  EXPECT_FLOAT_EQ(distance1, std::get<double>(vClusterTrackDistance->function(muon)));
841 
842  // add a Pion - no clusters matched here
843  trackFits.appendNew(position, momentum, cov6, charge, Const::pion, pValue, bField, CDCValue, 16777215, 0);
844  Track mySecondTrack;
845  mySecondTrack.setTrackFitResultIndex(Const::pion, 0);
846  Track* pionTrack = tracks.appendNew(mySecondTrack);
847  const Particle* pion = particles.appendNew(Particle(pionTrack, Const::pion));
848 
849  EXPECT_FLOAT_EQ(0.0, std::get<double>(vTrNClusters->function(pion)));
850  }
851 }
static const ChargedStable muon
muon particle
Definition: Const.h:651
static const ChargedStable pion
charged pion particle
Definition: Const.h:652
static const ParticleType Klong
K^0_L particle.
Definition: Const.h:669
@ c_DontWriteOut
Object/array should be NOT saved by output modules.
Definition: DataStore.h:71
static DataStore & Instance()
Instance of singleton Store.
Definition: DataStore.cc:54
void setInitializeActive(bool active)
Setter for m_initializeActive.
Definition: DataStore.cc:94
void reset(EDurability durability)
Frees memory occupied by data store items and removes all objects from the map.
Definition: DataStore.cc:86
ECL cluster data.
Definition: ECLCluster.h:27
void addHypothesis(EHypothesisBit bitmask)
Add bitmask to current hypothesis.
Definition: ECLCluster.h:129
void setTheta(double theta)
Set Theta of Shower (radian).
Definition: ECLCluster.h:217
void setConnectedRegionId(int crid)
Set connected region id.
Definition: ECLCluster.h:142
void setPhi(double phi)
Set Phi of Shower (radian).
Definition: ECLCluster.h:220
void setTime(double time)
Set time information.
Definition: ECLCluster.h:211
void setDeltaTime99(double dtime99)
Set 99% time containment range.
Definition: ECLCluster.h:214
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 setIsTrack(bool istrack)
Set m_isTrack true if the cluster matches with a track.
Definition: ECLCluster.h:104
@ c_nPhotons
CR is split into n photons (N1)
@ c_neutralHadron
CR is reconstructed as a neutral hadron (N2)
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
KLM cluster data.
Definition: KLMCluster.h:28
void setLayers(int layers)
Set number of layers with hits.
Definition: KLMCluster.h:145
void setClusterPosition(float globalX, float globalY, float globalZ)
Set global position.
Definition: KLMCluster.h:161
void setTime(float time)
Set time.
Definition: KLMCluster.h:138
void setInnermostLayer(int innermostLayer)
Set number of the innermost layer with hits.
Definition: KLMCluster.h:152
void setMomentumMag(float momentumMag)
Set momentum magnitude.
Definition: KLMCluster.h:172
ParticleGenerator is a generator for all the particles combined from the given ParticleLists.
Class to store reconstructed particles.
Definition: Particle.h:75
@ c_Unflavored
Is its own antiparticle or we don't know whether it is a particle/antiparticle.
Definition: Particle.h:95
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.
bool registerInDataStore(DataStore::EStoreFlags storeFlags=DataStore::c_WriteOut)
Register the object/array in the DataStore.
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
int getEntries() const
Get the number of objects in the array.
Definition: StoreArray.h:216
Type-safe access to single objects in the data store.
Definition: StoreObjPtr.h:96
Class that bundles various TrackFitResults.
Definition: Track.h:25
void setTrackFitResultIndex(const Const::ChargedStable &chargedStable, short index)
Set an index (for positive values) or unavailability-code (index = -1) for a specific mass hypothesis...
Definition: Track.h:174
const Var * getVariable(std::string name)
Get the variable belonging to the given key.
Definition: Manager.cc:57
static Manager & Instance()
get singleton instance.
Definition: Manager.cc:25
TEST_F(GlobalLabelTest, LargeNumberOfTimeDependentParameters)
Test large number of time-dep params for registration and retrieval.
Definition: globalLabel.cc:72
static Gearbox & getInstance()
Return reference to the Gearbox instance.
Definition: Gearbox.cc:81
double sqrt(double a)
sqrt for double
Definition: beamHelpers.h:28
double charge(int pdgCode)
Returns electric charge of a particle with given pdg code.
Definition: EvtPDLUtil.cc:44
Abstract base class for different kinds of events.
A variable returning a floating-point value for a given Particle.
Definition: Manager.h:146
FunctionPtr function
Pointer to function.
Definition: Manager.h:147