Commit in lcsim/src/org/lcsim/contrib/onoprien on MAIN
mcTrackFinder/ExampleDriver.java+97added 1.1
             /MCTrack.java+235added 1.1
             /MCTrackFinder.java+233added 1.1
             /package.html+11added 1.1
tester/ExampleDriverTrackingTest.java+51added 1.1
      /PiFinder.java+27added 1.1
      /PiTest.java+50added 1.1
      /RatedTrack.java+67added 1.1
      /TrackingTest.java+121added 1.1
      /TrackingTestBase.java+509added 1.1
      /package.html+8added 1.1
+1409
11 added files
New package name. Updated detector-dependent initialization.

lcsim/src/org/lcsim/contrib/onoprien/mcTrackFinder
ExampleDriver.java added at 1.1
diff -N ExampleDriver.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ExampleDriver.java	6 Apr 2007 21:40:27 -0000	1.1
@@ -0,0 +1,97 @@
+package org.lcsim.contrib.onoprien.mcTrackFinder;
+
+import java.util.*;
+import org.lcsim.event.*;
+import org.lcsim.util.swim.HelixSwimmer;
+import org.lcsim.util.Driver;
+import org.lcsim.spacegeom.*;
+import org.lcsim.util.aida.AIDA;
+import org.lcsim.recon.cat.GarfieldHitConverter;
+import org.lcsim.constants.Constants;
+
+/**
+ * An example driver showing how to use MCTrackFinder.
+ * In this case, the track finder is configured to emulate the performance of a
+ * vertex detector seeded algorithm by requiring hits in at least 3 VXD layers.
+ * 
+ * 
+ * 
+ * @author D. Onoprienko
+ * @version $Id: ExampleDriver.java,v 1.1 2007/04/06 21:40:27 onoprien Exp $
+ */
+public class ExampleDriver extends Driver {
+  
+   //private AIDA aida = AIDA.defaultInstance();
+   
+   public ExampleDriver() {
+     
+//     GarfieldHitConverter hitDigitizer = new GarfieldHitConverter();
+//     
+//     hitDigitizer.set("OUTPUT_COLLECTION_NAME", "GarfieldHitsRaw");
+//     add(hitDigitizer);
+     
+//     MCTrackFinder mcTrackFinder = new MCTrackFinder();
+//     mcTrackFinder.addTrackerHitList("GarfieldHitsRaw"); // Set input TrackerHit collection name
+//     mcTrackFinder.set("Track_Collection_Name", "CheaterTracks"); // Set output Track collection name
+//     mcTrackFinder.set("Min_VXD_Layers", 3); // Set cut on number of layers with SimTrackerHits
+//     add(mcTrackFinder);
+   }
+   
+   public void process(EventHeader event) {
+     System.out.println("Processing event " + event.getEventNumber());
+     super.process(event);
+    
+     int count2 = 0;
+     List<List<SimTrackerHit>> hitLists = event.get(SimTrackerHit.class);
+     for (List<SimTrackerHit> hitList : hitLists) {
+       for (SimTrackerHit hit : hitList) {
+         count2++;;
+       }
+     }
+     
+     HashMap<MCParticle, List<SimTrackerHit>> map = MCTrackFinder.getMCParticleToSimTrackerHitMap(event);
+     List<MCParticle> mcList = event.getMCParticles();
+     int count1 = 0;
+     for (MCParticle mc : mcList) {
+       System.out.println(" ");
+       System.out.println("Particle " + mc.getType().getName());
+       List<SimTrackerHit> hitList = map.get(mc);
+       for (SimTrackerHit hit : hitList) {
+         count1++;
+         System.out.println("  mc : " + hit.getMCParticle().getType().getName());
+       }
+     }
+     System.out.println(" ");
+     System.out.println("Expected " + count2 + "  Found " + count1);
+     
+     
+//     List<MCTrack> tracks = event.get(MCTrack.class,"CheaterTracks");
+//     System.out.println("Field " + Const.bField);
+//     int count = 0;
+//     for (MCTrack track : tracks) {
+//       count++;
+//       double[] pAuto = track.getMomentum();
+//       MCParticle mcPart = track.getMCParticle();
+//       HelixSwimmer swimmer = new HelixSwimmer(Const.bField);
+//       swimmer.setTrack(mcPart.getMomentum(), mcPart.getOrigin(), (int) mcPart.getCharge());
+//       double alpha = swimmer.getDistanceToPoint(new SpacePoint());
+//       SpaceVector pPCA = swimmer.getMomentumAtDistance(alpha);
+//       double[] pMan = pPCA.v();
+////       if (true) {
+//       if (Math.sqrt((pAuto[0]-pMan[0])*(pAuto[0]-pMan[0]) + (pAuto[1]-pMan[1])*(pAuto[1]-pMan[1]) + (pAuto[2]-pMan[2])*(pAuto[2]-pMan[2])) > 10e-10) {
+//         System.out.println(" ");
+//         double Pt = track.getPt();
+//         double radius = Pt/(Const.bField*Constants.fieldConversion);
+//         System.out.println("MC: Mom "+ mcPart.getMomentum() +" Charge "+ mcPart.getCharge() +" Pos "+mcPart.getOrigin() +" Rad " + radius);
+//         System.out.println(" ");
+//         System.out.println("Auto Mom " + pAuto[0] +" "+ pAuto[1] +" "+ pAuto[2] +" Alpha "+ track.getChi2());
+//         System.out.println("Manual Mom " + pMan[0] +" "+ pMan[1] +" "+ pMan[2] +" Alpha "+ alpha);
+//         System.out.println("Points:  Auto = " + track.getReferencePointX() +" "+ track.getReferencePointY());
+//         System.out.println("Points:   Man = " + swimmer.getPointAtDistance(alpha));
+//       }
+//     }
+//     System.out.println("Count " + count);
+//     System.out.println(" ");
+   }
+
+}
\ No newline at end of file

