Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
PHSimpleVertexFinder.cc
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file PHSimpleVertexFinder.cc
1 #include "PHSimpleVertexFinder.h"
2 
4 
5 #include <trackbase/TrkrCluster.h> // for TrkrCluster
6 #include <trackbase/TrkrDefs.h> // for cluskey, getLayer, TrkrId
8 #include <trackbase_historic/SvtxTrack.h> // for SvtxTrack, SvtxTrack::C...
10 
13 
14 #include <phool/PHCompositeNode.h>
15 #include <phool/PHNode.h>
16 #include <phool/PHNodeIterator.h>
18 
19 #include <phool/getClass.h>
20 #include <phool/phool.h>
21 
22 #include <cmath> // for sqrt, fabs, atan2, cos
23 #include <iostream> // for operator<<, basic_ostream
24 #include <map> // for map
25 #include <set> // for _Rb_tree_const_iterator
26 #include <utility> // for pair, make_pair
27 #include <iomanip>
28 
29 #include <vector>
30 #include <cassert>
31 #include <numeric>
32 #include <iostream>
33 #include <algorithm>
34 #include <functional>
35 
36 #include <Eigen/Dense>
37 
38 //____________________________________________________________________________..
40  : SubsysReco(name)
41 {
42 
43 }
44 
45 //____________________________________________________________________________..
47 {
48 
49 }
50 
51 //____________________________________________________________________________..
53 {
54  int ret = GetNodes(topNode);
55  if (ret != Fun4AllReturnCodes::EVENT_OK) return ret;
56  ret = CreateNodes(topNode);
57  if (ret != Fun4AllReturnCodes::EVENT_OK) return ret;
58  return ret;
59 }
60 
61 //____________________________________________________________________________..
63 {
64 
65  if(Verbosity() > 0)
66  std::cout << PHWHERE << " track map size " << _track_map->size() << std::endl;
67 
68  // reset maps
69  _vertex_track_map.clear();
70  _track_pair_map.clear();
71  _track_pair_pca_map.clear();
72  _vertex_position_map.clear();
73  _vertex_covariance_map.clear();
74  _vertex_set.clear();
75 
77 
78  // Find all instances where two tracks have a dca of < _dcacut, and capture the pair details
79  // Fills _track_pair_map and _track_pair_pca_map
80  checkDCAs();
81 
83  if(_track_pair_map.size() == 0)
84  {
86  checkDCAs();
87  }
88 
89  if(Verbosity() > 0)
90  { std::cout << "track pair map size " << _track_pair_map.size() << std::endl; }
91 
92  // get all connected pairs of tracks by looping over the track_pair map
93  std::vector<std::set<unsigned int>> connected_tracks = findConnectedTracks();
94 
95  // make vertices - each set of connected tracks is a vertex
96  for(unsigned int ivtx = 0; ivtx < connected_tracks.size(); ++ivtx)
97  {
98  if(Verbosity() > 1) std::cout << "process vertex " << ivtx << std::endl;
99 
100  for(auto it : connected_tracks[ivtx])
101  {
102  unsigned int id = it;
103  _vertex_track_map.insert(std::make_pair(ivtx, id));
104  if(Verbosity() > 2) std::cout << " adding track " << id << " to vertex " << ivtx << std::endl;
105  }
106  }
107 
108  // make a list of vertices
109  for(auto it : _vertex_track_map)
110  {
111  if(Verbosity() > 1) std::cout << " vertex " << it.first << " track " << it.second << std::endl;
112  _vertex_set.insert(it.first);
113  }
114 
115  // this finds average vertex positions after removal of outlying track pairs
117 
118  // average covariance for accepted tracks
119  for(auto it : _vertex_set)
120  {
121  matrix_t avgCov = matrix_t::Zero();
122  double cov_wt = 0.0;
123 
124  auto ret = _vertex_track_map.equal_range(it);
125  for (auto cit=ret.first; cit!=ret.second; ++cit)
126  {
127  unsigned int trid = cit->second;
128  matrix_t cov;
129  auto track = _track_map->get(trid);
130  for(int i = 0; i < 3; ++i)
131  for(int j = 0; j < 3; ++j)
132  cov(i,j) = track->get_error(i,j);
133 
134  avgCov += cov;
135  cov_wt++;
136  }
137 
138  avgCov /= sqrt(cov_wt);
139  if(Verbosity() > 2)
140  {
141  std::cout << "Average covariance for vertex " << it << " is:" << std::endl;
142  std::cout << std::setprecision(8) << avgCov << std::endl;
143  }
144  _vertex_covariance_map.insert(std::make_pair(it, avgCov));
145  }
146 
147  // Write the vertices to the vertex map on the node tree
148  //==============================================
149  if(_vertex_track_map.size() > 0)
151 
152  for(auto it : _vertex_set)
153  {
154  auto svtxVertex = std::make_unique<SvtxVertex_v2>();
155 
156  svtxVertex->set_chisq(0.0);
157  svtxVertex->set_ndof(0);
158  svtxVertex->set_t0(0);
159  svtxVertex->set_id(it);
160 
161  auto ret = _vertex_track_map.equal_range(it);
162  for (auto cit=ret.first; cit!=ret.second; ++cit)
163  {
164  unsigned int trid = cit->second;
165 
166  if(Verbosity() > 1) std::cout << " vertex " << it << " insert track " << trid << std::endl;
167  svtxVertex->insert_track(trid);
168  _track_map->get(trid)->set_vertex_id(it);
169  }
170 
171  Eigen::Vector3d pos = _vertex_position_map.find(it)->second;
172  svtxVertex->set_x(pos.x());
173  svtxVertex->set_y(pos.y());
174  svtxVertex->set_z(pos.z());
175  if(Verbosity() > 1) std::cout << " vertex " << it << " insert pos.x " << pos.x() << " pos.y " << pos.y() << " pos.z " << pos.z() << std::endl;
176 
177  auto vtxCov = _vertex_covariance_map.find(it)->second;
178  for(int i = 0; i < 3; ++i)
179  {
180  for(int j = 0; j < 3; ++j)
181  {
182  svtxVertex->set_error(i, j, vtxCov(i,j));
183  }
184  }
185 
186  _svtx_vertex_map->insert(svtxVertex.release());
187  }
188 
193  //=================================================
194  for(const auto& [trackkey, track] : *_track_map)
195  {
196  auto vtxid = track->get_vertex_id();
197 
199  if(_svtx_vertex_map->get(vtxid))
200  { continue; }
201 
202  float maxdz = std::numeric_limits<float>::max();
203  unsigned int newvtxid = std::numeric_limits<unsigned int>::max();
204 
205  for(const auto& [vtxkey, vertex] : *_svtx_vertex_map)
206  {
207  float dz = track->get_z() - vertex->get_z();
208  if(fabs(dz) < maxdz)
209  {
210  maxdz = dz;
211  newvtxid = vtxkey;
212  }
213  }
214 
215  track->set_vertex_id(newvtxid);
216  }
217 
219 }
220 
221 
222 
224 {
226 }
227 
229 {
230  PHNodeIterator iter(topNode);
231 
232  PHCompositeNode *dstNode = dynamic_cast<PHCompositeNode *>(iter.findFirst("PHCompositeNode", "DST"));
233 
235  if (!dstNode)
236  {
237  std::cerr << "DST Node missing, quitting" << std::endl;
238  throw std::runtime_error("failed to find DST node in PHActsInitialVertexFinder::createNodes");
239  }
240 
242  PHCompositeNode *svtxNode = dynamic_cast<PHCompositeNode *>(iter.findFirst("PHCompositeNode", "SVTX"));
243 
244  if (!svtxNode)
245  {
246  svtxNode = new PHCompositeNode("SVTX");
247  dstNode->addNode(svtxNode);
248  }
249 
250  _svtx_vertex_map = findNode::getClass<SvtxVertexMap>(topNode,"SvtxVertexMap");
251 
252  if(!_svtx_vertex_map)
253  {
256  _svtx_vertex_map, "SvtxVertexMap","PHObject");
257 
258  svtxNode->addNode(vertexNode);
259 
260  }
261 
263 }
265 {
266 
267  _track_map = findNode::getClass<SvtxTrackMap>(topNode, "SvtxTrackMap");
268  if (!_track_map)
269  {
270  std::cout << PHWHERE << " ERROR: Can't find SvtxTrackMap: " << std::endl;
272  }
273 
274 
275 
277 }
278 
280 {
281  // Loop over tracks and check for close DCA match with all other tracks
282  for(auto tr1_it = _track_map->begin(); tr1_it != _track_map->end(); ++tr1_it)
283  {
284  auto id1 = tr1_it->first;
285  auto tr1 = tr1_it->second;
286  if(tr1->get_quality() > _qual_cut) continue;
287  if(_require_mvtx)
288  {
289  unsigned int nmvtx = 0;
290  TrackSeed *siliconseed = tr1->get_silicon_seed();
291  if(!siliconseed)
292  { continue; }
293 
294  for(auto clusit = siliconseed->begin_cluster_keys(); clusit != siliconseed->end_cluster_keys(); ++clusit)
295  {
296  if(TrkrDefs::getTrkrId(*clusit) == TrkrDefs::mvtxId )
297  {
298  nmvtx++;
299  }
300  if(nmvtx >= _nmvtx_required) break;
301  }
302  if(nmvtx < _nmvtx_required) continue;
303  if(Verbosity() > 3) std::cout << " tr1 id " << id1 << " has nmvtx at least " << nmvtx << std::endl;
304  }
305 
306  // look for close DCA matches with all other such tracks
307  for(auto tr2_it = std::next(tr1_it); tr2_it != _track_map->end(); ++tr2_it)
308  {
309  auto id2 = tr2_it->first;
310  auto tr2 = tr2_it->second;
311  if(tr2->get_quality() > _qual_cut) continue;
312  if(_require_mvtx)
313  {
314  unsigned int nmvtx = 0;
315  TrackSeed *siliconseed = tr2->get_silicon_seed();
316  if(!siliconseed)
317  { continue; }
318 
319  for(auto clusit = siliconseed->begin_cluster_keys(); clusit != siliconseed->end_cluster_keys(); ++clusit)
320  {
321  if(TrkrDefs::getTrkrId(*clusit) == TrkrDefs::mvtxId)
322  {
323  nmvtx++;
324  }
325  if(nmvtx >= _nmvtx_required) break;
326  }
327  if(nmvtx < _nmvtx_required) continue;
328  if(Verbosity() > 3) std::cout << " tr2 id " << id2 << " has nmvtx at least " << nmvtx << std::endl;
329  }
330 
331  // find DCA of these two tracks
332  if(Verbosity() > 3) std::cout << "Check DCA for tracks " << id1 << " and " << id2 << std::endl;
333 
334  findDcaTwoTracks(tr1, tr2);
335 
336  }
337  }
338 
339 
340 }
341 
343 {
344  if(tr1->get_pt() < _track_pt_cut) return;
345  if(tr2->get_pt() < _track_pt_cut) return;
346 
347  unsigned int id1 = tr1->get_id();
348  unsigned int id2 = tr2->get_id();
349 
350  // get the line equations for the tracks
351 
352  Eigen::Vector3d a1(tr1->get_x(), tr1->get_y(), tr1->get_z());
353  Eigen::Vector3d b1(tr1->get_px() / tr1->get_p(), tr1->get_py() / tr1->get_p(), tr1->get_pz() / tr1->get_p());
354  Eigen::Vector3d a2(tr2->get_x(), tr2->get_y(), tr2->get_z());
355  Eigen::Vector3d b2(tr2->get_px() / tr2->get_p(), tr2->get_py() / tr2->get_p(), tr2->get_pz() / tr2->get_p());
356 
357  Eigen::Vector3d PCA1(0,0,0);
358  Eigen::Vector3d PCA2(0,0,0);
359  double dca = dcaTwoLines(a1, b1, a2, b2, PCA1, PCA2);
360 
361  if(Verbosity() > 3) { std::cout << " pair dca is " << dca << " _active_dcacut is " << _active_dcacut
362  << " PCA1.x " << PCA1.x() << " PCA1.y " << PCA1.y()
363  << " PCA2.x " << PCA2.x() << " PCA2.y " << PCA2.y() << std::endl; }
364 
365  // check dca cut is satisfied, and that PCA is close to beam line
366  if( fabs(dca) < _active_dcacut && (fabs(PCA1.x()) < _beamline_xy_cut && fabs(PCA1.y()) < _beamline_xy_cut) )
367  {
368  if(Verbosity() > 3)
369  {
370  std::cout << " good match for tracks " << tr1->get_id() << " and " << tr2->get_id() << " with pT " << tr1->get_pt() << " and " << tr2->get_pt() << std::endl;
371  std::cout << " a1.x " << a1.x() << " a1.y " << a1.y() << " a1.z " << a1.z() << std::endl;
372  std::cout << " a2.x " << a2.x() << " a2.y " << a2.y() << " a2.z " << a2.z() << std::endl;
373  std::cout << " PCA1.x() " << PCA1.x() << " PCA1.y " << PCA1.y() << " PCA1.z " << PCA1.z() << std::endl;
374  std::cout << " PCA2.x() " << PCA2.x() << " PCA2.y " << PCA2.y() << " PCA2.z " << PCA2.z() << std::endl;
375  std::cout << " dca " << dca << std::endl;
376  }
377 
378  // capture the results for successful matches
379  _track_pair_map.insert(std::make_pair(id1,std::make_pair(id2, dca)));
380  _track_pair_pca_map.insert( std::make_pair(id1, std::make_pair(id2, std::make_pair(PCA1, PCA2))) );
381  }
382 
383  return;
384 }
385 
386 double PHSimpleVertexFinder::dcaTwoLines(const Eigen::Vector3d &a1,const Eigen::Vector3d &b1,
387  const Eigen::Vector3d &a2,const Eigen::Vector3d &b2,
388  Eigen::Vector3d &PCA1, Eigen::Vector3d &PCA2)
389 {
390  // The shortest distance between two skew lines described by
391  // a1 + c * b1
392  // a2 + d * b2
393  // where a1, a2, are vectors representing points on the lines, b1, b2 are direction vectors, and c and d are scalars
394  // is:
395  // dca = (b1 x b2) .(a2-a1) / |b1 x b2|
396 
397  // bcrossb/mag_bcrossb is a unit vector perpendicular to both direction vectors b1 and b2
398  auto bcrossb = b1.cross(b2);
399  auto mag_bcrossb = bcrossb.norm();
400  // a2-a1 is the vector joining any arbitrary points on the two lines
401  auto aminusa = a2-a1;
402 
403  // The DCA of these two lines is the projection of a2-a1 along the direction of the perpendicular to both
404  // remember that a2-a1 is longer than (or equal to) the dca by definition
405  double dca = 999;
406  if( mag_bcrossb != 0)
407  dca = bcrossb.dot(aminusa) / mag_bcrossb;
408  else
409  return dca; // same track, skip combination
410 
411  // get the points at which the normal to the lines intersect the lines, where the lines are perpendicular
412  // Assume the shortest distance occurs at points A on line 1, and B on line 2, call the line AB
413  // AB = a2+d*b2 - (a1+c*b1)
414  // we need to find c and d where AB is perpendicular to both lines. so AB.b1 = 0 and AB.b2 = 0
415  // ( (a2 -a1) + d*b2 -c*b1 ).b1 = 0
416  // ( (a2 -a1) + d*b2 -c*b1 ).b2 = 0
417  // so we have two simultaneous equations in 2 unknowns
418  // (a2-a1).b1 + d*b2.b1 -c*b1.b1 = 0 => d*b2.b1 = c*b1.b1 - (a2-a1).b1 => d = (1/b2.b1) * (c*b1.b1 - (a2-a1).b1)
419  // (a2-a1).b2 + d*b2.b2 - c*b1.b2 = 0 => c*b1.b2 = (a2-a1).b2 + [(1/b2.b1) * (c*b1*b1 -(a2-a1).b1)}*b2.b2
420  // c*b1.b2 - (1/b2.b1) * c*b1.b1*b2.b2 = (a2-a1).b2 - (1/b2.b1)*(a2-a1).b1*b2.b2
421  // c *(b1.b2 - (1/b2.b1)*b1.b1*b2.b2) = (a2-a1).b2 - (1/b2.b1)*(a2-a1).b1*b2.b2
422  // call this: c*X = Y
423  // plug back into the d equation
424  // d = c*b1.b1 / b2.b1 - (a2-a1).b1 / b2.b1
425  // and call the d equation: d = c * F - G
426 
427  double X = b1.dot(b2) - b1.dot(b1) * b2.dot(b2) / b2.dot(b1);
428  double Y = (a2.dot(b2) - a1.dot(b2)) - (a2.dot(b1) - a1.dot(b1)) * b2.dot(b2) / b2.dot(b1) ;
429  double c = Y/X;
430 
431  double F = b1.dot(b1) / b2.dot(b1);
432  double G = - (a2.dot(b1) - a1.dot(b1)) / b2.dot(b1);
433  double d = c * F + G;
434 
435  // then the points of closest approach are:
436  PCA1 = a1+c*b1;
437  PCA2 = a2+d*b2;
438 
439  return dca;
440 
441 
442 }
443 
444 std::vector<std::set<unsigned int>> PHSimpleVertexFinder::findConnectedTracks()
445 {
446  std::vector<std::set<unsigned int>> connected_tracks;
447  std::set<unsigned int> connected;
448  std::set<unsigned int> used;
449  for(auto it : _track_pair_map)
450  {
451  unsigned int id1 = it.first;
452  unsigned int id2 = it.second.first;
453 
454  if( (used.find(id1) != used.end()) && (used.find(id2) != used.end()) )
455  {
456  if(Verbosity() > 3) std::cout << " tracks " << id1 << " and " << id2 << " are both in used , skip them" << std::endl;
457  continue;
458  }
459  else if( (used.find(id1) == used.end()) && (used.find(id2) == used.end()))
460  {
461  if(Verbosity() > 3) std::cout << " tracks " << id1 << " and " << id2 << " are both not in used , start a new connected set" << std::endl;
462  // close out and start a new connections set
463  if(connected.size() > 0)
464  {
465  connected_tracks.push_back(connected);
466  connected.clear();
467  if(Verbosity() > 3) std::cout << " closing out set " << std::endl;
468  }
469  }
470 
471  // get everything connected to id1 and id2
472  connected.insert(id1);
473  used.insert(id1);
474  connected.insert(id2);
475  used.insert(id2);
476  for(auto cit : _track_pair_map)
477  {
478  unsigned int id3 = cit.first;
479  unsigned int id4 = cit.second.first;
480  if( (connected.find(id3) != connected.end()) || (connected.find(id4) != connected.end()) )
481  {
482  if(Verbosity() > 3) std::cout << " found connection to " << id3 << " and " << id4 << std::endl;
483  connected.insert(id3);
484  used.insert(id3);
485  connected.insert(id4);
486  used.insert(id4);
487  }
488  }
489  }
490 
491  // close out the last set
492  if(connected.size() > 0)
493  {
494  connected_tracks.push_back(connected);
495  connected.clear();
496  if(Verbosity() > 3) std::cout << " closing out last set " << std::endl;
497  }
498 
499  if(Verbosity() > 3) std::cout << "connected_tracks size " << connected_tracks.size() << std::endl;
500 
501  return connected_tracks;
502 }
503 
505 {
506  // Note: std::multimap<unsigned int, std::pair<unsigned int, std::pair<Eigen::Vector3d, Eigen::Vector3d>>> _track_pair_pca_map
507 
508  for(auto it : _vertex_set)
509  {
510  unsigned int vtxid = it;
511  if(Verbosity() > 1) std::cout << "calculate average position for vertex " << vtxid << std::endl;
512 
513  // we need the median values of the x and y positions
514  std::vector<double> vx;
515  std::vector<double> vy;
516  std::vector<double> vz;
517 
518  double pca_median_x = 0.;
519  double pca_median_y = 0.;
520  double pca_median_z = 0.;
521 
522  Eigen::Vector3d new_pca_avge(0.,0.,0.);
523  double new_wt = 0.0;
524 
525  auto ret = _vertex_track_map.equal_range(vtxid);
526 
527  // Start by getting the positions for this vertex into vectors for the median calculation
528  for (auto cit=ret.first; cit!=ret.second; ++cit)
529  {
530  unsigned int tr1id = cit->second;
531  if(Verbosity() > 2) std::cout << " vectors: get entries for track " << tr1id << " for vertex " << vtxid << std::endl;
532 
533  // find all pairs for this vertex with tr1id
534  auto pca_range = _track_pair_pca_map.equal_range(tr1id);
535  for (auto pit=pca_range.first; pit!=pca_range.second; ++pit)
536  {
537  unsigned int tr2id = pit->second.first;
538 
539  Eigen::Vector3d PCA1 = pit->second.second.first;
540  Eigen::Vector3d PCA2 = pit->second.second.second;
541 
542  if(Verbosity() > 2)
543  std::cout << " vectors: tr1id " << tr1id << " tr2id " << tr2id
544  << " PCA1 " << PCA1.x() << " " << PCA1.y() << " " << PCA1.z()
545  << " PCA2 " << PCA2.x() << " " << PCA2.y() << " " << PCA2.z()
546  << std::endl;
547 
548  vx.push_back(PCA1.x());
549  vx.push_back(PCA2.x());
550  vy.push_back(PCA1.y());
551  vy.push_back(PCA2.y());
552  vz.push_back(PCA1.z());
553  vz.push_back(PCA2.z());
554  }
555  }
556 
557  // Get the medians for this vertex
558  // Using the median as a reference for rejecting outliers only makes sense for more than 2 tracks
559  if(vx.size() < 3)
560  {
561  new_pca_avge.x() = getAverage(vx);
562  new_pca_avge.y() = getAverage(vy);
563  new_pca_avge.z() = getAverage(vz);
564  _vertex_position_map.insert(std::make_pair(vtxid, new_pca_avge));
565  if(Verbosity() > 1)
566  std::cout << " Vertex has only 2 tracks, use average for PCA: " << new_pca_avge.x() << " " << new_pca_avge.y() << " " << new_pca_avge.z() << std::endl;
567 
568  // done with this vertex
569  continue;
570  }
571 
572  pca_median_x = getMedian(vx);
573  pca_median_y = getMedian(vy);
574  pca_median_z = getMedian(vz);
575  if(Verbosity() > 1) std::cout << "Median values: x " << pca_median_x << " y " << pca_median_y << " z : " << pca_median_z << std::endl;
576 
577  // Make the average vertex position with outlier rejection wrt the median
578  for (auto cit=ret.first; cit!=ret.second; ++cit)
579  {
580  unsigned int tr1id = cit->second;
581  if(Verbosity() > 2) std::cout << " average: get entries for track " << tr1id << " for vertex " << vtxid << std::endl;
582 
583  // find all pairs for this vertex with tr1id
584  auto pca_range = _track_pair_pca_map.equal_range(tr1id);
585  for (auto pit=pca_range.first; pit!=pca_range.second; ++pit)
586  {
587  unsigned int tr2id = pit->second.first;
588 
589  Eigen::Vector3d PCA1 = pit->second.second.first;
590  Eigen::Vector3d PCA2 = pit->second.second.second;
591 
592  if(
593  fabs(PCA1.x() - pca_median_x) < _outlier_cut &&
594  fabs(PCA1.y() - pca_median_y) < _outlier_cut &&
595  fabs(PCA2.x() - pca_median_x) < _outlier_cut &&
596  fabs(PCA2.y() - pca_median_y) < _outlier_cut
597  )
598  {
599  // good track pair, add to new average
600 
601  new_pca_avge += PCA1;
602  new_wt++;
603  new_pca_avge += PCA2;
604  new_wt++;
605  }
606  else
607  {
608  if(Verbosity() > 1) std::cout << "Reject pair with tr1id " << tr1id << " tr2id " << tr2id << std::endl;
609  }
610  }
611  }
612  if(new_wt > 0.0)
613  new_pca_avge = new_pca_avge / new_wt;
614  else
615  {
616  // There were no pairs that survived the track cuts, use the median values
617  new_pca_avge.x() = pca_median_x;
618  new_pca_avge.y() = pca_median_y;
619  new_pca_avge.z() = pca_median_z;
620  }
621 
622  _vertex_position_map.insert(std::make_pair(vtxid, new_pca_avge));
623 
624  }
625 
626  return;
627  }
628 
629 double PHSimpleVertexFinder::getMedian(std::vector<double> &v)
630 {
631  double median = 0.0;
632 
633  if( (v.size() % 2) == 0)
634  {
635  // even number of entries
636  // we want the average of the middle two numbers, v.size()/2 and v.size()/2-1
637  auto m1 = v.begin() + v.size()/2;
638  std::nth_element(v.begin(), m1, v.end());
639  double median1 = v[v.size()/2];
640 
641  auto m2 = v.begin() + v.size()/2 - 1;
642  std::nth_element(v.begin(), m2, v.end());
643  double median2 = v[v.size()/2 - 1];
644 
645  median = (median1 + median2) / 2.0;
646  if(Verbosity() > 2) std::cout << "The vector size is " << v.size()
647  << " element m is " << v.size() / 2 << " = " << v[v.size()/2]
648  << " element m-1 is " << v.size() / 2 -1 << " = " << v[v.size()/2-1]
649  << std::endl;
650  }
651  else
652  {
653  // odd number of entries
654  auto m = v.begin() + v.size()/2;
655  std::nth_element(v.begin(), m, v.end());
656  median = v[v.size()/2];
657  if(Verbosity() > 2) std::cout << "The vector size is " << v.size() << " element m is " << v.size() / 2 << " = " << v[v.size()/2] << std::endl;
658  }
659 
660  return median ;
661 }
662 double PHSimpleVertexFinder::getAverage(std::vector<double> &v)
663 {
664  double avge = 0.0;
665  double wt = 0.0;
666  for(auto it : v)
667  {
668  avge += it;
669  wt++;
670  }
671 
672  avge /= wt;
673  if(Verbosity() > 2)
674  {
675  std::cout << " average = " << avge << std::endl;
676  }
677 
678  return avge;
679 }
680 
681