Print

Print


Author: [log in to unmask]
Date: Thu Jan 15 10:28:42 2015
New Revision: 1936

Log:
Additional work and improvements on new cluster package.

Added:
    java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/CTPClusterDriver.java
    java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/SimpleReconClusterDriver.java
Removed:
    java/trunk/ecal-recon/src/test/java/org/hps/recon/ECalClusterICTest.java
Modified:
    java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/ClusterDriver.java
    java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/ClusterUtilities.java
    java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/DefaultClusterPropertyCalculator.java
    java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/GTPClusterDriver.java
    java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/ReconClusterDriver.java
    java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/ReconClusterer.java
    java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/SimpleReconClusterer.java
    java/trunk/ecal-recon/src/test/java/org/hps/recon/ecal/cluster/ClustererTest.java

Added: java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/CTPClusterDriver.java
 =============================================================================
--- java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/CTPClusterDriver.java	(added)
+++ java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/CTPClusterDriver.java	Thu Jan 15 10:28:42 2015
@@ -0,0 +1,21 @@
+package org.hps.recon.ecal.cluster;
+
+/**
+ * This is a Driver to wrap the GTPClusterer algorithm,
+ * allowing the <code>limitClusterRange</code> to be
+ * set publicly.
+ * 
+ * @see GTPClusterer
+ * 
+ * @author Jeremy McCormick <[log in to unmask]>
+ */
+public class CTPClusterDriver extends ClusterDriver {
+    
+    public CTPClusterDriver() {
+        clusterer = ClustererFactory.create("CTPClusterer");
+    }
+    
+    public void setClusterWindow(int clusterWindow) {
+        getClusterer().getCuts().setValue("clusterWindow", clusterWindow);
+    }
+}

Modified: java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/ClusterDriver.java
 =============================================================================
