Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
TpcSpaceChargeReconstruction.cc
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file TpcSpaceChargeReconstruction.cc
1 
10 
14 
15 #include <phool/getClass.h>
16 #include <phool/PHCompositeNode.h>
17 #include <phool/PHNodeIterator.h>
18 
20 
21 #include <trackbase/ActsGeometry.h>
22 #include <trackbase/TpcDefs.h>
23 #include <trackbase/TrkrCluster.h>
25 
28 
29 #include <TFile.h>
30 #include <TH1.h>
31 #include <TH2.h>
32 
33 #include <cassert>
34 #include <memory>
35 
36 namespace
37 {
38 
40  template<class T> inline constexpr T square( const T& x ) { return x*x; }
41 
43  template< class T>
44  inline constexpr T delta_phi( const T& phi )
45  {
46  if( phi >= M_PI ) return phi - 2*M_PI;
47  else if( phi < -M_PI ) return phi + 2*M_PI;
48  else return phi;
49  }
50 
52  template<class T> T get_r( const T& x, const T& y ) { return std::sqrt( square(x) + square(y) ); }
53 
55 
56  inline constexpr double get_sector_phi( int isec )
57  { return isec*M_PI/6; }
58 
59  // specify bins for which one will save histograms
60  static const std::vector<float> phi_rec = { get_sector_phi(9) };
61  static const std::vector<float> z_rec = { 5. };
62 
63  // phi range
64  static constexpr float m_phimin = 0;
65  static constexpr float m_phimax = 2.*M_PI;
66 
67  // TODO: could try to get the r and z range from TPC geometry
68  // r range
69  static constexpr float m_rmin = 20;
70  static constexpr float m_rmax = 78;
71 
72  // z range
73  static constexpr float m_zmin = -105.5;
74  static constexpr float m_zmax = 105.5;
75 
77  std::vector<TrkrDefs::cluskey> get_cluster_keys( SvtxTrack* track )
78  {
79  std::vector<TrkrDefs::cluskey> out;
80  for( const auto& seed: { track->get_silicon_seed(), track->get_tpc_seed() } )
81  {
82  if( seed )
83  { std::copy( seed->begin_cluster_keys(), seed->end_cluster_keys(), std::back_inserter( out ) ); }
84  }
85  return out;
86  }
87 
89  template<int type>
90  int count_clusters( const std::vector<TrkrDefs::cluskey>& keys )
91  {
92  return std::count_if( keys.begin(), keys.end(),
93  []( const TrkrDefs::cluskey& key ) { return TrkrDefs::getTrkrId(key) == type; } );
94  }
95 
96  [[maybe_unused]] std::ostream& operator << (std::ostream& out, const Acts::Vector3& vector )
97  {
98  out << "(" << vector.x() << ", " << vector.y() << ", " << vector.z() << ")";
99  return out;
100  }
101 
102 }
103 
104 //_____________________________________________________________________
106  SubsysReco( name)
107  , PHParameterInterface(name)
108  , m_matrix_container( new TpcSpaceChargeMatrixContainerv1 )
109 {
111 }
112 
113 //_____________________________________________________________________
115 { m_matrix_container->set_grid_dimensions( phibins, rbins, zbins ); }
116 
117 //_____________________________________________________________________
119 {
120  // reset counters
121  m_total_tracks = 0;
122  m_accepted_tracks = 0;
123 
124  m_total_clusters = 0;
126 
127  // histogram evaluation
129 
131 }
132 
133 //_____________________________________________________________________
135 {
136 
137  // load parameters
139  m_max_talpha = get_double_param( "spacecharge_max_talpha" );
140  m_max_drphi = get_double_param( "spacecharge_max_drphi" );
141  m_max_tbeta = get_double_param( "spacecharge_max_tbeta" );
142  m_max_dz = get_double_param( "spacecharge_max_dz" );
143 
144  // print
145  std::cout << "TpcSpaceChargeReconstruction::InitRun - m_outputfile: " << m_outputfile << std::endl;
146  std::cout << "TpcSpaceChargeReconstruction::InitRun - m_use_micromegas: " << std::boolalpha << m_use_micromegas << std::endl;
147  std::cout << "TpcSpaceChargeReconstruction::InitRun - m_max_talpha: " << m_max_talpha << std::endl;
148  std::cout << "TpcSpaceChargeReconstruction::InitRun - m_max_drphi: " << m_max_drphi << std::endl;
149  std::cout << "TpcSpaceChargeReconstruction::InitRun - m_max_tbeta: " << m_max_tbeta << std::endl;
150  std::cout << "TpcSpaceChargeReconstruction::InitRun - m_max_dz: " << m_max_dz << std::endl;
151  std::cout << "TpcSpaceChargeReconstruction::InitRun - m_min_pt: " << m_min_pt << " GeV/c" << std::endl;
152 
153  // also identify the matrix container
154  m_matrix_container->identify();
155 
157 }
158 
159 //_____________________________________________________________________
161 {
162 
163  // increment local event counter
164  ++m_event;
165 
166  // load nodes
167  const auto res = load_nodes(topNode);
168  if( res != Fun4AllReturnCodes::EVENT_OK ) return res;
169 
170  process_tracks();
172 }
173 
174 //_____________________________________________________________________
176 {
177 
178  // save matrix container in output file
179  if( m_matrix_container )
180  {
181  std::unique_ptr<TFile> outputfile( TFile::Open( m_outputfile.c_str(), "RECREATE" ) );
182  outputfile->cd();
183  m_matrix_container->Write( "TpcSpaceChargeMatrixContainer" );
184  }
185 
186  // save histograms
188  {
189  m_histogramfile->cd();
190  for( const auto& [cell,h]:m_h_drphi ) { if(h) h->Write(); }
191  for( const auto& [cell,h]:m_h_dz ) { if(h) h->Write(); }
192  for( const auto& [cell,h]:m_h_drphi_alpha ) { if(h) h->Write(); }
193  for( const auto& [cell,h]:m_h_dz_beta ) { if(h) h->Write(); }
194  m_histogramfile->Close();
195  }
196 
197  // print counters
198  std::cout
199  << "TpcSpaceChargeReconstruction::End -"
200  << " track statistics total: " << m_total_tracks
201  << " accepted: " << m_accepted_tracks
202  << " fraction: " << 100.*m_accepted_tracks/m_total_tracks << "%"
203  << std::endl;
204 
205  std::cout
206  << "TpcSpaceChargeReconstruction::End -"
207  << " cluster statistics total: " << m_total_clusters
208  << " accepted: " << m_accepted_clusters << " fraction: "
210  << std::endl;
211 
213 }
214 
215 //___________________________________________________________________________
217 {
218  // residual cuts
219  set_default_double_param( "spacecharge_max_talpha", 0.6 );
220  set_default_double_param( "spacecharge_max_drphi", 0.5 );
221  set_default_double_param( "spacecharge_max_tbeta", 1.5 );
222  set_default_double_param( "spacecharge_max_dz", 0.5 );
223 }
224 
225 //_____________________________________________________________________
227 {
228  // get necessary nodes
229  m_tgeometry = findNode::getClass<ActsGeometry>(topNode,"ActsGeometry");
230  assert( m_tgeometry );
231 
232  m_track_map = findNode::getClass<SvtxTrackMap>(topNode, "SvtxTrackMap");
234 
235  m_cluster_map = findNode::getClass<TrkrClusterContainer>(topNode,"CORRECTED_TRKR_CLUSTER");
236  if(m_cluster_map)
237  {
238 
239  if( m_event < 2 )
240  { std::cout << "TpcSpaceChargeReconstruction::load_nodes - Using CORRECTED_TRKR_CLUSTER node " << std::endl; }
241 
242  } else {
243 
244  if( m_event < 2 )
245  { std::cout << "TpcSpaceChargeReconstruction::load_nodes - CORRECTED_TRKR_CLUSTER node not found, using TRKR_CLUSTER" << std::endl; }
246  m_cluster_map = findNode::getClass<TrkrClusterContainer>(topNode,"TRKR_CLUSTER");
247 
248  }
249 
251 
252 
253  // tpc distortion corrections
254  m_dcc_static = findNode::getClass<TpcDistortionCorrectionContainer>(topNode,"TpcDistortionCorrectionContainerStatic");
255  m_dcc_average = findNode::getClass<TpcDistortionCorrectionContainer>(topNode,"TpcDistortionCorrectionContainerAverage");
256  m_dcc_fluctuation = findNode::getClass<TpcDistortionCorrectionContainer>(topNode,"TpcDistortionCorrectionContainerFluctuation");
257 
259 }
260 
261 //_____________________________________________________________________
263 {
264  std::cout << "TpcSpaceChargeReconstruction::create_histograms - writing evaluation histograms to: " << m_histogramfilename << std::endl;
265  m_histogramfile.reset( new TFile(m_histogramfilename.c_str(), "RECREATE") );
266  m_histogramfile->cd();
267 
268  // get grid dimensions from matrix container
269  int phibins = 0;
270  int rbins = 0;
271  int zbins = 0;
272  m_matrix_container->get_grid_dimensions( phibins, rbins, zbins );
273 
274  // get bins corresponding to selected angles
275  std::set<int> phibin_rec;
276  std::transform( phi_rec.begin(), phi_rec.end(), std::inserter( phibin_rec, phibin_rec.end() ), [&]( const float& phi ) { return phibins*(phi-m_phimin)/(m_phimax-m_phimin); } );
277 
278  std::set<int> zbin_rec;
279  std::transform( z_rec.begin(), z_rec.end(), std::inserter( zbin_rec, zbin_rec.end() ), [&]( const float& z ) { return zbins*(z-m_zmin)/(m_zmax-m_zmin); } );
280 
281  // keep track of all cell ids that match selected histograms
282  for( int iphi = 0; iphi < phibins; ++iphi )
283  for( int ir = 0; ir < rbins; ++ir )
284  for( int iz = 0; iz < zbins; ++iz )
285  {
286 
287  if( phibin_rec.find( iphi ) == phibin_rec.end() || zbin_rec.find( iz ) == zbin_rec.end() ) continue;
288  const auto icell = m_matrix_container->get_cell_index( iphi, ir, iz );
289 
290  {
291  // rphi residuals
292  const auto hname = Form( "residual_drphi_p%i_r%i_z%i", iphi, ir, iz );
293  auto h = new TH1F( hname, hname, 100, -m_max_drphi, +m_max_drphi );
294  h->GetXaxis()->SetTitle( "r.#Delta#phi_{cluster-track} (cm)" );
295  m_h_drphi.insert( std::make_pair( icell, h ) );
296  }
297 
298  {
299  // 2D histograms
300  const auto hname = Form( "residual_2d_drphi_p%i_r%i_z%i", iphi, ir, iz );
301  auto h = new TH2F( hname, hname, 100, -m_max_talpha, m_max_talpha, 100, -m_max_drphi, +m_max_drphi );
302  h->GetXaxis()->SetTitle( "tan#alpha" );
303  h->GetYaxis()->SetTitle( "r.#Delta#phi_{cluster-track} (cm)" );
304  m_h_drphi_alpha.insert( std::make_pair( icell, h ) );
305  }
306 
307  {
308  // z residuals
309  const auto hname = Form( "residual_dz_p%i_r%i_z%i", iphi, ir, iz );
310  auto h = new TH1F( hname, hname, 100, -m_max_dz, +m_max_dz );
311  h->GetXaxis()->SetTitle( "#Deltaz_{cluster-track} (cm)" );
312  m_h_dz.insert( std::make_pair( icell, h ) );
313  }
314 
315  {
316  // 2D histograms
317  static constexpr double max_tbeta = 0.5;
318  const auto hname = Form( "residual_2d_dz_p%i_r%i_z%i", iphi, ir, iz );
319  auto h = new TH2F( hname, hname, 100, -max_tbeta, max_tbeta, 100, -m_max_dz, +m_max_dz );
320  h->GetXaxis()->SetTitle( "tan#beta" );
321  h->GetYaxis()->SetTitle( "#Deltaz_{cluster-track} (cm)" );
322  m_h_dz_beta.insert( std::make_pair( icell, h ) );
323  }
324  }
325 }
326 
327 //_________________________________________________________________________________
329 {
330  // get global position from Acts transform
331  auto globalPosition = m_tgeometry->getGlobalPosition(key, cluster);
332 
333  // for the TPC calculate the proper z based on crossing and side
334  const auto trkrid = TrkrDefs::getTrkrId(key);
335  if(trkrid == TrkrDefs::tpcId)
336  {
337  const auto side = TpcDefs::getSide(key);
338  globalPosition.z() = m_clusterCrossingCorrection.correctZ(globalPosition.z(), side, crossing);
339 
340  // apply distortion corrections
341  if(m_dcc_static)
342  {
343  globalPosition = m_distortionCorrection.get_corrected_position( globalPosition, m_dcc_static );
344  }
345 
346  if(m_dcc_average)
347  {
348  globalPosition = m_distortionCorrection.get_corrected_position( globalPosition, m_dcc_average );
349  }
350 
351  if(m_dcc_fluctuation)
352  {
353  globalPosition = m_distortionCorrection.get_corrected_position( globalPosition, m_dcc_fluctuation );
354  }
355  }
356 
357  return globalPosition;
358 }
359 
360 //_____________________________________________________________________
362 {
363  if( !( m_track_map && m_cluster_map ) ) return;
364 
365  for(const auto &[trackKey, track] : *m_track_map)
366  {
367  ++m_total_tracks;
368  if( accept_track( track ) )
369  {
371  process_track( track );
372  }
373  }
374 }
375 
376 //_____________________________________________________________________
378 {
379 
380  // track pt
381  if(track->get_pt() < m_min_pt)
382  { return false; }
383 
384  // ignore tracks with too few mvtx, intt and micromegas hits
385  const auto cluster_keys( get_cluster_keys( track ) );
386  if( count_clusters<TrkrDefs::mvtxId>(cluster_keys) < 2 ) return false;
387  if( count_clusters<TrkrDefs::inttId>(cluster_keys) < 2 ) return false;
388  if( m_use_micromegas && count_clusters<TrkrDefs::micromegasId>(cluster_keys) < 2 ) return false;
389 
390  // all tests passed
391  return true;
392 }
393 
394 //_____________________________________________________________________
396 {
397 
398  // printout all track state
399  if( Verbosity() )
400  {
401  for( auto&& iter = track->begin_states(); iter != track->end_states(); ++iter )
402  {
403  const auto& [pathlength, state] = *iter;
404  const auto r = std::sqrt( square( state->get_x() ) + square( state->get_y() ));
405  const auto phi = std::atan2( state->get_y(), state->get_x() );
406  std::cout << "TpcSpaceChargeReconstruction::process_track -"
407  << " pathlength: " << pathlength
408  << " radius: " << r
409  << " phi: " << phi
410  << " z: " << state->get_z()
411  << std::endl;
412  }
413  }
414 
415  // store crossing
416  // it is needed for geting cluster's global position
417  const auto crossing = track->get_crossing();
418  assert( crossing != SHRT_MAX );
419 
420  // running track state
421  auto state_iter = track->begin_states();
422 
423  // loop over clusters
424  for( const auto& cluster_key:get_cluster_keys( track ) )
425  {
426 
427  auto cluster = m_cluster_map->findCluster( cluster_key );
428  if( !cluster )
429  {
430  std::cout << PHWHERE << " unable to find cluster for key " << cluster_key << std::endl;
431  continue;
432  }
433 
435 
436  // make sure
437  const auto detId = TrkrDefs::getTrkrId(cluster_key);
438  if(detId != TrkrDefs::tpcId) continue;
439 
440  // cluster r, phi and z
441  const auto global_position = get_global_position( cluster_key, cluster, crossing );
442  const auto cluster_r = get_r( global_position.x(), global_position.y() );
443  const auto cluster_phi = std::atan2( global_position.y(), global_position.x() );
444  const auto cluster_z = global_position.z();
445 
446  // cluster errors
447  double cluster_rphi_error = cluster->getRPhiError();
448  double cluster_z_error = cluster->getZError();
449 
450  /*
451  * as instructed by Christof, it should not be necessary to cut on small
452  * cluster errors any more with clusters of version 4 or higher
453  */
454 
455  // find track state that is the closest to cluster
456  /* this assumes that both clusters and states are sorted along r */
457  float dr_min = -1;
458  for( auto iter = state_iter; iter != track->end_states(); ++iter )
459  {
460  const auto dr = std::abs( cluster_r - get_r( iter->second->get_x(), iter->second->get_y() ) );
461  if( dr_min < 0 || dr < dr_min )
462  {
463  state_iter = iter;
464  dr_min = dr;
465  } else break;
466  }
467 
468 
469  // get relevant track state
470  const auto state = state_iter->second;
471 
472  // extrapolate track parameters to the cluster r
473  const auto track_r = get_r( state->get_x(), state->get_y() );
474  const auto dr = cluster_r - track_r;
475  const auto track_drdt = (state->get_x()*state->get_px() + state->get_y()*state->get_py())/track_r;
476  const auto track_dxdr = state->get_px()/track_drdt;
477  const auto track_dydr = state->get_py()/track_drdt;
478  const auto track_dzdr = state->get_pz()/track_drdt;
479 
480  // store state position
481  const auto track_x = state->get_x() + dr*track_dxdr;
482  const auto track_y = state->get_y() + dr*track_dydr;
483  const auto track_z = state->get_z() + dr*track_dzdr;
484  const auto track_phi = std::atan2( track_y, track_x );
485 
486  // get track angles
487  const auto cosphi( std::cos( track_phi ) );
488  const auto sinphi( std::sin( track_phi ) );
489  const auto track_pphi = -state->get_px()*sinphi + state->get_py()*cosphi;
490  const auto track_pr = state->get_px()*cosphi + state->get_py()*sinphi;
491  const auto track_pz = state->get_pz();
492  const auto talpha = -track_pphi/track_pr;
493  const auto tbeta = -track_pz/track_pr;
494 
495  // sanity check
496  if( std::isnan(talpha) )
497  {
498  std::cout << "TpcSpaceChargeReconstruction::process_track - talpha is nan" << std::endl;
499  continue;
500  }
501 
502  if( std::isnan(tbeta) )
503  {
504  std::cout << "TpcSpaceChargeReconstruction::process_track - tbeta is nan" << std::endl;
505  continue;
506  }
507 
508  // track errors
509  const auto track_rphi_error = state->get_rphi_error();
510  const auto track_z_error = state->get_z_error();
511 
512  // residuals
513  const auto drp = cluster_r*delta_phi( cluster_phi - track_phi );
514  const auto dz = cluster_z - track_z;
515 
516  // sanity checks
517  if( std::isnan(drp) )
518  {
519  std::cout << "TpcSpaceChargeReconstruction::process_track - drp is nan" << std::endl;
520  continue;
521  }
522 
523  if( std::isnan(dz) )
524  {
525  std::cout << "TpcSpaceChargeReconstruction::process_track - dz is nan" << std::endl;
526  continue;
527  }
528 
529  // residual errors squared
530  const auto erp = square(track_rphi_error) + square(cluster_rphi_error);
531  const auto ez = square(track_z_error) + square(cluster_z_error);
532 
533  // sanity check
534  // TODO: check whether this happens and fix upstream
535  if( std::isnan( erp ) )
536  {
537  std::cout << "TpcSpaceChargeReconstruction::process_track - erp is nan" << std::endl;
538  continue;
539  }
540 
541  if( std::isnan( ez ) )
542  {
543  std::cout << "TpcSpaceChargeReconstruction::process_track - ez is nan" << std::endl;
544  continue;
545  }
546 
547  // get cell
548  const auto i = get_cell_index( global_position );
549  if( i < 0 )
550  {
551  std::cout << "TpcSpaceChargeReconstruction::process_track - invalid cell index" << std::endl;
552  continue;
553  }
554 
555  if( m_savehistograms )
556  {
557  { const auto iter = m_h_drphi.find( i ); if( iter != m_h_drphi.end() ) iter->second->Fill( drp ); }
558  { const auto iter = m_h_drphi_alpha.find( i ); if( iter != m_h_drphi_alpha.end() ) iter->second->Fill( talpha, drp ); }
559  { const auto iter = m_h_dz.find( i ); if( iter != m_h_dz.end() ) iter->second->Fill( dz ); }
560  { const auto iter = m_h_dz_beta.find( i ); if( iter != m_h_dz_beta.end() ) iter->second->Fill( tbeta, dz ); }
561  }
562 
563  // check against limits
564  if( std::abs( talpha ) > m_max_talpha ) continue;
565  if( std::abs( tbeta ) > m_max_tbeta ) continue;
566 
567  // check against limits
568  if( std::abs( drp ) > m_max_drphi ) continue;
569  if( std::abs( dz ) > m_max_dz ) continue;
570 
571  if( Verbosity() )
572  {
573  std::cout << "TpcSpaceChargeReconstruction::process_track - layer: " << (int) TrkrDefs::getLayer(cluster_key) << std::endl;
574  std::cout << "TpcSpaceChargeReconstruction::process_track -"
575  << " cluster: (" << cluster_r << ", " << cluster_r*cluster_phi << ", " << cluster_z << ")"
576  << " (" << cluster_rphi_error << ", " << cluster_z_error << ")"
577  << std::endl;
578  std::cout << "TpcSpaceChargeReconstruction::process_track -"
579  << " track: (" << track_r << ", " << cluster_r*track_phi << ", " << track_z << ")"
580  << " (" << talpha << ", " << tbeta << ")"
581  << " (" << track_rphi_error << ", " << track_z_error << ")"
582  << std::endl;
583  std::cout << std::endl;
584  }
585 
586  // update matrices
587  // see https://indico.bnl.gov/event/7440/contributions/43328/attachments/31334/49446/talk.pdf for details
588  m_matrix_container->add_to_lhs(i, 0, 0, 1./erp );
589  m_matrix_container->add_to_lhs(i, 0, 1, 0 );
590  m_matrix_container->add_to_lhs(i, 0, 2, talpha/erp );
591 
592  m_matrix_container->add_to_lhs(i, 1, 0, 0 );
593  m_matrix_container->add_to_lhs(i, 1, 1, 1./ez );
594  m_matrix_container->add_to_lhs(i, 1, 2, tbeta/ez );
595 
596  m_matrix_container->add_to_lhs(i, 2, 0, talpha/erp );
597  m_matrix_container->add_to_lhs(i, 2, 1, tbeta/ez );
598  m_matrix_container->add_to_lhs(i, 2, 2, square(talpha)/erp + square(tbeta)/ez );
599 
600  m_matrix_container->add_to_rhs(i, 0, drp/erp );
601  m_matrix_container->add_to_rhs(i, 1, dz/ez );
602  m_matrix_container->add_to_rhs(i, 2, talpha*drp/erp + tbeta*dz/ez );
603 
604  // update entries in cell
605  m_matrix_container->add_to_entries(i);
606 
607  // increment number of accepted clusters
609 
610  }
611 
612 }
613 
614 //_____________________________________________________________________
616 {
617  // get grid dimensions from matrix container
618  int phibins = 0;
619  int rbins = 0;
620  int zbins = 0;
621  m_matrix_container->get_grid_dimensions( phibins, rbins, zbins );
622 
623  // phi
624  // bound check
625  float phi = std::atan2( global_position.y(), global_position.x() );
626  while( phi < m_phimin ) phi += 2.*M_PI;
627  while( phi >= m_phimax ) phi -= 2.*M_PI;
628  int iphi = phibins*(phi-m_phimin)/(m_phimax-m_phimin);
629 
630  // radius
631  const float r = get_r( global_position.x(), global_position.y() );
632  if( r < m_rmin || r >= m_rmax ) return -1;
633  int ir = rbins*(r-m_rmin)/(m_rmax-m_rmin);
634 
635  // z
636  const float z = global_position.z();
637  if( z < m_zmin || z >= m_zmax ) return -1;
638  int iz = zbins*(z-m_zmin)/(m_zmax-m_zmin);
639 
640  return m_matrix_container->get_cell_index( iphi, ir, iz );
641 }