Print

Print


Commit in lcsim/src/org/lcsim/recon/pfa/identifier on MAIN
TrackHelixExtrapolator.java+43-221.4 -> 1.5
TrackHelixPlusHitExtrapolator.java+288-701.3 -> 1.4
+331-92
2 modified files
MJC: Improvements to helix extrapolators for PFA: (1) Take into account the different kinds of tracker hit when using the last hit to correct helix -- barrel microstrip vs endcap axial-stereo vs vertex detector pixel; (2) Better handling of barrel/endcap overlap region -- correctly identify which is the first surface hit

lcsim/src/org/lcsim/recon/pfa/identifier
TrackHelixExtrapolator.java 1.4 -> 1.5
diff -u -r1.4 -r1.5
--- TrackHelixExtrapolator.java	23 Sep 2008 18:26:03 -0000	1.4
+++ TrackHelixExtrapolator.java	9 Oct 2008 21:38:28 -0000	1.5
@@ -76,13 +76,44 @@
 	// Get helix fit output
 	m_alphaIntercept = Double.NaN; 
         if (isValidBarrelIntercept(m_swimmer, alphaBarrel, m_cutSeparation)) {
-            m_alphaIntercept = alphaBarrel;
             validBarrel = true;
-        } else if (isValidEndcapIntercept(m_swimmer, alphaEndcap, m_cutSeparation)) {
-            m_alphaIntercept = alphaEndcap;
+        }
+	if (isValidEndcapIntercept(m_swimmer, alphaEndcap, m_cutSeparation)) {
             validEndcap = true;
         }
 