lcsim/src/org/lcsim/contrib/onoprien/mcTrackFinder
MCTrack.java added at 1.1
diff -N MCTrack.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ MCTrack.java	6 Apr 2007 21:40:27 -0000	1.1
@@ -0,0 +1,235 @@
+package org.lcsim.contrib.onoprien.mcTrackFinder;
+
+import java.util.*;
+import org.lcsim.recon.cat.util.BasicTrack;
+import org.lcsim.recon.cat.util.Const;
+import org.lcsim.event.*;
+import hep.physics.matrix.SymmetricMatrix;
+import hep.physics.vec.BasicHep3Vector;
+import org.lcsim.spacegeom.SpacePoint;
+import org.lcsim.spacegeom.SpaceVector;
+import org.lcsim.geometry.Subdetector;
+
+/**
+ * Representation of a track found by the "cheater" track finder. 
+ * Implements {@link Track}, and provides access to the underlying {@link MCParticle}
+ * object and to associated tracker hits. 
+ * <p>
+ * An object of this class can be assigned any number of integer status codes through
+ * the call to {@link #setStatus(int,int) setStatus(int category, int status)} method.
+ * This allows users to mark Monte Carlo tracks that belong to a certain group, for example.
+ * Assigned statuses are retrieved by {@link #getStatus(int) getStatus(int category)}.
+ *
+ * @author D. Onoprienko
+ * @version $Id: MCTrack.java,v 1.1 2007/04/06 21:40:27 onoprien Exp $
+ */
+public class MCTrack extends BasicTrack {
+  
+// -- Private fields :  --------------------------------------------------------
+  
+  private MCParticle _mcParticle;
+  private ArrayList<SimTrackerHit> _simHits;
+  private boolean _hitsSorted;
+  
+  HashMap<Integer,Integer> _status;
+  
+// -- Constructors :  ----------------------------------------------------------
+  
+  private void commonInit(MCParticle mcParticle) {
+    _mcParticle = mcParticle;
+    setHelixParameters(new SpacePoint(new BasicHep3Vector()), new SpacePoint(mcParticle.getOrigin()), 
+        new SpaceVector(mcParticle.getMomentum()), (int)mcParticle.getCharge());
+    _status = new HashMap<Integer,Integer>(2);
+  }
+  
+  public MCTrack(MCParticle mcParticle) {
+    super();
+    commonInit(mcParticle);
+    _simHits = new ArrayList<SimTrackerHit>(10);
+    _hitsSorted = false;
+  }
+  
+  public MCTrack(MCParticle mcParticle, List<TrackerHit> hitList) {
+    super();
+    commonInit(mcParticle);
+    _hits = new ArrayList<TrackerHit>(hitList.size());
+    _hits.addAll(hitList);
+    _simHits = new ArrayList<SimTrackerHit>(10);
+    sortHits();
+    _hitsSorted = true;
+  }
+  
+  public MCTrack(MCParticle mcParticle, List<TrackerHit> hitList, List<SimTrackerHit> simHitList) {
+    super();
+    commonInit(mcParticle);
+    _hits = new ArrayList<TrackerHit>(hitList.size());
+    _hits.addAll(hitList);
+    _simHits = new ArrayList<SimTrackerHit>(simHitList.size());
+    _simHits.addAll(simHitList);
+    sortHits();
+    _hitsSorted = true;
+  }
+  
+// -- Getters :  ---------------------------------------------------------------
+  
+  /** Returns MCParticle that produced this track. */
+  public MCParticle getMCParticle() {return _mcParticle;}
+  
+  /** Returns a list of associated tracker hits. */
+  public List<TrackerHit> getTrackerHits() {
+    if (!_hitsSorted) {
+      sortHits();
+      _hitsSorted = true;
+    }
+    return _hits;
+  }
+  
+  /** Returns a list of associated SimTrackerHits hits. */
+  public List<SimTrackerHit> getSimTrackerHits() {
+    if (!_hitsSorted) {
+      sortHits();
+      _hitsSorted = true;
+    }
+    return _simHits;
+  }
+  
+  /** Returns number of layers with SimTrackerHits that belong to this track. */
+  public int getNLayers() {
+    ArrayList<Layer> layers = new ArrayList<Layer>();
+    for (SimTrackerHit hit : _simHits) {
+      Layer layer = new Layer(hit.getLayer(), hit.getSubdetector());
+      if (!layers.contains(layer)) layers.add(layer);
+    }
+    return layers.size();
+  }
+  
+  /** Returns number of VXD layers with SimTrackerHits that belong to this track. */
+  public int getNVxdLayers() {
+    ArrayList<Layer> layers = new ArrayList<Layer>();
+    Layer layer;
+    for (SimTrackerHit hit : _simHits) {
+      Subdetector sub = hit.getSubdetector();
+      if (Const.det().isVXD(sub)) {
+        layer = new Layer(hit.getLayer(), sub);
+        if (!layers.contains(layer)) layers.add(layer);
+      }
+    }
+    return layers.size();
+  }
+  
+  /**
+   * Returns a status flag that has been previously assigned to this track through
+   * the call to {@link #setStatus(int,int) setStatus(int category, int status)}.
+   */
+  public int getStatus(int category) {
+    return _status.get(category);
+  }
+  
+  /** Returns THETA angle of the MCParticle that rpoduced the track. */
+  public double getTheta() {
+    return Math.acos(_momentum[2]/getP());
+  }
+    
+  /** Returns radius of innermost hit associated with this track. */
+  public double getRadiusOfInnermostHit() {
+    double rad = 0.;
+    if (!_hits.isEmpty()) {
+      if (!_hitsSorted) {
+        sortHits();
+        _hitsSorted = true;
+      }
+      double[] pos = _hits.get(0).getPosition();
+      rad = Math.hypot(pos[0], pos[1]);
+    }
+    return rad;
+  }
+  
+// -- Setters :  ---------------------------------------------------------------
+  
+  /** Add hit to the list of tracker hits associated with this track. */
+  public void addTrackerHit(TrackerHit hit) {
+    if (! _hits.contains(hit)) {
+      _hits.add(hit);
+      _hitsSorted = false;
+    }
+  }
+  
+  /** Add hits to the list of tracker hits associated with this track. */
+  public void addTrackerHits(List<TrackerHit> hitList) {
+    for (TrackerHit hit : hitList) if (! _hits.contains(hit)) _hits.add(hit);
+    _hitsSorted = false;
+  }
+  
+  /** Add hit to the list of SimTrackerHits associated with this track. */
+  public void addSimTrackerHit(SimTrackerHit hit) {
+    if (! _hits.contains(hit)) {
+      _simHits.add(hit);
+      _hitsSorted = false;
+    }
+  }
+  
+  /** Add hits to the list of SimTrackerHits associated with this track. */
+  public void addSimTrackerHits(List<SimTrackerHit> hitList) {
+    for (SimTrackerHit hit : hitList) if (! _simHits.contains(hit)) _simHits.add(hit);
+    _hitsSorted = false;
+  }
+  
+  /**
+   * Set an integer status flag for this track.
+   *
+   *  @param category  Identifier of a status category. A track can be assigned 
+   *                   statuses in any number of categories.
+   *  @param status    Status flag that can be later retrieved through the call to
+   *                   {@link #getStatus(int) getStatus(int category)}
+   */
+  public void setStatus(int category, int status) {_status.put(category, status);}
+  
+// -- Private helper methods :  ------------------------------------------------
+  
+  /**
+   * Sort hits in the order of increasing distance from the detector center.
+   */
+  private void sortHits() {
+    Collections.sort(_hits, new Comparator<TrackerHit>() {
+      public int compare(TrackerHit hit1, TrackerHit hit2) {
+        double[] pos = hit1.getPosition();
+        double r1 = pos[0]*pos[0]+pos[1]*pos[1]+pos[2]*pos[2];
+        pos = hit2.getPosition();
+        double r2 = pos[0]*pos[0]+pos[1]*pos[1]+pos[2]*pos[2];
+        return (int)Math.signum(r1-r2);
+      }
+    });
+    Collections.sort(_simHits, new Comparator<SimTrackerHit>() {
+      public int compare(SimTrackerHit hit1, SimTrackerHit hit2) {
+        double[] pos = hit1.getPoint();
+        double r1 = pos[0]*pos[0]+pos[1]*pos[1]+pos[2]*pos[2];
+        pos = hit2.getPoint();
+        double r2 = pos[0]*pos[0]+pos[1]*pos[1]+pos[2]*pos[2];
+        return (int)Math.signum(r1-r2);
+      }
+    });
+    
+  }
+  
+// -- Helper class to pack (subdetector, layer) pair :  ------------------------
+  
+  private class Layer {
+    
+    final int _layer;
+    final Subdetector _subDet;
+    
+    Layer(int layer, org.lcsim.geometry.Subdetector subDet) {
+      _layer = layer;
+      _subDet = subDet;
+    }
+    
+    public boolean equals(Object object) {
+      if (object.getClass() == Layer.class) {
+       Layer obj = (Layer)object;
+       return (obj._layer == _layer && obj._subDet == _subDet);
+      }
+      return super.equals(object);
+    }
+  }
+  
+}