--- java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/ClusterDriver.java	(original)
+++ java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/ClusterDriver.java	Thu Jan 15 10:28:42 2015
@@ -32,21 +32,23 @@
 public class ClusterDriver extends Driver {
     
     protected static Logger logger = LogUtil.create(ClusterDriver.class, new BasicFormatter(ClusterDriver.class.getSimpleName()));
+    
     protected String ecalName = "Ecal";    
     protected HPSEcal3 ecal;
     protected NeighborMap neighborMap;
     protected String outputClusterCollectionName = "EcalClusters";
     protected String inputHitCollectionName = "EcalCalHits";
     protected Clusterer clusterer;
+    protected double[] cuts;
+    
     protected boolean createEmptyClusterCollection = true;
     protected boolean raiseErrorNoHitCollection = false;
     protected boolean skipNoClusterEvents = false;
     protected boolean writeClusterCollection = true;
     protected boolean storeHits = true;
-    protected double[] cuts;
-    protected boolean sortHits;
-    protected boolean calculateProperties;
-    protected boolean applyCorrections;
+    protected boolean calculateProperties = false;
+    protected boolean applyCorrections = false;
+    protected boolean sortHits = false;
     
     /**
      * No argument constructor.
@@ -216,7 +218,7 @@
     /**
      * This method implements the default clustering procedure based on input parameters.
      */
-    public void process(EventHeader event) {
+    public void process(EventHeader event) {               
         if (event.hasCollection(CalorimeterHit.class, inputHitCollectionName)) {       
             List<CalorimeterHit> hits = event.get(CalorimeterHit.class, inputHitCollectionName);
             logger.fine("input hit collection " + inputHitCollectionName + " has " + hits.size() + " hits");

Modified: java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/ClusterUtilities.java
 =============================================================================
--- java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/ClusterUtilities.java	(original)
+++ java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/ClusterUtilities.java	Thu Jan 15 10:28:42 2015
@@ -3,6 +3,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -14,6 +15,7 @@
 import org.lcsim.event.MCParticle;
 import org.lcsim.event.SimCalorimeterHit;
 import org.lcsim.event.base.BaseCluster;
+import org.lcsim.event.base.ClusterPropertyCalculator;
 import org.lcsim.geometry.subdetector.HPSEcal3;
 
 /**
@@ -330,10 +332,7 @@
     
     /**
      * Apply HPS-specific energy and position corrections to a list of clusters in place.
-     * 
-     * @see DefaultClusterPropertyCalculator
-     * @see ClusterPositionCorrection
-     * @see ClusterEnergyCorrection
+     * @param clusters The list of clusters.
      */
     public static void applyCorrections(List<Cluster> clusters) {
                 
@@ -355,10 +354,7 @@
     
     /**
      * Apply HPS-specific energy and position corrections to a cluster.
-     * 
-     * @see DefaultClusterPropertyCalculator
-     * @see ClusterPositionCorrection
-     * @see ClusterEnergyCorrection
+     * @param cluster The input cluster.
      */
     public static void applyCorrections(Cluster cluster) {
                             
@@ -376,11 +372,20 @@
     
     /**
      * Call {@link org.lcsim.event.base.BaseCluster#calculateProperties()}
-     * on all clusters in the list.
+     * on all clusters in the list using the default calculator.
      * @param clusters The list of clusters.
      */
     public static void calculateProperties(List<Cluster> clusters) {
-        DefaultClusterPropertyCalculator calc = new DefaultClusterPropertyCalculator();
+        calculateProperties(clusters, new DefaultClusterPropertyCalculator());
+    }    
+    
+    /**
+     * Call {@link org.lcsim.event.base.BaseCluster#calculateProperties()}
+     * on all clusters in the list using the given calculator.
+     * @param clusters The list of clusters.
+     * @param calc The property calculator.
+     */
+    public static void calculateProperties(List<Cluster> clusters, ClusterPropertyCalculator calc) {
         for (Cluster cluster : clusters) {
             if (cluster instanceof BaseCluster) {
                 BaseCluster baseCluster = (BaseCluster)cluster;
@@ -389,4 +394,39 @@
             }
         }
     }    
+    
+    /**
+     * Create a map between a particle and its list of hits from a Cluster.
+     * @param cluster The input cluster.
+     * @return A map between a particle and its hits.
+     */
+    public static Map<MCParticle, List<SimCalorimeterHit>> createParticleHitMap(Cluster cluster) {
+        Map<MCParticle, List<SimCalorimeterHit>> particleHitMap = new HashMap<MCParticle, List<SimCalorimeterHit>>();
+        for (CalorimeterHit hit : cluster.getCalorimeterHits()) {
+            if (hit instanceof SimCalorimeterHit) {
+                SimCalorimeterHit simHit = (SimCalorimeterHit) hit;
+                for (int i = 0; i < simHit.getMCParticleCount(); i++) {
+                    MCParticle particle = simHit.getMCParticle(i);
+                    if (particleHitMap.get(particle) == null) {
+                        particleHitMap.put(particle, new ArrayList<SimCalorimeterHit>());
+                    }
+                    particleHitMap.get(particle).add(simHit);
+                }
+            }
+        }
+        return particleHitMap;
+    }
+    
+    /**
+     * Get the set of hits from a list of clusters.
+     * @param The input cluster list.
+     * @return The list of hits from all the clusters.
+     */
+    public static Set<CalorimeterHit> getHits(List<Cluster> clusters) {
+        Set<CalorimeterHit> hits = new HashSet<CalorimeterHit>();
+        for (Cluster cluster : clusters) {
+            hits.addAll(cluster.getCalorimeterHits());
+        }
+        return hits;
+    }
 }

Modified: java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/DefaultClusterPropertyCalculator.java
 =============================================================================
--- java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/DefaultClusterPropertyCalculator.java	(original)
+++ java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/DefaultClusterPropertyCalculator.java	Thu Jan 15 10:28:42 2015
@@ -47,6 +47,8 @@
      * {@link org.lcsim.event.base.TensorClusterPropertyCalculator#calculateProperties(List)}.
      */    
     public void calculateProperties(Cluster cluster) {
+        
+        reset();
                 
         calculateMaxDistance(cluster.getEnergy(), cluster.getParticleId() == 11 || cluster.getParticleId() == 0);
         

Modified: java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/GTPClusterDriver.java
 =============================================================================
--- java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/GTPClusterDriver.java	(original)
+++ java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/GTPClusterDriver.java	Thu Jan 15 10:28:42 2015
@@ -28,4 +28,8 @@
         GTPClusterer gtpClusterer = getClusterer();
         gtpClusterer.setLimitClusterRange(limitClusterRange);
     }        
+    
+    public void setClusterWindow(int clusterWindow) {
+        getClusterer().getCuts().setValue("clusterWindow", clusterWindow);
+    }
 }

Modified: java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/ReconClusterDriver.java
 =============================================================================
--- java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/ReconClusterDriver.java	(original)
+++ java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/ReconClusterDriver.java	Thu Jan 15 10:28:42 2015
@@ -42,27 +42,28 @@
     }
     
     public void setHitEnergyThreshold(double hitEnergyThreshold) {
-        getReconClusterer().getCuts().setValue("hitEnergyThreshold", hitEnergyThreshold);
+        getClusterer().getCuts().setValue("hitEnergyThreshold", hitEnergyThreshold);
     }
     
     public void setSeedEnergyThreshold(double seedEnergyThreshold) {
-        getReconClusterer().getCuts().setValue("seedEnergyThreshold", seedEnergyThreshold);
+        getClusterer().getCuts().setValue("seedEnergyThreshold", seedEnergyThreshold);
     }
     
     public void setClusterEnergyThreshold(double clusterEnergyThreshold) {
-        getReconClusterer().getCuts().setValue("clusterEnergyThreshold", clusterEnergyThreshold);
+        getClusterer().getCuts().setValue("clusterEnergyThreshold", clusterEnergyThreshold);
     }
     
     public void setMinTime(double minTime) {
-        getReconClusterer().getCuts().setValue("minTime", minTime);
+        getClusterer().getCuts().setValue("minTime", minTime);
     }
     
     public void setTimeWindow(double timeWindow) {
-        getReconClusterer().getCuts().setValue("timeWindow", timeWindow);
+        getClusterer().getCuts().setValue("timeWindow", timeWindow);
     }
     
     public void setUseTimeCut(boolean useTimeCut) {
-        getReconClusterer().setUseTimeCut(useTimeCut);
+        ReconClusterer clusterer = getClusterer();
+        clusterer.setUseTimeCut(useTimeCut);
     }    
         
     /**

Modified: java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/ReconClusterer.java
 =============================================================================
--- java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/ReconClusterer.java	(original)
+++ java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/ReconClusterer.java	Thu Jan 15 10:28:42 2015
@@ -6,16 +6,11 @@
 
 import java.awt.Point;
 import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
-import org.hps.recon.ecal.cluster.AbstractClusterer;
-import org.hps.recon.ecal.cluster.ClusterType;
-import org.hps.recon.ecal.cluster.Clusterer;
 import org.lcsim.detector.IGeometryInfo;
 import org.lcsim.detector.solids.Trd;
 import org.lcsim.event.CalorimeterHit;
@@ -27,23 +22,25 @@
  * <p>
  * This class creates clusters from a CalorimeterHit input collection.
  * <p>
- * This clustering logic is based on that from the 
- * <a href="https://misportal.jlab.org/ul/Physics/Hall-B/clas/viewFile.cfm/2005-001.pdf?documentId=6">CLAS-Note-2005-001</a>.
+ * This clustering logic is based on that from the <a
+ * href="https://misportal.jlab.org/ul/Physics/Hall-B/clas/viewFile.cfm/2005-001.pdf?documentId=6"
+ * >CLAS-Note-2005-001</a>.
  * <p>
- * The analysis and position corrections are described in 
- * <a href="https://misportal.jlab.org/mis/physics/hps_notes/index.cfm?note_year=2014">HPS Note 2014-001</a>.
+ * The analysis and position corrections are described in <a
+ * href="https://misportal.jlab.org/mis/physics/hps_notes/index.cfm?note_year=2014">HPS Note
+ * 2014-001</a>.
  * <p>
- * The algorithm sorts hits from highest to lowest energy and builds clusters around each local maximum/seed hit. 
- * Common hits are distributed between clusters when minimum between two clusters. There is a threshold cut for 
- * minimum hit energy, minimum cluster energy, and minimum seed hit energy. There is also a timing threshold with 
- * respect to the seed hit. All of these parameters are tunable and should be refined with more analysis. Energy 
- * corrections are applied separately.
+ * The algorithm sorts hits from highest to lowest energy and builds clusters around each local
+ * maximum/seed hit. Common hits are distributed between clusters when minimum between two clusters.
+ * There is a threshold cut for minimum hit energy, minimum cluster energy, and minimum seed hit
+ * energy. There is also a timing threshold with respect to the seed hit. All of these parameters
+ * are tunable and should be refined with more analysis. Energy corrections are applied separately.
  * 
  * @see AbstractClusterer
  * @see Clusterer
  * 
  * @author Holly Szumila-Vance <[log in to unmask]>
- * @author Kyle McCarty <[log in to unmask]> 
+ * @author Kyle McCarty <[log in to unmask]>
  * @author Jeremy McCormick <[log in to unmask]>
  */
 public class ReconClusterer extends AbstractClusterer {
@@ -78,7 +75,7 @@
         super(new String[] { "hitEnergyThreshold", "seedEnergyThreshold", "clusterEnergyThreshold", "minTime", "timeWindow" }, 
                 new double[] { 0.0075, 0.1, 0.3, 0.0, 20.0 });
     }
-    
+
     void setUseTimeCut(boolean useTimeCut) {
         this.useTimeCut = useTimeCut;
     }
@@ -91,9 +88,9 @@
         }
         clusterEnergyThreshold = getCuts().getValue("clusterEnergyThreshold");
         minTime = getCuts().getValue("minTime");
-        timeWindow = getCuts().getValue("timeWindow");      
-    }
-    
+        timeWindow = getCuts().getValue("timeWindow");
+    }
+
     /**
      * Get the list of rejected hits that was made from processing the last event.
      * @return The list of rejected hit.
@@ -101,12 +98,15 @@
     List<CalorimeterHit> getRejectedHitList() {
         return this.rejectedHitList;
     }
-    
-   
-    public List<Cluster> createClusters(EventHeader event, List<CalorimeterHit> hits) {	
+
+    public List<Cluster> createClusters(EventHeader event, List<CalorimeterHit> hits) {
+
+        // I am pretty sure this map must be cleared between events. --JM
+        correctedPositionMap.clear();
+
         // Create a list to store the event hits in.
         List<CalorimeterHit> hitList = hits;
-        
+
         // Create a list to store the newly created clusters in.
         ArrayList<Cluster> clusterList = new ArrayList<Cluster>();
 
@@ -116,7 +116,7 @@
 
         // Sort the list of hits by energy.
         ClusterUtilities.sortHitsUniqueEnergy(hitList);
-        
+
         // Filter the hit list of any hits that fail to pass the
         // designated threshold.
         for (int index = hitList.size() - 1; index >= 0; index--) {
@@ -137,13 +137,10 @@
 
         // Create a map to connect the cell ID of a calorimeter crystal to the hit which occurred in
         // that crystal.
-        HashMap<Long, CalorimeterHit> hitMap = new HashMap<Long, CalorimeterHit>();
-        for (CalorimeterHit hit : hitList) {
-            hitMap.put(hit.getCellID(), hit);
-        }
-        
+        Map<Long, CalorimeterHit> hitMap = ClusterUtilities.createHitMap(hitList);
+
         // Create a map to connect a seed hit to its cluster.
-        Map<CalorimeterHit, BaseCluster> seedToCluster = new HashMap<CalorimeterHit,BaseCluster>();
+        Map<CalorimeterHit, BaseCluster> seedToCluster = new HashMap<CalorimeterHit, BaseCluster>();
 
         // Map a crystal to a list of all clusters in which it is a member.
         Map<CalorimeterHit, List<CalorimeterHit>> commonHits = new HashMap<CalorimeterHit, List<CalorimeterHit>>();
@@ -152,10 +149,10 @@
         HashMap<CalorimeterHit, CalorimeterHit> hitToSeed = new HashMap<CalorimeterHit, CalorimeterHit>();
 
         // Loop through all calorimeter hits to locate seeds and perform
-        // first pass calculations for component and common hits. 
+        // first pass calculations for component and common hits.
         for (int ii = 0; ii <= hitList.size() - 1; ii++) {
             CalorimeterHit hit = hitList.get(ii);
-           
+
             // Get the set of all neighboring crystals to the current hit.
             Set<Long> neighbors = neighborMap.get(hit.getCellID());
 
@@ -187,34 +184,33 @@
                     break seedHitLoop;
                 }
             }
-     
+
             // Hit is a local maximum
-            if (isSeed){
-            	// Seed must pass minimum threshold
-            	if (hit.getCorrectedEnergy() >= seedEnergyThreshold)
-            	{
-            		// Create new cluster
+            if (isSeed) {
+                // Seed must pass minimum threshold
+                if (hit.getCorrectedEnergy() >= seedEnergyThreshold) {
+                    // Create new cluster
                     BaseCluster cluster = createBasicCluster();
                     clusterList.add(cluster);
                     seedToCluster.put(hit, cluster);
                     hitToSeed.put(hit, hit);
-            		
-            	}
-            	// Seed does not pass minimum threshold
-            	else{
+
+                }
+                // Seed does not pass minimum threshold
+                else {
                     rejectedHitList.add(hit);
-            		hitList.remove(ii);
+                    hitList.remove(ii);
                     ii--;
-            	}            
+                }
             }// end if isSeed
 
             // If this hit is not a seed hit, see if it should be
             // attached to any neighboring seed hits.
-            else {     	
+            else {
                 // Sort through the list of neighboring hits.
                 for (CalorimeterHit neighborHit : neighborHits) {
                     // Check whether the neighboring hit is a seed.
-                  //  if (seedToCluster.containsKey(neighborHit)) {
+                    // if (seedToCluster.containsKey(neighborHit)) {
                     if (hitToSeed.get(neighborHit) == neighborHit) {
 
                         // If the neighboring hit is a seed hit and the
@@ -285,10 +281,12 @@
                 // hit with the current secondary hit's seed.
                 if (!equalEnergies(secondaryNeighborHit, secondaryHit)) {
                     hitToSeed.put(secondaryNeighborHit, hitToSeed.get(secondaryHit));
-                } else {continue;}
+                } else {
+                    continue;
+                }
             }
         } // End component hits loop.
-        
+
         // Performs second pass calculations for common hits.
         commonHitsLoop: for (CalorimeterHit clusteredHit : hitToSeed.keySet()) {
 
@@ -346,63 +344,58 @@
                 }
             }
         } // End common hits loop.
-        
+
         // Remove any common hits from the clustered hits collection.
         for (CalorimeterHit commonHit : commonHits.keySet()) {
             hitToSeed.remove(commonHit);
             hitList.remove(commonHit);
         }
 
-        
         /*
-         * All hits are sorted from above. The next part of the code is for 
-         * building the output cluster collections.
+         * All hits are sorted from above. The next part of the code is for building the output
+         * cluster collections.
          */
-        	// Add all hits except for common hits
-        	for (CalorimeterHit ihit : hitList)
-        	{
-        		CalorimeterHit iseed = hitToSeed.get(ihit);
-        		BaseCluster icluster = seedToCluster.get(iseed);
-        		icluster.addHit(ihit);
-        	}
-        
-        	// Add common hits
-        	for (Map.Entry<CalorimeterHit, List<CalorimeterHit>> commHit : commonHits.entrySet())
-        	{
-        		CalorimeterHit seedA = commHit.getValue().get(0);
-        		CalorimeterHit seedB = commHit.getValue().get(1);
-        		double eclusterA = seedToCluster.get(seedA).getEnergy();
-        		double eclusterB = seedToCluster.get(seedB).getEnergy();       	
-        		double fractionA = eclusterA / (eclusterA + eclusterB);
-        		double fractionB = eclusterB / (eclusterA + eclusterB);        	
-        		double hitcontributionA = commHit.getKey().getCorrectedEnergy()*fractionA;
-        		double hitcontributionB = commHit.getKey().getCorrectedEnergy()*fractionB;
-        	
-        		BaseCluster clusterA = seedToCluster.get(seedA);
-        		BaseCluster clusterB = seedToCluster.get(seedB);
-
-        		clusterA.addHit(commHit.getKey(), hitcontributionA);
-        		clusterB.addHit(commHit.getKey(), hitcontributionB);
-        	}
-    
-        	// Remove clusters that do not pass cluster threshold and add to rejectedHitList.
-        	for (int j=0; j <= clusterList.size()-1; j++)
-        		{	
-        		Cluster checkcluster = clusterList.get(j);        	
-        		if (checkcluster.getEnergy() < clusterEnergyThreshold) {
-        			List<CalorimeterHit> clusterHits= checkcluster.getCalorimeterHits();
-        			for (CalorimeterHit nhit : clusterHits){
-        				rejectedHitList.add(nhit);
-        			}
-        			clusterList.remove(checkcluster);
-        			j--;
-        		} 
-        		else {    
-        			// Computer the position of the cluster and set it.
-        			calculatePosition(checkcluster);
-        			continue;
-        		}
-        	}
+        // Add all hits except for common hits
+        for (CalorimeterHit ihit : hitList) {
+            CalorimeterHit iseed = hitToSeed.get(ihit);
+            BaseCluster icluster = seedToCluster.get(iseed);
+            icluster.addHit(ihit);
+        }
+
+        // Add common hits
+        for (Map.Entry<CalorimeterHit, List<CalorimeterHit>> commHit : commonHits.entrySet()) {
+            CalorimeterHit seedA = commHit.getValue().get(0);
+            CalorimeterHit seedB = commHit.getValue().get(1);
+            double eclusterA = seedToCluster.get(seedA).getEnergy();
+            double eclusterB = seedToCluster.get(seedB).getEnergy();
+            double fractionA = eclusterA / (eclusterA + eclusterB);
+            double fractionB = eclusterB / (eclusterA + eclusterB);
+            double hitcontributionA = commHit.getKey().getCorrectedEnergy() * fractionA;
+            double hitcontributionB = commHit.getKey().getCorrectedEnergy() * fractionB;
+
+            BaseCluster clusterA = seedToCluster.get(seedA);
+            BaseCluster clusterB = seedToCluster.get(seedB);
+
+            clusterA.addHit(commHit.getKey(), hitcontributionA);
+            clusterB.addHit(commHit.getKey(), hitcontributionB);
+        }
+
+        // Remove clusters that do not pass cluster threshold and add to rejectedHitList.
+        for (int j = 0; j <= clusterList.size() - 1; j++) {
+            Cluster checkcluster = clusterList.get(j);
+            if (checkcluster.getEnergy() < clusterEnergyThreshold) {
+                List<CalorimeterHit> clusterHits = checkcluster.getCalorimeterHits();
+                for (CalorimeterHit nhit : clusterHits) {
+                    rejectedHitList.add(nhit);
+                }
+                clusterList.remove(checkcluster);
+                j--;
+            } else {
+                // Computer the position of the cluster and set it.
+                calculatePosition(checkcluster);
+                continue;
+            }
+        }
         return clusterList;
     }
 
