Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
QAG4SimulationMicromegas.cc
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file QAG4SimulationMicromegas.cc
2 #include <qautils/QAUtil.h>
3 #include <qautils/QAHistManagerDef.h>
4 
5 #include <g4detectors/PHG4CylinderGeom.h> // for PHG4CylinderGeom
7 
8 #include <g4main/PHG4Hit.h>
10 #include <g4main/PHG4Particle.h>
12 
13 #include <micromegas/MicromegasDefs.h> // for SegmentationType
14 
16 
17 #include <trackbase/ActsGeometry.h>
20 #include <trackbase/TrkrCluster.h>
23 #include <trackbase/TrkrDefs.h> // for getTrkrId, getHit...
24 #include <trackbase/TrkrHit.h>
25 #include <trackbase/TrkrHitSet.h>
28 
29 #include <g4eval/SvtxClusterEval.h> // for SvtxClusterEval
30 #include <g4eval/SvtxEvalStack.h>
31 #include <g4eval/SvtxTruthEval.h> // for SvtxTruthEval
32 
35 #include <fun4all/SubsysReco.h> // for SubsysReco
36 
38 #include <phool/getClass.h>
39 #include <phool/phool.h> // for PHWHERE
40 
41 #include <TAxis.h> // for TAxis
42 #include <TH1.h>
43 #include <TString.h> // for Form
44 #include <TVector3.h>
45 
46 #include <cassert>
47 #include <cmath> // for cos, sin, NAN
48 #include <iostream> // for operator<<, basic...
49 #include <iterator> // for distance
50 #include <map> // for map
51 #include <utility> // for pair, make_pair
52 #include <vector> // for vector
53 
54 //_____________________________________________________________________
55 namespace
56 {
58  template <class T>
59  inline constexpr T square(T x)
60  {
61  return x * x;
62  }
63 
65  struct interpolation_data_t
66  {
67  using list = std::vector<interpolation_data_t>;
68  double x() const { return position.x(); }
69  double y() const { return position.y(); }
70  double z() const { return position.z(); }
71 
72  double px() const { return momentum.x(); }
73  double py() const { return momentum.y(); }
74  double pz() const { return momentum.z(); }
75 
76  TVector3 position;
77  TVector3 momentum;
78  double weight = 1;
79  };
80 
82  template <double (interpolation_data_t::*accessor)() const>
83  double interpolate(const interpolation_data_t::list& hits, double z_extrap)
84  {
85  // calculate all terms needed for the interpolation
86  // need to use double everywhere here due to numerical divergences
87  double sw = 0;
88  double swz = 0;
89  double swz2 = 0;
90  double swx = 0;
91  double swzx = 0;
92 
93  bool valid(false);
94  for (const auto& hit : hits)
95  {
96  const double x = (hit.*accessor)();
97  const double w = hit.weight;
98  if (w <= 0) continue;
99 
100  valid = true;
101  const double z = hit.z();
102 
103  sw += w;
104  swz += w * z;
105  swz2 += w * square(z);
106  swx += w * x;
107  swzx += w * x * z;
108  }
109 
110  if (!valid) return NAN;
111 
112  const auto alpha = (sw * swzx - swz * swx);
113  const auto beta = (swz2 * swx - swz * swzx);
114  const auto denom = (sw * swz2 - square(swz));
115 
116  return (alpha * z_extrap + beta) / denom;
117  }
118 
119 } // namespace
120 
121 //________________________________________________________________________
123  : SubsysReco(name)
124  , m_truthContainer(nullptr)
125 {
126 }
127 
128 //________________________________________________________________________
130 {
131  // prevent multiple creations of histograms
132  if (m_initialized)
134  else
135  m_initialized = true;
136 
137  // find mvtx geometry
138  auto geom_container = findNode::getClass<PHG4CylinderGeomContainer>(topNode, "CYLINDERGEOM_MICROMEGAS_FULL");
139  if (!geom_container)
140  {
141  std::cout << PHWHERE << " unable to find DST node CYLINDERGEOM_MICROMEGAS_FULL" << std::endl;
143  }
144 
145  if (!m_svtxEvalStack)
146  {
147  m_svtxEvalStack.reset(new SvtxEvalStack(topNode));
148  // m_svtxEvalStack->set_strict(true);
149  m_svtxEvalStack->set_strict(false);
150  m_svtxEvalStack->set_verbosity(Verbosity());
151  }
152 
153  m_truthContainer = findNode::getClass<PHG4TruthInfoContainer>(topNode,
154  "G4TruthInfo");
155  if (!m_truthContainer)
156  {
157  std::cout << "QAG4SimulationTpc::InitRun - Fatal Error - "
158  << "unable to find node G4TruthInfo" << std::endl;
160  }
161 
162  // get layers from mvtx geometry
163  const auto range = geom_container->get_begin_end();
164  for (auto iter = range.first; iter != range.second; ++iter)
165  {
166  m_layers.insert(iter->first);
167  }
168 
169  // histogram manager
171  assert(hm);
172 
173  // create histograms
174  for (const auto& layer : m_layers)
175  {
176  if (Verbosity()) std::cout << PHWHERE << " adding layer " << layer << std::endl;
177 
178  // get layer geometry
179  const auto layergeom = dynamic_cast<CylinderGeomMicromegas*>(geom_container->GetLayerGeom(layer));
180  assert(layergeom);
181 
182  // get segmentation type
183  const auto segmentation_type = layergeom->get_segmentation_type();
184  const bool is_segmentation_phi = (segmentation_type == MicromegasDefs::SegmentationType::SEGMENTATION_PHI);
185 
186  {
187  // ADC distributions
188  auto h = new TH1F(Form("%sadc_%i", get_histo_prefix().c_str(), layer),
189  Form("micromegas ADC distribution layer_%i", layer),
190  1024, 0, 1024);
191  h->GetXaxis()->SetTitle("ADC");
192  hm->registerHisto(h);
193  }
194 
195  {
196  // residuals (cluster - truth)
197  const double max_residual = is_segmentation_phi ? 0.04 : 0.08;
198  auto h = new TH1F(Form("%sresidual_%i", get_histo_prefix().c_str(), layer),
199  Form("micromegas %s_{cluster-truth} layer_%i",
200  is_segmentation_phi ? "r#Delta#phi" : "#Deltaz", layer),
201  100, -max_residual, max_residual);
202  h->GetXaxis()->SetTitle(Form("%s_{cluster-truth} (cm)", is_segmentation_phi ? "r#Delta#phi" : "#Deltaz"));
203  hm->registerHisto(h);
204  }
205 
206  {
207  // cluster errors
208  const double max_error = is_segmentation_phi ? 0.04 : 0.08;
209  auto h = new TH1F(Form("%sresidual_error_%i", get_histo_prefix().c_str(), layer),
210  Form("micromegas %s error layer_%i",
211  is_segmentation_phi ? "r#Delta#phi" : "#Deltaz", layer),
212  100, 0, max_error);
213  h->GetXaxis()->SetTitle(Form("%s error (cm)", is_segmentation_phi ? "r#Delta#phi" : "#Deltaz"));
214  hm->registerHisto(h);
215  }
216 
217  {
218  // pulls (cluster - truth)
219  auto h = new TH1F(Form("%scluster_pulls_%i", get_histo_prefix().c_str(), layer),
220  Form("micromegas %s layer_%i",
221  is_segmentation_phi ? "#Delta#phi/#sigma#phi" : "#Deltaz/#sigmaz", layer),
222  100, -5, 5);
223  h->GetXaxis()->SetTitle(is_segmentation_phi ? "#Delta#phi/#sigma#phi" : "#Deltaz/#sigmaz");
224  hm->registerHisto(h);
225  }
226 
227  {
228  // cluster size
229  auto h = new TH1F(Form("%sclus_size_%i", get_histo_prefix().c_str(), layer), Form("micromegas cluster size layer_%i", layer), 20, 0, 20);
230  h->GetXaxis()->SetTitle("csize");
231  hm->registerHisto(h);
232  }
233  }
234 
236 }
237 
238 //_____________________________________________________________________
240 {
241  // load nodes
242  auto res = load_nodes(topNode);
243  if (res != Fun4AllReturnCodes::EVENT_OK) return res;
244  m_svtxEvalStack->next_event(topNode);
245  // run evaluation
246  evaluate_hits();
249 }
250 
251 //________________________________________________________________________
253 {
254  return std::string("h_") + Name() + std::string("_");
255 }
256 
257 //________________________________________________________________________
259 {
260  m_tGeometry = findNode::getClass<ActsGeometry>(topNode, "ActsGeometry");
261  if (!m_tGeometry)
262  {
263  std::cout << PHWHERE << "No acts tracking geometry, exiting."
264  << std::endl;
266  }
267 
268  m_micromegas_geonode = findNode::getClass<PHG4CylinderGeomContainer>(topNode, "CYLINDERGEOM_MICROMEGAS_FULL");
270  {
271  std::cout << PHWHERE << " ERROR: Can't find CYLINDERGEOM_MICROMEGAS_FULL." << std::endl;
273  }
274 
275  m_hitsets = findNode::getClass<TrkrHitSetContainer>(topNode, "TRKR_HITSET");
276  if (!m_hitsets)
277  {
278  std::cout << PHWHERE << " ERROR: Can't find TrkrHitSetContainer." << std::endl;
279  }
280 
281  m_cluster_map = findNode::getClass<TrkrClusterContainer>(topNode, "TRKR_CLUSTER");
282  if (!m_cluster_map)
283  {
284  std::cout << PHWHERE << " unable to find DST node TRKR_CLUSTER" << std::endl;
286  }
287 
288  m_cluster_hit_map = findNode::getClass<TrkrClusterHitAssoc>(topNode, "TRKR_CLUSTERHITASSOC");
289  if (!m_cluster_hit_map)
290  {
291  std::cout << PHWHERE << " unable to find DST node TRKR_CLUSTERHITASSOC" << std::endl;
293  }
294 
295  m_hit_truth_map = findNode::getClass<TrkrHitTruthAssoc>(topNode, "TRKR_HITTRUTHASSOC");
296  if (!m_hit_truth_map)
297  {
298  std::cout << PHWHERE << " unable to find DST node TRKR_HITTRUTHASSOC" << std::endl;
300  }
301 
302  m_g4hits_micromegas = findNode::getClass<PHG4HitContainer>(topNode, "G4HIT_MICROMEGAS");
303  if (!m_g4hits_micromegas)
304  {
305  std::cout << PHWHERE << " unable to find DST node G4HIT_MVTX" << std::endl;
307  }
308 
310 }
311 
312 //________________________________________________________________________
314 {
315  // do nothing if hitsets are not there
316  if (!m_hitsets) return;
317 
318  // histogram manager
320  assert(hm);
321 
322  // load relevant histograms
323  struct HistogramList
324  {
325  TH1* adc = nullptr;
326  };
327 
328  using HistogramMap = std::map<int, HistogramList>;
329  HistogramMap histograms;
330 
331  for (const auto& layer : m_layers)
332  {
333  HistogramList h;
334  h.adc = dynamic_cast<TH1*>(hm->getHisto(Form("%sadc_%i", get_histo_prefix().c_str(), layer)));
335  histograms.insert(std::make_pair(layer, h));
336  }
337 
338  // loop over hitsets
339  const auto hitsetrange = m_hitsets->getHitSets(TrkrDefs::TrkrId::micromegasId);
340  for (auto hitsetiter = hitsetrange.first; hitsetiter != hitsetrange.second; ++hitsetiter)
341  {
342  // get hitsetkey and layer
343  const auto hitsetkey = hitsetiter->first;
344  const auto layer = TrkrDefs::getLayer(hitsetkey);
345 
346  // get relevant histograms
347  const auto hiter = histograms.find(layer);
348  if (hiter == histograms.end()) continue;
349 
350  // get all of the hits from this hitset
351  TrkrHitSet* hitset = hitsetiter->second;
352  TrkrHitSet::ConstRange hitrange = hitset->getHits();
353 
354  // loop over hits
355  for (auto hit_it = hitrange.first; hit_it != hitrange.second; ++hit_it)
356  {
357  // store ADC
358  auto fill = [](TH1* h, float value)
359  { if( h ) h->Fill( value ); };
360  fill(hiter->second.adc, hit_it->second->getAdc());
361  }
362  }
363 }
364 
365 //________________________________________________________________________
367 {
368  SvtxTruthEval* trutheval = m_svtxEvalStack->get_truth_eval();
369  assert(trutheval);
370  SvtxClusterEval* clustereval = m_svtxEvalStack->get_cluster_eval();
371  assert(clustereval);
372  // histogram manager
374  assert(hm);
375  // load relevant histograms
376  struct HistogramList
377  {
378  TH1* residual = nullptr;
379  TH1* residual_error = nullptr;
380  TH1* pulls = nullptr;
381 
382  TH1* csize = nullptr;
383  };
384 
385  using HistogramMap = std::map<int, HistogramList>;
386  HistogramMap histograms;
387 
388  for (const auto& layer : m_layers)
389  {
390  HistogramList h;
391  h.residual = dynamic_cast<TH1*>(hm->getHisto(Form("%sresidual_%i", get_histo_prefix().c_str(), layer)));
392  h.residual_error = dynamic_cast<TH1*>(hm->getHisto(Form("%sresidual_error_%i", get_histo_prefix().c_str(), layer)));
393  h.pulls = dynamic_cast<TH1*>(hm->getHisto(Form("%scluster_pulls_%i", get_histo_prefix().c_str(), layer)));
394  h.csize = dynamic_cast<TH1*>(hm->getHisto(Form("%sclus_size_%i", get_histo_prefix().c_str(), layer)));
395 
396  histograms.insert(std::make_pair(layer, h));
397  }
398  // get primary truth particles
399 
400  PHG4TruthInfoContainer::ConstRange range = m_truthContainer->GetPrimaryParticleRange(); // only from primary particles
401 
402  for (PHG4TruthInfoContainer::ConstIterator iter = range.first;
403  iter != range.second;
404  ++iter)
405  {
406  PHG4Particle* g4particle = iter->second;
407  float gtrackID = g4particle->get_track_id();
408  float gflavor = g4particle->get_pid();
409  float gembed = trutheval->get_embed(g4particle);
410  float gprimary = trutheval->is_primary(g4particle);
411 
412  if (Verbosity() > 0)
413  std::cout << PHWHERE << " PHG4Particle ID " << gtrackID << " gembed " << gembed << " gflavor " << gflavor << " gprimary " << gprimary << std::endl;
414  const auto ckeyset = clustereval->all_clusters_from(g4particle);
415  // Get the truth clusters from this particle
416  const auto truth_clusters = trutheval->all_truth_clusters(g4particle);
417 
418  // get circle fit parameters first
421 
422  for (const auto& [gkey, gclus] : truth_clusters)
423  {
424  const auto layer = TrkrDefs::getLayer(gkey);
425  if (layer < 7) continue;
426 
427  float gx = gclus->getX();
428  float gy = gclus->getY();
429  float gz = gclus->getZ();
430 
431  xy_pts.emplace_back(gx, gy);
432  rz_pts.emplace_back(std::sqrt(gx * gx + gy * gy), gz);
433  }
434 
435  // fit a circle through x,y coordinates
436  const auto [R, X0, Y0] = TrackFitUtils::circle_fit_by_taubin(xy_pts);
437 
438  // skip chain entirely if fit fails
439  if (std::isnan(R)) continue;
440 
441  // process residuals and pulls
442  // get reco clusters
443 
444  for (const auto ckey : ckeyset)
445  {
446  const auto layer = TrkrDefs::getLayer(ckey);
447  const auto detID = TrkrDefs::getTrkrId(ckey);
448  if (detID != TrkrDefs::TrkrId::micromegasId) continue;
449 
450  // get tileid
451  const auto tileid = MicromegasDefs::getTileId(ckey);
452 
453  // load geometry
454  const auto layergeom = dynamic_cast<CylinderGeomMicromegas*>(m_micromegas_geonode->GetLayerGeom(layer));
455  if (!layergeom) continue;
456 
457  // get relevant histograms
458  const auto hiter = histograms.find(layer);
459  if (hiter == histograms.end()) continue;
460 
461  // get cluster
462  const auto cluster = m_cluster_map->findCluster(ckey);
463  const auto global = m_tGeometry->getGlobalPosition(ckey, cluster);
464 
465  // get segmentation type
466  const auto segmentation_type = MicromegasDefs::getSegmentationType(ckey);
467 
468  // get relevant cluster information
469  double rphi_error = cluster->getRPhiError();
470  double z_error = cluster->getZError();
471 
472  // convert cluster position to local tile coordinates
473  const TVector3 cluster_world(global(0), global(1), global(2));
474  const auto cluster_local = layergeom->get_local_from_world_coords(tileid, m_tGeometry, cluster_world);
475 
476  // find associated g4hits
477  const auto g4hits = find_g4hits(ckey);
478  // convert hits to list of interpolation_data_t
479  /* TODO: should actually use the same code as in TrackEvaluation */
480  interpolation_data_t::list hits;
481  for (const auto& g4hit : g4hits)
482  {
483  const auto weight = g4hit->get_edep();
484  for (int i = 0; i < 2; ++i)
485  {
486  // convert position to local
487  TVector3 g4hit_world(g4hit->get_x(i), g4hit->get_y(i), g4hit->get_z(i));
488  TVector3 g4hit_local = layergeom->get_local_from_world_coords(tileid, m_tGeometry, g4hit_world);
489 
490  // convert momentum to local
491  TVector3 momentum_world(g4hit->get_px(i), g4hit->get_py(i), g4hit->get_pz(i));
492  TVector3 momentum_local = layergeom->get_local_from_world_vect(tileid, m_tGeometry, momentum_world);
493 
494  hits.push_back({.position = g4hit_local, .momentum = momentum_local, .weight = weight});
495  }
496  }
497 
498  // do position interpolation along z
499  const auto z_extrap = cluster_local.z();
500  const TVector3 interpolation_local(
501  interpolate<&interpolation_data_t::x>(hits, z_extrap),
502  interpolate<&interpolation_data_t::y>(hits, z_extrap),
503  interpolate<&interpolation_data_t::z>(hits, z_extrap));
504 
505  // fill phi residuals, errors and pulls
506  auto fill = [](TH1* h, float value)
507  { if( h ) h->Fill( value ); };
508  switch (segmentation_type)
509  {
511  {
512  const auto drphi = cluster_local.x() - interpolation_local.x();
513  fill(hiter->second.residual, drphi);
514  fill(hiter->second.residual_error, rphi_error);
515  fill(hiter->second.pulls, drphi / rphi_error);
516  break;
517  }
518 
520  {
521  const auto dz = cluster_local.y() - interpolation_local.y();
522  fill(hiter->second.residual, dz);
523  fill(hiter->second.residual_error, z_error);
524  fill(hiter->second.pulls, dz / z_error);
525  break;
526  }
527  }
528 
529  // cluster size
530  // get associated hits
531  const auto hit_range = m_cluster_hit_map->getHits(ckey);
532  fill(hiter->second.csize, std::distance(hit_range.first, hit_range.second));
533  }
534  }
535 }
536 
537 //_____________________________________________________________________
539 {
540  // find hitset associated to cluster
541  G4HitSet out;
542  const auto hitset_key = TrkrDefs::getHitSetKeyFromClusKey(cluster_key);
543 
544  // loop over hits associated to clusters
545  const auto range = m_cluster_hit_map->getHits(cluster_key);
546  for (auto iter = range.first; iter != range.second; ++iter)
547  {
548  // hit key
549  const auto& hit_key = iter->second;
550 
551  // store hits to g4hit associations
552  TrkrHitTruthAssoc::MMap g4hit_map;
553  m_hit_truth_map->getG4Hits(hitset_key, hit_key, g4hit_map);
554 
555  // find corresponding g4 hist
556  for (auto truth_iter = g4hit_map.begin(); truth_iter != g4hit_map.end(); ++truth_iter)
557  {
558  // g4hit key
559  const auto g4hit_key = truth_iter->second.second;
560 
561  // g4 hit
562  PHG4Hit* g4hit = (TrkrDefs::getTrkrId(hitset_key) == TrkrDefs::micromegasId) ? m_g4hits_micromegas->findHit(g4hit_key) : nullptr;
563 
564  // insert in set
565  if (g4hit) out.insert(g4hit);
566  }
567  }
568 
569  return out;
570 }