Print

Print


Author: [log in to unmask]
Date: Mon Feb  9 08:42:35 2015
New Revision: 2081

Log:
Added temporary trigger diagnostic classes. These are subject to changes in both name and functionality and should not be used until officially integrated into the build.

Added:
    java/trunk/users/src/main/java/org/hps/users/kmccarty/PairTrigger.java
    java/trunk/users/src/main/java/org/hps/users/kmccarty/SinglesTrigger.java
    java/trunk/users/src/main/java/org/hps/users/kmccarty/Trigger.java
    java/trunk/users/src/main/java/org/hps/users/kmccarty/TriggerDiagnosticDriver.java

Added: java/trunk/users/src/main/java/org/hps/users/kmccarty/PairTrigger.java
 =============================================================================
--- java/trunk/users/src/main/java/org/hps/users/kmccarty/PairTrigger.java	(added)
+++ java/trunk/users/src/main/java/org/hps/users/kmccarty/PairTrigger.java	Mon Feb  9 08:42:35 2015
@@ -0,0 +1,136 @@
+package org.hps.users.kmccarty;
+
+import org.hps.readout.ecal.TriggerModule;
+
+public class PairTrigger<E> extends SinglesTrigger<E> {
+	// Define the supported trigger cuts.
+	private static final String PAIR_ENERGY_SUM_LOW = TriggerModule.PAIR_ENERGY_SUM_LOW;
+	private static final String PAIR_ENERGY_SUM_HIGH = TriggerModule.PAIR_ENERGY_SUM_HIGH;
+	private static final String PAIR_ENERGY_DIFFERENCE_HIGH = TriggerModule.PAIR_ENERGY_DIFFERENCE_HIGH;
+	private static final String PAIR_ENERGY_SLOPE_LOW = TriggerModule.PAIR_ENERGY_SLOPE_LOW;
+	private static final String PAIR_COPLANARITY_HIGH = TriggerModule.PAIR_COPLANARITY_HIGH;
+	
+	/**
+	 * Instantiates a new <code>PairTrigger</code> with all cut
+	 * states set to <code>false</code> and with the trigger source
+	 * defined according to the specified object.
+	 * @param source - The object from which the trigger cut states
+	 * are derived.
+	 */
+	protected PairTrigger(E source) {
+		// Instantiate the superclass.
+		super(source);
+		
+		// Add the supported cuts types.
+		addValidCut(PAIR_ENERGY_SUM_LOW);
+		addValidCut(PAIR_ENERGY_SUM_HIGH);
+		addValidCut(PAIR_ENERGY_DIFFERENCE_HIGH);
+		addValidCut(PAIR_ENERGY_SLOPE_LOW);
+		addValidCut(PAIR_COPLANARITY_HIGH);
+	}
+	
+	/**
+	 * Gets whether the pair energy sum lower bound cut was met.
+	 * @return Returns <code>true</code> if the cut was met and
+	 * <code>false</code> otherwise.
+	 */
+	public boolean getStateEnergySumLow() {
+		return getCutState(PAIR_ENERGY_SUM_LOW);
+	}
+	
+	/**
+	 * Gets whether the pair energy sum upper bound cut was met.
+	 * @return Returns <code>true</code> if the cut was met and
+	 * <code>false</code> otherwise.
+	 */
+	public boolean getStateEnergySumHigh() {
+		return getCutState(PAIR_ENERGY_SUM_HIGH);
+	}
+	
+	/**
+	 * Gets whether both the pair energy sum upper and lower bound cuts
+	 * were met.
+	 * @return Returns <code>true</code> if the cuts were met and
+	 * <code>false</code> otherwise.
+	 */
+	public boolean getStateEnergySum() {
+		return getCutState(PAIR_ENERGY_SUM_HIGH);
+	}
+	
+	/**
+	 * Gets whether the pair energy difference cut was met.
+	 * @return Returns <code>true</code> if the cut was met and
+	 * <code>false</code> otherwise.
+	 */
+	public boolean getStateEnergyDifference() {
+		return getCutState(PAIR_ENERGY_SUM_HIGH);
+	}
+	
+	/**
+	 * Gets whether the pair energy slope cut was met.
+	 * @return Returns <code>true</code> if the cut was met and
+	 * <code>false</code> otherwise.
+	 */
+	public boolean getStateEnergySlope() {
+		return getCutState(PAIR_ENERGY_SLOPE_LOW);
+	}
+	
+	/**
+	 * Gets whether the pair coplanarity cut was met.
+	 * @return Returns <code>true</code> if the cut was met and
+	 * <code>false</code> otherwise.
+	 */
+	public boolean getStateCoplanarity() {
+		return getCutState(PAIR_COPLANARITY_HIGH);
+	}
+	
+	/**
+	 * Sets whether the conditions for the pair energy sum lower bound
+	 * cut were met.
+	 * @param state - <code>true</code> indicates that the cut conditions
+	 * were met and <code>false</code> that they were not.
+	 */
+	public void getStateEnergySumLow(boolean state) {
+		setCutState(PAIR_ENERGY_SUM_LOW, state);
+	}
+	
+	/**
+	 * Sets whether the conditions for the pair energy sum upper bound
+	 * cut were met.
+	 * @param state - <code>true</code> indicates that the cut conditions
+	 * were met and <code>false</code> that they were not.
+	 */
+	public void getStateEnergySumHigh(boolean state) {
+		setCutState(PAIR_ENERGY_SUM_HIGH, state);
+	}
+	
+	/**
+	 * Sets whether the conditions for the pair energy difference cut
+	 * were met.
+	 * @param state - <code>true</code> indicates that the cut conditions
+	 * were met and <code>false</code> that they were not.
+	 */
+	public void getStateEnergyDifference(boolean state) {
+		setCutState(PAIR_ENERGY_DIFFERENCE_HIGH, state);
+	}
+	
+	/**
+	 * Sets whether the conditions for the pair energy slope cut were
+	 * met.
+	 * @param state - <code>true</code> indicates that the cut conditions
+	 * were met and <code>false</code> that they were not.
+	 */
+	public void getStateEnergySlope(boolean state) {
+		setCutState(PAIR_ENERGY_SLOPE_LOW, state);
+	}
+	
+	/**
+	 * Sets whether the conditions for the pair coplanarity cut were
+	 * met.
+	 * @param state - <code>true</code> indicates that the cut conditions
+	 * were met and <code>false</code> that they were not.
+	 */
+	public void getStateCoplanarity(boolean state) {
+		setCutState(PAIR_COPLANARITY_HIGH, state);
+	}
+}