@@ -430,44 +423,44 @@
         }
         return isSeed;
     }
-    
+
     /**
-     * Calculates the position of each cluster with no correction for particle type
-     * as documented in HPS Note 2014-001.
+     * Calculates the position of each cluster with no correction for particle type as documented in
+     * HPS Note 2014-001.
      * @param cluster
      */
-    private void calculatePosition(Cluster cluster){
+    private void calculatePosition(Cluster cluster) {
         final double w0 = 3.1;
         // calculated cluster x position
-    	double xCl = 0.0; 
-    	// calculated cluster y position
-        double yCl = 0.0; 
+        double xCl = 0.0;
+        // calculated cluster y position
+        double yCl = 0.0;
         double eNumX = 0.0;
         double eNumY = 0.0;
         double eDen = 0.0;
-        List<CalorimeterHit> clusterHits= cluster.getCalorimeterHits();
-		for (CalorimeterHit hit : clusterHits){
-			// This block fills a map with crystal to center of face of crystal
-	    	// Get the hit indices as a Point.
-	    	int ix = hit.getIdentifierFieldValue("ix");
-	    	int iy = hit.getIdentifierFieldValue("iy");
-	    	Point hitIndex = new Point(ix, iy);
-	    	
-	    	// Get the corrected position for this index pair.
-	    	double[] position = correctedPositionMap.get(hitIndex);
-	    	
-	    	if (position == null){
-	    		addToMap(hit);
-	    		position = correctedPositionMap.get(hitIndex);
-	    	}
-			
+        List<CalorimeterHit> clusterHits = cluster.getCalorimeterHits();
+        for (CalorimeterHit hit : clusterHits) {
+            // This block fills a map with crystal to center of face of crystal
+            // Get the hit indices as a Point.
+            int ix = hit.getIdentifierFieldValue("ix");
+            int iy = hit.getIdentifierFieldValue("iy");
+            Point hitIndex = new Point(ix, iy);
+
+            // Get the corrected position for this index pair.
+            double[] position = correctedPositionMap.get(hitIndex);
+
+            if (position == null) {
+                addToMap(hit);
+                position = correctedPositionMap.get(hitIndex);
+            }
+
             eNumX += Math.max(0.0, (w0 + Math.log(hit.getCorrectedEnergy() / cluster.getEnergy()))) * (correctedPositionMap.get(hitIndex)[0] / 10.0);
             eNumY += Math.max(0.0, (w0 + Math.log(hit.getCorrectedEnergy() / cluster.getEnergy()))) * (correctedPositionMap.get(hitIndex)[1] / 10.0);
             eDen += Math.max(0.0, (w0 + Math.log(hit.getCorrectedEnergy() / cluster.getEnergy())));
-			
-		}// end for iteration through clusterHits
-		
-		xCl = eNumX / eDen;
+
+        }// end for iteration through clusterHits
+
+        xCl = eNumX / eDen;
         yCl = eNumY / eDen;
 
         double[] clusterPosition = new double[3];
@@ -478,32 +471,30 @@
         Point hitIndex = new Point(ix, iy);
         clusterPosition[2] = correctedPositionMap.get(hitIndex)[2];
 
-        ((BaseCluster) cluster).setPosition(clusterPosition);    	    	
-    }
- 
+        ((BaseCluster) cluster).setPosition(clusterPosition);
+    }
+
     /**
-     * This constructs a mapping of a crystal to an x,y position at the face of the ecal.
-     * This fills the correctedPositionMap with the position of a crystal's face from the
-     * index x,y of the crystal.
+     * This constructs a mapping of a crystal to an x,y position at the face of the ecal. This fills
+     * the correctedPositionMap with the position of a crystal's face from the index x,y of the
+     * crystal.
      * @param hit
      */