lcsim/src/org/lcsim/contrib/onoprien/mcTrackFinder
MCTrackFinder.java added at 1.1
diff -N MCTrackFinder.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ MCTrackFinder.java	6 Apr 2007 21:40:27 -0000	1.1
@@ -0,0 +1,233 @@
+package org.lcsim.contrib.onoprien.mcTrackFinder;
+
+import java.util.*;
+import org.lcsim.recon.cat.util.NoSuchParameterException;
+import org.lcsim.event.*;
+import org.lcsim.util.Driver;
+import hep.physics.particle.Particle;
+
+/**
+ * Configurable "cheater" track finder.
+ * See descriptions of <tt>set()</tt> methods for the list of parameters that can be changed.
+ * <p>
+ * In addition, the cheater can be customized by overriding {@link #filter(MCParticle)} 
+ * method that selects potentially reconstructable Monte Carlo particles, 
+ * {@link #filter(MCTrack)} method that selects <tt>MCTracks</tt> to be saved into
+ * the output list, and {@link #findRelatedHits(MCParticle, List<TrackerHit>)} method
+ * that controls association of hits with <tt>MCParticle</tt>s.
+ * <p>
+ * The class also provides a number of static utility methods.
+ *
+ * @author D. Onoprienko
+ * @version $Id: MCTrackFinder.java,v 1.1 2007/04/06 21:40:27 onoprien Exp $
+ */
+public class MCTrackFinder extends Driver {
+
+// -- Constructors :  ----------------------------------------------------------
+  
+  public MCTrackFinder() {}
+
+// -- Setters :  ---------------------------------------------------------------
+  
+  /** 
+   * Add a list of TrackerHits to be processed.
+   * Hits from these collections will be associated with found tracks.
+   */
+  public void addTrackerHitList(String name) {
+    _trackerHitListNames.add(name);
+    _trackerHitListNames.trimToSize();
+  }
+
+  /** 
+   * Set any <tt>String</tt> parameter.
+   * The following parameters can be set with this method:<br>
+   * <tt>"Track_Collection_Name"</tt> - name of track collection to be saved into the event. Default: "MCTracks".
+   * @param name   Name of parameter to be set
+   * @param value  Value to be assigned to the parameter.
+   * @throws NoSuchParameterException Thrown if the supplied parameter name is unknown.
+   *         Subclasses may catch this exception after a call to <tt>super.set()</tt>
+   *         and set their own parameters.
+   */
+  public void set(String name, String value) {
+    if (name == "Track_Collection_Name") {
+      _trackListName = value;
+    } else {
+      throw new NoSuchParameterException(name, this.getClass());
+    }
+  }
+
+  /** 
+   * Set any integer parameter. 
+   * The following parameters can be set with this method:<br>
+   * <tt>"Min_Hits"</tt> - minimum number of TrackerHits required for the track to be found. Default: 0.<br>
+   * <tt>"Min_Sim_Hits"</tt> - minimum number of SimTrackerHits required for the track to be found. Default: 1.<br>
+   * <tt>"Min_Layers"</tt> - minimum number of layers with hits required for the track to be found. Default: 0.<br>
+   * <tt>"Min_VXD_Layers"</tt> - minimum number of vertex detector layers with hits required for the track to be found. Default: 0.<br>
+   * @param name   Name of parameter to be set
+   * @param value  Value to be assigned to the parameter.
+   * @throws NoSuchParameterException Thrown if the supplied parameter name is unknown.
+   *         Subclasses may catch this exception after a call to <tt>super.set()</tt>
+   *         and set their own parameters.
+   */
+  public void set(String name, int value) {
+    if (name.equalsIgnoreCase("Min_Hits")) {
+      _minHits = value;
+    } else if (name.equalsIgnoreCase("Min_Sim_Hits")) {
+      _minSimHits = value;
+    } else if (name.equalsIgnoreCase("Min_Layers")) {
+      _minLayers = value;
+    } else if (name.equalsIgnoreCase("Min_VXD_Layers")) {
+      _minVxdLayers = value;
+    } else {
+      throw new NoSuchParameterException(name, this.getClass());
+    }    
+  }
+  
+// -- Processing event :  ------------------------------------------------------
+  
+  /** Process event. */
+  public void process(EventHeader event) {
+    
+    super.process(event);
+    
+    // Create list of eligible MCParticles :
+    
+    List<MCParticle> list = event.getMCParticles();
+    ArrayList<MCParticle> mcPartList = new ArrayList<MCParticle>(list.size());
+    for (MCParticle mcPart : list) {
+      if (filter(mcPart)) mcPartList.add(mcPart);
+    }
+    
+    // Create list of TrackerHit hits to be supplied to the finder :
+    
+    List<TrackerHit> hitList = new ArrayList<TrackerHit>();
+    for (String name : _trackerHitListNames) {
+      hitList.addAll(event.get(TrackerHit.class, name));
+    }
+    
+    List<List<SimTrackerHit>> simTrackerHitLists = event.get(SimTrackerHit.class);
+    
+    // Create list of Monte Carlo Tracks
+
+    ArrayList<MCTrack> mcTrackList = new ArrayList<MCTrack>(100);
+    for (MCParticle mcPart : mcPartList) {
+      // Find TrackerHits related to this MCParticle
+      List<TrackerHit> hits = findRelatedHits(mcPart, hitList);
+      // Find SimTrackerHits related to this MCParticle
+      ArrayList<SimTrackerHit> simHits = new ArrayList<SimTrackerHit>(100);
+      for (List<SimTrackerHit> hList : simTrackerHitLists) {
+        for (SimTrackerHit simHit : hList) {
+          if (simHit.getMCParticle() == mcPart) simHits.add(simHit);
+        }
+      }
+      // Create MCTrack and add it to the output list if it passes the cuts
+      MCTrack mcTrack = new MCTrack(mcPart, hits, simHits);
+      if (filter(mcTrack)) mcTrackList.add(mcTrack);
+    }
+    mcTrackList.trimToSize();
+    
+    // Put list of tracks into the event :
+    
+    event.put(_trackListName, mcTrackList, MCTrack.class, 0);
+
+  }
+  
+  /**
+   * Selects hits from the supplied list that are associated with the given MCParticle.
+   * Default implementation assumes that <tt>getRawHits()</tt> method of hits in the list
+   * returns a list of <tt>SimTrackerHit</tt>s, and associates the hit with <tt>MCParticle</tt>
+   * if at least one of those <tt>SimTrackerHit</tt>s was produced by it.
+   * Method can be overwritten by subclasses to use a different algorithm.
+   */
+  protected List<TrackerHit> findRelatedHits(MCParticle mcPart, List<TrackerHit> hitList) {
+    ArrayList outList = new ArrayList<TrackerHit>();
+    for (TrackerHit hit : hitList) {
+      List rawHits = hit.getRawHits();
+      for (Object obj : rawHits) {
+        if (mcPart == ((SimTrackerHit)obj).getMCParticle()) {
+          outList.add(hit);
+          break;
+        }
+      }
+    }
+    return outList;
+  }
+
+  /**
+   * Returns true if the finder should try to find the track produced by this MCParticle.
+   * Default implementation accepts all charged particles with generator status other than
+   * DOCUMENTATION or INTERMEDIATE. Method can be overwritten by subclasses to use different
+   * selection criteria.
+   */
+  protected boolean filter(MCParticle mcParticle) {
+    return mcParticle.getGeneratorStatus() != Particle.DOCUMENTATION &&
+           mcParticle.getGeneratorStatus() != Particle.INTERMEDIATE &&
+           Math.abs(mcParticle.getCharge()) > .7;
+  }
+
+  
+  /**
+   * Returns true the track should be saved in the output list.
+   * Default implementation uses cut on number of layers with hits.
+   * Method can be overwritten by subclasses to use different selection criteria.
+   */
+  protected boolean filter(MCTrack track) {
+    
+    if (_minLayers > 0 && track.getNLayers() < _minLayers) return false;
+    if (_minVxdLayers > 0 && track.getNVxdLayers() < _minVxdLayers) return false;
+    
+    if (track.getSimTrackerHits().size() < _minSimHits) return false;
+    if (track.getTrackerHits().size() < _minHits) return false;
+    
+    return true;
+  }
+  
+// -- Static convenience methods :  --------------------------------------------
+  
+  /**
+   * Returns a mapping between <tt>MCParticle</tt> and found <tt>MCTrack</tt> objects.<br>
+   * An empty map is returned if <tt>MCTrackFinder</tt> has not been run for this event.
+   */
+  static public HashMap<MCParticle, MCTrack> getMCParticleToMCTrackMap(EventHeader event) {
+    List<List<MCTrack>> trackLists = event.get(MCTrack.class); 
+    HashMap<MCParticle, MCTrack> map = new HashMap<MCParticle, MCTrack>();
+    for (List<MCTrack> trackList : trackLists) {
+      for (MCTrack track : trackList) {
+        map.put(track.getMCParticle(), track);
+      }
+    }
+    return map;
+  }
+  
+  /**
+   * Returns a mapping between <tt>MCParticle</tt> and a list of <tt>SimTrackerHit</tt>s produced by it.<br>
+   * Warning: this is a time-expensive operation.
+   */
+  static public HashMap<MCParticle, List<SimTrackerHit>> getMCParticleToSimTrackerHitMap(EventHeader event) {
+    List<List<SimTrackerHit>> hitLists = event.get(SimTrackerHit.class);
+    List<MCParticle> mcList = event.getMCParticles();
+    HashMap<MCParticle, List<SimTrackerHit>> map
+            = new HashMap<MCParticle, List<SimTrackerHit>>(Math.round(mcList.size()*1.5f));
+    for (MCParticle mc : mcList) map.put(mc, new ArrayList<SimTrackerHit>(100));
+    for (List<SimTrackerHit> hitList : hitLists) {
+      for (SimTrackerHit hit : hitList) {
+        map.get(hit.getMCParticle()).add(hit);
+      }
+    }
+    for (List<SimTrackerHit> hitList : map.values()) {
+      ((ArrayList<SimTrackerHit>)hitList).trimToSize();
+    }
+    return map;
+ }
+  
+// -- Private parts :  ---------------------------------------------------------
+  
+  protected ArrayList<String> _trackerHitListNames = new ArrayList<String>(1);
+  protected String _trackListName = "MCTracks";
+  
+  protected int _minLayers = 0;
+  protected int _minVxdLayers = 0;
+  protected int _minSimHits = 1;
+  protected int _minHits = 0;
+  
+}

