CMS 3D CMS Logo

GlobalCosmicMuonTrajectoryBuilder.cc
Go to the documentation of this file.
1 
11 
14 
23 
24 using namespace std;
25 using namespace edm;
26 
27 //
28 // constructor
29 //
31  const MuonServiceProxy* service,edm::ConsumesCollector& iC) : theService(service) {
32  ParameterSet smootherPSet = par.getParameter<ParameterSet>("SmootherParameters");
33  theSmoother = new CosmicMuonSmoother(smootherPSet,theService);
34 
35  ParameterSet trackMatcherPSet = par.getParameter<ParameterSet>("GlobalMuonTrackMatcher");
36  theTrackMatcher = new GlobalMuonTrackMatcher(trackMatcherPSet,theService);
37 
38  theTkTrackToken = iC.consumes<reco::TrackCollection>(par.getParameter<InputTag>("TkTrackCollectionLabel"));
39 
40  theTrackerRecHitBuilderName = par.getParameter<string>("TrackerRecHitBuilder");
41  theMuonRecHitBuilderName = par.getParameter<string>("MuonRecHitBuilder");
42  thePropagatorName = par.getParameter<string>("Propagator");
43  category_ = "Muon|RecoMuon|CosmicMuon|GlobalCosmicMuonTrajectoryBuilder";
44 
45 }
46 
47 //
48 // destructor
49 //
50 
52 
53  if (theSmoother) delete theSmoother;
54  if (theTrackMatcher) delete theTrackMatcher;
55 }
56 
57 //
58 // set Event
59 //
61  event.getByToken(theTkTrackToken,theTrackerTracks);
62 
63 // edm::Handle<std::vector<Trajectory> > handleTrackerTrajs;
64 // if ( event.getByLabel(theTkTrackLabel,handleTrackerTrajs) && handleTrackerTrajs.isValid() ) {
65 // tkTrajsAvailable = true;
66 // allTrackerTrajs = &*handleTrackerTrajs;
67 // LogInfo("GlobalCosmicMuonTrajectoryBuilder")
68 // << "Tk Trajectories Found! " << endl;
69 // } else {
70 // LogInfo("GlobalCosmicMuonTrajectoryBuilder")
71 // << "No Tk Trajectories Found! " << endl;
72 // tkTrajsAvailable = false;
73 // }
74 
77 
78 }
79 
80 //
81 // reconstruct trajectories
82 //
85 
86  if (!theTrackerTracks.isValid()) {
87  LogTrace(category_)<< "Tracker Track collection is invalid!!!";
88  return result;
89  }
90 
91  LogTrace(category_) <<"Found "<<theTrackerTracks->size()<<" tracker Tracks";
92  if (theTrackerTracks->empty()) return result;
93 
94  vector<TrackCand> matched = match(muCand, theTrackerTracks);
95 
96  LogTrace(category_) <<"TrackMatcher found " << matched.size() << "tracker tracks matched";
97 
98  if ( matched.empty()) return result;
99  reco::TrackRef tkTrack = matched.front().second;
100 
101  if ( tkTrack.isNull() ) return result;
102  reco::TrackRef muTrack = muCand.second;
103 
104  ConstRecHitContainer muRecHits;
105 
106  if (muCand.first == nullptr || !muCand.first->isValid()) {
107  muRecHits = getTransientRecHits(*muTrack);
108  } else {
109  muRecHits = muCand.first->recHits();
110  }
111 
112  LogTrace(category_)<<"mu RecHits: "<<muRecHits.size();
113 
114  ConstRecHitContainer tkRecHits = getTransientRecHits(*tkTrack);
115 
116 // if ( !tkTrajsAvailable ) {
117 // tkRecHits = getTransientRecHits(*tkTrack);
118 // } else {
119 // tkRecHits = allTrackerTrajs->front().recHits();
120 // }
121 
122  ConstRecHitContainer hits; //= tkRecHits;
123  LogTrace(category_)<<"tk RecHits: "<<tkRecHits.size();
124 
125 // hits.insert(hits.end(), muRecHits.begin(), muRecHits.end());
126 // stable_sort(hits.begin(), hits.end(), DecreasingGlobalY());
127 
128  sortHits(hits, muRecHits, tkRecHits);
129 
130 // LogTrace(category_)<< "Used RecHits after sort: "<<hits.size()<<endl;;
131 // LogTrace(category_) <<utilities()->print(hits)<<endl;
132 // LogTrace(category_) << "== End of Used RecHits == "<<endl;
133 
134 
135 
136  TrajectoryStateOnSurface muonState1 = trajectoryStateTransform::innerStateOnSurface(*muTrack, *theService->trackingGeometry(), &*theService->magneticField());
137  TrajectoryStateOnSurface tkState1 = trajectoryStateTransform::innerStateOnSurface(*tkTrack, *theService->trackingGeometry(), &*theService->magneticField());
138 
139  TrajectoryStateOnSurface muonState2 = trajectoryStateTransform::outerStateOnSurface(*muTrack, *theService->trackingGeometry(), &*theService->magneticField());
140  TrajectoryStateOnSurface tkState2 = trajectoryStateTransform::outerStateOnSurface(*tkTrack, *theService->trackingGeometry(), &*theService->magneticField());
141 
142  TrajectoryStateOnSurface firstState1 =
143  ( muonState1.globalPosition().y() > tkState1.globalPosition().y() )? muonState1 : tkState1;
144  TrajectoryStateOnSurface firstState2 =
145  ( muonState2.globalPosition().y() > tkState2.globalPosition().y() )? muonState2 : tkState2;
146 
147  TrajectoryStateOnSurface firstState =
148  ( firstState1.globalPosition().y() > firstState2.globalPosition().y() )? firstState1 : firstState2;
149 
150  GlobalPoint front, back;
151  if(!hits.empty()){
152  front=hits.front()->globalPosition();
153  back=hits.back()->globalPosition();
154  if ( (front.perp()<130 && fabs(front.z())<300)|| (back.perp() <130 && fabs(back.z())<300)){
155  if (hits.front()->globalPosition().perp()>hits.back()->globalPosition().perp()) reverse(hits.begin(), hits.end());
156  tkState1 = trajectoryStateTransform::innerStateOnSurface(*tkTrack, *theService->trackingGeometry(), &*theService->magneticField());
157  tkState2 = trajectoryStateTransform::outerStateOnSurface(*tkTrack, *theService->trackingGeometry(), &*theService->magneticField());
158  firstState =( tkState1.globalPosition().perp() < tkState2.globalPosition().perp() )? tkState1 : tkState2;
159  }
160  }
161  if (!firstState.isValid()) return result;
162  LogTrace(category_) <<"firstTSOS pos: "<<firstState.globalPosition()<<"mom: "<<firstState.globalMomentum();
163 
164  // begin refitting
165 
167  vector<Trajectory> refitted = theSmoother->trajectories(seed,hits,firstState);
168 
169  if ( refitted.empty() ) {
170  LogTrace(category_)<<"smoothing trajectories fail";
171  refitted = theSmoother->fit(seed,hits,firstState); //FIXME
172  }
173 
174  if (refitted.empty()) {
175  LogTrace(category_)<<"refit fail";
176  return result;
177  }
178 
179  Trajectory* myTraj = new Trajectory(refitted.front());
180 
181  const std::vector<TrajectoryMeasurement>& mytms = myTraj->measurements();
182  LogTrace(category_)<<"measurements in final trajectory "<<mytms.size();
183  LogTrace(category_) <<"Orignally there are "<<tkTrack->found()<<" tk rhs and "<<muTrack->found()<<" mu rhs.";
184 
185  if ( mytms.size() <= tkTrack->found() ) {
186  LogTrace(category_)<<"insufficient measurements. skip... ";
187  return result;
188  }
189 
190  MuonCandidate* myCand = new MuonCandidate(myTraj,muTrack,tkTrack);
191  result.push_back(myCand);
192  LogTrace(category_)<<"final global cosmic muon: ";
193  for (std::vector<TrajectoryMeasurement>::const_iterator itm = mytms.begin();
194  itm != mytms.end(); ++itm ) {
195  LogTrace(category_)<<"updated pos "<<itm->updatedState().globalPosition()
196  <<"mom "<<itm->updatedState().globalMomentum();
197  }
198  return result;
199 }
200 
202 
203  if ( tkHits.empty() ) {
204  LogTrace(category_) << "No valid tracker hits";
205  return;
206  }
207  if ( muonHits.empty() ) {
208  LogTrace(category_) << "No valid muon hits";
209  return;
210  }
211 
212  ConstRecHitContainer::const_iterator frontTkHit = tkHits.begin();
213  ConstRecHitContainer::const_iterator backTkHit = tkHits.end() - 1;
214  while ( !(*frontTkHit)->isValid() && frontTkHit != backTkHit) {frontTkHit++;}
215  while ( !(*backTkHit)->isValid() && backTkHit != frontTkHit) {backTkHit--;}
216 
217  ConstRecHitContainer::const_iterator frontMuHit = muonHits.begin();
218  ConstRecHitContainer::const_iterator backMuHit = muonHits.end() - 1;
219  while ( !(*frontMuHit)->isValid() && frontMuHit != backMuHit) {frontMuHit++;}
220  while ( !(*backMuHit)->isValid() && backMuHit != frontMuHit) {backMuHit--;}
221 
222  if ( frontTkHit == backTkHit ) {
223  LogTrace(category_) << "No valid tracker hits";
224  return;
225  }
226  if ( frontMuHit == backMuHit ) {
227  LogTrace(category_) << "No valid muon hits";
228  return;
229  }
230 
231  GlobalPoint frontTkPos = (*frontTkHit)->globalPosition();
232  GlobalPoint backTkPos = (*backTkHit)->globalPosition();
233 
234  GlobalPoint frontMuPos = (*frontMuHit)->globalPosition();
235  GlobalPoint backMuPos = (*backMuHit)->globalPosition();
236 
237  //sort hits going from higher to lower positions
238  if ( frontTkPos.y() < backTkPos.y() ) {//check if tk hits order same direction
239  reverse(tkHits.begin(), tkHits.end());
240  }
241 
242  if ( frontMuPos.y() < backMuPos.y() ) {
243  reverse(muonHits.begin(), muonHits.end());
244  }
245 
246 // LogTrace(category_)<< "tkHits after sort: "<<tkHits.size()<<endl;;
247 // LogTrace(category_) <<utilities()->print(tkHits)<<endl;
248 // LogTrace(category_) << "== End of tkHits == "<<endl;
249 
250 // LogTrace(category_)<< "muonHits after sort: "<<muonHits.size()<<endl;;
251 // LogTrace(category_) <<utilities()->print(muonHits)<<endl;
252 // LogTrace(category_)<< "== End of muonHits == "<<endl;
253 
254  //separate muon hits into 2 different hemisphere
255  ConstRecHitContainer::iterator middlepoint = muonHits.begin();
256  bool insertInMiddle = false;
257 
258  for (ConstRecHitContainer::iterator ihit = muonHits.begin();
259  ihit != muonHits.end() - 1; ihit++ ) {
260  GlobalPoint ipos = (*ihit)->globalPosition();
261  GlobalPoint nextpos = (*(ihit+1))->globalPosition();
262  if ( (ipos-nextpos).mag() < 100.0 ) continue;
263 
264  GlobalPoint middle((ipos.x()+nextpos.x())/2, (ipos.y()+nextpos.y())/2, (ipos.z()+nextpos.z())/2);
265  LogTrace(category_)<<"ipos "<<ipos<<"nextpos"<<nextpos<<" middle "<<middle<<endl;
266  if ( (middle.perp() < ipos.perp()) && (middle.perp() < nextpos.perp() ) ) {
267  LogTrace(category_)<<"found middlepoint"<<endl;
268  middlepoint = ihit;
269  insertInMiddle = true;
270  break;
271  }
272  }
273 
274  //insert track hits in correct order
275  if ( insertInMiddle ) { //if tk hits should be sandwich
276  GlobalPoint jointpointpos = (*middlepoint)->globalPosition();
277  LogTrace(category_)<<"jointpoint "<<jointpointpos<<endl;
278  if ((frontTkPos - jointpointpos).mag() > (backTkPos - jointpointpos).mag() ) {//check if tk hits order same direction
279  reverse(tkHits.begin(), tkHits.end());
280  }
281  muonHits.insert(middlepoint+1, tkHits.begin(), tkHits.end());
282  hits = muonHits;
283  } else { // append at one end
284  if ( frontTkPos.y() < frontMuPos.y() ) { //insert at the end
285  LogTrace(category_)<<"insert at the end "<<frontTkPos << frontMuPos <<endl;
286 
287  hits = muonHits;
288  hits.insert(hits.end(), tkHits.begin(), tkHits.end());
289  } else { //insert at the beginning
290  LogTrace(category_)<<"insert at the beginning "<<frontTkPos << frontMuPos <<endl;
291  hits = tkHits;
292  hits.insert(hits.end(), muonHits.begin(), muonHits.end());
293  }
294  }
295 }
296 
298 
301 
303 
304 
305 
306  auto hitCloner = static_cast<TkTransientTrackingRecHitBuilder const *>(theTrackerRecHitBuilder.product())->cloner();
307  TrajectoryStateOnSurface currTsos = trajectoryStateTransform::innerStateOnSurface(track, *theService->trackingGeometry(), &*theService->magneticField());
308  for (trackingRecHit_iterator hit = track.recHitsBegin(); hit != track.recHitsEnd(); ++hit) {
309  if((*hit)->isValid()) {
310  DetId recoid = (*hit)->geographicalId();
311  if ( recoid.det() == DetId::Tracker ) {
312  TrajectoryStateOnSurface predTsos = theService->propagator(thePropagatorName)->propagate(currTsos, theService->trackingGeometry()->idToDet(recoid)->surface());
313  LogTrace(category_)<<"predtsos "<<predTsos.isValid();
314  if ( predTsos.isValid() ) {
315  currTsos = predTsos;
316  result.emplace_back(hitCloner(**hit,predTsos));
317  }
318  } else if ( recoid.det() == DetId::Muon ) {
319  result.push_back(theMuonRecHitBuilder->build(&**hit));
320  }
321  }
322  }
323  return result;
324 }
325 
326 std::vector<GlobalCosmicMuonTrajectoryBuilder::TrackCand> GlobalCosmicMuonTrajectoryBuilder::match(const TrackCand& mu, const edm::Handle<reco::TrackCollection>& tktracks) {
327 
328  std::vector<TrackCand> result;
329 
330 
331  TrajectoryStateOnSurface innerTsos =trajectoryStateTransform::innerStateOnSurface(*(mu.second), *theService->trackingGeometry(), &*theService->magneticField());
332  TrajectoryStateOnSurface outerTsos = trajectoryStateTransform::outerStateOnSurface(*(mu.second), *theService->trackingGeometry(), &*theService->magneticField());
333  //build tracker TrackCands and pick the best match if size greater than 2
334  vector<TrackCand> tkTrackCands;
337  TrackCand tkCand = TrackCand((Trajectory*)nullptr,tkTrack);
338  tkTrackCands.push_back(tkCand);
339  LogTrace(category_) << "chisq is " << theTrackMatcher->match(mu,tkCand,0,0);
340  LogTrace(category_) << "d is " << theTrackMatcher->match(mu,tkCand,1,0);
341  LogTrace(category_) << "r_pos is " << theTrackMatcher->match(mu,tkCand,2,0);
342  }
343 
344  // now if only 1 tracker tracks, return it
345  if (tkTrackCands.size() <= 1 ) {
346  return tkTrackCands;
347  }
348 
349  // if there're many tracker tracks
350 
351  // if muon is only on one side
352  GlobalPoint innerPos = innerTsos.globalPosition();
353  GlobalPoint outerPos = outerTsos.globalPosition();
354 
355  if ( ( innerPos.basicVector().dot( innerTsos.globalMomentum().basicVector() ) *
356  outerPos.basicVector().dot(outerTsos.globalMomentum().basicVector() ) > 0 ) ) {
357 
358  GlobalPoint geoInnerPos = (innerPos.mag() < outerPos.mag()) ? innerPos : outerPos;
359  LogTrace(category_) <<"geoInnerPos Mu "<<geoInnerPos<<endl;
360 
361  // if there're tracker tracks totally on the other half
362  // and there're tracker tracks on the same half
363  // remove the tracks on the other half
364  for(vector<TrackCand>::const_iterator itkCand = tkTrackCands.begin(); itkCand != tkTrackCands.end(); ++itkCand) {
365 
366  reco::TrackRef tkTrack = itkCand->second;
367 
368  GlobalPoint tkInnerPos(tkTrack->innerPosition().x(), tkTrack->innerPosition().y(), tkTrack->innerPosition().z());
369  GlobalPoint tkOuterPos(tkTrack->outerPosition().x(), tkTrack->outerPosition().y(), tkTrack->outerPosition().z());
370  LogTrace(category_) <<"tkTrack "<<tkInnerPos<<" "<<tkOuterPos<<endl;
371 
372  float closetDistance11 = (geoInnerPos - tkInnerPos).mag() ;
373  float closetDistance12 = (geoInnerPos - tkOuterPos).mag() ;
374  float closetDistance1 = (closetDistance11 < closetDistance12) ? closetDistance11 : closetDistance12;
375  LogTrace(category_) <<"closetDistance1 "<<closetDistance1<<endl;
376 
377  if (true || !isTraversing(*tkTrack) ) {
378  bool keep = true;
379  for(vector<TrackCand>::const_iterator itkCand2 = tkTrackCands.begin(); itkCand2 != tkTrackCands.end(); ++itkCand2) {
380  if (itkCand2 == itkCand ) continue;
381  reco::TrackRef tkTrack2 = itkCand2->second;
382 
383  GlobalPoint tkInnerPos2(tkTrack2->innerPosition().x(), tkTrack2->innerPosition().y(), tkTrack2->innerPosition().z());
384  GlobalPoint tkOuterPos2(tkTrack2->outerPosition().x(), tkTrack2->outerPosition().y(), tkTrack2->outerPosition().z());
385  LogTrace(category_) <<"tkTrack2 "<< tkInnerPos2 <<" "<<tkOuterPos2 <<endl;
386 
387  float farthestDistance21 = (geoInnerPos - tkInnerPos2).mag() ;
388  float farthestDistance22 = (geoInnerPos - tkOuterPos2).mag() ;
389  float farthestDistance2 = (farthestDistance21 > farthestDistance22) ? farthestDistance21 : farthestDistance22;
390  LogTrace(category_) <<"farthestDistance2 "<<farthestDistance2<<endl;
391 
392  if (closetDistance1 > farthestDistance2 - 1e-3) {
393  keep = false;
394  break;
395  }
396  }
397  if (keep) result.push_back(*itkCand);
398  else LogTrace(category_) <<"The Track is on different hemisphere"<<endl;
399  } else {
400  result.push_back(*itkCand);
401  }
402  }
403  if ( result.empty() ) {
404  //if all tk tracks on the other side, still keep them
405  result = tkTrackCands;
406  }
407  } else { // muon is traversing
408  result = tkTrackCands;
409  }
410 
411  // match muCand to tkTrackCands
412  vector<TrackCand> matched_trackerTracks = theTrackMatcher->match(mu, result);
413 
414  LogTrace(category_) <<"TrackMatcher found " << matched_trackerTracks.size() << "tracker tracks matched";
415 
416  //now pick the best matched one
417  if( matched_trackerTracks.size() < 2 ) {
418  return matched_trackerTracks;
419  } else {
420  // in case of more than 1 tkTrack,
421  // select the best-one based on distance (matchOption==1)
422  // at innermost Mu hit surface. (surfaceOption == 0)
423  result.clear();
425 
426  double quality = 1e6;
427  double max_quality = 1e6;
428  for( vector<TrackCand>::const_iterator iter = matched_trackerTracks.begin(); iter != matched_trackerTracks.end(); iter++) {
429  quality = theTrackMatcher->match(mu,*iter, 1, 0);
430  LogTrace(category_) <<" quality of tracker track is " << quality;
431  if( quality < max_quality ) {
432  max_quality=quality;
433  bestMatch = (*iter);
434  }
435  }
436  LogTrace(category_) <<" Picked tracker track with quality " << max_quality;
437  result.push_back(bestMatch);
438  return result;
439  }
440 }
441 
442 
444 
446  for (trackingRecHit_iterator hit = track.recHitsBegin(); hit != track.recHitsEnd(); ++hit) {
447  if((*hit)->isValid()) {
448  firstValid = hit;
449  break;
450  }
451  }
452 
453  trackingRecHit_iterator lastValid;
454  for (trackingRecHit_iterator hit = track.recHitsEnd() - 1; hit != track.recHitsBegin() - 1; --hit) {
455  if((*hit)->isValid()) {
456  lastValid = hit;
457  break;
458  }
459  }
460 
461  GlobalPoint posFirst = theService->trackingGeometry()->idToDet((*firstValid)->geographicalId())->position();
462 
463  GlobalPoint posLast = theService->trackingGeometry()->idToDet((*lastValid)->geographicalId() )->position();
464 
465  GlobalPoint middle((posFirst.x()+posLast.x())/2, (posFirst.y()+posLast.y())/2, (posFirst.z()+posLast.z())/2);
466 
467  if ( (middle.mag() < posFirst.mag()) && (middle.mag() < posLast.mag() ) ) {
468  return true;
469  }
470  return false;
471 
472 }
EDGetTokenT< ProductType > consumes(edm::InputTag const &tag)
T getParameter(std::string const &) const
T perp() const
Definition: PV3DBase.h:72
edm::ESHandle< TransientTrackingRecHitBuilder > theTrackerRecHitBuilder
edm::EDGetTokenT< reco::TrackCollection > theTkTrackToken
TrajectoryStateOnSurface outerStateOnSurface(const reco::Track &tk, const TrackingGeometry &geom, const MagneticField *field, bool withErr=true)
T mag() const
The vector magnitude. Equivalent to sqrt(vec.mag2())
edm::ESHandle< TransientTrackingRecHitBuilder > theMuonRecHitBuilder
std::vector< Track > TrackCollection
collection of Tracks
Definition: TrackFwd.h:14
T y() const
Definition: PV3DBase.h:63
GlobalPoint globalPosition() const
uint16_t size_type
GlobalCosmicMuonTrajectoryBuilder(const edm::ParameterSet &, const MuonServiceProxy *service, edm::ConsumesCollector &iC)
Constructor.
TransientTrackingRecHit::ConstRecHitContainer ConstRecHitContainer
DataContainer const & measurements() const
Definition: Trajectory.h:196
const int keep
ConstRecHitContainer getTransientRecHits(const reco::Track &) const
T mag() const
Definition: PV3DBase.h:67
std::pair< const Trajectory *, reco::TrackRef > TrackCand
bool isTraversing(const reco::Track &tk) const
std::vector< Trajectory * > trajectories(const TrajectorySeed &) override
dummy implementation, unused in this class
T z() const
Definition: PV3DBase.h:64
const int mu
Definition: Constants.h:22
void sortHits(ConstRecHitContainer &, ConstRecHitContainer &, ConstRecHitContainer &)
trackingRecHit_iterator recHitsBegin() const
Iterator to first hit on the track.
Definition: Track.h:104
std::vector< MuonCandidate * > CandidateContainer
Definition: MuonCandidate.h:20
double match(const TrackCand &sta, const TrackCand &track, int matchOption=0, int surfaceOption=1) const
bool isValid() const
Definition: HandleBase.h:74
bool isNull() const
Checks for null.
Definition: Ref.h:250
#define LogTrace(id)
virtual RecHitPointer build(const TrackingRecHit *p) const =0
build a tracking rechit from an existing rechit
std::vector< ConstRecHitPointer > ConstRecHitContainer
Definition: DetId.h:18
std::vector< Trajectory > fit(const Trajectory &) const
def bestMatch(object, matchCollection)
Definition: deltar.py:139
std::vector< TrackCand > match(const TrackCand &, const edm::Handle< reco::TrackCollection > &)
check if tk and muon Tracks are matched
void setEvent(const edm::Event &) override
pass the Event to the algo at each event
HLT enums.
GlobalVector globalMomentum() const
static int position[264][3]
Definition: ReadPGInfo.cc:509
Detector det() const
get the detector field from this detid
Definition: DetId.h:35
T x() const
Definition: PV3DBase.h:62
const BasicVectorType & basicVector() const
Definition: PV3DBase.h:56
T const * product() const
Definition: ESHandle.h:86
TrajectoryContainer trajectories(const Trajectory &traj) const override
edm::Handle< reco::TrackCollection > theTrackerTracks
TrackingRecHitCollection::base::const_iterator trackingRecHit_iterator
iterator over a vector of reference to TrackingRecHit in the same collection
Definition: event.py:1
TrajectoryStateOnSurface innerStateOnSurface(const reco::Track &tk, const TrackingGeometry &geom, const MagneticField *field, bool withErr=true)
T dot(const Basic3DVector &rh) const
Scalar product, or "dot" product, with a vector of same type.
trackingRecHit_iterator recHitsEnd() const
Iterator to last hit on the track.
Definition: Track.h:109