-    private void addToMap(CalorimeterHit hit){
-    	int ix = hit.getIdentifierFieldValue("ix");
-    	int iy = hit.getIdentifierFieldValue("iy");
-    	Point hitIndex = new Point(ix, iy);
-    	// If the result is null, it hasn't been calculated yet.
-    	// Calculate the corrected position.
-    	IGeometryInfo geom = hit.getDetectorElement().getGeometry();
-    	double[] pos = geom.transformLocalToGlobal(VecOp.add(geom.transformGlobalToLocal(geom.getPosition()), (Hep3Vector) new BasicHep3Vector(0, 0, -1 * ((Trd) geom.getLogicalVolume().getSolid()).getZHalfLength()))).v();
-
-    	// Store the result in the map.
-    	correctedPositionMap.put(hitIndex, pos);      	
-    }
-    
-    
-    
+    private void addToMap(CalorimeterHit hit) {
+        int ix = hit.getIdentifierFieldValue("ix");
+        int iy = hit.getIdentifierFieldValue("iy");
+        Point hitIndex = new Point(ix, iy);
+        // If the result is null, it hasn't been calculated yet.
+        // Calculate the corrected position.
+        IGeometryInfo geom = hit.getDetectorElement().getGeometry();
+        double[] pos = geom.transformLocalToGlobal(VecOp.add(geom.transformGlobalToLocal(geom.getPosition()), (Hep3Vector) new BasicHep3Vector(0, 0, -1 * ((Trd) geom.getLogicalVolume().getSolid()).getZHalfLength()))).v();
+
+        // Store the result in the map.
+        correctedPositionMap.put(hitIndex, pos);
+    }
+
     public ClusterType getClusterType() {
         return ClusterType.RECON;
     }