lcsim/src/org/lcsim/contrib/onoprien/mcTrackFinder
package.html added at 1.1
diff -N package.html
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ package.html	6 Apr 2007 21:40:27 -0000	1.1
@@ -0,0 +1,11 @@
+<html>
+<body>
+"Cheater" track finder that uses Monte Carlo truth information to find tracks.
+The track finder is configurable so that it can, for example,  simulate 
+performance of a VXD-based algorithm (by requiring hits in at least 3 VXD 
+layers).
+<p>
+{@link MCTrackFinder} class also provides a number of utility methods for 
+mapping <tt>MCParticle</tt>s to simulated hits and found tracks.
+</body>
+</html>

lcsim/src/org/lcsim/contrib/onoprien/tester
ExampleDriverTrackingTest.java added at 1.1
diff -N ExampleDriverTrackingTest.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ExampleDriverTrackingTest.java	6 Apr 2007 21:40:27 -0000	1.1
@@ -0,0 +1,51 @@
+package org.lcsim.contrib.onoprien.tester;
+
+import java.util.*;
+import org.lcsim.recon.cat.EmcalMipStubs;
+import org.lcsim.recon.cat.GarfieldHitConverter;
+import org.lcsim.recon.cat.GarfieldTrackFinder;
+import org.lcsim.recon.cat.util.Const;
+import org.lcsim.event.*;
+import org.lcsim.recon.cluster.nn.NearestNeighborClusterDriver;
+import org.lcsim.util.Driver;
+import org.lcsim.util.aida.AIDA;
+import org.lcsim.contrib.onoprien.mcTrackFinder.*;
+
+/**
+ * An example driver that runs calorimeter-assisted track finder, and uses
+ * tester package to analyse its performance.
+ *
+ * @author D. Onoprienko
+ * @version $Id: ExampleDriverTrackingTest.java,v 1.1 2007/04/06 21:40:27 onoprien Exp $
+ */
+public class ExampleDriverTrackingTest extends Driver {
+  
+   private AIDA aida = AIDA.defaultInstance();
+   
+   public ExampleDriverTrackingTest() {
+
+     GarfieldHitConverter hitDigitizer = new GarfieldHitConverter();
+     hitDigitizer.set("OUTPUT_COLLECTION_NAME", "GarfieldHits");
+     add(hitDigitizer);
+
+     add(new NearestNeighborClusterDriver(2, 2, 1, 2, 0.));
+
+     add(new EmcalMipStubs("GarfieldMipStubs",0));
+     add(new GarfieldTrackFinder(0));
+     
+     MCTrackFinder mcFinder = new MCTrackFinder();
+     mcFinder.addTrackerHitList("GarfieldHits");
+     mcFinder.set("Track_Collection_Name", "CheaterTracks");
+     mcFinder.set("Min_Layers", 3);
+     add(mcFinder);
+     
+     TrackingTest test = new TrackingTest("General Track Finding Test");
+     test.set("RECONSTRUCTED_TRACK_COLLECTION_NAME", "GarfieldTracks");
+     test.set("MC_TRACK_COLLECTION_NAME", "CheaterTracks");
+     test.set("MCTrackCut_Pt", 1.0*Const.GeV);
+     test.set("MCTrackCut_Theta", 45.*Const.degree);
+     test.set("MCTrackCut_nLayers", 3);
+     add(test);
+   }
+
+}
\ No newline at end of file

lcsim/src/org/lcsim/contrib/onoprien/tester
PiFinder.java added at 1.1
diff -N PiFinder.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ PiFinder.java	6 Apr 2007 21:40:28 -0000	1.1
@@ -0,0 +1,27 @@
+package org.lcsim.contrib.onoprien.tester;
+
+import org.lcsim.event.*;
+import org.lcsim.contrib.onoprien.mcTrackFinder.MCTrackFinder;
+import org.lcsim.contrib.onoprien.mcTrackFinder.MCTrack;
+import org.lcsim.recon.cat.util.Const;
+
+/**
+ * Cheater track finder that finds only tracks left by charged pions from K short decays.
+ *
+ * @author onoprien
+ * @version $Id: PiFinder.java,v 1.1 2007/04/06 21:40:28 onoprien Exp $
+ */
+public class PiFinder extends MCTrackFinder {
+  
+  public PiFinder() {}
+
+  /**
+   * Overrides {@link MCTrackFinder.filter(MCParticle)} to select charged pions from KS0 decays.
+   */
+  protected boolean filter(MCParticle mcParticle) {
+    return super.filter(mcParticle) &&
+      (mcParticle.getPDGID() == Const.Particle.PI_MINUS.PDGID || mcParticle.getPDGID() == Const.Particle.PI_PLUS.PDGID ) &&
+      (mcParticle.getParents().get(0).getPDGID() == Const.Particle.K_SHORT.PDGID);
+  }
+  
+}

lcsim/src/org/lcsim/contrib/onoprien/tester
PiTest.java added at 1.1
diff -N PiTest.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ PiTest.java	6 Apr 2007 21:40:28 -0000	1.1
@@ -0,0 +1,50 @@
+package org.lcsim.contrib.onoprien.tester;
+
+
+import java.util.*;
+import org.lcsim.recon.cat.EmcalMipStubs;
+import org.lcsim.recon.cat.GarfieldHitConverter;
+import org.lcsim.recon.cat.GarfieldTrackFinder;
+import org.lcsim.event.*;
+import org.lcsim.recon.cluster.nn.NearestNeighborClusterDriver;
+import org.lcsim.util.Driver;
+import org.lcsim.util.aida.AIDA;
+import org.lcsim.contrib.onoprien.mcTrackFinder.*;
+
+/**
+ * Track finding efficiency test for charged pions from K short decays.
+ *
+ * @author D. Onoprienko
+ * @version $Id: PiTest.java,v 1.1 2007/04/06 21:40:28 onoprien Exp $
+ */
+public class PiTest extends TrackingTest {
+  
+// -- Constructor :  -----------------------------------------------------------
+  
+  public PiTest() {
+
+    super("KS0 Pions Track Finding Test");
+
+    GarfieldHitConverter hitDigitizer = new GarfieldHitConverter();
+    hitDigitizer.set("OUTPUT_COLLECTION_NAME", "GarfieldHits");
+    add(hitDigitizer);
+
+    add(new NearestNeighborClusterDriver(2, 2, 1, 2, 0.));
+
+    add(new EmcalMipStubs("GarfieldMipStubs",0));
+    add(new GarfieldTrackFinder(0));
+
+    MCTrackFinder mcFinder = new PiFinder();
+    mcFinder.addTrackerHitList("GarfieldHits");
+    mcFinder.set("Track_Collection_Name", "PiTracks");
+    mcFinder.set("Min_Layers", 0);
+    add(mcFinder);
+
+    set("RECONSTRUCTED_TRACK_COLLECTION_NAME", "GarfieldTracks");
+    set("MC_TRACK_COLLECTION_NAME", "PiTracks");
+    //set("MCTrackCut_Pt", 1.0*Const.GeV);
+    //set("MCTrackCut_Theta", 45.*Const.degree);
+    set("MCTrackCut_nLayers", 3);
+  }
+
+}