Added: java/trunk/users/src/main/java/org/hps/users/kmccarty/SinglesTrigger.java
 =============================================================================
--- java/trunk/users/src/main/java/org/hps/users/kmccarty/SinglesTrigger.java	(added)
+++ java/trunk/users/src/main/java/org/hps/users/kmccarty/SinglesTrigger.java	Mon Feb  9 08:42:35 2015
@@ -0,0 +1,146 @@
+package org.hps.users.kmccarty;
+
+import org.hps.readout.ecal.TriggerModule;
+
+public class SinglesTrigger<E> extends Trigger<E> {
+	// Define the supported trigger cuts.
+	private static final String CLUSTER_HIT_COUNT_LOW = TriggerModule.CLUSTER_HIT_COUNT_LOW;
+	private static final String CLUSTER_SEED_ENERGY_LOW = TriggerModule.CLUSTER_SEED_ENERGY_LOW;
+	private static final String CLUSTER_SEED_ENERGY_HIGH = TriggerModule.CLUSTER_SEED_ENERGY_HIGH;
+	private static final String CLUSTER_TOTAL_ENERGY_LOW = TriggerModule.CLUSTER_TOTAL_ENERGY_LOW;
+	private static final String CLUSTER_TOTAL_ENERGY_HIGH = TriggerModule.CLUSTER_TOTAL_ENERGY_HIGH;
+	
+	/**
+	 * Instantiates a new <code>SinglesTrigger</code> with all cut
+	 * states set to <code>false</code> and with the trigger source
+	 * defined according to the specified object.
+	 * @param source - The object from which the trigger cut states
+	 * are derived.
+	 */
+	protected SinglesTrigger(E source) {
+		// Instantiate the superclass.
+		super(source);
+		
+		// Add the supported cuts types.
+		addValidCut(CLUSTER_HIT_COUNT_LOW);
+		addValidCut(CLUSTER_SEED_ENERGY_LOW);
+		addValidCut(CLUSTER_SEED_ENERGY_HIGH);
+		addValidCut(CLUSTER_TOTAL_ENERGY_LOW);
+		addValidCut(CLUSTER_TOTAL_ENERGY_HIGH);
+	}
+	
+	/**
+	 * Gets whether the cluster hit count cut was met.
+	 * @return Returns <code>true</code> if the cut was met and
+	 * <code>false</code> otherwise.
+	 */
+	public boolean getStateHitCount() {
+		return getCutState(CLUSTER_HIT_COUNT_LOW);
+	}
+	
+	/**
+	 * Gets whether the cluster seed energy lower bound cut was met.
+	 * @return Returns <code>true</code> if the cut was met and
+	 * <code>false</code> otherwise.
+	 */
+	public boolean getStateSeedEnergyLow() {
+		return getCutState(CLUSTER_SEED_ENERGY_LOW);
+	}
+	
+	/**
+	 * Gets whether the cluster seed energy upper bound cut was met.
+	 * @return Returns <code>true</code> if the cut was met and
+	 * <code>false</code> otherwise.
+	 */
+	public boolean getStateSeedEnergyHigh() {
+		return getCutState(CLUSTER_SEED_ENERGY_HIGH);
+	}
+	
+	/**
+	 * Gets whether both the cluster seed energy upper and lower bound
+	 * cuts were met.
+	 * @return Returns <code>true</code> if the cuts were met and
+	 * <code>false</code> otherwise.
+	 */
+	public boolean getStateSeedEnergy() {
+		return getCutState(CLUSTER_SEED_ENERGY_LOW) && getCutState(CLUSTER_SEED_ENERGY_HIGH);
+	}
+	
+	/**
+	 * Gets whether the cluster total energy lower bound cut was met.
+	 * @return Returns <code>true</code> if the cut was met and
+	 * <code>false</code> otherwise.
+	 */
+	public boolean getStateClusterEnergyLow() {
+		return getCutState(CLUSTER_TOTAL_ENERGY_LOW);
+	}
+	
+	/**
+	 * Gets whether the cluster total energy upper bound cut was met.
+	 * @return Returns <code>true</code> if the cut was met and
+	 * <code>false</code> otherwise.
+	 */
+	public boolean getStateClusterEnergyHigh() {
+		return getCutState(CLUSTER_TOTAL_ENERGY_HIGH);
+	}
+	
+	/**
+	 * Gets whether both the cluster total energy upper and lower bound
+	 * cuts were met.
+	 * @return Returns <code>true</code> if the cuts were met and
+	 * <code>false</code> otherwise.
+	 */
+	public boolean getStateClusterEnergy() {
+		return getCutState(CLUSTER_TOTAL_ENERGY_LOW) && getCutState(CLUSTER_TOTAL_ENERGY_HIGH);
+	}
+	
+	/**
+	 * Sets whether the conditions for the cluster hit count cut were
+	 * met.
+	 * @param state - <code>true</code> indicates that the cut conditions
+	 * were met and <code>false</code> that they were not.
+	 */
+	public void setStateHitCount(boolean state) {
+		setCutState(CLUSTER_HIT_COUNT_LOW, state);
+	}
+	
+	/**
+	 * Sets whether the conditions for the cluster seed energy lower
+	 * bound cut were met.
+	 * @param state - <code>true</code> indicates that the cut conditions
+	 * were met and <code>false</code> that they were not.
+	 */
+	public void setStateSeedEnergyLow(boolean state) {
+		setCutState(CLUSTER_SEED_ENERGY_LOW, state);
+	}
+	
+	/**
+	 * Sets whether the conditions for the cluster seed energy upper
+	 * bound cut were met.
+	 * @param state - <code>true</code> indicates that the cut conditions
+	 * were met and <code>false</code> that they were not.
+	 */
+	public void setStateSeedEnergyHigh(boolean state) {
+		setCutState(CLUSTER_SEED_ENERGY_HIGH, state);
+	}
+	
+	/**
+	 * Sets whether the conditions for the cluster total energy lower
+	 * bound cut were met.
+	 * @param state - <code>true</code> indicates that the cut conditions
+	 * were met and <code>false</code> that they were not.
+	 */
+	public void setStateClusterEnergyLow(boolean state) {
+		setCutState(CLUSTER_TOTAL_ENERGY_LOW, state);
+	}
+	
+	/**
+	 * Sets whether the conditions for the cluster total energy upper
+	 * bound cut were met.
+	 * @param state - <code>true</code> indicates that the cut conditions
+	 * were met and <code>false</code> that they were not.
+	 */
+	public void setStateClusterEnergyHigh(boolean state) {
+		setCutState(CLUSTER_TOTAL_ENERGY_HIGH, state);
+	}
+}