-    
+
 }

Added: java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/SimpleReconClusterDriver.java
 =============================================================================
--- java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/SimpleReconClusterDriver.java	(added)
+++ java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/SimpleReconClusterDriver.java	Thu Jan 15 10:28:42 2015
@@ -0,0 +1,23 @@
+package org.hps.recon.ecal.cluster;
+
+
+/**
+ * This is an implementation of {@link ClusterDriver} specialized for the
+ * {@link SimpleReconClusterer}.  
+ *  
+ * @see SimpleReconClusterer
+ * 
+ * @author Jeremy McCormick <[log in to unmask]>
+ */
+public class SimpleReconClusterDriver extends ClusterDriver {
+                
+    public SimpleReconClusterDriver() {
+        // Setup the Clusterer with the correct type.
+        clusterer = ClustererFactory.create("SimpleReconClusterer");
+    }
+    
+    public void setUseTimeCut(boolean useTimeCut) {
+        SimpleReconClusterer clusterer = getClusterer();
+        clusterer.setUseTimeCut(useTimeCut);
+    }    
+}

Modified: java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/SimpleReconClusterer.java
 =============================================================================
--- java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/SimpleReconClusterer.java	(original)
+++ java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/SimpleReconClusterer.java	Thu Jan 15 10:28:42 2015
@@ -31,18 +31,17 @@
     double minEnergy;
     double minTime;
     double timeWindow;
-    boolean timeCut;
+    boolean useTimeCut;
 
     /**
      * Initialize the algorithm with default cuts.
      */
     SimpleReconClusterer() {
-        super(new String[] { "minEnergy", "minTime", "timeWindow", "timeCut" }, new double[] { 0.001, 0.0, 20.0, 0. });
+        super(new String[] { "minEnergy", "minTime", "timeWindow" }, new double[] { 0.001, 0.0, 20.0 });
     }
 
     public void initialize() {
         // Setup class variables from cuts.
-        timeCut = (getCuts().getValue("timeCut") == 1.0);
         minEnergy = getCuts().getValue("minEnergy");
         minTime = getCuts().getValue("minTime");
         timeWindow = getCuts().getValue("timeWindow");
@@ -61,7 +60,7 @@
                 continue;
             }
             // if time cut is being used, reject hits outside the time window
-            if (timeCut && (h.getTime() < minTime || h.getTime() > minTime + timeWindow)) {
+            if (useTimeCut && (h.getTime() < minTime || h.getTime() > minTime + timeWindow)) {
                 continue;
             }
             sortedHitList.add(h);
@@ -129,4 +128,8 @@
     public ClusterType getClusterType() {
         return ClusterType.SIMPLE_RECON;
     }
+    
+    public void setUseTimeCut(boolean useTimeCut) {
+        this.useTimeCut = useTimeCut;
+    }
 }

Modified: java/trunk/ecal-recon/src/test/java/org/hps/recon/ecal/cluster/ClustererTest.java
 =============================================================================