lcsim/src/org/lcsim/contrib/onoprien/tester
RatedTrack.java added at 1.1
diff -N RatedTrack.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ RatedTrack.java	6 Apr 2007 21:40:28 -0000	1.1
@@ -0,0 +1,67 @@
+package org.lcsim.contrib.onoprien.tester;
+
+import hep.physics.matrix.SymmetricMatrix;
+import java.util.*;
+import org.lcsim.event.*;
+import org.lcsim.contrib.onoprien.mcTrackFinder.MCTrack;
+
+/**
+ * The class keeps references to a reconstructed track, associated
+ * Monte Carlo tracks, and allows assignment and retrieval of any number
+ * of integer status flags. 
+ *
+ * @author D. Onoprienko
+ * @version $Id: RatedTrack.java,v 1.1 2007/04/06 21:40:28 onoprien Exp $
+ */
+public class RatedTrack {
+
+// -- Constructors :  ----------------------------------------------------------
+  
+  /** Create RatedTrack by wrapping a reconstructed track. */
+  public RatedTrack(Track track) {
+    _track = track;
+    _mcTrackList = new ArrayList<MCTrack>(1);
+    _status = new HashMap<Integer,Integer>(2);
+  }
+  
+// -- Getters :  ---------------------------------------------------------------
+  
+  /** Returns underlying reconstructed Track object. */
+  public Track getTrack() {return _track;}
+
+  /** Returns a list of associated Monte Carlo tracks. */
+  public List<MCTrack> getMCTracks() {return _mcTrackList;}
+  
+  /**
+   * Returns a status flag that has been previously assigned to this track through
+   * the call to {@link #setStatus(int,int) setStatus(int category, int status)}.
+   */
+  public int getStatus(int category) {return _status.get(category);}
+  
+// -- Setters :  ---------------------------------------------------------------
+
+  /** 
+   * Add an <tt>MCTrack</tt> to the list of Monte Carlo 
+   * tracks associated with this <tt>RatedTrack</tt>. 
+   */
+  public void addMCTrack(MCTrack mcTrack) {_mcTrackList.add(mcTrack);}
+  
+  /**
+   * Set an integer status flag for this track.
+   *
+   *  @param category  Identifier of a status category. A track can be assigned 
+   *                   statuses in any number of categories.
+   *  @param status    Status flag that can be later retrieved through the call to
+   *                   {@link #getStatus(int) getStatus(int category)}
+   */
+  public void setStatus(int category, int status) {_status.put(category, status);}
+  
+  
+// -- Private data :  ----------------------------------------------------------
+  
+  private Track _track;
+  private ArrayList<MCTrack> _mcTrackList;
+  
+  HashMap<Integer,Integer> _status;
+  
+}

lcsim/src/org/lcsim/contrib/onoprien/tester
TrackingTest.java added at 1.1
diff -N TrackingTest.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ TrackingTest.java	6 Apr 2007 21:40:28 -0000	1.1
@@ -0,0 +1,121 @@
+package org.lcsim.contrib.onoprien.tester;
+
+import java.util.*;
+import java.lang.reflect.*;
+import org.lcsim.event.*;
+import org.lcsim.contrib.onoprien.mcTrackFinder.*;
+import org.lcsim.util.Driver;
+import hep.physics.particle.Particle;
+import org.lcsim.util.aida.AIDA;
+import hep.aida.ITree;
+
+/**
+ * A general track finding performance test.
+ * See {@link ExampleDriverTrackingTest} for an example of use.
+ *
+ * @author D. Onoprienko
+ * @version $Id: TrackingTest.java,v 1.1 2007/04/06 21:40:28 onoprien Exp $
+ */
+public class TrackingTest extends TrackingTestBase {
+  
+// -- Indices into the statistics (per event and cumulative) arrays :  ---------
+
+  protected final int _I_nTotal;
+  protected final int _I_eTotal;
+  protected final int _I_ptTotal;
+  protected final int _I_nGood;
+  protected final int _I_eGood;
+  protected final int _I_ptGood;
+  protected final int _I_nGoodFound;
+  protected final int _I_eGoodFound;
+  protected final int _I_ptGoodFound;
+  protected final int _I_nFound;
+  protected final int _I_nFake;
+  
+// -- Constructors :  ----------------------------------------------------------
+  
+  /** Default constructor. */
+  public TrackingTest() {
+    super("TrackingTest");
+    _I_nTotal = createIndex("Number of MCTracks tracks");
+    _I_eTotal = createIndex("Total energy of MCTracks tracks");
+    _I_ptTotal = createIndex("Total Pt of MCTracks tracks");
+    _I_nGood = createIndex("Number of reconstructable MCTracks");
+    _I_eGood = createIndex("Total energy of reconstructable MCTracks", IndexType.NOPRINT);
+    _I_ptGood = createIndex("Total Pt of reconstructable tracks", IndexType.NOPRINT);
+    _I_nGoodFound = createIndex("Number of found reconstructable tracks");
+    _I_eGoodFound = createIndex("Total energy of found reconstructable tracks", IndexType.NOPRINT);
+    _I_ptGoodFound = createIndex("Total Pt of found reconstructable tracks", IndexType.NOPRINT);
+    _I_nFound = createIndex("Number of found tracks", IndexType.NOPRINT);
+    _I_nFake = createIndex("Number of fake tracks", IndexType.PRINTEVENT);
+  }
+  
+  /** Constructor taking the name of the test. */
+  public TrackingTest(String name) {
+    this();
+    set("TEST_NAME", name);
+  }
+  
+// -- Processing event :  ------------------------------------------------------
+  
+  protected void analyzeEvent(EventHeader event) {
+    
+    // Number, energy, and Pt of all tracks, reconstructable tracks, and found reconstructable tracks
+    
+    for (MCTrack mcTrack : _mcTrackList) {
+      _statE[_I_nTotal] += 1.;
+      _statE[_I_eTotal] += mcTrack.getMCParticle().getEnergy();
+      _statE[_I_ptTotal] += mcTrack.getPt();
+      if (mcTrack.getStatus(IN_CLASS) == YES) {
+        _statE[_I_nGood] += 1.;
+        _statE[_I_eGood] += mcTrack.getMCParticle().getEnergy();
+        _statE[_I_ptGood] += mcTrack.getPt();
+        if (isReconstructed(mcTrack)) {
+          _statE[_I_nGoodFound] += 1.;
+          _statE[_I_eGoodFound] += mcTrack.getMCParticle().getEnergy();
+          _statE[_I_ptGoodFound] += mcTrack.getPt();
+        }
+      }
+    }
+    
+    // Number of reconstracted tracks and fakes
+    
+    _statE[_I_nFound] = _ratedTrackList.size();
+    for (RatedTrack rTrack : _ratedTrackList) {
+      if (isFake(rTrack)) _statE[_I_nFake] += 1.;
+    }
+    
+    // Fill histograms for reconstructed tracks in class :
+    
+//    for (RatedTrack rTrack : _ratedTrackList) {
+//      MCTrack mcTrack = rTrack.getMCTrack();
+//      if (mcTrack != null && mcTrack.getStatus(IN_CLASS) == YES) {
+//        double p = mcTrack.getP();
+//        double dp = rTrack.getP()-p;
+//        _aida.cloud1D(_testName + " deltaP over P").fill(dp/p);
+//      }
+//    }
+
+  }
+  
+  protected void endOfRun() {
+    
+    System.out.println("");
+    System.out.println("   End-of-run statictics from " + _testName);
+    System.out.println("");
+    
+    System.out.println("Reconstructable to all tracks ratios:");
+    System.out.println("  Tracks: " + _statC[_I_nGood]/_statC[_I_nTotal]);
+    System.out.println("  Energy: " + _statC[_I_eGood]/_statC[_I_eTotal]);
+    System.out.println("      Pt: " + _statC[_I_ptGood]/_statC[_I_ptTotal]);
+    
+    System.out.println("Track finding efficiency (Found/Reconstructable):");
+    System.out.println("  Tracks: " + _statC[_I_nGoodFound]/_statC[_I_nGood]);
+    System.out.println("  Energy: " + _statC[_I_eGoodFound]/_statC[_I_eGood]);
+    System.out.println("      Pt: " + _statC[_I_ptGoodFound]/_statC[_I_ptGood]);
+    
+    System.out.println("Purity: " + (1.-_statC[_I_nFake]/_statC[_I_nFound]));
+  }
+
+// -- Private fields :  --------------------------------------------------------
+}