Added: java/trunk/users/src/main/java/org/hps/users/kmccarty/Trigger.java
 =============================================================================
--- java/trunk/users/src/main/java/org/hps/users/kmccarty/Trigger.java	(added)
+++ java/trunk/users/src/main/java/org/hps/users/kmccarty/Trigger.java	Mon Feb  9 08:42:35 2015
@@ -0,0 +1,130 @@
+package org.hps.users.kmccarty;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * Class <code>Trigger</code> stores a set cut states indicating whether
+ * specific cut conditions associated with a trigger were met or not as
+ * well as the state of the overall trigger. It is the responsibility of
+ * implementing classes to specify the supported cut states and also
+ * to define when the trigger conditions are met.
+ * 
+ * @author Kyle McCarty <[log in to unmask]>
+ */
+public abstract class Trigger<E> {
+	// Track whether the trigger conditions were met.
+	private boolean passTrigger = false;
+	// Store the cut condition states.
+	private Map<String, Boolean> passMap = new HashMap<String, Boolean>();
+	// Store the cluster associated with the trigger.
+	private final E source;
+	
+	/**
+	 * Creates a new <code>Trigger</code> object with the argument
+	 * specifying the object from whence the trigger state is derived.
+	 * @param source - The trigger source object.
+	 */
+	protected Trigger(E source) {
+		this.source = source;
+	}
+	
+	/**
+	 * Adds a cut to the set of cuts tracked by this trigger.
+	 * @param cut - The identifier for the cut.
+	 */
+	protected void addValidCut(String cut) {
+		passMap.put(cut, new Boolean(false));
+	}
+	
+	/**
+	 * Gets the state of the specified cut.
+	 * @param cut - The identifier for the cut.
+	 * @return Returns <code>true</code> if the conditions for the
+	 * specified cut were met and <code>false</code> otherwise.
+	 * @throws IllegalArgumentException Occurs if the specified cut
+	 * is not supported by the object.
+	 */
+	protected boolean getCutState(String cut) throws IllegalArgumentException {
+		if(passMap.containsKey(cut)) {
+			return passMap.get(cut);
+		} else {
+			throw new IllegalArgumentException(String.format("Trigger cut \"%s\" is not a supported trigger cut.", cut));
+		}
+	}
+	
+	/**
+	 * Gets the object to which the trigger cuts are applied.
+	 * @return Returns the trigger source object.
+	 */
+	public E getTriggerSource() { return source; }
+	
+	/**
+	 * Gets whether the conditions for the trigger were met.
+	 * @return Returns <code>true</code> if the conditions for the
+	 * trigger were met and <code>false</code> if they were not.
+	 */
+	public boolean getTriggerState() {
+		return passTrigger;
+	}
+	
+	/**
+	 * Removes a cut from the set of cuts tracked by the trigger.
+	 * @param cut - The identifier for the cut.
+	 */
+	protected void removeValidCut(String cut) {
+		passMap.remove(cut);
+	}
+	
+	/**
+	 * Checks whether the all of the trigger cut conditions were met.
+	 * @return Returns <code>true</code> if all of the cut conditions
+	 * were met and <code>false</code> otherwise.
+	 */
+	private boolean isValidTrigger() {
+		// Iterate over all of the cuts and look for any that have not
+		// been met.
+		for(Entry<String, Boolean> cut : passMap.entrySet()) {
+			if(!cut.getValue()) { return false; }
+		}
+		
+		// If there are no cut conditions that have not been met, then
+		// the trigger is valid.
+		return true;
+	}
+	
+	/**
+	 * Sets whether the conditions for the specified cut were met.
+	 * @param cut - The identifier for the cut.
+	 * @param state - <code>true</code> indicates that the conditions
+	 * for the cut were met and <code>false</code> that they were not.
+	 * @throws IllegalArgumentException Occurs if the specified cut
+	 * is not supported by the object.
+	 */
+	protected void setCutState(String cut, boolean state) throws IllegalArgumentException {
+		if(passMap.containsKey(cut)) {
+			// Set the cut state.
+			passMap.put(cut, state);
+			
+			// If the cut state is true, then all cut conditions may have
+			// been met. Check whether this is true and, if so, set the
+			// trigger state accordingly.
+			if(state && isValidTrigger()) { passTrigger = true; }
+			else { passTrigger = false; }
+		} else {
+			throw new IllegalArgumentException(String.format("Trigger cut \"%s\" is not a supported trigger cut.", cut));
+		}
+	}
+	
+	/**
+	 * Indicates whether the specified cut state is tracked by this
+	 * object or not.
+	 * @param cut - The identifier for the cut.
+	 * @return Returns <code>true</code> if the cut state is tracked
+	 * by this object and <code>false</code> otherwise.
+	 */
+	protected boolean supportsCut(String cut) {
+		return passMap.containsKey(cut);
+	}
+}