--- java/trunk/ecal-recon/src/test/java/org/hps/recon/ecal/cluster/ClustererTest.java	(original)
+++ java/trunk/ecal-recon/src/test/java/org/hps/recon/ecal/cluster/ClustererTest.java	Thu Jan 15 10:28:42 2015
@@ -1,12 +1,15 @@
 package org.hps.recon.ecal.cluster;
 
 import hep.aida.ICloud1D;
+import hep.aida.ICloud2D;
 import hep.aida.IHistogram1D;
 import hep.aida.IHistogram2D;
 
 import java.io.File;
 import java.io.IOException;
 import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Set;
 import java.util.logging.Level;
@@ -29,22 +32,28 @@
 
 /**
  * This test does basic sanity checks on the output from the Clusterer algorithms,
- * and it creates an AIDA file with useful plots.
+ * and it creates an AIDA file with some useful plots, as well as optionally writes
+ * an LCIO file with the event data plus the clusters.
  * 
  * @author Jeremy McCormick <[log in to unmask]>
  */
 public class ClustererTest extends TestCase {
     
-    static int nEvents = 100;
+    static int nEvents = 1000;
     static final String fileLocation = "http://www.lcsim.org/test/hps-java/MockDataReconTest.slcio";
     File inputFile;
     File testOutputDir;
     
     static class ClustererTestSetup {
         
-        boolean writeLcioFile = false;
-        boolean checkSeedHit = false;
-        boolean applyCorrections = false;        
+        boolean writeLcioFile;
+        boolean checkSeedHit;
+        boolean applyCorrections;        
+        boolean checkHitEnergy;
+        boolean checkPropCalc;
+        boolean checkPosition;
+        boolean calculateProperties;
+        boolean sortHits;
         double[] cuts = null;
         ClusterType clusterType; 
         
@@ -70,8 +79,33 @@
             return this;
         }        
         
+        ClustererTestSetup calculateProperties() {
+            calculateProperties = true;
+            return this;
+        }
+        
+        ClustererTestSetup sortHits() {
+            sortHits = true;
+            return this;
+        }
+        
+        ClustererTestSetup checkHitEnergy() {
+            checkHitEnergy = true;
+            return this;
+        }
+        
+        ClustererTestSetup checkPropCalc() {
+            checkPropCalc = true;
+            return this;
+        }
+        
         ClustererTestSetup checkClusterType(ClusterType clusterType) {
             this.clusterType = clusterType;
+            return this;
+        }
+        
+        ClustererTestSetup checkPosition() {
+            this.checkPosition = true;
             return this;
         }
     }