+	// Check for special case in corner of barrel/endcap overlap region
+	if ( validEndcap && validBarrel ) {
+	    // Both apparently valid... check again
+	    boolean tightValidEndcap = isValidEndcapIntercept(m_swimmer, alphaEndcap, 0.0);
+	    boolean tightValidBarrel = isValidBarrelIntercept(m_swimmer, alphaBarrel, 0.0);
+	    if (tightValidEndcap && tightValidBarrel) {
+		// This can happen if the track has moderate pT -- it goes into the
+		// barrel, spirals out again, and eventually hits the endcap.
+		// If this is what happened then it should reach the barrel first.
+		if (alphaEndcap < alphaBarrel) {
+		    Hep3Vector interceptEndcap = m_swimmer.getPointAtDistance(alphaEndcap);
+		    Hep3Vector interceptBarrel = m_swimmer.getPointAtDistance(alphaBarrel);
+		    double rEndcap = Math.sqrt(interceptEndcap.x()*interceptEndcap.x() + interceptEndcap.y()*interceptEndcap.y());
+		    double rBarrel = Math.sqrt(interceptBarrel.x()*interceptBarrel.x() + interceptBarrel.y()*interceptBarrel.y());
+		    double zEndcap = interceptEndcap.z();
+		    double zBarrel = interceptBarrel.z();
+		    throw new AssertionError("Track hits endcap THEN barrel -- this doesn't make sense!\nEndcap intercept at alpha="+alphaEndcap+" has r="+rEndcap+", z="+zEndcap+"\nBarrel intercept at alpha="+alphaBarrel+" has r="+rBarrel+", z="+zBarrel);
+		} else {
+		    validEndcap = false;
+		}
+	    } else if (!tightValidEndcap && !tightValidBarrel) {
+		throw new AssertionError("Invalid state");
+	    } else {
+		// Only one valid solution -- OK
+		validEndcap = tightValidEndcap;
+		validBarrel = tightValidBarrel;
+	    }
+	}
+
+	if (validEndcap) { m_alphaIntercept = alphaEndcap; }
+	if (validBarrel) { m_alphaIntercept = alphaBarrel; }
+
 	// Did we make a successful extrapolation?
         if ( Double.isNaN(m_alphaIntercept)) {
 	    // No -- extrapolation failed
@@ -92,23 +123,11 @@
 	    // Invalid state
 	    if (m_debug) { System.out.println("DEBUG: "+this.getClass().getName()+" failed to extrapolate: not a valid barrel or endcap point"); }
 	    return null;
+	} else if ( validEndcap && validBarrel ) {
+	    // Invalid state
+	    if (m_debug) { System.out.println("DEBUG: "+this.getClass().getName()+" failed to extrapolate: valid barrel AND endcap point"); }
+	    throw new AssertionError("DEBUG: "+this.getClass().getName()+" failed to extrapolate: valid barrel AND endcap point");
 	} else {
-	    // One or both solutions valid.
-	    if ( validEndcap && validBarrel ) {
-		// Both apparently valid... check again
-		boolean tightValidEndcap = isValidEndcapIntercept(m_swimmer, alphaEndcap, 0.0);
-		boolean tightValidBarrel = isValidBarrelIntercept(m_swimmer, alphaBarrel, 0.0);
-		if (tightValidEndcap && tightValidBarrel) {
-		    throw new AssertionError("Invalid state");
-		} else if (!tightValidEndcap && !tightValidBarrel) {
-		    throw new AssertionError("Invalid state");
-		} else {
-		    // Only one valid solution -- OK
-		    validEndcap = tightValidEndcap;
-		    validBarrel = tightValidBarrel;
-		}
-	    }
-
 	    // Extrapolation succeeded.
 	    m_intercept = m_swimmer.getPointAtDistance(m_alphaIntercept);
 	    m_barrelValid = validBarrel;
@@ -180,12 +199,14 @@
 	}
     }
     protected boolean isValidBarrelIntercept(HelixSwimmer swimmer, double alpha, double uncertainty) {
-        // Must have -m_ECAL_barrel_z <= z <= +m_ECAL_barrel_z (within errors)
+        // OLD: // Must have -m_ECAL_barrel_z <= z <= +m_ECAL_barrel_z (within errors)
+	// NEW: // Above AND must have -m_ECAL_endcap_z <= z <=  m_ECAL_endcap_z
         //double uncertainty = m_cutSeparation;
         Hep3Vector intercept = swimmer.getPointAtDistance(alpha);
         double z = intercept.z();
-        boolean zInRange = (z >= m_ECAL_barrel_zmin-uncertainty && z <= m_ECAL_barrel_zmax+uncertainty);
-        return zInRange;
+        boolean zInRangeBarrel = (z >= m_ECAL_barrel_zmin-uncertainty && z <= m_ECAL_barrel_zmax+uncertainty);
+	boolean vetoEndcap = (z >= -m_ECAL_endcap_z && z <= m_ECAL_endcap_z);
+        return zInRangeBarrel && vetoEndcap;
     }
     protected boolean isValidEndcapIntercept(HelixSwimmer swimmer, double alpha, double uncertainty) {
         // Must have m_ECAL_endcap_rmin <= r <= m_ECAL_endcap_rmax (within errors)

lcsim/src/org/lcsim/recon/pfa/identifier
TrackHelixPlusHitExtrapolator.java 1.3 -> 1.4
diff -u -r1.3 -r1.4
--- TrackHelixPlusHitExtrapolator.java	23 Sep 2008 18:26:03 -0000	1.3
+++ TrackHelixPlusHitExtrapolator.java	9 Oct 2008 21:38:28 -0000	1.4
@@ -35,89 +35,307 @@
 	// Start by swimming as per parent:
 	super.performExtrapolation(tr);
 	
-	// Now scan over tracker hits and find the one with the outermost Z:
+	// Now scan over tracker hits and find the outermost one.
+	// Note that the details here depend on what kind of hit
+	// we're looking at.
 	List<TrackerHit> trackHits = tr.getTrackerHits();
 	if (trackHits.size() == 0) { throw new AssertionError("Track found with no track hits!"); }
-	TrackerHit trackHitWithLargestModZ = null;
-	Hep3Vector positionOfTrackHitWithLargestModZ = null;
-	for (TrackerHit trackHit : trackHits) {
-	    Hep3Vector pos = new BasicHep3Vector(trackHit.getPosition());
-	    if (trackHitWithLargestModZ==null) {
-		trackHitWithLargestModZ = trackHit;
-		positionOfTrackHitWithLargestModZ = pos;
+	TrackerHit outermostHit = findOutermostHit(trackHits);
+
+	// Find the POCA to the hit with the old helix:
+	double alpha = Double.NaN;
+	Hep3Vector pointOfClosestApproachToTrackHit = null;
+	Hep3Vector offset = null;
+
+	if (outermostHit instanceof  org.lcsim.event.base.BaseTrackerHitMC) {
+	    // This cheated hit has exact 3D info
+	    Hep3Vector positionOfOutermostHit = new BasicHep3Vector(outermostHit.getPosition());
+	    alpha = m_swimmer.getTrackLengthToPoint(positionOfOutermostHit);
+	    pointOfClosestApproachToTrackHit = m_swimmer.getPointAtLength(alpha);
+	    // Correct with a 3D step from POCA-to-hit to the hit itself.
+	    offset = VecOp.sub(positionOfOutermostHit, pointOfClosestApproachToTrackHit);
+	} else if (outermostHit instanceof org.lcsim.fit.helicaltrack.HelicalTrack2DHit) {
+	    // This is a barrel hit with 2D hit info (and a weak constraint on z)
+	    // Correct with an offset in r-phi but not in z
+	    org.lcsim.fit.helicaltrack.HelicalTrack2DHit hit = (org.lcsim.fit.helicaltrack.HelicalTrack2DHit)(outermostHit);
+	    double r = hit.r();
+	    alpha = m_swimmer.getDistanceToRadius(r);
+	    pointOfClosestApproachToTrackHit = m_swimmer.getPointAtLength(alpha);
+	    // Correct in xy but not in z since no z information
+	    offset = new BasicHep3Vector(hit.x() - pointOfClosestApproachToTrackHit.x(), hit.y() - pointOfClosestApproachToTrackHit.y(), 0.0);
+	} else if (outermostHit instanceof org.lcsim.fit.helicaltrack.HelicalTrackCross) {
+	    // This is an endcap hit with hit info from axial+stereo strips.
+	    // Uncertainty is ambiguous somehow -- unclear...
+	    org.lcsim.fit.helicaltrack.HelicalTrackCross hit = (org.lcsim.fit.helicaltrack.HelicalTrackCross)(outermostHit);
+	    double z = hit.z();
+	    alpha = m_swimmer.getDistanceToZ(z);
+	    pointOfClosestApproachToTrackHit = m_swimmer.getPointAtLength(alpha);
+	    // Step from extrapolation point (x, y, z) to hit position (x', y', z) -- note that both have same z.
+	    offset = new BasicHep3Vector(hit.x() - pointOfClosestApproachToTrackHit.x(), hit.y() - pointOfClosestApproachToTrackHit.y(), 0.0);
+	} else if (outermostHit instanceof org.lcsim.fit.helicaltrack.HelicalTrack3DHit) {
+	    // This is a vertex detector hit with 3D hit info
+	    org.lcsim.fit.helicaltrack.HelicalTrack3DHit hit = (org.lcsim.fit.helicaltrack.HelicalTrack3DHit)(outermostHit);
+	    Hep3Vector positionOfOutermostHit = new BasicHep3Vector(hit.x(), hit.y(), hit.z());
+	    alpha = m_swimmer.getTrackLengthToPoint(positionOfOutermostHit);
+	    pointOfClosestApproachToTrackHit = m_swimmer.getPointAtLength(alpha);
+	    // Correct with a 3D step from POCA-to-hit to the hit itself.
+	    offset = VecOp.sub(positionOfOutermostHit, pointOfClosestApproachToTrackHit);
+	} else {
+	    // Unknown!
+	    Hep3Vector positionOfOutermostHit = new BasicHep3Vector(outermostHit.getPosition());
+	    double r = Math.sqrt(positionOfOutermostHit.x()*positionOfOutermostHit.x() + positionOfOutermostHit.y()*positionOfOutermostHit.y());
+	    double z = positionOfOutermostHit.z();
+	    throw new AssertionError("ERROR: Unknown hit of type "+outermostHit.getClass().getName()+" at r="+r+", z="+z);
+	}
+
+	Hep3Vector momentumAtPOCA = m_swimmer.getMomentumAtLength(alpha);
+	Hep3Vector newPoint = VecOp.add(pointOfClosestApproachToTrackHit, offset);
+
+	// Make a new helix swimmer:
+	HelixSwimmer newHelix = new HelixSwimmer(m_fieldStrength[2]);
+	newHelix.setTrack(momentumAtPOCA, newPoint, tr.getCharge());
+	// Over-write old helix swimmer:
+	m_swimmer = newHelix;
+	
+	// Try swimming to the barrel:
+	double  alphaBarrel = swimToBarrel(m_swimmer);
+	boolean validBarrel = false;
+	// Try swimming to the endcap:
+	double  alphaEndcap = swimToEndcap(m_swimmer);
+	boolean validEndcap = false;
+	
+	// Get helix fit output
+	m_alphaIntercept = Double.NaN; 
+	m_intercept = null;
+	if (isValidBarrelIntercept(m_swimmer, alphaBarrel, m_cutSeparation)) {
+	    validBarrel = true;
+	}
+	if (isValidEndcapIntercept(m_swimmer, alphaEndcap, m_cutSeparation)) {
+	    validEndcap = true;
+	}
+
+	// Check for special case in corner of barrel/endcap overlap region
+	if ( validEndcap && validBarrel ) {
+	    // Both apparently valid... check again
+	    boolean tightValidEndcap = isValidEndcapIntercept(m_swimmer, alphaEndcap, 0.0);
+	    boolean tightValidBarrel = isValidBarrelIntercept(m_swimmer, alphaBarrel, 0.0);
+	    if (tightValidEndcap && tightValidBarrel) {
+		// This can happen if the track has moderate pT -- it goes into the
+		// barrel, spirals out again, and eventually hits the endcap.
+		// If this is what happened then it should reach the barrel first.
+		if (alphaEndcap < alphaBarrel) {
+		    Hep3Vector interceptEndcap = m_swimmer.getPointAtDistance(alphaEndcap);
+		    Hep3Vector interceptBarrel = m_swimmer.getPointAtDistance(alphaBarrel);
+		    double rEndcap = Math.sqrt(interceptEndcap.x()*interceptEndcap.x() + interceptEndcap.y()*interceptEndcap.y());
+		    double rBarrel = Math.sqrt(interceptBarrel.x()*interceptBarrel.x() + interceptBarrel.y()*interceptBarrel.y());
+		    double zEndcap = interceptEndcap.z();
+		    double zBarrel = interceptBarrel.z();
+		    throw new AssertionError("Track hits endcap THEN barrel -- this doesn't make sense!\nEndcap intercept at alpha="+alphaEndcap+" has r="+rEndcap+", z="+zEndcap+"\nBarrel intercept at alpha="+alphaBarrel+" has r="+rBarrel+", z="+zBarrel);
+		} else {
+		    validEndcap = false;
+		}
+	    } else if (!tightValidEndcap && !tightValidBarrel) {
+		throw new AssertionError("Invalid state");
 	    } else {
-		if (Math.abs(pos.z()) > Math.abs(positionOfTrackHitWithLargestModZ.z())) {
-		    trackHitWithLargestModZ = trackHit;
-		    positionOfTrackHitWithLargestModZ = pos;
+		// Only one valid solution -- OK
+		validEndcap = tightValidEndcap;
+		validBarrel = tightValidBarrel;
+	    }
+	}
+	
+	if (validEndcap) { m_alphaIntercept = alphaEndcap; }
+	if (validBarrel) { m_alphaIntercept = alphaBarrel; }
+
+	// Did we make a successful extrapolation?
+	if ( Double.isNaN(m_alphaIntercept)) {
+	    // No -- extrapolation failed
+	    if (m_debug) { System.out.println("DEBUG: "+this.getClass().getName()+" failed to extrapolate: alpha is NaN"); }
+	    return null;
+	} else if ( !(validEndcap || validBarrel) ) {
+	    // Invalid state
+	    if (m_debug) { System.out.println("DEBUG: "+this.getClass().getName()+" failed to extrapolate: not a valid barrel or endcap point"); }
+	    return null;
+	} else if ( validEndcap && validBarrel ) {
+	    // Invalid state
+	    if (m_debug) { System.out.println("DEBUG: "+this.getClass().getName()+" failed to extrapolate: valid barrel AND endcap point"); }
+	    throw new AssertionError("DEBUG: "+this.getClass().getName()+" failed to extrapolate: valid barrel AND endcap point");
+	    //return null;
+	} else {
+	    // Extrapolation succeeded.
+	    m_intercept = m_swimmer.getPointAtDistance(m_alphaIntercept);
+	    m_barrelValid = validBarrel;
+	    m_endcapValid = validEndcap;
+	    if (m_debug) { 
+		System.out.print("DEBUG: "+this.getClass().getName()+" extrapolated OK. validEndcap="+validEndcap+" and validBarrel="+validBarrel+" and m_alphaIntercept="+m_alphaIntercept); 
+		if (m_intercept == null) {
+		    System.out.print(" -- but intercept point is null!");
+		} else {
+		    double r = Math.sqrt(m_intercept.x()*m_intercept.x() + m_intercept.y()*m_intercept.y());
+		    System.out.print(" -- intercept point at r="+r+", z="+m_intercept.z());
 		}
+		System.out.println();
 	    }
+	    // Output
+	    HelixExtrapolationResult output = new HelixExtrapolationResult(new TrackHelixPlusHitExtrapolator(this));
+	    return output;
 	}
+    }
 
-	if (trackHitWithLargestModZ == null) {
-	    throw new AssertionError("Unable to determine outermost hit!");
+    TrackerHit findOutermostHit(List<TrackerHit> trackerHits) {
+	boolean cheatHitFound = false;
+	boolean helicalHitFound = false;
+	for (TrackerHit hit : trackerHits) {
+	    boolean thisHitIsCheatHit = (hit instanceof org.lcsim.event.base.BaseTrackerHitMC);
+	    boolean thisHitIsHelicalHit = (hit instanceof org.lcsim.fit.helicaltrack.HelicalTrackCross || hit instanceof org.lcsim.fit.helicaltrack.HelicalTrack2DHit || hit instanceof org.lcsim.fit.helicaltrack.HelicalTrack3DHit);
+	    if (thisHitIsCheatHit && thisHitIsHelicalHit) {
+		throw new AssertionError("Ambiguous hit of class "+hit.getClass().getName());
+	    } else if (!thisHitIsCheatHit && !thisHitIsHelicalHit) {
+		throw new AssertionError("Unidentified hit of unknown class "+hit.getClass().getName());
+	    }
+	    if (thisHitIsCheatHit) { 
+		cheatHitFound = true;
+	    }
+	    if (thisHitIsHelicalHit) {
+		helicalHitFound = true;
+	    }
+	}
+	if (cheatHitFound && helicalHitFound) {
+	    throw new AssertionError("Mixed list of hits!");
+	}
+	if (cheatHitFound) {
+	    return findOutermostCheatHit(trackerHits);
+	} else if (helicalHitFound) {
+	    return findOutermostHelicalHit(trackerHits);
 	} else {
-	    // Find the POCA to the hit with the old helix:
-	    double alpha = m_swimmer.getTrackLengthToPoint(positionOfTrackHitWithLargestModZ);
-	    Hep3Vector pointOfClosestApproachToTrackHit = m_swimmer.getPointAtLength(alpha);
-	    Hep3Vector momentumAtPOCA = m_swimmer.getMomentumAtLength(alpha);
-	    
-	    // Make a new helix swimmer:
-	    HelixSwimmer newHelix = new HelixSwimmer(m_fieldStrength[2]);
-	    newHelix.setTrack(momentumAtPOCA, positionOfTrackHitWithLargestModZ, tr.getCharge());
-	    // Over-write old helix swimmer:
-	    m_swimmer = newHelix;
-
-	    // Try swimming to the barrel:
-	    double  alphaBarrel = swimToBarrel(m_swimmer);
-	    boolean validBarrel = false;
-	    // Try swimming to the endcap:
-	    double  alphaEndcap = swimToEndcap(m_swimmer);
-	    boolean validEndcap = false;
-
-	    // Get helix fit output
-	    m_alphaIntercept = Double.NaN; 
-	    m_intercept = null;
-	    if (isValidBarrelIntercept(m_swimmer, alphaBarrel, m_cutSeparation)) {
-		m_alphaIntercept = alphaBarrel;
-		validBarrel = true;
-	    } else if (isValidEndcapIntercept(m_swimmer, alphaEndcap, m_cutSeparation)) {
-		m_alphaIntercept = alphaEndcap;
-		validEndcap = true;
-	    }
-
-	    // Did we make a successful extrapolation?
-	    if ( Double.isNaN(m_alphaIntercept)) {
-		// No -- extrapolation failed
-		if (m_debug) { System.out.println("DEBUG: "+this.getClass().getName()+" failed to extrapolate: alpha is NaN"); }
-		return null;
-	    } else if ( !(validEndcap || validBarrel) ) {
-		// Invalid state
-		if (m_debug) { System.out.println("DEBUG: "+this.getClass().getName()+" failed to extrapolate: not a valid barrel or endcap point"); }
-		return null;
-	    } else if ( validEndcap && validBarrel ) {
-		// Invalid state
-		if (m_debug) { System.out.println("DEBUG: "+this.getClass().getName()+" failed to extrapolate: valid barrel AND endcap point"); }
-		throw new AssertionError("DEBUG: "+this.getClass().getName()+" failed to extrapolate: valid barrel AND endcap point");
-		//return null;
+	    throw new AssertionError("Unclassified list of hits!");
+	}
+    }
+
+    TrackerHit findOutermostCheatHit(List<TrackerHit> trackerHits) {
+	// Find hit with outermost Z
+	TrackerHit outermostHit = null;
+	Hep3Vector positionOfOutermostHit = null;
+	for (TrackerHit trackHit : trackerHits) {
+	    Hep3Vector pos = new BasicHep3Vector(trackHit.getPosition());
+	    if (outermostHit==null) {
+		outermostHit = trackHit;
+		positionOfOutermostHit = pos;
 	    } else {
-		// Extrapolation succeeded.
-		m_intercept = m_swimmer.getPointAtDistance(m_alphaIntercept);
-		m_barrelValid = validBarrel;
-		m_endcapValid = validEndcap;
-		if (m_debug) { 
-		    System.out.print("DEBUG: "+this.getClass().getName()+" extrapolated OK. validEndcap="+validEndcap+" and validBarrel="+validBarrel+" and m_alphaIntercept="+m_alphaIntercept); 
-		    if (m_intercept == null) {
-			System.out.print(" -- but intercept point is null!");
+		if (Math.abs(pos.z()) > Math.abs(positionOfOutermostHit.z())) {
+		    outermostHit = trackHit;
+		    positionOfOutermostHit = pos;
+		}
+	    }
+	}
+	return outermostHit;
+    }
+    
+    TrackerHit findOutermostHelicalHit(List<TrackerHit> trackerHits) {
+	// This is a little tricky. The rules are:
+	//  1) Any outer tracker layer > any vertex detector layer
+	//  2) For two vertex detector hits, the one with the larger |z| wins.
+	//  3) For any two outer tracker barrel layers, the one with the larger r wins.
+	//  4) For any two outer tracker endcap layers, the one with the larger |z| wins.
+	//  5) For a tracker barrel & endcap layer...
+	//       5a) If barrel |zmin| > endcap |z|, the barrel wins
+	//       5b) If barrel |zmax| < endcap |z|, the endcap wins
+	//       5c) If there is ambiguity, the barrel wins (a little arbitrary)
+	TrackerHit outermostHit = null;
+	for (TrackerHit hit : trackerHits) {
+	    if (outermostHit == null) {
+		outermostHit = hit;
+	    } else {
+		boolean outermostHitIsVertexHit = (outermostHit instanceof org.lcsim.fit.helicaltrack.HelicalTrack3DHit);
+		boolean outermostHitIsBarrelHit = (outermostHit instanceof org.lcsim.fit.helicaltrack.HelicalTrack2DHit);
+		boolean outermostHitIsEndcapHit = (outermostHit instanceof org.lcsim.fit.helicaltrack.HelicalTrackCross);
+		if (!(outermostHitIsVertexHit || outermostHitIsBarrelHit || outermostHitIsEndcapHit)) { throw new AssertionError("Unidentified hit"); }
+		if (outermostHitIsVertexHit && outermostHitIsBarrelHit) { throw new AssertionError("Ambiguous hit"); }
+		if (outermostHitIsVertexHit && outermostHitIsEndcapHit) { throw new AssertionError("Ambiguous hit"); }
+		if (outermostHitIsBarrelHit && outermostHitIsEndcapHit) { throw new AssertionError("Ambiguous hit"); }
+		boolean currentHitIsVertexHit = (hit instanceof org.lcsim.fit.helicaltrack.HelicalTrack3DHit);
+		boolean currentHitIsBarrelHit = (hit instanceof org.lcsim.fit.helicaltrack.HelicalTrack2DHit);
+		boolean currentHitIsEndcapHit = (hit instanceof org.lcsim.fit.helicaltrack.HelicalTrackCross);
+		if (!(currentHitIsVertexHit || currentHitIsBarrelHit || currentHitIsEndcapHit)) { throw new AssertionError("Unidentified hit"); }
+		if (currentHitIsVertexHit && currentHitIsBarrelHit) { throw new AssertionError("Ambiguous hit"); }
+		if (currentHitIsVertexHit && currentHitIsEndcapHit) { throw new AssertionError("Ambiguous hit"); }
+		if (currentHitIsBarrelHit && currentHitIsEndcapHit) { throw new AssertionError("Ambiguous hit"); }
+
+		if (currentHitIsVertexHit) {
+		    if (outermostHitIsVertexHit) {
+			//  2) For two vertex detector hits, the one with the larger |z| wins.
+			double currentHit_z = Math.abs(((org.lcsim.fit.helicaltrack.HelicalTrack3DHit)(hit)).z());
+			double outermostHit_z = Math.abs(((org.lcsim.fit.helicaltrack.HelicalTrack3DHit)(outermostHit)).z());
+			if (currentHit_z > outermostHit_z) {
+			    outermostHit = hit;
+			}
+			continue;
+		    } else {
+			//  1) Any outer tracker layer > any vertex detector layer
+			continue;
+		    }
+		} else {
+		    if (outermostHitIsVertexHit) {
+			//  1) Any outer tracker layer > any vertex detector layer
+			outermostHit = hit;
+			continue;
 		    } else {
-			double r = Math.sqrt(m_intercept.x()*m_intercept.x() + m_intercept.y()*m_intercept.y());
-			System.out.print(" -- intercept point at r="+r+", z="+m_intercept.z());
+			if (outermostHitIsBarrelHit && currentHitIsBarrelHit) {
+			    //  3) For any two outer tracker barrel layers, the one with the larger r wins.
+			    double currentHit_r = ((org.lcsim.fit.helicaltrack.HelicalTrack2DHit)(hit)).r();
+			    double outermostHit_r = ((org.lcsim.fit.helicaltrack.HelicalTrack2DHit)(outermostHit)).r();
+			    if (currentHit_r > outermostHit_r) {
+				outermostHit = hit;
+			    }
+			    continue;
+			} else if (outermostHitIsEndcapHit && currentHitIsEndcapHit) {
+			    //  4) For any two outer tracker endcap layers, the one with the larger |z| wins.
+			    double currentHit_z = Math.abs(((org.lcsim.fit.helicaltrack.HelicalTrackCross)(hit)).z());
+			    double outermostHit_z = Math.abs(((org.lcsim.fit.helicaltrack.HelicalTrackCross)(outermostHit)).z());
+			    if (currentHit_z > outermostHit_z) {
+				outermostHit = hit;
+			    }
+			    continue;
+			} else if (outermostHitIsBarrelHit && currentHitIsEndcapHit) {
+			    // One barrel and one endcap layer
+			    double barrel_zmin = Math.abs(((org.lcsim.fit.helicaltrack.HelicalTrack2DHit)(outermostHit)).zmin());
+			    double barrel_zmax = Math.abs(((org.lcsim.fit.helicaltrack.HelicalTrack2DHit)(outermostHit)).zmax());
+			    double endcap_z = Math.abs(((org.lcsim.fit.helicaltrack.HelicalTrackCross)(hit)).z());
+			    if (barrel_zmin > endcap_z) {
+				// 5a) If barrel |zmin| > endcap |z|, the barrel wins
+				continue;
+			    } else if (barrel_zmax < endcap_z) {
+				// 5b) If barrel |zmax| < endcap |z|, the endcap wins
+				outermostHit = hit;
+				continue;
+			    } else {
+				// 5c) If there is ambiguity, the barrel wins (a little arbitrary)
+				continue;
+			    }
+			} else if (outermostHitIsEndcapHit&&currentHitIsBarrelHit) {
+			    // One barrel and one endcap layer
+			    double barrel_zmin = Math.abs(((org.lcsim.fit.helicaltrack.HelicalTrack2DHit)(hit)).zmin());
+			    double barrel_zmax = Math.abs(((org.lcsim.fit.helicaltrack.HelicalTrack2DHit)(hit)).zmax());
+			    double endcap_z = Math.abs(((org.lcsim.fit.helicaltrack.HelicalTrackCross)(outermostHit)).z());
+			    if (barrel_zmin > endcap_z) {
+				// 5a) If barrel |zmin| > endcap |z|, the barrel wins
+				outermostHit = hit;
+				continue;
+			    } else if (barrel_zmax < endcap_z) {
+				// 5b) If barrel |zmax| < endcap |z|, the endcap wins
+				continue;
+			    } else {
+				// 5c) If there is ambiguity, the barrel wins (a little arbitrary)
+				outermostHit = hit;
+				continue;
+			    }
+			}
 		    }
-		    System.out.println();
 		}
-		// Output
-		HelixExtrapolationResult output = new HelixExtrapolationResult(new TrackHelixPlusHitExtrapolator(this));
-		return output;
+		// Shouldn't reach here
+		throw new AssertionError("Failed to classify! outermostHit is "+outermostHit.getClass().getName()+" and hit is "+hit.getClass().getName());
 	    }
 	}
+
+	return outermostHit;
     }
 }
CVSspam 0.2.8