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