Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
SvtxTruthEval.cc
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file SvtxTruthEval.cc
1 #include "SvtxTruthEval.h"
2 
3 #include "BaseTruthEval.h"
4 
5 #include <g4main/PHG4Hit.h>
8 
9 #include <g4main/PHG4Particle.h>
10 
11 #include <trackbase/ActsGeometry.h>
12 #include <trackbase/InttDefs.h>
13 #include <trackbase/MvtxDefs.h>
14 #include <trackbase/TpcDefs.h>
16 #include <trackbase/TrkrDefs.h>
17 
19 
20 #include <g4detectors/PHG4CylinderGeom.h> // for PHG4CylinderGeom
24 
25 #include <mvtx/CylinderGeom_Mvtx.h>
26 
27 #include <intt/CylinderGeomIntt.h>
28 
29 #include <phool/getClass.h>
30 #include <phool/phool.h> // for PHWHERE
31 
32 #include <TVector3.h>
33 
34 #include <cassert>
35 #include <cmath> // for sqrt, NAN, fabs
36 #include <cstdlib> // for abs
37 #include <iostream>
38 #include <map>
39 #include <set>
40 #include <utility>
41 
43  : _basetrutheval(topNode)
44 {
45  get_node_pointers(topNode);
46 }
47 
49 {
50  if (_verbosity > 1)
51  {
52  if ((_errors > 0) || (_verbosity > 1))
53  {
54  std::cout << "SvtxTruthEval::~SvtxTruthEval() - Error Count: " << _errors << std::endl;
55  }
56  }
57 }
58 
60 {
61  _cache_all_truth_hits.clear();
67 
68  _basetrutheval.next_event(topNode);
69 
70  get_node_pointers(topNode);
71 }
72 
74 std::set<PHG4Hit*> SvtxTruthEval::all_truth_hits()
75 {
76  if (!has_node_pointers())
77  {
78  ++_errors;
79  return std::set<PHG4Hit*>();
80  }
81 
82  if (_do_cache)
83  {
84  if (!_cache_all_truth_hits.empty())
85  {
86  return _cache_all_truth_hits;
87  }
88  }
89 
90  // since the SVTX can be composed of two different trackers this is a
91  // handy function to spill out all the g4hits from both "detectors"
92 
93  std::set<PHG4Hit*> truth_hits;
94 
95  // loop over all the g4hits in the cylinder layers
96  if (_g4hits_svtx)
97  {
99  g4iter != _g4hits_svtx->getHits().second;
100  ++g4iter)
101  {
102  PHG4Hit* g4hit = g4iter->second;
103  truth_hits.insert(g4hit);
104  }
105  }
106 
107  // loop over all the g4hits in the ladder layers
108  if (_g4hits_tracker)
109  {
111  g4iter != _g4hits_tracker->getHits().second;
112  ++g4iter)
113  {
114  PHG4Hit* g4hit = g4iter->second;
115  truth_hits.insert(g4hit);
116  }
117  }
118 
119  // loop over all the g4hits in the maps layers
120  if (_g4hits_maps)
121  {
123  g4iter != _g4hits_maps->getHits().second;
124  ++g4iter)
125  {
126  PHG4Hit* g4hit = g4iter->second;
127  truth_hits.insert(g4hit);
128  }
129  }
130 
131  // loop over all the g4hits in the maicromegas layers
132  if (_g4hits_mms)
133  {
134  for (PHG4HitContainer::ConstIterator g4iter = _g4hits_mms->getHits().first;
135  g4iter != _g4hits_mms->getHits().second;
136  ++g4iter)
137  {
138  PHG4Hit* g4hit = g4iter->second;
139  truth_hits.insert(g4hit);
140  }
141  }
142 
143  if (_do_cache)
144  {
145  _cache_all_truth_hits = truth_hits;
146  }
147 
148  return truth_hits;
149 }
150 
152 {
153  if (!has_node_pointers())
154  {
155  ++_errors;
156  return std::set<PHG4Hit*>();
157  }
158 
159  if (_strict)
160  {
161  assert(particle);
162  }
163  else if (!particle)
164  {
165  ++_errors;
166  return std::set<PHG4Hit*>();
167  }
168  // if( _cache_all_truth_hits_g4particle.count(particle)==0){
170  {
172  }
173 
174  if (_do_cache)
175  {
176  std::map<PHG4Particle*, std::set<PHG4Hit*>>::iterator iter =
177  _cache_all_truth_hits_g4particle.find(particle);
178  if (iter != _cache_all_truth_hits_g4particle.end())
179  {
180  return iter->second;
181  }
182  }
183  // return empty if we dont find anything int he cache
184 
185  std::set<PHG4Hit*> truth_hits;
186  return truth_hits;
187 }
188 
190 {
191  std::multimap<const int, PHG4Hit*> temp_clusters_from_particles;
192  // loop over all the g4hits in the cylinder layers
193  if (_g4hits_svtx)
194  {
196  g4iter != _g4hits_svtx->getHits().second;
197  ++g4iter)
198  {
199  PHG4Hit* g4hit = g4iter->second;
200  temp_clusters_from_particles.insert(std::make_pair(g4hit->get_trkid(), g4hit));
201  }
202  }
203 
204  // loop over all the g4hits in the ladder layers
205  if (_g4hits_tracker)
206  {
208  g4iter != _g4hits_tracker->getHits().second;
209  ++g4iter)
210  {
211  PHG4Hit* g4hit = g4iter->second;
212  temp_clusters_from_particles.insert(std::make_pair(g4hit->get_trkid(), g4hit));
213  }
214  }
215 
216  // loop over all the g4hits in the maps ladder layers
217  if (_g4hits_maps)
218  {
220  g4iter != _g4hits_maps->getHits().second;
221  ++g4iter)
222  {
223  PHG4Hit* g4hit = g4iter->second;
224  temp_clusters_from_particles.insert(std::make_pair(g4hit->get_trkid(), g4hit));
225  }
226  }
227 
228  // loop over all the g4hits in the micromegas layers
229  if (_g4hits_mms)
230  {
231  for (PHG4HitContainer::ConstIterator g4iter = _g4hits_mms->getHits().first;
232  g4iter != _g4hits_mms->getHits().second;
233  ++g4iter)
234  {
235  PHG4Hit* g4hit = g4iter->second;
236  temp_clusters_from_particles.insert(std::make_pair(g4hit->get_trkid(), g4hit));
237  }
238  }
239 
241  for (PHG4TruthInfoContainer::ConstIterator iter = range.first;
242  iter != range.second; ++iter)
243  {
244  PHG4Particle* g4particle = iter->second;
245  std::set<PHG4Hit*> truth_hits;
246  std::multimap<const int, PHG4Hit*>::const_iterator lower_bound = temp_clusters_from_particles.lower_bound(g4particle->get_track_id());
247  std::multimap<const int, PHG4Hit*>::const_iterator upper_bound = temp_clusters_from_particles.upper_bound(g4particle->get_track_id());
248  std::multimap<const int, PHG4Hit*>::const_iterator cfp_iter;
249  for (cfp_iter = lower_bound; cfp_iter != upper_bound; ++cfp_iter)
250  {
251  PHG4Hit* g4hit = cfp_iter->second;
252  truth_hits.insert(g4hit);
253  }
254  _cache_all_truth_hits_g4particle.insert(std::make_pair(g4particle, truth_hits));
255  }
256 }
257 
258 std::map<TrkrDefs::cluskey, std::shared_ptr<TrkrCluster>> SvtxTruthEval::all_truth_clusters(PHG4Particle* particle)
259 {
260  using output_type_t = std::map<TrkrDefs::cluskey, std::shared_ptr<TrkrCluster>>;
261  if (!has_node_pointers())
262  {
263  ++_errors;
264  return output_type_t();
265  }
266 
267  if (_strict)
268  {
269  assert(particle);
270  }
271  else if (!particle)
272  {
273  ++_errors;
274  return output_type_t();
275  }
276 
277  if (_do_cache)
278  {
279  const auto iter = _cache_all_truth_clusters_g4particle.find(particle);
280  if (iter != _cache_all_truth_clusters_g4particle.end())
281  {
282  return iter->second;
283  }
284  }
285 
286  if (_verbosity > 1)
287  {
288  std::cout << PHWHERE << " Truth clustering for particle " << particle->get_track_id() << std::endl;
289  };
290 
291  // get all g4hits for this particle
292  std::set<PHG4Hit*> g4hits = all_truth_hits(particle);
293 
294  float ng4hits = g4hits.size();
295  if (ng4hits == 0)
296  {
297  return output_type_t();
298  }
299 
300  // container for storing truth clusters //std::map<unsigned int, TrkrCluster*> truth_clusters;
301  output_type_t truth_clusters;
302 
303  // convert truth hits for this particle to truth clusters in each layer
304  // loop over layers
305 
306  unsigned int layer;
307  for (layer = 0; layer < _nlayers_maps + _nlayers_intt + _nlayers_tpc + _nlayers_mms; ++layer)
308  {
309  float gx = NAN;
310  float gy = NAN;
311  float gz = NAN;
312  float gt = NAN;
313  float gedep = NAN;
314 
315  std::vector<PHG4Hit*> contributing_hits;
316  std::vector<double> contributing_hits_energy;
317  std::vector<std::vector<double>> contributing_hits_entry;
318  std::vector<std::vector<double>> contributing_hits_exit;
319  LayerClusterG4Hits(g4hits, contributing_hits, contributing_hits_energy, contributing_hits_entry, contributing_hits_exit, layer, gx, gy, gz, gt, gedep);
320  if (!(gedep > 0))
321  {
322  continue;
323  }
324 
325  // we have the cluster in this layer from this truth particle
326  // add the cluster to a TrkrCluster object
327  TrkrDefs::cluskey ckey;
328  if (layer >= _nlayers_maps + _nlayers_intt && layer < _nlayers_maps + _nlayers_intt + _nlayers_tpc) // in TPC
329  {
330  unsigned int side = 0;
331  if (gz > 0)
332  {
333  side = 1;
334  }
335  // need dummy sector here
336  unsigned int sector = 0;
337  ckey = TpcDefs::genClusKey(layer, sector, side, iclus);
338  }
339  else if (layer < _nlayers_maps) // in MVTX
340  {
341  unsigned int stave = 0;
342  unsigned int chip = 0;
343  unsigned int strobe = 0;
344  ckey = MvtxDefs::genClusKey(layer, stave, chip, strobe, iclus);
345  }
346  else if (layer >= _nlayers_maps && layer < _nlayers_maps + _nlayers_intt) // in INTT
347  {
348  // dummy ladder and phi ID
349  unsigned int ladderzid = 0;
350  unsigned int ladderphiid = 0;
351  uint16_t crossing = 0;
352  ckey = InttDefs::genClusKey(layer, ladderzid, ladderphiid, crossing, iclus);
353  }
354  else if (layer >= _nlayers_maps + _nlayers_intt + _nlayers_tpc) // in MICROMEGAS
355  {
356  unsigned int tile = 0;
359  TrkrDefs::hitsetkey hkey = MicromegasDefs::genHitSetKey(layer, segtype, tile);
360  ckey = TrkrDefs::genClusKey(hkey, iclus);
361  }
362  else
363  {
364  std::cout << PHWHERE << "Bad layer number: " << layer << std::endl;
365  continue;
366  }
367 
368  auto clus = std::make_shared<TrkrClusterv1>();
369  iclus++;
370 
371  // estimate cluster ADC value
372  unsigned int adc_value = getAdcValue(gedep);
373  // std::cout << " gedep " << gedep << " adc_value " << adc_value << std::endl;
374  clus->setAdc(adc_value);
375  clus->setPosition(0, gx);
376  clus->setPosition(1, gy);
377  clus->setPosition(2, gz);
378  clus->setGlobal();
379 
380  // record which g4hits contribute to this truth cluster
381  for (auto& contributing_hit : contributing_hits)
382  {
383  _truth_cluster_truth_hit_map.insert(std::make_pair(ckey, contributing_hit));
384  }
385 
386  // Estimate the size of the truth cluster
387  float g4phisize = NAN;
388  float g4zsize = NAN;
389  G4ClusterSize(ckey, layer, contributing_hits_entry, contributing_hits_exit, g4phisize, g4zsize);
390 
391  for (int i1 = 0; i1 < 3; ++i1)
392  {
393  for (int i2 = 0; i2 < 3; ++i2)
394  {
395  clus->setSize(i1, i2, 0.0);
396  clus->setError(i1, i2, 0.0);
397  }
398  }
399  clus->setError(0, 0, gedep); // stores truth energy
400  clus->setSize(1, 1, g4phisize);
401  clus->setSize(2, 2, g4zsize);
402  clus->setError(1, 1, g4phisize / std::sqrt(12));
403  clus->setError(2, 2, g4zsize / std::sqrt(12.0));
404 
405  truth_clusters.insert(std::make_pair(ckey, clus));
406 
407  } // end loop over layers for this particle
408 
409  if (_do_cache)
410  {
411  _cache_all_truth_clusters_g4particle.insert(std::make_pair(particle, truth_clusters));
412  }
413 
414  return truth_clusters;
415 }
416 
417 void SvtxTruthEval::LayerClusterG4Hits(const std::set<PHG4Hit*>& truth_hits, std::vector<PHG4Hit*>& contributing_hits, std::vector<double>& contributing_hits_energy, std::vector<std::vector<double>>& contributing_hits_entry, std::vector<std::vector<double>>& contributing_hits_exit, float layer, float& x, float& y, float& z, float& t, float& e)
418 {
419  bool use_geo = true;
420 
421  // Given a set of g4hits, cluster them within a given layer of the TPC
422 
423  float gx = 0.0;
424  float gy = 0.0;
425  float gz = 0.0;
426  float gr = 0.0;
427  float gt = 0.0;
428  float gwt = 0.0;
429 
430  if (layer >= _nlayers_maps + _nlayers_intt && layer < _nlayers_maps + _nlayers_intt + _nlayers_tpc) // in TPC
431  {
432  // std::cout << "layer = " << layer << " _nlayers_maps " << _nlayers_maps << " _nlayers_intt " << _nlayers_intt << std::endl;
433 
434  // This calculates the truth cluster position for the TPC from all of the contributing g4hits from a g4particle, typically 2-4 for the TPC
435  // Complicated, since only the part of the energy that is collected within a layer contributes to the position
436  //===============================================================================
437 
439  // get layer boundaries here for later use
440  // radii of layer boundaries
441  float rbin = GeoLayer->get_radius() - GeoLayer->get_thickness() / 2.0;
442  float rbout = GeoLayer->get_radius() + GeoLayer->get_thickness() / 2.0;
443 
444  if (_verbosity > 1)
445  {
446  std::cout << " TruthEval::LayerCluster hits for layer " << layer << " with rbin " << rbin << " rbout " << rbout << std::endl;
447  }
448 
449  // we do not assume that the truth hits know what layer they are in
450  for (auto this_g4hit : truth_hits)
451  {
452  float rbegin = std::sqrt(this_g4hit->get_x(0) * this_g4hit->get_x(0) + this_g4hit->get_y(0) * this_g4hit->get_y(0));
453  float rend = std::sqrt(this_g4hit->get_x(1) * this_g4hit->get_x(1) + this_g4hit->get_y(1) * this_g4hit->get_y(1));
454  // std::cout << " Eval: g4hit " << this_g4hit->get_hit_id() << " layer " << layer << " rbegin " << rbegin << " rend " << rend << std::endl;
455 
456  // make sure the entry point is at lower radius
457  float xl[2];
458  float yl[2];
459  float zl[2];
460 
461  if (rbegin < rend)
462  {
463  xl[0] = this_g4hit->get_x(0);
464  yl[0] = this_g4hit->get_y(0);
465  zl[0] = this_g4hit->get_z(0);
466  xl[1] = this_g4hit->get_x(1);
467  yl[1] = this_g4hit->get_y(1);
468  zl[1] = this_g4hit->get_z(1);
469  }
470  else
471  {
472  xl[0] = this_g4hit->get_x(1);
473  yl[0] = this_g4hit->get_y(1);
474  zl[0] = this_g4hit->get_z(1);
475  xl[1] = this_g4hit->get_x(0);
476  yl[1] = this_g4hit->get_y(0);
477  zl[1] = this_g4hit->get_z(0);
478  std::swap(rbegin, rend);
479  // std::cout << "swapped in and out " << std::endl;
480  }
481 
482  // check that the g4hit is not completely outside the cluster layer. Just skip this g4hit if it is
483  if (rbegin < rbin && rend < rbin)
484  {
485  continue;
486  }
487  if (rbegin > rbout && rend > rbout)
488  {
489  continue;
490  }
491 
492  if (_verbosity > 1)
493  {
494  std::cout << " keep g4hit with rbegin " << rbegin << " rend " << rend
495  << " xbegin " << xl[0] << " xend " << xl[1]
496  << " ybegin " << yl[0] << " yend " << yl[1]
497  << " zbegin " << zl[0] << " zend " << zl[1]
498  << std::endl;
499  }
500 
501  float xin = xl[0];
502  float yin = yl[0];
503  float zin = zl[0];
504  float xout = xl[1];
505  float yout = yl[1];
506  float zout = zl[1];
507 
508  float local_t = NAN;
509 
510  if (rbegin < rbin)
511  {
512  // line segment begins before boundary, find where it crosses
513  local_t = line_circle_intersection(xl, yl, zl, rbin);
514  if (local_t > 0)
515  {
516  xin = xl[0] + local_t * (xl[1] - xl[0]);
517  yin = yl[0] + local_t * (yl[1] - yl[0]);
518  zin = zl[0] + local_t * (zl[1] - zl[0]);
519  }
520  }
521 
522  if (rend > rbout)
523  {
524  // line segment ends after boundary, find where it crosses
525  local_t = line_circle_intersection(xl, yl, zl, rbout);
526  if (local_t > 0)
527  {
528  xout = xl[0] + local_t * (xl[1] - xl[0]);
529  yout = yl[0] + local_t * (yl[1] - yl[0]);
530  zout = zl[0] + local_t * (zl[1] - zl[0]);
531  }
532  }
533 
534  double rin = std::sqrt(xin * xin + yin * yin);
535  double rout = std::sqrt(xout * xout + yout * yout);
536 
537  // we want only the fraction of edep inside the layer
538  double efrac = this_g4hit->get_edep() * (rout - rin) / (rend - rbegin);
539  gx += (xin + xout) * 0.5 * efrac;
540  gy += (yin + yout) * 0.5 * efrac;
541  gz += (zin + zout) * 0.5 * efrac;
542  gt += this_g4hit->get_avg_t() * efrac;
543  gr += (rin + rout) * 0.5 * efrac;
544  gwt += efrac;
545 
546  if (_verbosity > 1)
547  {
548  std::cout << " rin " << rin << " rout " << rout
549  << " xin " << xin << " xout " << xout << " yin " << yin << " yout " << yout << " zin " << zin << " zout " << zout
550  << " edep " << this_g4hit->get_edep()
551  << " this_edep " << efrac << std::endl;
552  std::cout << " xavge " << (xin + xout) * 0.5 << " yavge " << (yin + yout) * 0.5 << " zavge " << (zin + zout) * 0.5 << " ravge " << (rin + rout) * 0.5
553  << std::endl;
554  }
555 
556  // Capture entry and exit points
557  std::vector<double> entry_loc;
558  entry_loc.push_back(xin);
559  entry_loc.push_back(yin);
560  entry_loc.push_back(zin);
561  std::vector<double> exit_loc;
562  exit_loc.push_back(xout);
563  exit_loc.push_back(yout);
564  exit_loc.push_back(zout);
565 
566  // this_g4hit is inside the layer, add it to the vectors
567  contributing_hits.push_back(this_g4hit);
568  contributing_hits_energy.push_back(this_g4hit->get_edep() * (zout - zin) / (zl[1] - zl[0]));
569  contributing_hits_entry.push_back(entry_loc);
570  contributing_hits_exit.push_back(exit_loc);
571 
572  } // loop over contributing hits
573 
574  if (gwt == 0)
575  {
576  e = gwt;
577  return; // will be discarded
578  }
579 
580  gx /= gwt;
581  gy /= gwt;
582  gz /= gwt;
583  gr = (rbin + rbout) * 0.5;
584  gt /= gwt;
585 
586  if (_verbosity > 1)
587  {
588  std::cout << " weighted means: gx " << gx << " gy " << gy << " gz " << gz << " gr " << gr << " e " << gwt << std::endl;
589  }
590 
591  if (use_geo)
592  {
593  // The energy weighted values above have significant scatter due to fluctuations in the energy deposit from Geant
594  // Calculate the geometric mean positions instead
595  float rentry = 999.0;
596  float xentry = 999.0;
597  float yentry = 999.0;
598  float zentry = 999.0;
599  float rexit = -999.0;
600  float xexit = -999.0;
601  float yexit = -999.0;
602  float zexit = -999.0;
603 
604  for (unsigned int ientry = 0; ientry < contributing_hits_entry.size(); ++ientry)
605  {
606  float tmpx = contributing_hits_entry[ientry][0];
607  float tmpy = contributing_hits_entry[ientry][1];
608  float tmpr = std::sqrt(tmpx * tmpx + tmpy * tmpy);
609 
610  if (tmpr < rentry)
611  {
612  rentry = tmpr;
613  xentry = contributing_hits_entry[ientry][0];
614  yentry = contributing_hits_entry[ientry][1];
615  zentry = contributing_hits_entry[ientry][2];
616  }
617 
618  tmpx = contributing_hits_exit[ientry][0];
619  tmpy = contributing_hits_exit[ientry][1];
620  tmpr = std::sqrt(tmpx * tmpx + tmpy * tmpy);
621 
622  if (tmpr > rexit)
623  {
624  rexit = tmpr;
625  xexit = contributing_hits_exit[ientry][0];
626  yexit = contributing_hits_exit[ientry][1];
627  zexit = contributing_hits_exit[ientry][2];
628  }
629  }
630 
631  float geo_r = (rentry + rexit) * 0.5;
632  float geo_x = (xentry + xexit) * 0.5;
633  float geo_y = (yentry + yexit) * 0.5;
634  float geo_z = (zentry + zexit) * 0.5;
635 
636  if (rexit > 0)
637  {
638  gx = geo_x;
639  gy = geo_y;
640  gz = geo_z;
641  gr = geo_r;
642  }
643 
644  if (_verbosity > 1)
645  {
646  std::cout << " rentry " << rentry << " rexit " << rexit
647  << " xentry " << xentry << " xexit " << xexit << " yentry " << yentry << " yexit " << yexit << " zentry " << zentry << " zexit " << zexit << std::endl;
648 
649  std::cout << " geometric means: geo_x " << geo_x << " geo_y " << geo_y << " geo_z " << geo_z << " geo r " << geo_r << " e " << gwt << std::endl
650  << std::endl;
651  }
652  }
653 
654  } // if TPC
655  else
656  {
657  // not TPC, one g4hit per cluster
658  for (auto this_g4hit : truth_hits)
659  {
660  if (this_g4hit->get_layer() != (unsigned int) layer)
661  {
662  continue;
663  }
664 
665  gx = this_g4hit->get_avg_x();
666  gy = this_g4hit->get_avg_y();
667  gz = this_g4hit->get_avg_z();
668  gt = this_g4hit->get_avg_t();
669  gwt += this_g4hit->get_edep();
670 
671  // Capture entry and exit points
672  std::vector<double> entry_loc;
673  entry_loc.push_back(this_g4hit->get_x(0));
674  entry_loc.push_back(this_g4hit->get_y(0));
675  entry_loc.push_back(this_g4hit->get_z(0));
676  std::vector<double> exit_loc;
677  exit_loc.push_back(this_g4hit->get_x(1));
678  exit_loc.push_back(this_g4hit->get_y(1));
679  exit_loc.push_back(this_g4hit->get_z(1));
680 
681  // this_g4hit is inside the layer, add it to the vectors
682  contributing_hits.push_back(this_g4hit);
683  contributing_hits_energy.push_back(this_g4hit->get_edep());
684  contributing_hits_entry.push_back(entry_loc);
685  contributing_hits_exit.push_back(exit_loc);
686  }
687  } // not TPC
688 
689  x = gx;
690  y = gy;
691  z = gz;
692  t = gt;
693  e = gwt;
694 
695  return;
696 }
697 
698 void SvtxTruthEval::G4ClusterSize(TrkrDefs::cluskey ckey, unsigned int layer, std::vector<std::vector<double>> contributing_hits_entry, std::vector<std::vector<double>> contributing_hits_exit, float& g4phisize, float& g4zsize)
699 {
700  // sort the contributing g4hits in radius
701  double inner_radius = 100.;
702  double inner_x = NAN;
703  double inner_y = NAN;
704  double inner_z = NAN;
705  ;
706 
707  double outer_radius = 0.;
708  double outer_x = NAN;
709  double outer_y = NAN;
710  double outer_z = NAN;
711 
712  for (unsigned int ihit = 0; ihit < contributing_hits_entry.size(); ++ihit)
713  {
714  double rad1 = std::sqrt(pow(contributing_hits_entry[ihit][0], 2) + pow(contributing_hits_entry[ihit][1], 2));
715  if (rad1 < inner_radius)
716  {
717  inner_radius = rad1;
718  inner_x = contributing_hits_entry[ihit][0];
719  inner_y = contributing_hits_entry[ihit][1];
720  inner_z = contributing_hits_entry[ihit][2];
721  }
722 
723  double rad2 = std::sqrt(pow(contributing_hits_exit[ihit][0], 2) + pow(contributing_hits_exit[ihit][1], 2));
724  if (rad2 > outer_radius)
725  {
726  outer_radius = rad2;
727  outer_x = contributing_hits_exit[ihit][0];
728  outer_y = contributing_hits_exit[ihit][1];
729  outer_z = contributing_hits_exit[ihit][2];
730  }
731  }
732 
733  double inner_phi = atan2(inner_y, inner_x);
734  double outer_phi = atan2(outer_y, outer_x);
735  double avge_z = (outer_z + inner_z) / 2.0;
736 
737  // Now fold these with the expected diffusion and shaping widths
738  // assume spread is +/- equals this many sigmas times diffusion and shaping when extending the size
739  double sigmas = 2.0;
740 
741  double radius = (inner_radius + outer_radius) / 2.;
742  if (radius > 28 && radius < 80) // TPC
743  {
745 
746  double tpc_length = 211.0; // cm
747  double drift_velocity = 8.0 / 1000.0; // cm/ns
748 
749  // Phi size
750  //======
751  double diffusion_trans = 0.006; // cm/SQRT(cm)
752  double phidiffusion = diffusion_trans * std::sqrt(tpc_length / 2. - fabs(avge_z));
753 
754  double added_smear_trans = 0.085; // cm
755  double gem_spread = 0.04; // 400 microns
756 
757  if (outer_phi < inner_phi)
758  {
759  std::swap(outer_phi, inner_phi);
760  }
761 
762  // convert diffusion from cm to radians
763  double g4max_phi = outer_phi + sigmas * std::sqrt(pow(phidiffusion, 2) + pow(added_smear_trans, 2) + pow(gem_spread, 2)) / radius;
764  double g4min_phi = inner_phi - sigmas * std::sqrt(pow(phidiffusion, 2) + pow(added_smear_trans, 2) + pow(gem_spread, 2)) / radius;
765 
766  // find the bins containing these max and min z edges
767  unsigned int phibinmin = layergeom->get_phibin(g4min_phi);
768  unsigned int phibinmax = layergeom->get_phibin(g4max_phi);
769  unsigned int phibinwidth = phibinmax - phibinmin + 1;
770  g4phisize = (double) phibinwidth * layergeom->get_phistep() * layergeom->get_radius();
771 
772  // Z size
773  //=====
774  double g4max_z = 0;
775  double g4min_z = 0;
776 
777  outer_z = fabs(outer_z);
778  inner_z = fabs(inner_z);
779 
780  double diffusion_long = 0.015; // cm/SQRT(cm)
781  double zdiffusion = diffusion_long * std::sqrt(tpc_length / 2. - fabs(avge_z));
782  double zshaping_lead = 32.0 * drift_velocity; // ns * cm/ns = cm
783  double zshaping_tail = 48.0 * drift_velocity;
784  double added_smear_long = 0.105; // cm
785 
786  // largest z reaches gems first, make that the outer z
787  if (outer_z < inner_z)
788  {
789  std::swap(outer_z, inner_z);
790  }
791  g4max_z = outer_z + sigmas * std::sqrt(pow(zdiffusion, 2) + pow(added_smear_long, 2) + pow(zshaping_lead, 2));
792  g4min_z = inner_z - sigmas * std::sqrt(pow(zdiffusion, 2) + pow(added_smear_long, 2) + pow(zshaping_tail, 2));
793 
794  // find the bins containing these max and min z edges
795  unsigned int binmin = layergeom->get_zbin(g4min_z);
796  unsigned int binmax = layergeom->get_zbin(g4max_z);
797  if (binmax < binmin)
798  {
799  std::swap(binmax, binmin);
800  }
801  unsigned int binwidth = binmax - binmin + 1;
802 
803  // multiply total number of bins that include the edges by the bin size
804  g4zsize = (double) binwidth * layergeom->get_zstep();
805  }
806  else if (radius > 5 && radius < 20) // INTT
807  {
808  // All we have is the position and layer number
809 
810  CylinderGeomIntt* layergeom = dynamic_cast<CylinderGeomIntt*>(_intt_geom_container->GetLayerGeom(layer));
811 
812  // inner location
813  double world_inner[3] = {inner_x, inner_y, inner_z};
814  TVector3 world_inner_vec = {inner_x, inner_y, inner_z};
815 
816  int segment_z_bin, segment_phi_bin;
817  layergeom->find_indices_from_world_location(segment_z_bin, segment_phi_bin, world_inner);
818 
820  auto surf = _tgeometry->maps().getSiliconSurface(hitsetkey);
821  TVector3 local_inner_vec = layergeom->get_local_from_world_coords(surf, _tgeometry, world_inner);
822  double yin = local_inner_vec[1];
823  double zin = local_inner_vec[2];
824  int strip_y_index, strip_z_index;
825  layergeom->find_strip_index_values(segment_z_bin, yin, zin, strip_y_index, strip_z_index);
826 
827  // outer location
828  double world_outer[3] = {outer_x, outer_y, outer_z};
829  TVector3 world_outer_vec = {outer_x, outer_y, outer_z};
830 
831  layergeom->find_indices_from_world_location(segment_z_bin, segment_phi_bin, world_outer);
833  auto osurf = _tgeometry->maps().getSiliconSurface(ohitsetkey);
834  TVector3 local_outer_vec = layergeom->get_local_from_world_coords(osurf, _tgeometry, world_outer_vec);
835  double yout = local_outer_vec[1];
836  double zout = local_outer_vec[2];
837  int strip_y_index_out, strip_z_index_out;
838  layergeom->find_strip_index_values(segment_z_bin, yout, zout, strip_y_index_out, strip_z_index_out);
839 
840  int strips = abs(strip_y_index_out - strip_y_index) + 1;
841  int cols = abs(strip_z_index_out - strip_z_index) + 1;
842 
843  double strip_width = (double) strips * layergeom->get_strip_y_spacing(); // cm
844  double strip_length = (double) cols * layergeom->get_strip_z_spacing(); // cm
845 
846  g4phisize = strip_width;
847  g4zsize = strip_length;
848 
849  /*
850  if(Verbosity() > 1)
851  std::cout << " INTT: layer " << layer << " strips " << strips << " strip pitch " << layergeom->get_strip_y_spacing() << " g4phisize "<< g4phisize
852  << " columns " << cols << " strip_z_spacing " << layergeom->get_strip_z_spacing() << " g4zsize " << g4zsize << std::endl;
853  */
854  }
855  else if (radius > 80) // MICROMEGAS
856  {
857  // made up for now
858  g4phisize = 300e-04;
859  g4zsize = 300e-04;
860  }
861  else // MVTX
862  {
863  unsigned int stave, stave_outer;
864  unsigned int chip, chip_outer;
865  int row, row_outer;
866  int column, column_outer;
867 
868  // add diffusion to entry and exit locations
869  double max_diffusion_radius = 25.0e-4; // 25 microns
870  double min_diffusion_radius = 8.0e-4; // 8 microns
871 
872  CylinderGeom_Mvtx* layergeom = dynamic_cast<CylinderGeom_Mvtx*>(_mvtx_geom_container->GetLayerGeom(layer));
873 
874  TVector3 world_inner = {inner_x, inner_y, inner_z};
875  std::vector<double> world_inner_vec = {world_inner[0], world_inner[1], world_inner[2]};
876  layergeom->get_sensor_indices_from_world_coords(world_inner_vec, stave, chip);
878  auto isurf = _tgeometry->maps().getSiliconSurface(ihitsetkey);
879  TVector3 local_inner = layergeom->get_local_from_world_coords(isurf, _tgeometry, world_inner);
880 
881  TVector3 world_outer = {outer_x, outer_y, outer_z};
882  std::vector<double> world_outer_vec = {world_outer[0], world_outer[1], world_outer[2]};
883  layergeom->get_sensor_indices_from_world_coords(world_outer_vec, stave_outer, chip_outer);
885  auto osurf = _tgeometry->maps().getSiliconSurface(ohitsetkey);
886  TVector3 local_outer = layergeom->get_local_from_world_coords(osurf, _tgeometry, world_outer);
887 
888  double diff = max_diffusion_radius * 0.6; // factor of 0.6 gives decent agreement with low occupancy reco clusters
889  if (local_outer[0] < local_inner[0])
890  {
891  diff = -diff;
892  }
893  local_outer[0] += diff;
894  local_inner[0] -= diff;
895 
896  double diff_outer = min_diffusion_radius * 0.6;
897  if (local_outer[2] < local_inner[2])
898  {
899  diff_outer = -diff_outer;
900  }
901  local_outer[2] += diff_outer;
902  local_inner[2] -= diff_outer;
903 
904  layergeom->get_pixel_from_local_coords(local_inner, row, column);
905  layergeom->get_pixel_from_local_coords(local_outer, row_outer, column_outer);
906 
907  if (row_outer < row)
908  {
909  std::swap(row_outer, row);
910  }
911  unsigned int rows = row_outer - row + 1;
912  g4phisize = (double) rows * layergeom->get_pixel_x();
913 
914  if (column_outer < column)
915  {
916  std::swap(column_outer, column);
917  }
918  unsigned int columns = column_outer - column + 1;
919  g4zsize = (double) columns * layergeom->get_pixel_z();
920 
921  /*
922  if(Verbosity() > 1)
923  std::cout << " MVTX: layer " << layer << " rows " << rows << " pixel x " << layergeom->get_pixel_x() << " g4phisize "<< g4phisize
924  << " columns " << columns << " pixel_z " << layergeom->get_pixel_z() << " g4zsize " << g4zsize << std::endl;
925  */
926  }
927 }
928 
930 {
931  std::set<PHG4Hit*> g4hit_set;
932 
933  std::pair<std::multimap<TrkrDefs::cluskey, PHG4Hit*>::iterator,
934  std::multimap<TrkrDefs::cluskey, PHG4Hit*>::iterator>
935  ret = _truth_cluster_truth_hit_map.equal_range(ckey);
936 
937  for (std::multimap<TrkrDefs::cluskey, PHG4Hit*>::iterator jter = ret.first; jter != ret.second; ++jter)
938  {
939  g4hit_set.insert(jter->second);
940  }
941 
942  return g4hit_set;
943 }
944 
946 {
947  if (!has_node_pointers())
948  {
949  ++_errors;
950  return nullptr;
951  }
952 
953  if (_strict)
954  {
955  assert(particle);
956  }
957  else if (!particle)
958  {
959  ++_errors;
960  return nullptr;
961  }
962 
963  PHG4Hit* innermost_hit = nullptr;
964  float innermost_radius = FLT_MAX;
965 
966  std::set<PHG4Hit*> truth_hits = all_truth_hits(particle);
967  for (auto candidate : truth_hits)
968  {
969  float x = candidate->get_x(0); // use entry points
970  float y = candidate->get_y(0); // use entry points
971  float r = std::sqrt(x * x + y * y);
972  if (r < innermost_radius)
973  {
974  innermost_radius = r;
975  innermost_hit = candidate;
976  }
977  }
978 
979  return innermost_hit;
980 }
981 
983 {
984  if (!has_node_pointers())
985  {
986  ++_errors;
987  return nullptr;
988  }
989 
990  if (_strict)
991  {
992  assert(particle);
993  }
994  else if (!particle)
995  {
996  ++_errors;
997  return nullptr;
998  }
999 
1000  PHG4Hit* outermost_hit = nullptr;
1001  float outermost_radius = FLT_MAX * -1.0;
1002 
1003  if (_do_cache)
1004  {
1005  std::map<PHG4Particle*, PHG4Hit*>::iterator iter =
1006  _cache_get_outermost_truth_hit.find(particle);
1007  if (iter != _cache_get_outermost_truth_hit.end())
1008  {
1009  return iter->second;
1010  }
1011  }
1012 
1013  std::set<PHG4Hit*> truth_hits = all_truth_hits(particle);
1014  for (auto candidate : truth_hits)
1015  {
1016  float x = candidate->get_x(1); // use exit points
1017  float y = candidate->get_y(1); // use exit points
1018  float r = std::sqrt(x * x + y * y);
1019  if (r > outermost_radius)
1020  {
1021  outermost_radius = r;
1022  outermost_hit = candidate;
1023  }
1024  }
1025  if (_do_cache)
1026  {
1027  _cache_get_outermost_truth_hit.insert(std::make_pair(particle, outermost_hit));
1028  }
1029 
1030  return outermost_hit;
1031 }
1032 
1034 {
1035  return _basetrutheval.get_particle(g4hit);
1036 }
1037 
1039 {
1040  return _basetrutheval.get_embed(particle);
1041 }
1042 
1044 {
1045  return _basetrutheval.get_vertex(particle);
1046 }
1047 
1049 {
1050  return _basetrutheval.is_primary(particle);
1051 }
1052 
1054 {
1055  if (!has_node_pointers())
1056  {
1057  ++_errors;
1058  return nullptr;
1059  }
1060 
1061  if (_strict)
1062  {
1063  assert(g4hit);
1064  }
1065  else if (!g4hit)
1066  {
1067  ++_errors;
1068  return nullptr;
1069  }
1070 
1071  if (_do_cache)
1072  {
1073  std::map<PHG4Hit*, PHG4Particle*>::iterator iter =
1075  if (iter != _cache_get_primary_particle_g4hit.end())
1076  {
1077  return iter->second;
1078  }
1079  }
1080 
1082 
1083  if (_do_cache)
1084  {
1085  _cache_get_primary_particle_g4hit.insert(std::make_pair(g4hit, primary));
1086  }
1087 
1088  if (_strict)
1089  {
1090  assert(primary);
1091  }
1092  else if (!primary)
1093  {
1094  ++_errors;
1095  }
1096 
1097  return primary;
1098 }
1099 
1101 {
1102  return _basetrutheval.get_primary_particle(particle);
1103 }
1104 
1106 {
1107  return _basetrutheval.get_particle(trackid);
1108 }
1109 
1111 {
1112  return _basetrutheval.is_g4hit_from_particle(g4hit, particle);
1113 }
1114 
1116 {
1117  return _basetrutheval.are_same_particle(p1, p2);
1118 }
1119 
1121 {
1122  return _basetrutheval.are_same_vertex(vtx1, vtx2);
1123 }
1124 
1126 {
1127  _tgeometry = findNode::getClass<ActsGeometry>(topNode, "ActsGeometry");
1128  _truthinfo = findNode::getClass<PHG4TruthInfoContainer>(topNode, "G4TruthInfo");
1129 
1130  _g4hits_mms = findNode::getClass<PHG4HitContainer>(topNode, "G4HIT_MICROMEGAS");
1131  _g4hits_svtx = findNode::getClass<PHG4HitContainer>(topNode, "G4HIT_TPC");
1132  _g4hits_tracker = findNode::getClass<PHG4HitContainer>(topNode, "G4HIT_INTT");
1133  _g4hits_maps = findNode::getClass<PHG4HitContainer>(topNode, "G4HIT_MVTX");
1134 
1135  _mms_geom_container = findNode::getClass<PHG4CylinderGeomContainer>(topNode, "CYLINDERGEOM_MICROMEGAS_FULL");
1136  _tpc_geom_container = findNode::getClass<PHG4TpcCylinderGeomContainer>(topNode, "CYLINDERCELLGEOM_SVTX");
1137  _intt_geom_container = findNode::getClass<PHG4CylinderGeomContainer>(topNode, "CYLINDERGEOM_INTT");
1138  _mvtx_geom_container = findNode::getClass<PHG4CylinderGeomContainer>(topNode, "CYLINDERGEOM_MVTX");
1139 
1140  return;
1141 }
1142 
1144 {
1145  if (_strict)
1146  {
1147  assert(_truthinfo);
1148  }
1149  else if (!_truthinfo)
1150  {
1151  return false;
1152  }
1153 
1154  if (_strict)
1155  {
1157  }
1158  else if (!_g4hits_mms && !_g4hits_svtx && !_g4hits_tracker && !_g4hits_maps)
1159  {
1160  return false;
1161  }
1162 
1163  return true;
1164 }
1165 
1166 float SvtxTruthEval::line_circle_intersection(float x[], float y[], float z[], float radius)
1167 {
1168  // parameterize the line in terms of t (distance along the line segment, from 0-1) as
1169  // x = x0 + t * (x1-x0); y=y0 + t * (y1-y0); z = z0 + t * (z1-z0)
1170  // parameterize the cylinder (centered at x,y = 0,0) as x^2 + y^2 = radius^2, then
1171  // (x0 + t*(x1-z0))^2 + (y0+t*(y1-y0))^2 = radius^2
1172  // (x0^2 + y0^2 - radius^2) + (2x0*(x1-x0) + 2y0*(y1-y0))*t + ((x1-x0)^2 + (y1-y0)^2)*t^2 = 0 = C + B*t + A*t^2
1173  // quadratic with: A = (x1-x0)^2+(y1-y0)^2 ; B = 2x0*(x1-x0) + 2y0*(y1-y0); C = x0^2 + y0^2 - radius^2
1174  // solution: t = (-B +/- std::sqrt(B^2 - 4*A*C)) / (2*A)
1175 
1176  float A = (x[1] - x[0]) * (x[1] - x[0]) + (y[1] - y[0]) * (y[1] - y[0]);
1177  float B = 2.0 * x[0] * (x[1] - x[0]) + 2.0 * y[0] * (y[1] - y[0]);
1178  float C = x[0] * x[0] + y[0] * y[0] - radius * radius;
1179  float tup = (-B + std::sqrt(B * B - 4.0 * A * C)) / (2.0 * A);
1180  float tdn = (-B - std::sqrt(B * B - 4.0 * A * C)) / (2.0 * A);
1181 
1182  // The limits are 0 and 1, but we allow a little for floating point precision
1183  float t;
1184  if (tdn >= -0.0e-4 && tdn <= 1.0004)
1185  {
1186  t = tdn;
1187  }
1188  else if (tup >= -0.0e-4 && tup <= 1.0004)
1189  {
1190  t = tup;
1191  }
1192  else
1193  {
1194  std::cout << PHWHERE << " **** Oops! No valid solution for tup or tdn, tdn = " << tdn << " tup = " << tup << std::endl;
1195  std::cout << " radius " << radius << " rbegin " << std::sqrt(x[0] * x[0] + y[0] * y[0]) << " rend " << std::sqrt(x[1] * x[1] + y[1] * y[1]) << std::endl;
1196  std::cout << " x0 " << x[0] << " x1 " << x[1] << std::endl;
1197  std::cout << " y0 " << y[0] << " y1 " << y[1] << std::endl;
1198  std::cout << " z0 " << z[0] << " z1 " << z[1] << std::endl;
1199  std::cout << " A " << A << " B " << B << " C " << C << std::endl;
1200 
1201  t = -1;
1202  }
1203 
1204  return t;
1205 }
1206 
1207 unsigned int SvtxTruthEval::getAdcValue(double gedep)
1208 {
1209  // see TPC digitizer for algorithm
1210 
1211  // drift electrons per GeV of energy deposited in the TPC
1212  double Ne_dEdx = 1.56; // keV/cm
1213  double CF4_dEdx = 7.00; // keV/cm
1214  double Ne_NTotal = 43; // Number/cm
1215  double CF4_NTotal = 100; // Number/cm
1216  double Tpc_NTot = 0.5 * Ne_NTotal + 0.5 * CF4_NTotal;
1217  double Tpc_dEdx = 0.5 * Ne_dEdx + 0.5 * CF4_dEdx;
1218  double Tpc_ElectronsPerKeV = Tpc_NTot / Tpc_dEdx;
1219  double electrons_per_gev = Tpc_ElectronsPerKeV * 1e6;
1220 
1221  double gem_amplification = 1400; // GEM output electrons per drifted electron
1222  double input_electrons = gedep * electrons_per_gev * gem_amplification;
1223 
1224  // convert electrons after GEM to ADC output
1225  double ChargeToPeakVolts = 20;
1226  double ADCSignalConversionGain = ChargeToPeakVolts * 1.60e-04 * 2.4; // 20 (or 30) mV/fC * fC/electron * scaleup factor
1227  double adc_input_voltage = input_electrons * ADCSignalConversionGain; // mV, see comments above
1228  unsigned int adc_output = (unsigned int) (adc_input_voltage * 1024.0 / 2200.0); // input voltage x 1024 channels over 2200 mV max range
1229  if (adc_output > 1023)
1230  {
1231  adc_output = 1023;
1232  }
1233 
1234  return adc_output;
1235 }