Print

Print


Commit in lcsim/src/org/lcsim/recon/cluster/structural on MAIN
ChargedNeutralFragmentSeparator.java+347added 1.1
Try to figure out whether a fragment is charged or neutral

lcsim/src/org/lcsim/recon/cluster/structural
ChargedNeutralFragmentSeparator.java added at 1.1
diff -N ChargedNeutralFragmentSeparator.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ChargedNeutralFragmentSeparator.java	3 Oct 2006 00:22:48 -0000	1.1
@@ -0,0 +1,347 @@
+package org.lcsim.recon.cluster.structural;
+
+import java.util.*; 
+import hep.physics.vec.*;
+import java.io.IOException; 
+
+import org.lcsim.util.decision.DecisionMakerSingle;
+import org.lcsim.event.Cluster;
+import org.lcsim.event.EventHeader;
+import org.lcsim.util.Driver;
+import org.lcsim.event.Track;
+import org.lcsim.event.MCParticle;
+import org.lcsim.event.SimCalorimeterHit;
+import org.lcsim.event.CalorimeterHit;
+import org.lcsim.util.swim.HelixSwimmer;
+import org.lcsim.geometry.subdetector.CylindricalCalorimeter;
+import org.lcsim.geometry.Detector;
+
+import hep.aida.ITree;
+import hep.aida.IAnalysisFactory; 
+import hep.aida.IHistogramFactory; 
+import hep.aida.ICloud1D;
+
+/**
+ *  A class that takes a small cluster or single hit and
+ *  tries to decide whether it is charged or neutral.
+ *
+ * @version $Id: ChargedNeutralFragmentSeparator.java,v 1.1 2006/10/03 00:22:48 mcharles Exp $
+ */
+
+public class ChargedNeutralFragmentSeparator extends Driver implements DecisionMakerSingle<Cluster> 
+{
+    /** Trivial constructor */
+    public ChargedNeutralFragmentSeparator() {}
+
+    /** 
+     * Convenience constructor for the case where we pull down
+     * a new copy of a named list of tracks every event automatically.
+     *
+     * @param trackListName Named list of tracks in the event
+     */
+    public ChargedNeutralFragmentSeparator(String trackListName) {
+	setChargedTracksList(trackListName);
+    }
+
+    /** 
+     * Convenience constructor for the case where the user sets the
+     * list of tracks manually.
+     *
+     * @param tracks List of tracks
+     */
+    public ChargedNeutralFragmentSeparator(List<Track> tracks) {
+	setChargedTracksList(tracks);
+    }
+
+    /**
+     * Return true of the object appears to be charged, false if neutral.
+     */
+    public boolean valid (Cluster clus)
+    {
+	// Find the tracks in the event
+	List<Track> tracksToUse = null;
+	if (m_tracksListName != null) {
+	    tracksToUse = m_event.get(Track.class, m_tracksListName);
+	} else {
+	    tracksToUse = m_tracks;
+	}
+
+	// How many tracks are there in the event?
+	int nTracks = tracksToUse.size();
+	// Solid angle per track
+	double solidAnglePerTrack = Math.PI * 4.0 / ( (double)(nTracks) );
+	// Arbitrary choice of factor... say 5
+	double scaleFactor = 1.5;
+	double coneSolidAngle = scaleFactor * solidAnglePerTrack;
+	double coneOpeningAngle = Math.sqrt(4.0 * coneSolidAngle / Math.PI);
+	
+	// Where is the center of the cone?
+	BasicHep3Vector orig = new BasicHep3Vector(0,0,0);
+	BasicHep3Vector clusterPosition = new BasicHep3Vector(clus.getPosition());
+	Hep3Vector clusterDirection = VecOp.sub(clusterPosition, orig);
+	double dotProductThreshold = Math.cos(0.5*coneOpeningAngle);
+
+	// Loop over tracks, checking whether they fall within this cone...
+	List<Track> acceptedTracks = new Vector<Track> ();
+	for (Track tr : tracksToUse) {
+	    // It's not sufficient to use the momentum -- need to take the
+	    // curvature of the track into account somehow.
+	    Hep3Vector swumTrackIntercept = swimTrack(tr);
+	    if (swumTrackIntercept != null) {
+		double dotProduct = VecOp.dot(VecOp.unit(clusterDirection), VecOp.unit(swumTrackIntercept));
+		if (dotProduct > dotProductThreshold) {
+		    acceptedTracks.add(tr);
+		}
+	    }
+	}
+
+	// Use truth information to check whether this cluster is really charged or neutral
+	List<MCParticle> mcList = m_event.get(MCParticle.class, "GenFinalStateParticles");
+	Set<MCParticle> contributingParticles = findMCParticles(clus, mcList);
+	int countChargedContributors = 0;
+	int countNeutralContributors = 0;
+	for (MCParticle part : contributingParticles) {
+	    if (Math.abs(part.getCharge()) < 0.5) {
+		countNeutralContributors++;
+	    } else {
+		countChargedContributors++;
+	    }
+	}
+	String chargeType = new String("error");
+	if (countNeutralContributors>0 && countChargedContributors==0) {
+	    chargeType = "neutral";
+	} else if (countNeutralContributors==0 && countChargedContributors>0) {
+	    chargeType = "charged";
+	} else if (countNeutralContributors>0 && countChargedContributors>0) {
+	    chargeType = "mixed";
+	}
+
+	String printme = new String();
+	printme =  "DEBUG: Event has "+tracksToUse.size()+" tracks and scale factor is "
+	    +scaleFactor+" so coneSolidAngle="+coneSolidAngle
+	    +" and coneOpeningAngle="+coneOpeningAngle
+	    +". For a ["+chargeType+"] cluster with "
+	    +clus.getCalorimeterHits().size()
+	    +" hits, I found "
+	    +acceptedTracks.size()+" tracks nearby";
+	if (acceptedTracks.size() >= scaleFactor) {
+	    printme += " => charged";
+	} else {
+	    printme += " => neutral";
+	}
+	System.out.println(printme);
+	
+	boolean recoIsCharged = acceptedTracks.size() >= scaleFactor;
+	boolean trueIsCharged = (countChargedContributors>0 && countNeutralContributors==0);
+	if (recoIsCharged == trueIsCharged) {
+	    m_fragPerformance.fill(1);
+	} else {
+	    m_fragPerformance.fill(0);
+	}
+	if (trueIsCharged) {
+	    m_fragPerformanceCharged.fill( (double)(acceptedTracks.size()) / scaleFactor );
+	} else {
+	    m_fragPerformanceNeutral.fill( (double)(acceptedTracks.size()) / scaleFactor );
+	}
+
+	return (acceptedTracks.size() >= scaleFactor);
+    }
+
+    public void setChargedTracksList(String listName) {
+	m_tracksListName = listName;
+	m_tracks = null;
+    }
+    public void setChargedTracksList(List<Track> tracks) {
+	m_tracksListName = null;
+	m_tracks = tracks;
+    }
+    public void process(EventHeader event) {
+	m_event = event;
+	initGeometry(event);
+	Detector det = event.getDetector();
+	double[] zero = {0, 0, 0};
+	double[] fieldStrength = det.getFieldMap().getField(zero);
+	m_swimmer = new HelixSwimmer(fieldStrength[2]);
+    }
+
+    protected boolean m_init = false;
+    protected HelixSwimmer m_swimmer = null;
+    protected EventHeader m_event = null;
+    protected String      m_tracksListName = null;
+    protected List<Track> m_tracks = null;
+
+    protected double m_ECAL_barrel_zmin;
+    protected double m_ECAL_barrel_zmax;
+    protected double m_ECAL_barrel_r;
+    protected double m_ECAL_endcap_z;
+    protected double m_ECAL_endcap_rmin;
+    protected double m_ECAL_endcap_rmax;
+
+    // FOR DEBUG
+
+    ITree m_tree;
+    IHistogramFactory m_histoFactory = null;
+    ICloud1D m_fragPerformance;
+    ICloud1D m_fragPerformanceNeutral;
+    ICloud1D m_fragPerformanceCharged;
+    public void initPlots()
+    {
+	IAnalysisFactory af = IAnalysisFactory.create();
+        try {
+            m_tree = af.createTreeFactory().create("charged-neutral.aida","xml",false,true); 
+	    m_histoFactory = af.createHistogramFactory(m_tree); 
+	    m_fragPerformance = m_histoFactory.createCloud1D("fragPerformance");
+	    m_fragPerformanceNeutral = m_histoFactory.createCloud1D("fragPerformanceNeutral");
+	    m_fragPerformanceCharged = m_histoFactory.createCloud1D("fragPerformanceCharged");
+	} catch (IOException ioe1) {
+            ioe1.printStackTrace(); 
+	}
+    }
+
+    public void suspend() {
+        try {
+            m_tree.commit();
+        } catch(IOException ioe1) {
+            ioe1.printStackTrace(); 
+        }
+        super.suspend();
+    }
+
+    /**
+     * Internal utility routine, belongs in another class
+     */
+    protected Set<MCParticle> findMCParticles(Cluster clus, List<MCParticle> mcList)
+    {
+	Set clusterParticles = new HashSet<MCParticle>();
+	for (CalorimeterHit hit : clus.getCalorimeterHits()) {
+	    Set<MCParticle> hitParticles = findMCParticles(hit, mcList);
+	    clusterParticles.addAll(hitParticles);
+	}
+	return clusterParticles;
+    }
+
+    /**
+     * Internal utility routine, belongs in another class
+     */
+    protected Set<MCParticle> findMCParticles(CalorimeterHit hit, List<MCParticle> mcList)
+    {
+        if ( ! (hit instanceof SimCalorimeterHit) ) {
+            throw new AssertionError("Non-simulated hit!");
+        } else {
+            SimCalorimeterHit simHit = (SimCalorimeterHit) (hit);
+            Set<MCParticle> contributingParticlesFromList = new HashSet<MCParticle>();
+            int nContributingParticles = simHit.getMCParticleCount();
+            for (int i=0; i<nContributingParticles; i++) {
+                MCParticle part = simHit.getMCParticle(i);
+                List<MCParticle> parentsInList = findParentsInList(part, mcList);
+                contributingParticlesFromList.addAll(parentsInList);
+            }
+            return contributingParticlesFromList;
+        }
+    }
+
+    /**
+     * Internal utility routine, belongs in another class
+     */
+    protected List<MCParticle> findParentsInList(MCParticle part, List<MCParticle> mcList) 
+    {
+        List<MCParticle> outputList = new Vector<MCParticle>();
+        if (mcList.contains(part)) {
+            // Already in there
+            outputList.add(part);
+        } else {
+            // Not in there -- recurse up through parents
+            List<MCParticle> parents = part.getParents();
+            if (parents.size()==0) {
+                // Ran out of options -- add nothing and return below
+            } else {
+                for (MCParticle parent : parents) {
+                    List<MCParticle> ancestorsInList = findParentsInList(parent, mcList);
+                    outputList.addAll(ancestorsInList);
+                }
+            }
+        }
+        return outputList;
+    }   
+
+    protected void initGeometry(EventHeader event) 
+    {
+	if (!m_init) {
+	    Detector det = event.getDetector();
+	    CylindricalCalorimeter emb = ((CylindricalCalorimeter) det.getSubdetectors().get("EMBarrel"));
+	    CylindricalCalorimeter eme = ((CylindricalCalorimeter) det.getSubdetectors().get("EMEndcap"));
+	    m_ECAL_barrel_zmin = emb.getZMin();
+	    m_ECAL_barrel_zmax = emb.getZMax();
+	    m_ECAL_barrel_r = emb.getLayering().getDistanceToLayerSensorMid(0);
+	    m_ECAL_endcap_z = eme.getLayering().getDistanceToLayerSensorMid(0);
+	    m_ECAL_endcap_rmin = eme.getInnerRadius();
+	    m_ECAL_endcap_rmax = eme.getOuterRadius();
+	    m_init = true;
+	    System.out.println(this.getClass().getName()+": Init: m_ECAL_barrel_zmin="+m_ECAL_barrel_zmin);
+	    System.out.println(this.getClass().getName()+": Init: m_ECAL_barrel_zmax="+m_ECAL_barrel_zmax);
+	    System.out.println(this.getClass().getName()+": Init: m_ECAL_barrel_r="+m_ECAL_barrel_r);
+	    System.out.println(this.getClass().getName()+": Init: m_ECAL_endcap_z="+m_ECAL_endcap_z);
+	    System.out.println(this.getClass().getName()+": Init: m_ECAL_endcap_rmin="+m_ECAL_endcap_rmin);
+	    System.out.println(this.getClass().getName()+": Init: m_ECAL_endcap_rmax="+m_ECAL_endcap_rmax);
+	}
+    }
+
+    // Swim a track
+    protected Hep3Vector swimTrack(Track tr) {
+	double alpha = Double.NaN;
+	m_swimmer.setTrack(tr);
+	// 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;
+	// Fixme: Here we should check that the track really does go all the
+	// way to the ECAL instead of stopping/decaying/interacting earlier.
+	// This used to be done with the checkDecayPoint() method in
+	// contrib.uiowa.structural.SwimToECAL
+	if (isValidBarrelIntercept(m_swimmer, alphaBarrel)) {
+	    alpha = alphaBarrel;
+	    validBarrel = true;
+	} else if (isValidEndcapIntercept(m_swimmer, alphaEndcap)) {
+	    alpha = alphaEndcap;
+	    validEndcap = true;
+	}
+
+	if ( ! Double.isNaN(alpha) ) {
+	    if (validEndcap || validBarrel) {
+		if ( !(validEndcap && validBarrel) ) {
+		    // OK
+		    Hep3Vector trackPoint = m_swimmer.getPointAtDistance(alpha);
+		    return trackPoint;
+		}
+	    }
+	}
+
+	// Unable to extrapolate
+	return null;
+    }
+
+    protected double swimToBarrel(HelixSwimmer swimmer) {
+	// Look for a hit in the first layer of the ECAL barrel
+	return swimmer.getDistanceToRadius(m_ECAL_barrel_r);
+    }
+    protected double swimToEndcap(HelixSwimmer swimmer) {
+	// Look for a hit in the first layer of the ECAL endcap
+	return swimmer.getDistanceToZ(m_ECAL_endcap_z);
+    }
+    protected boolean isValidBarrelIntercept(HelixSwimmer swimmer, double alpha) {
+	// Must have -m_ECAL_barrel_z <= z <= +m_ECAL_barrel_z
+	Hep3Vector intercept = swimmer.getPointAtDistance(alpha);
+	double z = intercept.z();
+	boolean zInRange = (z >= m_ECAL_barrel_zmin && z <= m_ECAL_barrel_zmax);
+	return zInRange;
+    }
+    protected boolean isValidEndcapIntercept(HelixSwimmer swimmer, double alpha) {
+	// Must have m_ECAL_endcap_rmin <= r <= m_ECAL_endcap_rmax
+	Hep3Vector intercept = swimmer.getPointAtDistance(alpha);
+	double r = Math.sqrt(intercept.x()*intercept.x() + intercept.y()*intercept.y());
+	boolean rInRange = (r >= m_ECAL_endcap_rmin && r <= m_ECAL_endcap_rmax);
+	return rInRange;
+    }
+}
CVSspam 0.2.8