Added: java/trunk/users/src/main/java/org/hps/users/kmccarty/TriggerDiagnosticDriver.java
 =============================================================================
--- java/trunk/users/src/main/java/org/hps/users/kmccarty/TriggerDiagnosticDriver.java	(added)
+++ java/trunk/users/src/main/java/org/hps/users/kmccarty/TriggerDiagnosticDriver.java	Mon Feb  9 08:42:35 2015
@@ -0,0 +1,330 @@
+package org.hps.users.kmccarty;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.hps.readout.ecal.TriggerModule;
+import org.hps.readout.ecal.triggerbank.AbstractIntData;
+import org.hps.readout.ecal.triggerbank.SSPCluster;
+import org.hps.readout.ecal.triggerbank.SSPData;
+import org.hps.readout.ecal.triggerbank.TIData;
+import org.hps.recon.ecal.CalorimeterHitUtilities;
+import org.lcsim.detector.converter.compact.EcalCrystal;
+import org.lcsim.event.CalorimeterHit;
+import org.lcsim.event.Cluster;
+import org.lcsim.event.EventHeader;
+import org.lcsim.event.GenericObject;
+import org.lcsim.geometry.Detector;
+import org.lcsim.util.Driver;
+
+public class TriggerDiagnosticDriver extends Driver {
+	// Store the LCIO collection names for the needed objects.
+	private String bankCollectionName = "TriggerBank";
+	private String clusterCollectionName = "EcalClusters";
+	
+	// Store the lists of parsed objects.
+	private TIData tiBank;
+	private SSPData sspBank;
+	private List<Cluster> reconClusters;
+	private List<SSPCluster> sspClusters;
+	private List<List<PairTrigger<Cluster>>> reconPairsTriggers = new ArrayList<List<PairTrigger<Cluster>>>(2);
+	private List<List<PairTrigger<SSPCluster>>> sspPairsTriggers = new ArrayList<List<PairTrigger<SSPCluster>>>(2);
+	private List<List<SinglesTrigger<Cluster>>> reconSinglesTriggers = new ArrayList<List<SinglesTrigger<Cluster>>>(2);
+	private List<List<SinglesTrigger<SSPCluster>>> sspSinglesTriggers = new ArrayList<List<SinglesTrigger<SSPCluster>>>(2);
+	
+	// Trigger modules for performing trigger analysis.
+	private TriggerModule[] singlesTrigger = new TriggerModule[2];
+	private TriggerModule[] pairsTrigger = new TriggerModule[2];
+	
+	// Store internal variables.
+	private double energyAcceptance = 0.05;
+	
+	/*
+	@Override
+	public void detectorChanged(Detector detector) {
+		for(EcalCrystal crystal : detector.getSubdetector("Ecal").getDetectorElement().findDescendants(EcalCrystal.class)) {
+			System.out.println(crystal.getIdentifier().getValue());
+			CalorimeterHit tempHit = CalorimeterHitUtilities.create(1.000, 10.0, crystal.getIdentifier().getValue());
+			
+			int ix = tempHit.getIdentifierFieldValue("ix");
+			int iy = tempHit.getIdentifierFieldValue("iy");
+			double[] xyz = tempHit.getPosition();
+			
+			System.out.printf("(%3d, %3d) --> (%.2f, %.2f)%n", ix, iy, xyz[0], xyz[1]);
+		}
+	}
+	*/
+	
+	/**
+	 * Define the trigger modules. This should be replaced by parsing
+	 * the DAQ configuration at some point.
+	 */
+	@Override
+	public void startOfData() {
+		// Define the first singles trigger.
+		singlesTrigger[0] = new TriggerModule();
+		singlesTrigger[0].setCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_LOW, 0.500);
+		singlesTrigger[0].setCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_HIGH, 8.191);
+		singlesTrigger[0].setCutValue(TriggerModule.CLUSTER_HIT_COUNT_LOW, 0);
+		
+		// Define the second singles trigger.
+		singlesTrigger[1] = new TriggerModule();
+		singlesTrigger[1].setCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_LOW, 0.000);
+		singlesTrigger[1].setCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_HIGH, 8.191);
+		singlesTrigger[1].setCutValue(TriggerModule.CLUSTER_HIT_COUNT_LOW, 0);
+		
+		// Define the first pairs trigger.
+		pairsTrigger[0] = new TriggerModule();
+		pairsTrigger[0].setCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_LOW, 0.000);
+		pairsTrigger[0].setCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_HIGH, 8.191);
+		pairsTrigger[0].setCutValue(TriggerModule.CLUSTER_HIT_COUNT_LOW, 0);
+		pairsTrigger[0].setCutValue(TriggerModule.PAIR_ENERGY_SUM_LOW, 0.000);
+		pairsTrigger[0].setCutValue(TriggerModule.PAIR_ENERGY_SUM_HIGH, 8.191);
+		pairsTrigger[0].setCutValue(TriggerModule.PAIR_ENERGY_DIFFERENCE_HIGH, 8.191);
+		pairsTrigger[0].setCutValue(TriggerModule.PAIR_ENERGY_SLOPE_LOW, 0.000);
+		pairsTrigger[0].setCutValue(TriggerModule.PAIR_ENERGY_SLOPE_F, 0.001);
+		pairsTrigger[0].setCutValue(TriggerModule.PAIR_COPLANARITY_HIGH, 180);
+		
+		// Define the second pairs trigger.
+		pairsTrigger[1] = new TriggerModule();
+		pairsTrigger[1].setCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_LOW, 0.000);
+		pairsTrigger[1].setCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_HIGH, 8.191);
+		pairsTrigger[1].setCutValue(TriggerModule.CLUSTER_HIT_COUNT_LOW, 0);
+		pairsTrigger[1].setCutValue(TriggerModule.PAIR_ENERGY_SUM_LOW, 0.000);
+		pairsTrigger[1].setCutValue(TriggerModule.PAIR_ENERGY_SUM_HIGH, 8.191);
+		pairsTrigger[1].setCutValue(TriggerModule.PAIR_ENERGY_DIFFERENCE_HIGH, 8.191);
+		pairsTrigger[1].setCutValue(TriggerModule.PAIR_ENERGY_SLOPE_LOW, 0.000);
+		pairsTrigger[1].setCutValue(TriggerModule.PAIR_ENERGY_SLOPE_F, 0.001);
+		pairsTrigger[1].setCutValue(TriggerModule.PAIR_COPLANARITY_HIGH, 180);
+		
+		// Instantiate the triggers lists.
+		for(int triggerNum = 0; triggerNum < 2; triggerNum++) {
+			reconPairsTriggers.add(new ArrayList<PairTrigger<Cluster>>());
+			sspPairsTriggers.add(new ArrayList<PairTrigger<SSPCluster>>());
+			reconSinglesTriggers.add(new ArrayList<SinglesTrigger<Cluster>>());
+			sspSinglesTriggers.add(new ArrayList<SinglesTrigger<SSPCluster>>());
+		}
+	}
+	
+	/**
+	 * Gets the banks and clusters from the event.
+	 */
+	@Override
+	public void process(EventHeader event) {
+		// Get the reconstructed clusters.
+		if(event.hasCollection(Cluster.class, clusterCollectionName)) {
+			reconClusters = event.get(Cluster.class, clusterCollectionName);
+		}
+		
+		// Get the SSP clusters.
+		if(event.hasCollection(GenericObject.class, bankCollectionName)) {
+			// Get the bank list.
+			List<GenericObject> bankList = event.get(GenericObject.class, bankCollectionName);
+			
+			// Search through the banks and get the SSP and TI banks.
+			for(GenericObject obj : bankList) {
+				// If this is an SSP bank, parse it.
+				if(AbstractIntData.getTag(obj) == SSPData.BANK_TAG) {
+					sspBank = new SSPData(obj);
+				}
+				
+				// Otherwise, if this is a TI bank, parse it.
+				else if(AbstractIntData.getTag(obj) == TIData.BANK_TAG) {
+					tiBank = new TIData(obj);
+				}
+			}
+			
+			// If there is an SSP bank, get the list of SSP clusters.
+			if(sspBank != null) {
+				sspClusters = sspBank.getClusters();
+			}
+		}
+		
+		// Check that all of the collections and objects are present.
+		boolean allPresent = true;
+		if(sspBank == null) {
+			System.out.println("SSP bank not found!");
+			allPresent = false;
+		} if(tiBank == null) {
+			System.out.println("TI bank not found!");
+			allPresent = false;
+		} if(sspClusters == null) {
+			System.out.println("SSP clusters not found!");
+			allPresent = false;
+		} if(reconClusters == null) {
+			System.out.println("Reconstructed clusters not found!");
+			allPresent = false;
+		}
+		
+		// Do nothing further if an object is missing.
+		if(!allPresent) { return; }
+		
+		// Otherwise, print out the two cluster collections.
+		System.out.printf("Summary for Event %d at time %d%n", event.getEventNumber(), event.getTimeStamp());
+		System.out.println("Reconstructed Clusters:");
+		for(Cluster cluster : reconClusters) {
+			System.out.println("\t" + reconClusterToString(cluster));
+		}
+		
+		System.out.println("SSP Clusters:");
+		for(SSPCluster cluster : sspClusters) {
+			System.out.println("\t" + sspClusterToString(cluster));
+		}
+		
+		// Perform the cluster verification step.
+		verifyClusters();
+		
+		// Construct lists of triggers for the SSP clusters and the
+		// reconstructed clusters.
+		constructTriggers();
+		
+		System.out.println("\n\n");
+	}
+	
+	private void verifyClusters() {
+		// Track which clusters match and whether a given cluster
+		// has been matched or not.
+		Set<Cluster> reconClusterSet = new HashSet<Cluster>(reconClusters.size());
+		Set<SSPCluster> sspClusterSet = new HashSet<SSPCluster>(sspClusters.size());
+		Map<Cluster, SSPCluster> pairMap = new HashMap<Cluster, SSPCluster>(reconClusters.size());
+		
+		// Iterate over the reconstructed clusters and check whether
+		// there is a matching SSP cluster or not.
+		for(Cluster reconCluster : reconClusters) {
+			// Get the cluster's x- and y- indices.
+			int ix = reconCluster.getCalorimeterHits().get(0).getIdentifierFieldValue("ix");
+			int iy = reconCluster.getCalorimeterHits().get(0).getIdentifierFieldValue("iy");
+			
+			// Look for an unmatched cluster with the same indices.
+			matchLoop:
+			for(SSPCluster sspCluster : sspClusters) {
+				// TODO: Implement time cut on cluster matching.
+				if(sspCluster.getXIndex() == ix && sspCluster.getYIndex() == iy) {
+					// If this SSP cluster is already matched, it can
+					// not be used again.
+					if(sspClusterSet.contains(sspCluster)) {
+						continue matchLoop;
+					}
+					
+					// We also require that the SSP cluster reports an
+					// energy within a certain percentage of the recon
+					// cluster.
+					double[] energy = { sspCluster.getEnergy() * (1 - energyAcceptance),
+							sspCluster.getEnergy() * (1 + energyAcceptance) };
+					
+					// If the energies are within range, consider this
+					// a matched cluster pair. They must also have the
+					// same hit count.
+					// TODO: Fix hit inconsistency bug.
+					if(reconCluster.getEnergy() >= energy[0] && reconCluster.getEnergy() <= energy[1]
+							&& reconCluster.getCalorimeterHits().size() == sspCluster.getHitCount()) {
+						// Add the two clusters to the matched sets.
+						sspClusterSet.add(sspCluster);
+						reconClusterSet.add(reconCluster);
+						
+						// Map the two clusters together.
+						pairMap.put(reconCluster, sspCluster);
+						
+						// Skip to the next recon cluster.
+						continue matchLoop;
+					}
+				}
+			}
+		} // End matchLoop
+		
+		// Output the cluster matches and note which clusters failed
+		// to be paired. These may suggest an error.
+		System.out.println("Matched Clusters:");
+		for(Entry<Cluster, SSPCluster> pair : pairMap.entrySet()) {
+			System.out.printf("\t%s --> %s%n", reconClusterToString(pair.getKey()), sspClusterToString(pair.getValue()));
+		}
+		System.out.println("Unmatched Clusters:");
+		for(SSPCluster sspCluster : sspClusters){
+			if(!sspClusterSet.contains(sspCluster)) {
+				System.out.printf("\tSSP   :: %s%n", sspClusterToString(sspCluster));
+			}
+		}
+		for(Cluster reconCluster : reconClusters){
+			if(!reconClusterSet.contains(reconCluster)) {
+				System.out.printf("\tRecon :: %s%n", reconClusterToString(reconCluster));
+			}
+		}
+	}
+	
+	private void constructTriggers() {
+		// Run the SSP clusters through the singles trigger to determine
+		// whether they pass it or not.
+		for(SSPCluster cluster : sspClusters) {
+			for(int triggerNum = 0; triggerNum < 2; triggerNum++) {
+				// For a cluster to have formed it is assumed to have passed
+				// the cluster seed energy cuts. This can not be verified
+				// since the SSP bank does not report individual hit. 
+				boolean passSeedLow = true;
+				boolean passSeedHigh = true;
+				
+				// The remaining cuts may be acquired from trigger module.
+				boolean passClusterLow = singlesTrigger[triggerNum].clusterTotalEnergyCutLow(cluster);
+				boolean passClusterHigh = singlesTrigger[triggerNum].clusterTotalEnergyCutHigh(cluster);
+				boolean passHitCount = singlesTrigger[triggerNum].clusterHitCountCut(cluster);
+				
+				// Make a trigger to store the results.
+				SinglesTrigger<SSPCluster> trigger = new SinglesTrigger<SSPCluster>(cluster);
+				trigger.setStateSeedEnergyLow(passSeedLow);
+				trigger.setStateSeedEnergyHigh(passSeedHigh);
+				trigger.setStateClusterEnergyLow(passClusterLow);
+				trigger.setStateClusterEnergyHigh(passClusterHigh);
+				trigger.setStateHitCount(passHitCount);
+				
+				// Store the trigger.
+				sspSinglesTriggers.get(triggerNum).add(trigger);
+			}
+		}
+		
+		// Run the reconstructed clusters through the singles trigger
+		// to determine whether they pass it or not.
+		for(Cluster cluster : reconClusters) {
+			for(int triggerNum = 0; triggerNum < 2; triggerNum++) {
+				// For a cluster to have formed it is assumed to have passed
+				// the cluster seed energy cuts. This can not be verified
+				// since the SSP bank does not report individual hit. 
+				boolean passSeedLow = true;
+				boolean passSeedHigh = true;
+				
+				// The remaining cuts may be acquired from trigger module.
+				boolean passClusterLow = singlesTrigger[triggerNum].clusterTotalEnergyCutLow(cluster);
+				boolean passClusterHigh = singlesTrigger[triggerNum].clusterTotalEnergyCutHigh(cluster);
+				boolean passHitCount = singlesTrigger[triggerNum].clusterHitCountCut(cluster);
+				
+				// Make a trigger to store the results.
+				SinglesTrigger<Cluster> trigger = new SinglesTrigger<Cluster>(cluster);
+				trigger.setStateSeedEnergyLow(passSeedLow);
+				trigger.setStateSeedEnergyHigh(passSeedHigh);
+				trigger.setStateClusterEnergyLow(passClusterLow);
+				trigger.setStateClusterEnergyHigh(passClusterHigh);
+				trigger.setStateHitCount(passHitCount);
+				
+				// Store the trigger.
+				reconSinglesTriggers.get(triggerNum).add(trigger);
+			}
+		}
+	}
+	
+	private static final String reconClusterToString(Cluster cluster) {
+		return String.format("Cluster at (%3d, %3d) with %.3f GeV and %d hits at %4.0f ns.",
+				cluster.getCalorimeterHits().get(0).getIdentifierFieldValue("ix"),
+				cluster.getCalorimeterHits().get(0).getIdentifierFieldValue("iy"),
+				cluster.getEnergy(), cluster.getCalorimeterHits().size(),
+				cluster.getCalorimeterHits().get(0).getTime());
+	}
+	
+	private static final String sspClusterToString(SSPCluster cluster) {
+		return String.format("Cluster at (%3d, %3d) with %.3f GeV and %d hits at %4d ns.",
+				cluster.getXIndex(), cluster.getYIndex(), cluster.getEnergy(),
+				cluster.getHitCount(), cluster.getTime());
+	}
+}