Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
SecondaryVertexFinder.cc
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file SecondaryVertexFinder.cc
2 #include "ActsPropagator.h"
3 
5 
6 #include <trackbase/TrkrCluster.h> // for TrkrCluster
7 #include <trackbase/TrkrDefs.h> // for cluskey, getLayer, TrkrId
10 #include <trackbase/ActsGeometry.h>
11 #include <trackbase/TpcDefs.h>
12 
13 #include <trackbase_historic/SvtxTrack.h> // for SvtxTrack, SvtxTrack::C...
19 
23 
24 #include <phool/PHCompositeNode.h>
25 #include <phool/PHNode.h>
26 #include <phool/PHNodeIterator.h>
28 
29 #include <phool/getClass.h>
30 #include <phool/phool.h>
31 
32 #include <cmath> // for sqrt, fabs, atan2, cos
33 #include <iostream> // for operator<<, basic_ostream
34 #include <map> // for map
35 #include <set> // for _Rb_tree_const_iterator
36 #include <utility> // for pair, make_pair
37 #include <iomanip>
38 
39 #include <vector>
40 #include <cassert>
41 #include <numeric>
42 #include <iostream>
43 #include <algorithm>
44 #include <functional>
45 
46 #include <Eigen/Dense>
47 
48 #include <TLorentzVector.h>
49 #include <TNtuple.h>
50 #include <TH2D.h>
51 #include <TH1D.h>
52 #include <TFile.h>
53 
54 //____________________________________________________________________________..
56  : SubsysReco(name)
57 {
58 
59 }
60 
61 //____________________________________________________________________________..
63 {
64 
65 }
66 
67 //____________________________________________________________________________..
69 {
71  {
72  CreateOutputNode(topNode);
73  }
74 
75  int ret = GetNodes(topNode);
76  if (ret != Fun4AllReturnCodes::EVENT_OK) return ret;
77 
78  if(_write_ntuple)
79  {
80  recomass = new TH2D("recomass", "invariant mass vs pT", 1000, 0, 5, 4000,0,2);
81  hdecaypos = new TH2D("hdecaypos","decay radius",200, -40, 40, 200,-40,40);
82  hdecaypos->GetXaxis()->SetTitle("decay X (cm)");
83  hdecaypos->GetYaxis()->SetTitle("decay Y (cm)");
84  hdecay_radius = new TH1D("hdecay_radius", "Decay Radius", 200, 0, 40.0);
85 
86  ntp = new TNtuple("ntp","decay_pairs","x1:y1:z1:px1:py1:pz1:dca3dxy1:dca3dz1:vposx1:vposy1:vposz1:vmomx1:vmomy1:vmomz1:pca_relx_1:pca_rely_1:pca_relz_1:eta1:charge1:tpcClusters_1:quality1:eta1:x2:y2:z2:px2:py2:pz2:dca3dxy2:dca3dz2:vposx2:vposy2:vposz2:vmomx2:vmomy2:vmomz2:pca_relx_2:pca_rely_2:pca_relz_2:eta2:charge2:tpcClusters_2:quality2:eta2:vertex_x:vertex_y:vertex_z:pair_dca:invariant_mass:invariant_pt:path:has_silicon1:has_silicon2");
87  }
88 
89  return ret;
90 }
91 
92 void SecondaryVertexFinder::fillNtp(SvtxTrack *track1, SvtxTrack *track2, double dca3dxy1, double dca3dz1, double dca3dxy2, double dca3dz2, Eigen::Vector3d vpos1, Eigen::Vector3d vmom1, Eigen::Vector3d vpos2, Eigen::Vector3d vmom2, Acts::Vector3 pca_rel1, Acts::Vector3 pca_rel2, double pair_dca, double invariantMass, double invariantPt, double path, int has_silicon_1, int has_silicon_2)
93 {
94  double px1 = track1->get_px();
95  double py1 = track1->get_py();
96  double pz1 = track1->get_pz();
97  auto tpcSeed1 = track1->get_tpc_seed();
98  size_t tpcClusters1 = tpcSeed1->size_cluster_keys();
99  double eta1 = asinh(pz1 / sqrt(pow(px1, 2) + pow(py1, 2)));
100 
101  double px2 = track2->get_px();
102  double py2 = track2->get_py();
103  double pz2 = track2->get_pz();
104  auto tpcSeed2 = track2->get_tpc_seed();
105  size_t tpcClusters2 = tpcSeed2->size_cluster_keys();
106  double eta2 = asinh(pz2 / sqrt(pow(px2, 2) + pow(py2, 2)));
107 
108  auto vtxid = track1->get_vertex_id();
109  auto svtxVertex = _svtx_vertex_map->get(vtxid);
110  if(!svtxVertex){ return; }
111 
112  float reco_info[] = {
113  track1->get_x(), track1->get_y(), track1->get_z(),
114  track1->get_px(), track1->get_py(), track1->get_pz(),
115  (float) dca3dxy1, (float) dca3dz1, (float) vpos1(0), (float) vpos1(1), (float) vpos1(2),
116  (float) vmom1(0), (float) vmom1(1), (float) vmom1(2),
117  (float) pca_rel1(0), (float) pca_rel1(1), (float) pca_rel1(2),
118  (float) eta1, (float) track1->get_charge(), (float) tpcClusters1,
119  (float) track1->get_quality(), (float) eta1,
120  track2->get_x(), track2->get_y(), track2->get_z(),
121  track2->get_px(), track2->get_py(), track2->get_pz(),
122  (float) dca3dxy2, (float) dca3dz2, (float) vpos2(0), (float) vpos2(1), (float) vpos2(2),
123  (float) vmom2(0), (float) vmom2(1), (float) vmom2(2),
124  (float) pca_rel2(0), (float) pca_rel2(1), (float) pca_rel2(2),
125  (float) eta2, (float) track2->get_charge(), (float) tpcClusters2,
126  (float) track2->get_quality(), (float) eta2,
127  svtxVertex->get_x(), svtxVertex->get_y(), svtxVertex->get_z(),
128  (float) pair_dca,(float) invariantMass, (float) invariantPt, (float) path,
129  (float) has_silicon_1, (float) has_silicon_2};
130 
131 
132  ntp->Fill(reco_info);
133 }
134 
135 //____________________________________________________________________________..
137 {
138  if(Verbosity() > 0)
139  std::cout << PHWHERE << " track map size " << _track_map->size() << std::endl;
140 
141  // Loop over tracks and check for close DCA match with all other tracks
142  for(auto tr1_it = _track_map->begin(); tr1_it != _track_map->end(); ++tr1_it)
143  {
144  auto id1 = tr1_it->first;
145  auto tr1 = tr1_it->second;
146 
147  auto vertexId = tr1->get_vertex_id();
148  const SvtxVertex* svtxVertex = _svtx_vertex_map->get(vertexId);
149  if(!svtxVertex)
150  {
151  if(Verbosity() > 1)
152  { std::cout << PHWHERE << " Failed to find vertex id " << vertexId << " skip this track " << std::endl; }
153  continue;
154  }
155  if(svtxVertex->size_tracks() == 0) continue; // event did not have a reconstructed vertex, vertex is bogus
156 
157  // Reverse or remove this to consider TPC-only tracks too
158  if(_require_mvtx && !hasSiliconSeed(tr1)) continue;
159 
160  int has_silicon_1 = 0;
161  if(hasSiliconSeed(tr1)) has_silicon_1 = 1;
162 
163 
164  if(Verbosity() > 3)
165  {
166  std::cout << "Track1 " << id1 << " details: " << std::endl;
167  outputTrackDetails(tr1);
168  }
169 
170  if(tr1->get_quality() > _qual_cut) continue;
171 
172  auto tpc_seed1 = tr1->get_tpc_seed();
173  int ntpc1 = tpc_seed1->size_cluster_keys();
174  if(ntpc1 < 20) continue;
175 
176  float dca3dxy1, dca3dz1,dca3dxysigma1, dca3dzsigma1;
177  get_dca(tr1, dca3dxy1, dca3dz1, dca3dxysigma1, dca3dzsigma1);
178  //std::cout << " tr1 " << id1 << " dca3dxy1 = " << dca3dxy1 << " dca3dz1 = " << dca3dz1 << std::endl;
179 
180  if(!dca3dxy1)
181  {
182  std::cout << " get_dca returned NAN " << std::endl;
183  continue;
184  }
185  if(fabs(dca3dxy1) < _track_dcaxy_cut) continue;
186  if(fabs(dca3dz1) < _track_dcaz_cut) continue;
187 
188  // look for close DCA matches with all other such tracks
189  for(auto tr2_it = std::next(tr1_it); tr2_it != _track_map->end(); ++tr2_it)
190  {
191  auto id2 = tr2_it->first;
192  auto tr2 = tr2_it->second;
193 
194  // Reverse or remove this to consider TPC tracks too
195  if(_require_mvtx && !hasSiliconSeed(tr2)) continue;
196 
197  int has_silicon_2 = 0;
198  if(hasSiliconSeed(tr2)) has_silicon_2 = 1;
199 
200  if(Verbosity() > 3)
201  {
202  std::cout << "Track2 " << id2 << " details: " << std::endl;
203  outputTrackDetails(tr2);
204  }
205 
206  if(tr2->get_charge() == tr1->get_charge()) continue;
207 
208  if(tr2->get_quality() > _qual_cut) continue;
209 
210  auto tpc2_seed = tr2->get_tpc_seed();
211  int ntpc2 = tpc2_seed->size_cluster_keys();
212  if(ntpc2 < 20) continue;
213 
214  float dca3dxy2, dca3dz2,dca3dxysigma2, dca3dzsigma2;
215  get_dca(tr2, dca3dxy2, dca3dz2, dca3dxysigma2, dca3dzsigma2);
216  //std::cout << " tr2 " << id2 << " dca3dxy2 = " << dca3dxy2 << " dca3dz2 = " << dca3dz2 << std::endl;
217  if(!dca3dxy2)
218  {
219  std::cout << " get_dca returned NAN " << std::endl;
220  continue;
221  }
222  if(fabs(dca3dxy2) < _track_dcaxy_cut) continue;
223  if(fabs(dca3dz2) < _track_dcaz_cut) continue;
224 
225  // find DCA and PCA of these two tracks
226  if(Verbosity() > 3)
227  { std::cout << "Check pair DCA for tracks " << id1 << " and " << id2 << std::endl;}
228 
229  double R1;
230  Eigen::Vector2d center1;
231  getCircleXYTrack(tr1, R1, center1);
232  if(Verbosity() > 2)
233  {
234  std::cout << std::endl << "Track 1 circle: " << std::endl;
235  std::cout << " tr1.x " << tr1->get_x() << " tr1.y " << tr1->get_y() << " tr1.z " << tr1->get_z() << " charge " << tr1->get_charge() << std::endl;
236  std::cout << " tr1.px " << tr1->get_px() << " tr1.py " << tr1->get_py() << " tr1.pz " << tr1->get_pz() << std::endl;
237  std::cout << " track1 " << tr1->get_id() << " circle R " << R1 << " (x, y) " << center1(0) << " " << center1(1) << std::endl;
238  }
239  double R2;
240  Eigen::Vector2d center2;
241  getCircleXYTrack(tr2, R2, center2);
242  if(Verbosity() > 2)
243  {
244  std::cout << "Track 2 circle: " << std::endl;
245  std::cout << " tr2.x " << tr2->get_x() << " tr2.y " << tr2->get_y() << " tr2.z " << tr2->get_z() << " charge " << tr2->get_charge() << std::endl;
246  std::cout << " tr2.px " << tr2->get_px() << " tr2.py " << tr2->get_py() << " tr2.pz " << tr2->get_pz() << std::endl;
247  std::cout << " track2 " << tr2->get_id() << " circle R " << R2 << " (x, y) " << center2(0) << " " << center2(1) << std::endl;
248  }
249 
250  std::vector<double> intersections;
251  if( !circle_circle_intersection(R1, center1(0), center1(1), R2, center2(0), center2(1), intersections) )
252  {
253  if(Verbosity() > 2) std::cout << " - no intersections, skip this pair" << std::endl;
254  continue;
255  }
256 
257  Eigen::Vector2d intersection[2];
258  if(intersections.size() == 2)
259  {
260  intersection[0](0) = intersections[0];
261  intersection[0](1) = intersections[1];
262  }
263  if(intersections.size() == 4)
264  {
265  intersection[1](0) = intersections[2];
266  intersection[1](1) = intersections[3];
267  }
268 
269  // process both intersections
270  for(int i=0; i<2; ++i)
271  {
272  if(intersection[i].norm() == 0) continue;
273 
274  double vradius = sqrt(intersection[i](0)*intersection[i](0) + intersection[i](1) * intersection[i](1));
275  if(vradius > _max_intersection_radius) continue;
276 
277  double z1 = getZFromIntersectionXY(tr1, R1, center1, intersection[i]);
278  double z2 = getZFromIntersectionXY(tr2, R2, center2, intersection[i]);
279  if(Verbosity() > 2)
280  std::cout << " track intersection " << i << " at (x,y) " << intersection[i](0) << " " << intersection[i](1)
281  << " radius " << vradius << " est. z1 " << z1 << " est. z2 " << z2 << std::endl;
282 
283  if( fabs(z1-z2) > 2.0) // skip this intersection, it is not good
284  {
285  if(Verbosity() > 2) std::cout << " z-mismatch - wrong intersection, skip it " << std::endl;
286  continue;
287  }
288 
289  Eigen::Vector3d vpos1(0,0,0), vmom1(0,0,0);
290  Eigen::Vector3d vpos2(0,0,0), vmom2(0,0,0);
291 
292  // Project the tracks to the intersection
293  Eigen::Vector3d intersect1(intersection[i](0), intersection[i](1), z1);
294  if(!projectTrackToPoint(tr1, intersect1, vpos1, vmom1)) continue;
295  if(Verbosity() > 2)
296  {
297  std::cout << " Projected track 1 to point " << intersect1(0) << " " << intersect1(1) << " " << intersect1(2) << std::endl;
298  std::cout << " has vpos " << vpos1(0) << " " << vpos1(1) << " " << vpos1(2) << std::endl;
299  }
300  Eigen::Vector3d intersect2(intersection[i](0), intersection[i](1), z2);
301  if(!projectTrackToPoint(tr2, intersect2, vpos2, vmom2)) continue;
302  if(Verbosity() > 2)
303  {
304  std::cout << " Projected track 2 to point " << intersect2(0) << " " << intersect2(1) << " " << intersect2(2) << std::endl;
305  std::cout << " has vpos " << vpos2(0) << " " << vpos2(1) << " " << vpos2(2) << std::endl;
306  }
307 
308  // check that the z positions are close
309  if(fabs(vpos1(2) - vpos2(2)) > _projected_track_z_cut)
310  {
311  if(Verbosity() > 0)
312  { std::cout << " Warning: projected z positions are screwed up, should not be" << std::endl; }
313  continue;
314  }
315 
316  if(Verbosity() > 2)
317  {
318  std::cout << "Summary for projected pair:" << std::endl;
319  std::cout << " Fitted tracks: " << std::endl;
320  std::cout << " tr1.x " << tr1->get_x() << " tr1.y " << tr1->get_y() << " tr1.z " << tr1->get_z() << std::endl;
321  std::cout << " tr2.x " << tr2->get_x() << " tr2.y " << tr2->get_y() << " tr2.z " << tr2->get_z() << std::endl;
322  std::cout << " tr1.px " << tr1->get_px() << " tr1.py " << tr1->get_py() << " tr1.pz " << tr1->get_pz() << std::endl;
323  std::cout << " tr2.px " << tr2->get_px() << " tr2.py " << tr2->get_py() << " tr2.pz " << tr2->get_pz() << std::endl;
324  std::cout << " Projected tracks: " << std::endl;
325  std::cout << " pos1.x " << vpos1(0) << " pos1.y " << vpos1(1) << " pos1.z " << vpos1(2) << std::endl;
326  std::cout << " pos2.x " << vpos2(0) << " pos2.y " << vpos2(1) << " pos2.z " << vpos2(2) << std::endl;
327  std::cout << " mom1.x " << vmom1(0) << " mom1.y " << vmom1(1) << " mom1.z " << vmom1(2) << std::endl;
328  std::cout << " mom2.x " << vmom2(0) << " mom2.y " << vmom2(1) << " mom2.z " << vmom2(2) << std::endl;
329  }
330 
331  // Improve the pair dca using a local straight line approximation
332  double pair_dca;
333  Eigen::Vector3d PCA1(0,0,0), PCA2(0,0,0);
334  findPcaTwoLines(vpos1, vmom1, vpos2, vmom2, pair_dca, PCA1, PCA2);
335  if(Verbosity() > 2 )
336  {
337  std::cout << " pair_dca " << pair_dca << " two_track_dcacut " << _two_track_dcacut << std::endl;
338  std::cout << " PCA1 " << PCA1(0) << " " << PCA1(1) << " " << PCA1(2) << std::endl;
339  std::cout << " PCA2 " << PCA2(0) << " " << PCA2(1) << " " << PCA2(2) << std::endl;
340  }
341 
342  if(fabs(pair_dca) > _two_track_dcacut) { continue; }
343 
344  // calculate the invariant mass using the track states at the decay vertex
345 
346  TLorentzVector t1;
347  Float_t E1 = sqrt(pow(vmom1(0),2) + pow(vmom1(1),2) + pow(vmom1(2),2)
348  + pow(_decaymass,2));
349  t1.SetPxPyPzE(vmom1(0),vmom1(1),vmom1(2),E1);
350 
351  TLorentzVector t2;
352  Float_t E2 = sqrt(pow(vmom2(0),2) + pow(vmom2(1),2) + pow(vmom2(2),2)
353  + pow(_decaymass,2));
354  t2.SetPxPyPzE(vmom2(0),vmom2(1),vmom2(2),E2);
355 
356  TLorentzVector tsum = t1+t2;
357 
358  // calculate the decay length
359  Eigen::Vector3d PCA = (vpos1+vpos2) / 2.0; // average the PCA of the track pair
360  auto vtxid = tr1->get_vertex_id();
361  auto vertex1 = _svtx_vertex_map->get(vtxid);
362  Eigen::Vector3d VTX(vertex1->get_x(), vertex1->get_y(), vertex1->get_z());
363  Eigen::Vector3d path = PCA - VTX;
364  double decay_radius = sqrt( pow(PCA(0),2) + pow(PCA(1),2) );
365 
366  if(path.norm() > _min_path_cut)
367  {
368  if(Verbosity() > 0)
369  {
370  std::cout << " Pair mass " << tsum.M() << " pair pT " << tsum.Pt()
371  << " decay length " << path.norm() << std::endl;
372  }
373 
374  if(_write_ntuple)
375  {
376  fillNtp(tr1, tr2, dca3dxy1, dca3dz1, dca3dxy2, dca3dz2, vpos1, vmom1, vpos2, vmom2,
377  PCA1, PCA2, pair_dca, tsum.M(), tsum.Pt(), path.norm(), has_silicon_1, has_silicon_2);
378  }
379 
381  {
382  if(passConversionElectronCuts(tsum, tr1, tr2, pair_dca, PCA, VTX))
383  {
384  if(Verbosity() > 0)
385  std::cout << " **** inserting tracks " << tr1->get_id() << " and " << tr2->get_id() << std::endl;
386 
387  // Add decay particles to output node
388  _track_map_electrons->insertWithKey(tr1, tr1->get_id());
389  _track_map_electrons->insertWithKey(tr2, tr2->get_id());
390 
391  if(_write_ntuple)
392  {
393  // these are just to check on the effect of the cuts
394  recomass->Fill(tsum.Pt(), tsum.M());
395  hdecay_radius->Fill(decay_radius);
396  hdecaypos->Fill( PCA(0), PCA(1));
397  }
398  }
399  }
400  }
401  }
402  }
403 
404  }
405 
406  if(Verbosity() > 0)
407  {
408  std::cout << PHWHERE << " electron track map size " << _track_map_electrons->size() << std::endl;
409  for(auto tr_it = _track_map_electrons->begin(); tr_it != _track_map_electrons->end(); ++tr_it)
410  {
411  auto id = tr_it->first;
412  auto tr = tr_it->second;
413 
414  std::cout << " Electron track " << id
415  << " x " << tr->get_x() << " y " << tr->get_y() << " z " << tr->get_z()
416  << " px " << tr->get_px() << " py " << tr->get_py() << " pz " << tr->get_pz()
417  << std::endl;
418  }
419  }
420 
422 }
423 
424 bool SecondaryVertexFinder::passConversionElectronCuts(TLorentzVector tsum, SvtxTrack* tr1, SvtxTrack* tr2, float pair_dca, Eigen::Vector3d PCA, Eigen::Vector3d VTX)
425 {
426  bool pass = false;
427 
428  // additional single track cuts
429  auto tpc_seed1 = tr1->get_tpc_seed();
430  unsigned int ntpc1 = tpc_seed1->size_cluster_keys();
431  auto tpc_seed2 = tr2->get_tpc_seed();
432  unsigned int ntpc2 = tpc_seed2->size_cluster_keys();
433  if(ntpc1 < _min_tpc_clusters || ntpc2 < _min_tpc_clusters) {return pass;}
434 
435  // pair cuts
436  if(fabs(pair_dca) > _conversion_pair_dcacut) {return pass;};
437 
438  if(tsum.M() > _max_mass_cut) {return pass;}
439 
440  if(tsum.Pt() < _invariant_pt_cut) {return pass;}
441 
442  Eigen::Vector3d path = PCA - VTX;
443  if(path.norm() < 0.2) {return pass;}
444 
445  // Angle between path vector and reco pair momentum vector
446  Eigen::Vector3d invariant_mom(tsum.Px(), tsum.Py(), tsum.Pz());
447  double costheta = path.dot(invariant_mom) / (path.norm() * invariant_mom.norm());
448  // std::cout << " costheta " << costheta << std::endl;
449  if(costheta < _costheta_cut) {return pass;};
450 
451  // selects "decays" with zero mass
452  if(fabs(tr1->get_eta() - tr2->get_eta()) > _deta_cut) {return pass;};
453 
454 
455  pass = true;
456  return pass;
457 }
458 
459 double SecondaryVertexFinder::getZFromIntersectionXY(SvtxTrack *track, double& R, Eigen::Vector2d& center, Eigen::Vector2d intersection)
460 {
461  // Starting at the track base position, the path in the XY plane and the path in the Z direction are proportional
462  // They are related by: dpath/dz = pT/pz
463 
464  // The xy path to the intersection is an arc due to a rotation of pT:
465  double phi0 = atan2(track->get_y() - center(1), track->get_x() - center(0));
466  double phi1 = atan2(intersection(1) - center(1), intersection(0) - center(0));
467  double xypath = R * fabs(phi1-phi0);
468 
469  double zpath = xypath * track->get_pz() / track->get_pt();
470  double z = track->get_z() + zpath;
471  if(Verbosity() > 3)
472  {
473  std::cout << " Radius " << R << " center xy " << center(0) << " " << center(1) << " pT " << track->get_pt() << " pz " << track->get_pz() << std::endl;
474  std::cout << " phi0 " << phi0 << " y0 " << track->get_y() << " x0 " << track->get_x() << std::endl;
475  std::cout << " phi1 " << phi1 << " intersection y " << intersection(1) << " intersection x " << intersection(0) << std::endl;
476  std::cout << " xypath " << xypath << " zpath " << zpath << " z0 " << track->get_z() << " z " << z << std::endl;
477  }
478 
479  return z;
480 }
481 
482 void SecondaryVertexFinder::getCircleXYTrack(SvtxTrack *track, double& R, Eigen::Vector2d& center)
483 {
484  // Get the circle equation for the track
485  double Bz = 1.4; // T
486  double x = track->get_x();
487  double y = track->get_y();
488  double px = track->get_px();
489  double py = track->get_py();
490  double pt = track->get_pt();
491  int charge = track->get_charge();
492 
493  // radius of curvature is from pT and B field
494  R = 3.3 * pt / (Bz * (double) charge) * 100; // convert from m to cm, sign changes for negative charge
495 
496  // the normal unit vector in (x,y) space at (x,y) is:
497  Eigen::Vector2d normal(py, -px);
498  normal /= normal.norm();
499 
500  // The circle center is at
501  Eigen::Vector2d base(x,y);
502  center = base + R*normal;
503 
504  // needed the sign to get the direction of the center, now we drop it
505  R = fabs(R);
506 
507  return;
508 
509 }
510 
511 bool SecondaryVertexFinder::projectTrackToPoint(SvtxTrack* track, Eigen::Vector3d& PCA, Eigen::Vector3d& pos, Eigen::Vector3d& mom)
512 {
513 
514  bool ret = true;
515  PCA *= Acts::UnitConstants::cm;
516 
518  auto perigee = Acts::Surface::makeShared<Acts::PerigeeSurface>(PCA);
519 
520  ActsPropagator propagator(_tGeometry);
521  auto params = propagator.makeTrackParams(track, _svtx_vertex_map);
522  if(!params.ok())
523  {
525  return false;
526  }
527 
528  propagator.verbosity(Verbosity());
529  auto result = propagator.propagateTrack(params.value(), perigee); \
530  if(result.ok())
531  {
532  auto resultparams = result.value().second;
533  auto projectionPos = resultparams.position(_tGeometry->geometry().getGeoContext());
534  const auto momentum = resultparams.momentum();
535  pos(0) = projectionPos.x() / Acts::UnitConstants::cm;
536  pos(1) = projectionPos.y() / Acts::UnitConstants::cm;
537  pos(2) = projectionPos.z() / Acts::UnitConstants::cm;
538 
539  mom(0) = momentum.x();
540  mom(1) = momentum.y();
541  mom(2) = momentum.z();
542  }
543  else
544  {
545  if(Verbosity() > 0)
546  {
547  std::cout << " Failed projection of track with: " << std::endl;
548  std::cout << " x,y,z = " << track->get_x() << " " << track->get_y() << " " << track->get_z() << std::endl;
549  std::cout << " px,py,pz = " << track->get_px() << " " << track->get_py() << " " << track->get_pz() << std::endl;
550  std::cout << " to point (x,y,z) = " << PCA(0) / Acts::UnitConstants::cm << " "
551  << PCA(1) / Acts::UnitConstants::cm << " " << PCA(2) / Acts::UnitConstants::cm << std::endl;
552  }
553 
554  ret = false;
555  }
556 
557  return ret;
558 
559 }
560 
562 {
563  auto tpc_seed = tr->get_tpc_seed();
564  int ntpc = tpc_seed->size_cluster_keys();
565 
566  auto silicon_seed = tr->get_silicon_seed();
567 
568  int nsilicon = 0;
569  if(silicon_seed)
570  { nsilicon = silicon_seed->size_cluster_keys(); }
571 
572  auto pt = tr->get_pt();
573  auto eta = tr->get_eta();
574  auto x = tr->get_x();
575  auto y = tr->get_y();
576  auto z = tr->get_z();
577  auto qual = tr->get_quality();
578 
579  std::cout << " ntpc " << ntpc << " nsilicon " << nsilicon << " quality " << qual
580  << " eta " << eta << std::endl;
581  std::cout << " pt " << pt << " x " << x << " y " << y << " z " << z << std::endl;
582 
583  auto vtxid = tr->get_vertex_id();
584  auto vertex = _svtx_vertex_map->get(vtxid);
585  std::cout << " vtxid " << vtxid
586  << " vertex x " << vertex->get_x()
587  << " vertex y " << vertex->get_y()
588  << " vertex z " << vertex->get_z()
589  << std::endl;
590 
591 }
592 
594 {
595  bool ret = false;
596  auto silicon_seed = tr->get_silicon_seed();
597  if(silicon_seed) ret = true;
598 
599  return ret;
600  }
601 
603  float& dca3dxy, float& dca3dz,
604  float& dca3dxysigma, float& dca3dzsigma)
605 {
606  dca3dxy = NAN;
607  Acts::Vector3 pos(track->get_x(),
608  track->get_y(),
609  track->get_z());
610  Acts::Vector3 mom(track->get_px(),
611  track->get_py(),
612  track->get_pz());
613 
614  auto vtxid = track->get_vertex_id();
615  auto svtxVertex = _svtx_vertex_map->get(vtxid);
616  if(!svtxVertex)
617  {
618  std::cout << " Failed to find vertex for track " << std::endl;
619  return;
620  }
621 
622  Acts::Vector3 vertex(svtxVertex->get_x(),
623  svtxVertex->get_y(),
624  svtxVertex->get_z());
625 
626  if(Verbosity() > 3)
627  {
628  std::cout << " track " << track->get_id() << " vertex id is " << vtxid
629  << " vertex is " << svtxVertex->get_x() << " "
630  << svtxVertex->get_y() << " "
631  << svtxVertex->get_z() << std::endl;
632  }
633 
634  pos -= vertex;
635 
637  for(int i = 0; i < 3; ++i)
638  {
639  for(int j = 0; j < 3; ++j)
640  {
641  posCov(i, j) = track->get_error(i, j);
642  }
643  }
644 
645  Acts::Vector3 r = mom.cross(Acts::Vector3(0.,0.,1.));
646  float phi = atan2(r(1), r(0));
647  phi *= -1.0; // rotates vector clockwise to x axis
648 
650  Acts::RotationMatrix3 rot_T;
651  rot(0,0) = cos(phi);
652  rot(0,1) = -sin(phi);
653  rot(0,2) = 0;
654  rot(1,0) = sin(phi);
655  rot(1,1) = cos(phi);
656  rot(1,2) = 0;
657  rot(2,0) = 0;
658  rot(2,1) = 0;
659  rot(2,2) = 1;
660 
661  rot_T = rot.transpose();
662 
663  Acts::Vector3 pos_R = rot * pos;
664  Acts::ActsSquareMatrix<3> rotCov = rot * posCov * rot_T;
665 
666  dca3dxy = pos_R(0);
667  dca3dz = pos_R(2);
668  dca3dxysigma = sqrt(rotCov(0,0));
669  dca3dzsigma = sqrt(rotCov(2,2));
670 }
671 
672 void SecondaryVertexFinder::findPcaTwoLines(Eigen::Vector3d pos1, Eigen::Vector3d mom1, Eigen::Vector3d pos2, Eigen::Vector3d mom2,
673  double &dca, Eigen::Vector3d &PCA1, Eigen::Vector3d &PCA2)
674 {
675  Eigen::Vector3d a1(pos1(0), pos1(1), pos1(2));
676  Eigen::Vector3d b1(mom1(0) / mom1.norm(), mom1(1) / mom1.norm(), mom1(2) / mom1.norm());
677  Eigen::Vector3d a2(pos2(0), pos2(1), pos2(2));
678  Eigen::Vector3d b2(mom2(0) / mom2.norm(), mom2(1) / mom2.norm(), mom2(2) / mom2.norm());
679 
680  // The shortest distance between two skew lines described by
681  // a1 + c * b1, a2 + d * b2
682  // where a1, a2, are vectors representing points on the lines, b1, b2 are direction vectors,
683  // and c and d are scalars, is:
684  // dca = (b1 x b2) .(a2-a1) / |b1 x b2|
685 
686  // bcrossb/mag_bcrossb is a unit vector perpendicular to both direction vectors b1 and b2
687  auto bcrossb = b1.cross(b2);
688  auto mag_bcrossb = bcrossb.norm();
689  // a2-a1 is the vector joining any arbitrary points on the two lines
690  auto aminusa = a2-a1;
691 
692  // The DCA of these two lines is the projection of a2-a1 along the direction of the perpendicular to both
693  // remember that a2-a1 is longer than (or equal to) the dca by definition
694  dca = 999;
695  if( mag_bcrossb != 0)
696  dca = bcrossb.dot(aminusa) / mag_bcrossb;
697  else
698  return; // same track, skip combination
699 
700  // get the points at which the normal to the lines intersect the lines, where the lines are perpendicular
701 
702  double X = b1.dot(b2) - b1.dot(b1) * b2.dot(b2) / b2.dot(b1);
703  double Y = (a2.dot(b2) - a1.dot(b2)) - (a2.dot(b1) - a1.dot(b1)) * b2.dot(b2) / b2.dot(b1) ;
704  double c = Y/X;
705 
706  double F = b1.dot(b1) / b2.dot(b1);
707  double G = - (a2.dot(b1) - a1.dot(b1)) / b2.dot(b1);
708  double d = c * F + G;
709 
710  // then the points of closest approach are:
711  PCA1 = a1+c*b1;
712  PCA2 = a2+d*b2;
713 
714  return;
715 }
716 
717 
718 
719 //===========================
720 // replace with a call to TrackFitUtils
721 //===========================
722 bool SecondaryVertexFinder::circle_circle_intersection(double r0, double x0, double y0, double r1, double x1, double y1, std::vector<double>& intersectionXY)
723 {
724  bool ret = true;
725 
726  Eigen::Vector2d p0(x0,y0);
727  Eigen::Vector2d p1(x1,y1);
728 
729  double d = (p0 - p1).norm();
730 
731  // std::cout << " d " << d << " r1-r0 " << fabs(r1-r0) << std::endl;
732 
733  // no intersection possible
734  if(d < fabs(r1-r0)) return false; // one circle inside the other
735  if(d > r0+r1)
736  {
737  // careful: conversion electrons will look like zero mass decays
738  // fluctuations may cause the circles to (just) not touch - what to do about that?
739  // if d - (r0+r1) < dr, then there is only one PCA, and it is on the line between the two centers
740  double dr = 0.2; // 2 mm
741  if( fabs(d - (r0+r1)) < dr)
742  {
743  // find the closest point on circle 0 to the center of circle 1
744  Eigen::Vector2d u0 = (p1 - p0);
745  u0 /= u0.norm();
746  Eigen::Vector2d PCA0 = p0 + u0 * r0;
747 
748  Eigen::Vector2d u1 = (p0 - p1);
749  u1 /= u1.norm();
750  Eigen::Vector2d PCA1 = p1 + u1 * r1;
751 
752  auto PCA = (PCA0+PCA1) / 2.0;
753  intersectionXY.push_back(PCA(0));
754  intersectionXY.push_back(PCA(1));
755 
756  if(Verbosity() > 2) std::cout << " *** Special case: Barely touching circles: " << " PCA.x, PCA.y " << PCA(0) << " " << PCA(1) << std::endl;
757  return ret;
758  }
759  else
760  return false;
761  }
762 
763  double a=(r0*r0-r1*r1+d*d)/(2*d);
764  double h = sqrt(r0*r0 - a*a);
765 
766  double x2= x0 + a*(x1-x0)/d;
767  double y2=y0+a*(y1-y0)/d;
768 
769  double x3a=x2+h*(y1-y0)/d; // also x3=x2-h*(y1-y0)/d
770  double y3a=y2-h*(x1-x0)/d; // also y3=y2+h*(x1-x0)/d
771 
772  double x3b=x2-h*(y1-y0)/d; // also x3=x2-h*(y1-y0)/d
773  double y3b=y2+h*(x1-x0)/d; // also y3=y2+h*(x1-x0)/d
774 
775  intersectionXY.push_back(x3a);
776  intersectionXY.push_back(y3a);
777  intersectionXY.push_back(x3b);
778  intersectionXY.push_back(y3b);
779 
780  return ret;
781 
782 }
783 
785 {
786  if(_write_ntuple)
787  {
788  TFile *fout = new TFile(outfile.c_str(),"recreate");
789  recomass->Write();
790  hdecaypos->Write();
791  hdecay_radius->Write();
792  ntp->Write();
793  fout->Close();
794  }
795 
797 }
798 
800 {
801 
802  PHNodeIterator iter(topNode);
803 
804  PHCompositeNode *dstNode = dynamic_cast<PHCompositeNode*>(iter.findFirst("PHCompositeNode", "DST"));
805 
806  if (!dstNode)
807  {
808  std::cerr << "DST node is missing, quitting" << std::endl;
809  throw std::runtime_error("Failed to find DST node in SecondaryVertexFinder");
810  }
811 
812  PHNodeIterator dstIter(topNode);
813  PHCompositeNode *svtxNode = dynamic_cast<PHCompositeNode *>(dstIter.findFirst("PHCompositeNode", "SVTX"));
814 
815  if (!svtxNode)
816  {
817  svtxNode = new PHCompositeNode("SVTX");
818  dstNode->addNode(svtxNode);
819  }
820 
822  {
824  auto tracks_node = new PHIODataNode<PHObject>( _track_map_electrons, "SvtxTrackMapElectrons", "PHObject");
825  svtxNode->addNode(tracks_node);
826  if (Verbosity())
827  { std::cout << "Svtx/SvtxTrackMapElectrons node added" << std::endl; }
828  }
829 
830  return true;
831 }
832 
834 {
835 
836  _track_map = findNode::getClass<SvtxTrackMap>(topNode, "SvtxTrackMap");
837  if (!_track_map)
838  {
839  std::cout << PHWHERE << " ERROR: Can't find SvtxTrackMap: " << std::endl;
841  }
842 
843  /*
844  _cluster_map = findNode::getClass<TrkrClusterContainer>(topNode, "TRKR_CLUSTER");
845  if (!_cluster_map)
846  {
847  std::cout << PHWHERE << " ERROR: Can't find node TRKR_CLUSTER" << std::endl;
848  return Fun4AllReturnCodes::ABORTEVENT;
849  }
850  */
851 
852  _svtx_vertex_map = findNode::getClass<SvtxVertexMap>(topNode,"SvtxVertexMap");
853  if(!_svtx_vertex_map)
854  {
855  std::cout << PHWHERE << "No vertex node, quit!" << std::endl;
857  }
858 
859  _tGeometry = findNode::getClass<ActsGeometry>(topNode,"ActsGeometry");
860  if(!_tGeometry)
861  {
862  std::cout << PHWHERE << "Error, can't find acts tracking geometry" << std::endl;
864  }
865 
867 }
868 
869