@@ -98,7 +132,12 @@
      */
     public void testReconClusterer() {        
         runClustererTest("ReconClusterer", 
-                new ClustererTestSetup().writeLcioFile().checkSeedHit().applyCorrections().checkClusterType(ClusterType.RECON));
+                new ClustererTestSetup()
+                    .writeLcioFile()
+                    .checkSeedHit()
+                    .checkClusterType(ClusterType.RECON)
+                    .checkHitEnergy()
+                    .checkPosition());
     }
     
     /**
@@ -106,7 +145,12 @@
      */
     public void testSimpleReconClusterer() {
         runClustererTest("SimpleReconClusterer", 
-                new ClustererTestSetup().writeLcioFile().checkSeedHit().checkClusterType(ClusterType.SIMPLE_RECON));
+                new ClustererTestSetup()
+                    .writeLcioFile()
+                    .checkSeedHit()
+                    .checkClusterType(ClusterType.SIMPLE_RECON)
+                    .checkHitEnergy()
+                    .checkPosition());
     }
     
     /**
@@ -114,7 +158,11 @@
      */
     public void testNearestNeighborClusterer() {    
         runClustererTest("NearestNeighborClusterer", 
-                new ClustererTestSetup().writeLcioFile().checkClusterType(ClusterType.NN));
+                new ClustererTestSetup(new double[] { 0.0075, 3 })
+                    .writeLcioFile()
+                    .checkClusterType(ClusterType.NN)
+                    .checkHitEnergy()
+                    .checkPosition());
     }
     
     /**
@@ -122,7 +170,11 @@
      */
     public void testLegacyClusterer() {
         runClustererTest("LegacyClusterer", 
-                new ClustererTestSetup().writeLcioFile().checkClusterType(ClusterType.LEGACY));
+                new ClustererTestSetup()
+                    .writeLcioFile()
+                    .checkClusterType(ClusterType.LEGACY)
+                    .checkHitEnergy()
+                    .checkPosition());
     }
     
     /**
@@ -130,7 +182,11 @@
      */
     public void testGTPOnlineClusterer() {
         runClustererTest("GTPOnlineClusterer", 
-                new ClustererTestSetup().writeLcioFile().checkSeedHit().checkClusterType(ClusterType.GTP_ONLINE));
+                new ClustererTestSetup()
+                    .writeLcioFile().checkSeedHit()
+                    .checkClusterType(ClusterType.GTP_ONLINE)
+                    .checkHitEnergy()
+                    .checkPosition());
     }
     
     /**
@@ -138,7 +194,10 @@
      */
     public void testCTPClusterer() {
         runClustererTest("CTPClusterer", 
-                new ClustererTestSetup().writeLcioFile().checkClusterType(ClusterType.CTP));
+                new ClustererTestSetup()
+                    .writeLcioFile()
+                    .checkClusterType(ClusterType.CTP)
+                    .checkPosition());
     }
     
     /**
@@ -146,7 +205,11 @@
      */
     public void testGTPClusterer() {
         runClustererTest("GTPClusterer", 
-                new ClustererTestSetup().writeLcioFile().checkSeedHit().checkClusterType(ClusterType.GTP));
+                new ClustererTestSetup()
+                    .writeLcioFile()
+                    .checkClusterType(ClusterType.GTP)
+                    .checkHitEnergy()
+                    .checkPosition());
     }
     
     /**
@@ -184,14 +247,14 @@
         clusterDriver.setInputHitCollectionName("EcalHits");       
         clusterDriver.setOutputClusterCollectionName(clusterCollectionName);
         clusterDriver.setRaiseErrorNoHitCollection(true);
-        clusterDriver.setCalculateProperties(true);
-        clusterDriver.setSortHits(true);
+        clusterDriver.setCalculateProperties(setup.calculateProperties);
+        clusterDriver.setSortHits(setup.sortHits);              
         clusterDriver.setApplyCorrections(setup.applyCorrections);
         clusterDriver.getLogger().setLevel(Level.CONFIG);
         loop.add(clusterDriver);                         
         
         // This Driver generates plots and the output LCIO file.
-        loop.add(new ClusterCheckDriver(clusterCollectionName, setup.checkSeedHit, setup.clusterType));
+        loop.add(new ClusterCheckDriver(clusterCollectionName, setup));
         
         if (setup.writeLcioFile) {
             loop.add(new LCIODriver(testOutputDir.getPath() + File.separator + clustererName + ".slcio"));
@@ -233,30 +296,36 @@
         IHistogram1D shapeParam3H1D;
         ICloud1D ithetaC1D;
         ICloud1D iphiC1D;
+        ICloud1D nParticlesC1D;
+        ICloud1D particleEnergyC1D;
         IHistogram2D particleVsClusterEnergyH2D;
         IHistogram1D particleMinusClusterEnergyH1D;
+        ICloud2D clusterPositionC2D;
+        ICloud2D highestParticleEnergyVsClusterEnergyC2D;
+        ICloud2D clusterVsHitCountC2d;
+        IHistogram1D earliestHitTimeH1D;
+        IHistogram1D latestHitTimeH1D;
         String clusterCollectionName;
-        String clustererName;        
-        boolean checkSeedHit = true;
-        ClusterType clusterType;
-        
-        ClusterCheckDriver(String clusterCollectionName, boolean checkSeedHit, ClusterType clusterType) {
+        String clustererName;
+        
+        ClustererTestSetup setup;
+        
+        ClusterCheckDriver(String clusterCollectionName, ClustererTestSetup setup) {
             this.clusterCollectionName = clusterCollectionName;
-            this.checkSeedHit = checkSeedHit;
-            this.clusterType = clusterType;
+            this.setup = setup;
         }        
         
         public void startOfData() {
             energyH1D = aida.histogram1D(clusterCollectionName + "/Cluster Energy", 300, 0.0, 3.0);
             uncorrectedEnergyH1D = aida.histogram1D(clusterCollectionName + "/Uncorrected Cluster Energy", 200, 0.0, 2.0);
-            countH1D = aida.histogram1D(clusterCollectionName + "/Cluster Count", 10, -0.5, 9.5);
+            countH1D = aida.histogram1D(clusterCollectionName + "/Cluster Count", 20, -0.5, 19.5);
             sizeH1D = aida.histogram1D(clusterCollectionName + "/Cluster Size", 30, 0.5, 30.5);
             highestHitEnergyH1D = aida.histogram1D(clusterCollectionName + "/Highest Hit Energy", 300, 0.0, 1.5);
-            hitEnergyH1D = aida.histogram1D(clusterCollectionName + "/Hit Energy", 300, 0.0, 1.5);
+            hitEnergyH1D = aida.histogram1D(clusterCollectionName + "/Hit Energy", 600, 0.0, 1.5);
             rawVsCorrectedH2D = aida.histogram2D(clusterCollectionName + "/Raw vs Corrected Energy", 100, 0.0, 2.0, 100, 0.0, 2.0);
-            positionXH1D = aida.histogram1D(clusterCollectionName + "/Position X", 300, 0., 1500.0);
-            positionYH1D = aida.histogram1D(clusterCollectionName + "/Position Y", 500, 0., 1000.0);
-            positionZH1D = aida.histogram1D(clusterCollectionName + "/Position Z", 1000, 0, 2000.0);
+            positionXH1D = aida.histogram1D(clusterCollectionName + "/Position X", 500, 0., 500.0);
+            positionYH1D = aida.histogram1D(clusterCollectionName + "/Position Y", 500, 0., 100.0);
+            positionZH1D = aida.histogram1D(clusterCollectionName + "/Position Z", 200, 1400., 1500.0);
             shapeParam1H1D = aida.histogram1D(clusterCollectionName + "/Shape Param 1", 500, -5, 95);
             shapeParam2H1D = aida.histogram1D(clusterCollectionName + "/Shape Param 2", 520, -10, 250);
             shapeParam3H1D = aida.histogram1D(clusterCollectionName + "/Shape Param 3", 520, -10, 250);
@@ -264,47 +333,102 @@
             particleMinusClusterEnergyH1D = aida.histogram1D(clusterCollectionName + "/MCParticle Minus Cluster E", 200, -2.0, 2.0);
             ithetaC1D = aida.cloud1D(clusterCollectionName + "/ITheta");
             iphiC1D = aida.cloud1D(clusterCollectionName + "/IPhi");
+            clusterPositionC2D = aida.cloud2D(clusterCollectionName + "/Position XY", Integer.MAX_VALUE);
+            nParticlesC1D = aida.cloud1D(clusterCollectionName + "/MCParticle Count");
+            particleEnergyC1D = aida.cloud1D(clusterCollectionName + "/MCParticle Total Energy");
+            highestParticleEnergyVsClusterEnergyC2D = aida.cloud2D(clusterCollectionName + "/Highest Particle E vs Cluster E");
+            earliestHitTimeH1D = aida.histogram1D(clusterCollectionName + "/Earliest Hit Time", 500, 0., 500.);
+            latestHitTimeH1D = aida.histogram1D(clusterCollectionName + "/Latest Hit Time", 500, 0., 500.);
+            clusterVsHitCountC2d = aida.cloud2D(clusterCollectionName + "/Cluster Vs Hit Count");
         }
         
         public void process(EventHeader event) {
             List<Cluster> clusters = event.get(Cluster.class, this.clusterCollectionName);
             for (Cluster cluster : clusters) {
-                assertTrue("The cluster energy is invalid.", cluster.getEnergy() > 0.);
-                assertTrue("The cluster has no hits.", !cluster.getCalorimeterHits().isEmpty());
-                if (checkSeedHit) {
-                    assertEquals("First hit is not seed.", cluster.getCalorimeterHits().get(0), ClusterUtilities.findHighestEnergyHit(cluster));
-                }
-                assertTrue("Cluster properties not calculated.", !((BaseCluster)cluster).needsPropertyCalculation());
-                if (clusterType != null) {
-                    assertEquals("Cluster type is not correct.", clusterType, ClusterType.getClusterType(cluster.getType()));
-                }
-                energyH1D.fill(cluster.getEnergy());
-                double rawEnergy = ClusterUtilities.computeRawEnergy(cluster);
-                uncorrectedEnergyH1D.fill(rawEnergy);
-                sizeH1D.fill(cluster.getCalorimeterHits().size());
-                rawVsCorrectedH2D.fill(rawEnergy, cluster.getEnergy());
-                for (CalorimeterHit hit : cluster.getCalorimeterHits()) {
-                    hitEnergyH1D.fill(hit.getCorrectedEnergy());
-                }                
-                highestHitEnergyH1D.fill(ClusterUtilities.findHighestEnergyHit(cluster).getCorrectedEnergy());
-                positionXH1D.fill(Math.abs(cluster.getPosition()[0]));
-                positionYH1D.fill(Math.abs(cluster.getPosition()[1]));
-                positionZH1D.fill(Math.abs(cluster.getPosition()[2]));
-                shapeParam1H1D.fill(cluster.getShape()[0]);
-                shapeParam2H1D.fill(cluster.getShape()[1]);
-                shapeParam3H1D.fill(cluster.getShape()[2]);
-                iphiC1D.fill(Math.toDegrees(cluster.getIPhi()));
-                ithetaC1D.fill(Math.toDegrees(cluster.getITheta()));                                
                 
-                Set<MCParticle> particles = ClusterUtilities.findMCParticles(cluster);
-                double particleEnergy = 0;
-                for (MCParticle particle : particles) {
-                    particleEnergy += particle.getEnergy();
-                }
-                particleVsClusterEnergyH2D.fill(particleEnergy, cluster.getEnergy());
-                particleMinusClusterEnergyH1D.fill(particleEnergy - cluster.getEnergy());
+                // Test assertions.
+                checkCluster(cluster);                
+                
+                // Fill plots.
+                fillClusterPlots(cluster);
             }            
             countH1D.fill(clusters.size());
+            clusterVsHitCountC2d.fill(clusters.size(), ClusterUtilities.getHits(clusters).size());
+        }
+
+        /**
+         * @param cluster
+         */
+        private void fillClusterPlots(Cluster cluster) {
+            energyH1D.fill(cluster.getEnergy());
+            double rawEnergy = ClusterUtilities.computeRawEnergy(cluster);
+            uncorrectedEnergyH1D.fill(rawEnergy);
+            sizeH1D.fill(cluster.getCalorimeterHits().size());
+            rawVsCorrectedH2D.fill(rawEnergy, cluster.getEnergy());
+            for (CalorimeterHit hit : cluster.getCalorimeterHits()) {
+                hitEnergyH1D.fill(hit.getCorrectedEnergy());
+            }                
+            highestHitEnergyH1D.fill(ClusterUtilities.findHighestEnergyHit(cluster).getCorrectedEnergy());
+            positionXH1D.fill(Math.abs(cluster.getPosition()[0]));
+            positionYH1D.fill(Math.abs(cluster.getPosition()[1]));
+            positionZH1D.fill(Math.abs(cluster.getPosition()[2]));
+            shapeParam1H1D.fill(cluster.getShape()[0]);
+            shapeParam2H1D.fill(cluster.getShape()[1]);
+            shapeParam3H1D.fill(cluster.getShape()[2]);
+            iphiC1D.fill(Math.toDegrees(cluster.getIPhi()));
+            ithetaC1D.fill(Math.toDegrees(cluster.getITheta()));                                
+            clusterPositionC2D.fill(cluster.getPosition()[0], cluster.getPosition()[1]);
+            
+            //Map<MCParticle, List<SimCalorimeterHit>> particleHitMap = ClusterUtilities.createParticleHitMap(cluster);
+            
+            Set<MCParticle> particles = ClusterUtilities.findMCParticles(cluster);
+            double particleEnergy = 0;
+            double highestParticleEnergy = Double.MIN_VALUE;
+            for (MCParticle particle : particles) {
+                particleEnergy += particle.getEnergy();
+                if (particle.getEnergy() > highestParticleEnergy) {
+                    highestParticleEnergy = particle.getEnergy();
+                }
+            }
+            particleVsClusterEnergyH2D.fill(particleEnergy, cluster.getEnergy());
+            particleMinusClusterEnergyH1D.fill(particleEnergy - cluster.getEnergy());
+            nParticlesC1D.fill(particles.size());
+            particleEnergyC1D.fill(particleEnergy);
+            highestParticleEnergyVsClusterEnergyC2D.fill(highestParticleEnergy, cluster.getEnergy());
+
+            List<CalorimeterHit> hitsForTime = new ArrayList<CalorimeterHit>();
+            hitsForTime.addAll(cluster.getCalorimeterHits());
+            Collections.sort(hitsForTime, new CalorimeterHit.TimeComparator());
+            earliestHitTimeH1D.fill(hitsForTime.get(0).getTime());
+            latestHitTimeH1D.fill(hitsForTime.get(hitsForTime.size() - 1).getTime());
+        }
+
+        /**
+         * @param cluster
+         */
+        private void checkCluster(Cluster cluster) {
+            assertTrue("The cluster energy is invalid.", cluster.getEnergy() > 0.);
+            assertTrue("The cluster has no hits.", !cluster.getCalorimeterHits().isEmpty());
+            if (setup.checkSeedHit) {
+                assertEquals("First hit is not seed.", cluster.getCalorimeterHits().get(0), ClusterUtilities.findHighestEnergyHit(cluster));
+            }
+            if (setup.checkPropCalc) {
+                assertTrue("Cluster properties not calculated.", !((BaseCluster)cluster).needsPropertyCalculation());
+            }
+            if (setup.clusterType != null) {
+                assertEquals("Cluster type is not correct.", setup.clusterType, ClusterType.getClusterType(cluster.getType()));
+            }
+            if (setup.checkHitEnergy) {
+                for (CalorimeterHit hit : cluster.getCalorimeterHits()) {
+                    assertTrue("Hit energy " + hit.getCorrectedEnergy() + " is <= 0.", hit.getCorrectedEnergy() > 0.);
+                }
+            }
+            if (setup.checkPosition) {
+                double[] position = cluster.getPosition();
+                assertTrue("Position X is invalid.", Math.abs(position[0]) < 400. && position[0] != 0.);
+                assertTrue("Position Y is invalid.", Math.abs(position[1]) > 25. && Math.abs(position[1]) < 90.);
+                assertTrue("Position Z is invalid.", position[2] > 1460. && position[2] < 1480.);
+            }
         }              
     }