lcsim/src/org/lcsim/contrib/onoprien/tester
TrackingTestBase.java added at 1.1
diff -N TrackingTestBase.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ TrackingTestBase.java	6 Apr 2007 21:40:28 -0000	1.1
@@ -0,0 +1,509 @@
+package org.lcsim.contrib.onoprien.tester;
+
+import java.util.*;
+import java.lang.reflect.*;
+import javax.swing.*;
+import java.io.*;
+import org.lcsim.recon.cat.util.NoSuchParameterException;
+import org.lcsim.event.*;
+import org.lcsim.contrib.onoprien.mcTrackFinder.*;
+import org.lcsim.util.Driver;
+import org.lcsim.geometry.Detector;
+import hep.physics.particle.Particle;
+import org.lcsim.util.aida.AIDA;
+import hep.aida.ITree;
+
+
+/**
+ * Abstract driver class that provides infrastructure and utility methods for testing track finders.
+ * See {@link TrackingTest} and {@link TrackingTestKs} for examples of use.
+ * <p>
+ * Classes implementing particular tests should extend this class, overriding<br>
+ * {@link #analyzeEvent(EventHeader)}<br>
+ * and, if necessary,<br>
+ * {@link #eventFilter(EventHeader event)},<br>
+ * {@link #matchTracks(Track track, MCTrack mcTrack)},<br>
+ * {@link #mcTrackFilter(MCTrack mcTrack)},<br>
+ * {@link #isReconstructed(MCTrack mcTrack)},<br>
+ * {@link #isFake(RatedTrack track)},<br>
+ * {@link #startOfRun()}, and {@link #endOfRun()} methods.<br>
+ * Default implementations are provided for these methods.
+ * <p>
+ * Subclusses extending this driver should be added to the event processing chain
+ * after track findind stage. Cheater track finder 
+ * (see {@link org.lcsim.contrib.onoprien.mcTrackFinder}) should also be run before the test 
+ * to create a list of MCTracks and put it into the event. 
+ * <p>
+ * When the driver's {@link #process(EventHeader event)}
+ * method is called by the framework, it fetches a list of Monte Carlo tracks,
+ * marks those for which {@link #mcTrackFilter(MCTrack mcTrack)} returns 
+ * <tt>true</tt> as being in the requested class (subsequent calls to
+ * {@link MCTrack#getStatus(int) mcTrack.getStatus(TrackingTestBase.IN_CLASS)}
+ * will return <tt>TrackingTestBase.YES</tt>),
+ * associates MCTracks and reconstructed tracks for which 
+ * {@link #matchTracks(Track track, MCTrack mcTrack)} returns <tt>true</tt> with 
+ * each other, and then calls
+ * {@link #analyzeEvent(EventHeader)} method that should be implemented by subclasses.
+ * <p>
+ * Only events for which {@link #eventFilter(EventHeader)} returns <tt>true</tt> are processed.
+ * <p>
+ * There is an optional facility for accumulating and printing statistics.
+ * Any number of variables for accumulating statistics can be created with
+ * {@link #createIndex(String name, IndexType... args)}. Data put into
+ * these variables is automatically accumulated at the end of event processing,
+ * and can be printed out with {@link #printStatistics(EventHeader event)} and 
+ * {@link #printCumulativeStatistics()}.
+ *
+ * @author D. Onoprienko
+ * @version $Id: TrackingTestBase.java,v 1.1 2007/04/06 21:40:28 onoprien Exp $
+ */
+abstract public class TrackingTestBase extends Driver {
+  
+// -- Constants defining status categories and values:  ------------------------
+  
+  public static final int IN_CLASS = -1;
+  public static final int FAKE = -2;
+  
+  public static final int YES = -1;
+  public static final int NO = -2;
+  
+// -- Constructors :  ----------------------------------------------------------
+
+  /** Default constructor. */
+  public TrackingTestBase() {
+    _testName = "";
+    _aida = AIDA.defaultInstance();
+    _tree = _aida.tree();
+    _mcTrackListName = "";
+    _recoTrackListName = "";
+    _runInProgress = false;
+  }
+  
+  /** Constructor taking the name of the test. */
+  public TrackingTestBase(String name) {
+    this();
+    set("TEST_NAME", name);
+  }
+  
+  // -- Setters :  -------------------------------------------------------------
+  
+  /** 
+   * Set any <tt>String</tt> parameter.
+   * The following parameters can be set with this method:<br>
+   * <tt>"TEST_NAME"</tt> - name of the test. The name will printed with the test output statistics.<br>
+   * <tt>"RECONSTRUCTED_TRACK_COLLECTION_NAME"</tt> - name of reconstructed track 
+   *                         collection produced by the track finder being tested.<br>
+   * <tt>"MC_TRACK_COLLECTION_NAME"</tt> - name of MCTrack collection created by previously run MCTrackFinder.
+   * @param name   Name of parameter to be set. Case ignored.
+   * @param value  Value to be assigned to the parameter.
+   * @throws NoSuchParameterException Thrown if the supplied parameter name is unknown.
+   *         Subclasses may catch this exception after a call to <tt>super.set()</tt>
+   *         and set their own parameters.
+   */
+  public void set(String name, String value) {
+    if (name.equalsIgnoreCase("TEST_NAME")) {
+        _testName = value;
+    } else if (name.equalsIgnoreCase("RECONSTRUCTED_TRACK_COLLECTION_NAME")) {
+        _recoTrackListName = value;
+    } else if (name.equalsIgnoreCase("MC_TRACK_COLLECTION_NAME")) {
+        _mcTrackListName = value;
+    } else {
+      throw new NoSuchParameterException(name, this.getClass());
+    }
+  }
+
+  /** 
+   * Set any double parameter. 
+   * The following parameters can be set with this method:<br>
+   * <tt>"MCTrackCut_Pt"</tt> - cut on Pt for MCTrack to be considered reconstructable. Default: 0.<br>
+   * <tt>"MCTrackCut_Theta"</tt> - cut on Theta for MCTrack to be considered reconstructable. Default: 0.<br>
+   * <tt>"MCTrackCut_Rapidity"</tt> - cut on pseudo-rapidity for MCTrack to be considered reconstructable. Default: infinity.<br>
+   * @param name   Name of parameter to be set. Case ignored.
+   * @param value  Value to be assigned to the parameter.
+   * @throws NoSuchParameterException Thrown if the supplied parameter name is unknown.
+   *         Subclasses may catch this exception after a call to <tt>super.set()</tt>
+   *         and set their own parameters.
+   */
+  public void set(String name, double value) {
+    if (name.equalsIgnoreCase("MCTrackCut_Pt")) {
+      _mcTrackCut_Pt = value;
+    } else if (name.equalsIgnoreCase("MCTrackCut_Theta")) {
+      _mcTrackCut_theta = value;
+    } else if (name.equalsIgnoreCase("MCTrackCut_Rapidity")) {
+      if (value < 10000.) {
+        _mcTrackCut_theta = 2.*Math.atan(Math.exp(-value));
+      } else {
+        _mcTrackCut_theta = 0.;
+      }
+    } else {
+      throw new NoSuchParameterException(name, this.getClass());
+    }    
+  }
+
+  /** 
+   * Set any integer parameter. 
+   * The following parameters can be set with this method:<br>
+   * <tt>"MCTrackCut_nLayers"</tt> - cut on minimum number of layers with hits 
+   *                   for MCTrack to be considered reconstructable. Default: 3.<br>
+   * @param name   Name of parameter to be set. Case ignored.
+   * @param value  Value to be assigned to the parameter.
+   * @throws NoSuchParameterException Thrown if the supplied parameter name is unknown.
+   *         Subclasses may catch this exception after a call to <tt>super.set()</tt>
+   *         and set their own parameters.
+   */
+  public void set(String name, int value) {
+    if (name.equalsIgnoreCase("MCTrackCut_nLayers")) {
+      _mcTrackCut_nLayers = value;
+    } else {
+      throw new NoSuchParameterException(name, this.getClass());
+    }
+  }
+  
+// -- Processing event :  ------------------------------------------------------
+  
+  /** Process event. */
+  public final void process(EventHeader event) {
+    
+    super.process(event);
+    
+    // Skip undesired events
+    
+    if (!eventFilter(event)) return;
+    
+    // Reset "per event" statistics
+    
+    if (_statE != null) Arrays.fill(_statE, 0.);
+    
+    // Fetch list of Monte Carlo Tracks, and set IN_CLASS flag for them
+    
+    _mcTrackList = event.get(MCTrack.class, _mcTrackListName);
+    for (MCTrack mcTrack : _mcTrackList) {
+      mcTrack.setStatus(IN_CLASS, mcTrackFilter(mcTrack) ? YES : NO);
+    }
+    
+    // Associate reconstructed tracks with Monte Carlo Tracks
+    
+    _recoTrackList = event.get(Track.class, _recoTrackListName);
+    _ratedTrackList = new ArrayList<RatedTrack>(_recoTrackList.size());
+    
+    for (Track track : _recoTrackList) {
+      RatedTrack ratedTrack = new RatedTrack(track);
+      for (MCTrack mcTrack : _mcTrackList) {
+        if (matchTracks(track, mcTrack)) {
+          ratedTrack.addMCTrack(mcTrack);
+          mcTrack.addTrack(track);
+        }
+      }
+      _ratedTrackList.add(ratedTrack);
+    }
+    
+    // User's analysis
+    
+    analyzeEvent(event);
+    
+    // Update cumulative statistics
+    
+    if (_statE != null) {
+      for (int i=0; i<_statE.length; i++) _statC[i] += _statE[i];
+    }
+    _nEvents++;
+    
+    // Clean up
+    
+    for (MCTrack mcTrack : _mcTrackList) {
+      mcTrack.removeTracks();
+    }
+    _mcTrackList = null;
+    _recoTrackList = null;
+    _ratedTrackList = null;
+    
+  }
+  
+// -- Selector methods to be overridden by subclasses :  -----------------------
+  
+  /**
+   * Returns <code>true</code> for events that should be included in analysis.
+   * Default implementation always returns <code>true</code>.
+   * Subclasses should overwrite this method if some events need to be excluded.
+   */
+  protected boolean eventFilter(EventHeader event) {
+    return true;
+  }
+  
+  /** 
+   * Returns <tt>true</tt> if Monte Carlo track belongs to the class that needs to be included in analysis.
+   * Cuts on Pt, Theta, and a minimum number of layers with hits.
+   * Cut values can be set at run time, leaving them at default values means no cuts.
+   * Subclasses can overwrite this method to provide their own definition of "reconstructable" track.
+   */
+  protected boolean mcTrackFilter(MCTrack mcTrack) {
+    if (mcTrack.getPt() < _mcTrackCut_Pt) return false;
+    if (mcTrack.getTheta() < _mcTrackCut_theta) return false;
+    if (mcTrack.getNLayers() < _mcTrackCut_nLayers) return false;
+    return true;
+  }
+  
+  /** 
+   * Returns <code>true</code> if reconstructed track and Monte Carlo track are deemed to describe the same track.
+   * Default implementation: tracks match if they have common hits in at least 3 layers.
+   * (Alternative definition to be considered later: Tracks match if MCParticle 
+   * contributed to hits of reconstructed track in at least 3 layers).
+   * <i>Current implementation simply asks for 3 common hits since
+   * {@link TrackerHit} is completely "detector blind" at the moment.</i>
+   * Subclasses can overwrite this method to provide their own definition of matching tracks.
+   */
+  protected boolean matchTracks(Track track, MCTrack mcTrack) {
+    List<TrackerHit> recoTrackHitList = track.getTrackerHits();
+    List<TrackerHit> mcTrackHitList = mcTrack.getTrackerHits();
+    int nMatch = 0;
+    for (TrackerHit hit : mcTrackHitList) {
+      if (recoTrackHitList.contains(hit)) nMatch++;
+    }
+    return nMatch > 2;
+  }
+  
+  /** 
+   * Definition of a successfully reconstracted MCTrack.
+   * This method is not called internally by any <tt>TrackingTestBase</tt> methods,
+   * but can be used (or overridded) by subclasses.
+   * What I would like to do here is to require that there is a reconstructed 
+   * track that picked up hits from this MCTrack in at least 3 layers.
+   * Given current limitations of {@link TrackerHit} that has no layer information,
+   * the default implementation returns <tt>true</tt> if any reconstructed 
+   * tracks have been associated with MCTrack supplied as a parameter. If default
+   * implementation of {@link matchTracks(Track, MCTrack)} is used, that means
+   * requiring a reconstructed track with 3 hits common with the given MCTrack.
+   */
+  protected boolean isReconstructed(MCTrack mcTrack) {
+    return mcTrack.getTracks().size() > 0;
+  }
+
+  /**
+   * Definition of fake track.
+   * This method is not called internally by any <tt>TrackingTestBase</tt> methods,
+   * but can be used (or overridded) by subclasses.
+   * In the future, I'd like to consider the track fake if there is no MCParticle
+   * that contributed to its hits in all but one layer.
+   * Given current limitations of {@link TrackerHit}, the actual definition is:
+   * the track is fake if there is no MCTrack that contains all but one of its hits.
+   */
+  protected boolean isFake(RatedTrack track) {
+    List<MCTrack> mcTrackList = track.getMCTracks();
+    if (mcTrackList.size() == 0) return true;
+    List<TrackerHit> recoHitList = track.getTrack().getTrackerHits();
+    for (MCTrack mcTrack : mcTrackList) {
+      int nSomeoneElsesHits = 0;
+      for (TrackerHit hit : recoHitList) {
+        if (!mcTrack.getTrackerHits().contains(hit)) nSomeoneElsesHits++;
+      }
+      if (nSomeoneElsesHits <= 1) return false;
+    }
+    return true;
+  }
+  
+// -- User analysis hooks :  ---------------------------------------------------
+  
+  /** User analysis before the run. */
+  protected void startOfRun(Detector detector) {}
+  
+  /** User analysis of the event. */
+  abstract protected void analyzeEvent(EventHeader event);
+  
+  /** User analysis before the run. */
+  protected void endOfRun() {}
+  
+  /**
+   * Called when all data processing is finished.
+   * Unless overwritten in the subclass, prints out standard cumulative statistics.
+   */
+  final protected void endOfData() {
+    super.endOfData();
+    endOfRun();
+    _runInProgress = false;
+  }
+  
+  /**
+   * Called before the first event is processed, or after a rewind.
+   * Unless overwritten in the subclass, resets standard cumulative statistics.
+   */
+  final public void detectorChanged(Detector detector) {
+    super.detectorChanged(detector);
+    if (_runInProgress) {
+      endOfRun();
+    }
+    _nEvents = 0;
+    if (_statE != null) Arrays.fill(_statC, 0.);
+    _runInProgress = true;
+    startOfRun(detector);
+  }
+  
+// -- Protected helper methods:  -----------------------------------------------
+  
+  /** 
+   * Print to the log.
+   * The output destination can be set with <tt>set("LOG_FILE", destination)</tt>.
+   * See {@link #set(String, String)} for details.
+   */
+  protected void printLog(String title, String... messages) {
+    if (title != null) {
+      System.out.println("");
+      System.out.println(title);
+      System.out.println("");
+    }
+    for (String line : messages) {
+      System.out.println(line);
+    }
+  }
+  
+  protected enum IndexType {NOSUM, SUM, SUM2, NOPRINT, PRINTEVENT, PRINTSUM, PRINTALL}
+  
+  /**
+   * Creates a new statistical variable and returns an index that can be used to access it.
+   * "Per event" data can be put into <code>_statE[index]</code>, these variables are reset
+   * to zero at the beginning of each event. At the end of the event, statistics is
+   * automatically accumulated into <code>_statC[index]</code>. The name of the variable
+   * is accessable through <code>_statName[index]</code>.
+   *
+   * @param name  Name to be assigned to the variable.
+   * @param args  Optional flags defining accumulation type and printing preferences. Possible flags and their meanings:<p>
+   *    &nbsp; NOSUM - do not accumulate this variable.<br>
+   *    &nbsp; SUM - <code>_statE[index]</code> added to <code>_statC[index]</code> at the end of event.<br>
+   *    &nbsp; SUM2 - square of <code>_statE[index]</code> added to <code>_statC[index]</code> at the end of event.<br>
+   *    &nbsp; If no flag is given, SUM is assumed.<p>
+   *    &nbsp; NOPRINT - variable is not printed by {@link #printStatistics(EventHeader event)} and {@link #printCumulativeStatistics()}<br>
+   *    &nbsp; PRINTEVENT - variable is printed by {@link #printStatistics(EventHeader event)} but not by {@link #printCumulativeStatistics()}<br>
+   *    &nbsp; PRINTSUM - variable is printed by {@link #printCumulativeStatistics()} but not by {@link #printStatistics(EventHeader event)}<br>
+   *    &nbsp; PRINTALL - variable is printed by both {@link #printCumulativeStatistics()} and {@link #printStatistics(EventHeader event)}<br>
+   *    &nbsp; &nbsp; If no flag is given, PRINTALL is assumed.<p>
+   * @return  Index into {@link #_statE}, {@link #_statC}, and {@link #_statName} arrays that can be used to access the variable being created.
+   */
+  protected int createIndex(String name, IndexType... args) {
+    
+    int size;
+    if (_statE == null) {
+      _statE = new double[] {0.};
+      _statC = new double[] {0.};
+      _statName = new String[] {name};
+      _statAccumulateFlag = new IndexType[] {IndexType.SUM};
+      _statPrintFlag = new IndexType[] {IndexType.PRINTALL};
+      size = 0;
+    } else {
+      
+      size = _statE.length;
+      
+      double[] tempD = new double[size+1];
+      System.arraycopy(_statE, 0, tempD, 0, size);
+      _statE = tempD;
+      _statE[size] = 0.;
+      
+      tempD = new double[size+1];
+      System.arraycopy(_statC, 0, tempD, 0, size);
+      _statC = tempD;
+      _statC[size] = 0.;
+      
+      String[] tempS = new String[size+1];
+      System.arraycopy(_statName, 0, tempS, 0, size);
+      _statName = tempS;
+      _statName[size] = name;
+      
+      IndexType[] tempI = new IndexType[size+1];
+      System.arraycopy(_statAccumulateFlag, 0, tempI, 0, size);
+      _statAccumulateFlag = tempI;
+      _statAccumulateFlag[size] = IndexType.SUM;
+      
+      tempI = new IndexType[size+1];
+      System.arraycopy(_statPrintFlag, 0, tempI, 0, size);
+      _statPrintFlag = tempI;
+      _statPrintFlag[size] = IndexType.PRINTSUM;
+    }
+    
+    if (args != null) {
+      for (IndexType type : args) {
+        switch (type) {
+          case NOSUM:
+          case SUM:
+          case SUM2:
+            _statAccumulateFlag[size] = type; break;
+          case NOPRINT:
+          case PRINTEVENT:
+          case PRINTSUM:
+          case PRINTALL:
+            _statPrintFlag[size] = type; break;
+        }
+      }
+    }
+    
+    return size;
+  }
+  
+  /** Print standard statistics at the end of an event to standard output. */
+  protected void printStatistics(EventHeader event) {
+    System.out.println("");
+    System.out.println("Tracking test: " + _testName + ", statistics for event " + event.getEventNumber());
+    for (int i=0; i<_statE.length; i++) {
+      if (_statPrintFlag[i] == IndexType.PRINTEVENT || _statPrintFlag[i] == IndexType.PRINTALL) {
+        System.out.println("  " + _statName[i] + "  " + _statE[i]);
+      }
+    }
+    System.out.println("");
+  }
+  
+  /** 
+   * Print cumulative statistics to the log. 
+   * The output destination can be set with <tt>set("LOG_FILE", destination)</tt>.
+   * See {@link #set(String, String)} for details.
+   * @param messages  Array of strings that should be printed before standard statistics.
+   * @param printStandard   Flag to indicate whether to print standard statistics.
+   */
+  protected void printCumulativeStatistics(boolean printStandard, String... messages) {
+    ArrayList<String> msgList = new ArrayList<String>(20);
+    msgList.add("");
+    if (messages != null) for (String msg : messages) msgList.add("  " + msg);
+    if (printStandard && _nEvents > 0) {
+      for (int i=0; i<_statE.length; i++) {
+        if (_statPrintFlag[i] == IndexType.PRINTSUM || _statPrintFlag[i] == IndexType.PRINTALL) {
+          msgList.add("  Average  " + _statName[i] + "  " + _statC[i]/_nEvents);
+        }
+      }
+    }
+    msgList.add("");
+    String[] msgArray = new String[msgList.size()];
+    msgArray = msgList.toArray(msgArray);
+    printLog("Tracking test: " + _testName + " cumulative statistics  (" + _nEvents + " events)", msgArray);
+  }
+  
+  /** 
+   * Print standard cumulative statistics. 
+   * The output destination can be set with <tt>set("LOG_FILE", destination)</tt>.
+   * See {@link #set(String, String)} for details.
+   */
+  protected void printCumulativeStatistics() {printCumulativeStatistics(true);}
+  
+// -- Private fields :  --------------------------------------------------------
+  
+  protected String _testName = "";
+  
+  protected List<MCTrack> _mcTrackList;
+  protected List<Track> _recoTrackList;
+  protected ArrayList<RatedTrack> _ratedTrackList;
+  
+  protected String _mcTrackListName;
+  protected String _recoTrackListName;
+
+  protected double _mcTrackCut_Pt = 0.;
+  protected double _mcTrackCut_theta = 0.;
+  protected int _mcTrackCut_nLayers = 0;
+
+  protected int _nEvents;         // Number of events analyzed
+  protected double[] _statE;      // Array to hold per event statistics
+  protected double[] _statC;      // Array to hold cumulative statistics
+  protected String[] _statName;   // Array to hold names of statical variables
+  protected IndexType[]  _statAccumulateFlag;  // Array to hold flags indicating accumulation methods
+  protected IndexType[]  _statPrintFlag       ;  // Array to hold flags indicating accumulation printing options
+  
+  protected AIDA _aida;
+  protected ITree _tree;
+  
+  private boolean _runInProgress;
+  
+}

lcsim/src/org/lcsim/contrib/onoprien/tester
package.html added at 1.1
diff -N package.html
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ package.html	6 Apr 2007 21:40:28 -0000	1.1
@@ -0,0 +1,8 @@
+<html>
+<body>
+Performance testing package for track finders.
+<p>
+The package contains a base class ({@link TrackingTestBase}) for writing track finder
+performance tests, and a number of concrete tests.
+</body>
+</html>
CVSspam 0.2.8