LISTSERV mailing list manager LISTSERV 16.5

Help for HPS-SVN Archives


HPS-SVN Archives

HPS-SVN Archives


HPS-SVN@LISTSERV.SLAC.STANFORD.EDU


View:

Message:

[

First

|

Previous

|

Next

|

Last

]

By Topic:

[

First

|

Previous

|

Next

|

Last

]

By Author:

[

First

|

Previous

|

Next

|

Last

]

Font:

Proportional Font

LISTSERV Archives

LISTSERV Archives

HPS-SVN Home

HPS-SVN Home

HPS-SVN  March 2016

HPS-SVN March 2016

Subject:

r4326 - in /java/trunk/analysis/src/main/java/org/hps/analysis/trigger: ./ util/

From:

[log in to unmask]

Reply-To:

Notification of commits to the hps svn repository <[log in to unmask]>

Date:

Wed, 23 Mar 2016 07:24:38 -0000

Content-Type:

text/plain

Parts/Attachments:

Parts/Attachments

text/plain (1963 lines)

Author: [log in to unmask]
Date: Wed Mar 23 00:24:36 2016
New Revision: 4326

Log:
Updated trigger diagnostics. Code is being split up and cleaned up to be more easily used by others, and also to eliminate unnecessary objects. Currently, new version performs full diagnostics, but is missing an output table and some plots. This is to be updated. Old diagnostics remain and are currently the active version until the new version is fully finalized.

Added:
    java/trunk/analysis/src/main/java/org/hps/analysis/trigger/ClusterDiagnosticModule.java   (with props)
    java/trunk/analysis/src/main/java/org/hps/analysis/trigger/DiagnosticsManagementDriver.java   (with props)
    java/trunk/analysis/src/main/java/org/hps/analysis/trigger/PairTriggerDiagnosticModule.java   (with props)
    java/trunk/analysis/src/main/java/org/hps/analysis/trigger/SinglesTriggerDiagnosticModule.java   (with props)
Modified:
    java/trunk/analysis/src/main/java/org/hps/analysis/trigger/SimTriggerData.java
    java/trunk/analysis/src/main/java/org/hps/analysis/trigger/SimTriggerModule.java
    java/trunk/analysis/src/main/java/org/hps/analysis/trigger/util/PairTrigger.java
    java/trunk/analysis/src/main/java/org/hps/analysis/trigger/util/SinglesTrigger.java
    java/trunk/analysis/src/main/java/org/hps/analysis/trigger/util/Trigger.java

Added: java/trunk/analysis/src/main/java/org/hps/analysis/trigger/ClusterDiagnosticModule.java
 =============================================================================
--- java/trunk/analysis/src/main/java/org/hps/analysis/trigger/ClusterDiagnosticModule.java	(added)
+++ java/trunk/analysis/src/main/java/org/hps/analysis/trigger/ClusterDiagnosticModule.java	Wed Mar 23 00:24:36 2016
@@ -0,0 +1,341 @@
+package org.hps.analysis.trigger;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.hps.analysis.trigger.data.ClusterMatchedPair;
+import org.hps.analysis.trigger.util.OutputLogger;
+import org.hps.analysis.trigger.util.TriggerDiagnosticUtil;
+import org.hps.record.triggerbank.SSPCluster;
+import org.hps.record.triggerbank.TriggerModule;
+import org.lcsim.event.CalorimeterHit;
+import org.lcsim.event.Cluster;
+
+/**
+ * 
+ * @author Kyle McCarty <[log in to unmask]>
+ */
+public class ClusterDiagnosticModule {
+    /** Indicates the number of hardware clusters processed. */
+    int hardwareClusterCount = 0;
+    /** Indicates the number of simulated clusters processed. */
+    int simulatedClusterCount = 0;
+    /** Indicates the number of hardware clusters that were not at risk
+     * of pulse-clipping which were processed. */
+    int goodSimulatedClusterCount = 0;
+    /** Indicates the number simulated/hardware cluster pairs that were
+     * successfully verified. */
+    int matchedClusters = 0;
+    /** Indicates the number simulated/hardware cluster pairs that did
+     * not verify because no two clusters were found with a matching
+     * time-stamp. */
+    int failedMatchTime = 0;
+    /** Indicates the number simulated/hardware cluster pairs that did
+     * not verify because no two clusters were found with energies within
+     * the allowed bounds of one another. */
+    int failedMatchEnergy = 0;
+    /** Indicates the number simulated/hardware cluster pairs that did
+     * not verify because no two clusters were found with hit counts
+     * within the allowed bounds of one another. */
+    int failedMatchHitCount = 0;
+    /** Indicates the number simulated/hardware cluster pairs that did
+     * not verify because no two clusters were found with a matching
+     * seed position. */
+    int failedMatchPosition = 0;
+    
+    /** Indicates whether or not all of the simulated clusters defined
+     * in the object (excluding those at risk of pulse-clipping) were
+     * successfully verified or not. A value of <code>true</code> means
+     * that all applicable clusters were verified, and <code>false</code>
+     * that at least one was not.*/
+    boolean clusterFail = false;
+    
+    /**
+     * 
+     * @param hits
+     * @param hardwareClusters
+     * @param simulatedClusters
+     * @param nsa
+     * @param nsb
+     * @param windowWidth
+     * @param hitVerificationThreshold
+     * @param energyVerificationThreshold
+     */
+    ClusterDiagnosticModule(List<CalorimeterHit> hits, List<SSPCluster> hardwareClusters, List<Cluster> simulatedClusters,
+            int nsa, int nsb, int windowWidth, int hitVerificationThreshold, double energyVerificationThreshold) {
+        // Output the clustering diagnostic header.
+        OutputLogger.printNewLine(2);
+        OutputLogger.println("======================================================================");
+        OutputLogger.println("==== Clustering Diagnostics ==========================================");
+        OutputLogger.println("======================================================================");
+        
+        // Output the FADC hits, hardware clusters, and simulated
+        // clusters to the diagnostic logger.
+        OutputLogger.printNewLine(2);
+        OutputLogger.println("==== Event Summary ===================================================");
+        OutputLogger.println("======================================================================");
+        
+        // Output the FADC hits.
+        OutputLogger.println("FADC Hits:");
+        for(CalorimeterHit hit : hits) {
+            int ix = hit.getIdentifierFieldValue("ix");
+            int iy = hit.getIdentifierFieldValue("iy");
+            OutputLogger.printf("\tHit at (%3d, %3d) with %7.3f GeV at time %3.0f ns%n", ix, iy, hit.getCorrectedEnergy(), hit.getTime());
+        }
+        if(hits.isEmpty()) { OutputLogger.println("\tNone"); }
+        
+        // Output the simulated clusters from the software.
+        OutputLogger.printNewLine(2);
+        OutputLogger.println("Software Clusters:");
+        for(Cluster cluster : simulatedClusters) {
+            OutputLogger.printf("\t%s%n", TriggerDiagnosticUtil.clusterToString(cluster));
+            for(CalorimeterHit hit : cluster.getCalorimeterHits()) {
+                int ix = hit.getIdentifierFieldValue("ix");
+                int iy = hit.getIdentifierFieldValue("iy");
+                OutputLogger.printf("\t\t> (%3d, %3d) :: %7.3f GeV%n", ix, iy, hit.getCorrectedEnergy());
+            }
+        }
+        if(simulatedClusters.isEmpty()) { OutputLogger.println("\tNone"); }
+        
+        // Output the reported clusters from the hardware.
+        OutputLogger.printNewLine(2);
+        OutputLogger.println("Hardware Clusters:");
+        for(SSPCluster cluster : hardwareClusters) {
+            OutputLogger.printf("\t%s%n", TriggerDiagnosticUtil.clusterToString(cluster));
+        }
+        if(hardwareClusters.isEmpty()) { OutputLogger.println("\tNone"); }
+        
+        // When hits are written to data by the FADC, the pulse height
+        // is only written within the bounds of the event window. Thus,
+        // if a hit is too close to the beginning or the end of the
+        // event window, it will experience "pulse-clipping" where the
+        // hit loses a part of its energy. Clusters containing these
+        // hits will often fail verification because the reduced energy
+        // despite this not indicating an actual problem. To avoid
+        // this, simulated clusters that are at risk of pulse-clipping
+        // are excluded from cluster verification.
+        OutputLogger.printNewLine(2);
+        OutputLogger.println("==== Pulse-Clipping Verification =====================================");
+        OutputLogger.println("======================================================================");
+        
+        // Iterate through each simulated cluster and keep only the
+        // clusters safe from pulse-clipping.
+        List<Cluster> goodSimulatedClusters = new ArrayList<Cluster>();
+        OutputLogger.printNewLine(2);
+        OutputLogger.println("Simulated Cluster Pulse-Clipping Check:");
+        for(Cluster cluster : simulatedClusters) {
+            boolean isSafe = TriggerDiagnosticUtil.isVerifiable(cluster, nsa, nsb, windowWidth);
+            if(isSafe) { goodSimulatedClusters.add(cluster); }
+            OutputLogger.printf("\t%s [ %7s ]%n", TriggerDiagnosticUtil.clusterToString(cluster),
+                    isSafe ? "Safe" : "Clipped");
+        }
+        if(goodSimulatedClusters.isEmpty()) { OutputLogger.println("\tNone"); }
+        
+        // Print the header for cluster verification.
+        OutputLogger.printNewLine(2);
+        OutputLogger.println("==== Cluster Accuracy Verification ===================================");
+        OutputLogger.println("======================================================================");
+        
+        // Generate a list of matched simulated/hardware cluster pairs
+        // and the verification status of each pair.
+        List<ClusterMatchedPair> matchedPairs = matchSimulatedToHardware(goodSimulatedClusters, hardwareClusters,
+                energyVerificationThreshold, hitVerificationThreshold);
+        
+        // Get the number of clusters of each type processed.
+        hardwareClusterCount = hardwareClusters.size();
+        simulatedClusterCount = simulatedClusters.size();
+        goodSimulatedClusterCount = goodSimulatedClusters.size();
+        
+        // Iterate over the list of pairs and extract statistical data
+        // for this set of clusters.
+        for(ClusterMatchedPair pair : matchedPairs) {
+            if(pair.isMatch()) { matchedClusters++; }
+            if(pair.isTimeFailState()) { failedMatchTime++; }
+            if(pair.isEnergyFailState()) { failedMatchEnergy++; }
+            if(pair.isHitCountFailState()) { failedMatchHitCount++; }
+            if(pair.isPositionFailState()) { failedMatchPosition++; }
+        }
+        
+        // The verification process is consider to fail when any not
+        // pulse-clipped simulated cluster fails to verify.
+        if(matchedPairs.size() != matchedClusters) { clusterFail = true; }
+        
+        // Output the results summary header.
+        OutputLogger.printNewLine(2);
+        OutputLogger.println("==== Cluster Verification Summary ====================================");
+        OutputLogger.println("======================================================================");
+        
+        // Output the cluster pairs that successfully verified.
+        OutputLogger.println();
+        OutputLogger.println("Verified Simulated/Hardware Cluster Pairs:");
+        if(matchedClusters != 0) {
+            for(ClusterMatchedPair pair : matchedPairs) {
+                if(pair.isMatch()) {
+                    OutputLogger.printf("\t%s --> %s%n",
+                            TriggerDiagnosticUtil.clusterToString(pair.getReconstructedCluster()),
+                            TriggerDiagnosticUtil.clusterToString(pair.getSSPCluster()));
+                }
+            }
+        } else { OutputLogger.println("\tNone"); }
+        
+        // Output the event statistics to the diagnostics logger.
+        OutputLogger.println();
+        OutputLogger.println("Event Statistics:");
+        OutputLogger.printf("\tRecon Clusters     :: %d%n", simulatedClusters.size());
+        OutputLogger.printf("\tClusters Matched   :: %d%n", matchedClusters);
+        OutputLogger.printf("\tFailed (Position)  :: %d%n", failedMatchPosition);
+        OutputLogger.printf("\tFailed (Time)      :: %d%n", failedMatchTime);
+        OutputLogger.printf("\tFailed (Energy)    :: %d%n", failedMatchEnergy);
+        OutputLogger.printf("\tFailed (Hit Count) :: %d%n", failedMatchHitCount);
+        if(goodSimulatedClusters.isEmpty()) {
+            OutputLogger.printf("\tCluster Efficiency :: N/A %n", 100.0 * matchedClusters / goodSimulatedClusters.size());
+        } else {
+            OutputLogger.printf("\tCluster Efficiency :: %3.0f%%%n", 100.0 * matchedClusters / goodSimulatedClusters.size());
+        }
+    }
+    
+    /**
+     * Performs cluster matching between a collection of simulated
+     * clusters and a collection of hardware clusters using the strictly
+     * time-compliant algorithm. Simulated clusters are matched with
+     * a hardware cluster by comparing the x- and y-indices of the two
+     * clusters, as well as their time-stamps. If all of these values
+     * match, the clusters are considered to be the same. The cluster
+     * then undergoes verification by requiring that both the cluster
+     * energies and hit counts are within a certain programmable range
+     * of one another. Matched clusters are then stored along with a
+     * flag that indicates whether they were properly verified or not.
+     * Simulated clusters that do not match any hardware cluster in both
+     * position and time are treated as failing to have verified.
+     * @param simulatedClusters - A collection of GTP clusters generated
+     * by the software simulation.
+     * @param hardwareClusters - A collection of GTP clusters reported
+     * in the SSP bank by the hardware.
+     * @param energyWindow - The window of allowed deviation between
+     * the simulated cluster and hardware cluster energies. Units are
+     * in GeV.
+     * @param hitWindow - The window of allowed deviation between
+     * the simulated cluster and hardware cluster hit counts.
+     * @return Returns a <code>List</code> containing all the matched
+     * simulated/hardware cluster pairs as well as their verification
+     * statuses.
+     */
+    private static final List<ClusterMatchedPair> matchSimulatedToHardware(Collection<Cluster> simulatedClusters,
+            Collection<SSPCluster> hardwareClusters, double energyWindow, int hitWindow) {
+        // Store the list of clusters, along with their matches (if
+        // applicable) and their pair verification status.
+        List<ClusterMatchedPair> pairList = new ArrayList<ClusterMatchedPair>();
+        
+        // Store the clusters which have been successfully paired.
+        Set<SSPCluster> hardwareMatched = new HashSet<SSPCluster>(hardwareClusters.size());
+        
+        // Find simulated/hardware cluster matched pairs.
+        simLoop:
+        for(Cluster simCluster : simulatedClusters) {
+            // Track whether a position-matched cluster was found.
+            boolean matchedPosition = false;
+            
+            // VERBOSE :: Output the cluster being matched.
+            OutputLogger.printf("Considering %s%n", TriggerDiagnosticUtil.clusterToString(simCluster));
+            
+            // Search through the hardware clusters for a match.
+            hardwareLoop:
+            for(SSPCluster hardwareCluster : hardwareClusters) {
+                // VERBOSE :: Output the hardware cluster being considered.
+                OutputLogger.printf("\t%s ", TriggerDiagnosticUtil.clusterToString(hardwareCluster));
+                
+                // Clusters must be matched in a one-to-one relationship,
+                // so clusters that have already been matched should
+                // be skipped.
+                if(hardwareMatched.contains(hardwareCluster)) {
+                    OutputLogger.printf("[ %7s; %9s ]%n", "fail", "matched");
+                    continue hardwareLoop;
+                }
+                
+                // If the clusters are the same, they must have the same
+                // x- and y-indices.
+                if(TriggerModule.getClusterXIndex(simCluster) != TriggerModule.getClusterXIndex(hardwareCluster)
+                        || TriggerModule.getClusterYIndex(simCluster) != TriggerModule.getClusterYIndex(hardwareCluster)) {
+                    OutputLogger.printf("[ %7s; %9s ]%n", "fail", "position");
+                    continue hardwareLoop;
+                }
+                
+                // Note that the cluster matched another cluster in
+                // position. This is used to determine why a cluster
+                // failed to verify, if necessary.
+                matchedPosition = true;
+                
+                // If the clusters are the same, they should occur at
+                // the same time as well.
+                if(TriggerModule.getClusterTime(simCluster) != TriggerModule.getClusterTime(hardwareCluster)) {
+                    OutputLogger.printf("[ %7s; %9s ]%n", "fail", "time");
+                    continue hardwareLoop;
+                }
+                
+                // It is impossible for two clusters to exist at the
+                // same place and the same time, so clusters that pass
+                // both the time comparison and position comparison are
+                // assumed to be the same.
+                hardwareMatched.add(hardwareCluster);
+                
+                // While time and position matched clusters are considered
+                // to be the same cluster, the clusters must have similar
+                // energies and hit counts to be properly verified. First
+                // perform the energy check. The hardware cluster must
+                // match the simulated cluster energy to within a given
+                // bound.
+                if(TriggerModule.getValueClusterTotalEnergy(hardwareCluster) >= TriggerModule.getValueClusterTotalEnergy(simCluster) - energyWindow
+                        && TriggerModule.getValueClusterTotalEnergy(hardwareCluster) <= TriggerModule.getValueClusterTotalEnergy(simCluster) + energyWindow) {
+                    // Next, check that the hardware cluster matches the
+                    // simulated cluster in hit count to within a given
+                    // bound.
+                    if(TriggerModule.getClusterHitCount(hardwareCluster) >= TriggerModule.getClusterHitCount(simCluster) - hitWindow &&
+                            TriggerModule.getClusterHitCount(hardwareCluster) <= TriggerModule.getClusterHitCount(simCluster) + hitWindow) {
+                        // The cluster is a match.
+                        pairList.add(new ClusterMatchedPair(simCluster, hardwareCluster, TriggerDiagnosticUtil.CLUSTER_STATE_MATCHED));
+                        OutputLogger.printf("[ %7s; %9s ]%n", "success", "matched");
+                        continue simLoop;
+                    }
+                    
+                    // If the hit counts of the two clusters are not
+                    // sufficiently close, the clusters fail to verify.
+                    else {
+                        pairList.add(new ClusterMatchedPair(simCluster, hardwareCluster, TriggerDiagnosticUtil.CLUSTER_STATE_FAIL_HIT_COUNT));
+                        OutputLogger.printf("[ %7s; %9s ]%n", "fail", "hit count");
+                        continue simLoop;
+                    } // End hit count check.
+                }
+                
+                // If the energies of the two clusters are not
+                // sufficiently close, the clusters fail to verify.
+                else {
+                    pairList.add(new ClusterMatchedPair(simCluster, hardwareCluster, TriggerDiagnosticUtil.CLUSTER_STATE_FAIL_ENERGY));
+                    OutputLogger.printf("[ %7s; %9s ]%n", "fail", "energy");
+                    continue simLoop;
+                } // End energy check.
+            } // End hardware loop.
+            
+            // This point may only be reached if a cluster failed to
+            // match with another cluster in either time or position.
+            // Check whether a cluster at the same position was found.
+            // If it was not, then the cluster fails to verify by reason
+            // of position.
+            if(!matchedPosition) {
+                pairList.add(new ClusterMatchedPair(simCluster, null, TriggerDiagnosticUtil.CLUSTER_STATE_FAIL_POSITION));
+            }
+            
+            // Otherwise, the cluster had a potential match found at
+            // the same position, but the time-stamps did not align.
+            else {
+                pairList.add(new ClusterMatchedPair(simCluster, null, TriggerDiagnosticUtil.CLUSTER_STATE_FAIL_TIME));
+            }
+        } // End sim loop.
+        
+        // Return the list of clusters, their matches, and their
+        // verification states.
+        return pairList;
+    }
+}

Added: java/trunk/analysis/src/main/java/org/hps/analysis/trigger/DiagnosticsManagementDriver.java
 =============================================================================
--- java/trunk/analysis/src/main/java/org/hps/analysis/trigger/DiagnosticsManagementDriver.java	(added)
+++ java/trunk/analysis/src/main/java/org/hps/analysis/trigger/DiagnosticsManagementDriver.java	Wed Mar 23 00:24:36 2016
@@ -0,0 +1,891 @@
+package org.hps.analysis.trigger;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.hps.analysis.trigger.util.OutputLogger;
+import org.hps.analysis.trigger.util.PairTrigger;
+import org.hps.analysis.trigger.util.SinglesTrigger;
+import org.hps.analysis.trigger.util.TriggerDiagnosticUtil;
+import org.hps.record.daqconfig.ConfigurationManager;
+import org.hps.record.daqconfig.DAQConfig;
+import org.hps.record.daqconfig.PairTriggerConfig;
+import org.hps.record.daqconfig.SinglesTriggerConfig;
+import org.hps.record.triggerbank.AbstractIntData;
+import org.hps.record.triggerbank.SSPCluster;
+import org.hps.record.triggerbank.SSPData;
+import org.hps.record.triggerbank.SSPPairTrigger;
+import org.hps.record.triggerbank.SSPSinglesTrigger;
+import org.hps.record.triggerbank.TIData;
+import org.hps.record.triggerbank.TriggerModule;
+import org.lcsim.event.CalorimeterHit;
+import org.lcsim.event.Cluster;
+import org.lcsim.event.EventHeader;
+import org.lcsim.event.GenericObject;
+import org.lcsim.util.Driver;
+
+public class DiagnosticsManagementDriver extends Driver {
+    // Track global statistical information.
+    private int events = 0;
+    private int noiseEvents = 0;
+    private int clusterFailEvents = 0;
+    private int singlesFailEvents = 0;
+    private int pairFailEvents = 0;
+    private int[][] tiEvents = new int[2][6];
+    
+    // Track global cluster statistical information.
+    private int simClusterCount = 0;
+    private int hardwareClusterCount = 0;
+    private int matchedClusters = 0;
+    private int matchClusterFailPosition = 0;
+    private int matchClusterFailHitCount = 0;
+    private int matchClusterFailEnergy = 0;
+    private int matchClusterFailTime = 0;
+    
+    // Track global trigger information.
+    private int[] allHardwareTriggerCount = new int[4];
+    private int[] allSimSimTriggerCount = new int[4];
+    private int[] allHardwareSimTriggerCount = new int[4];
+    private int[] allMatchedSimSimTriggers = new int[4];
+    private int[] allMatchedHardwareSimTriggers = new int[4];
+    private int[][] tiHardwareTriggerCount = new int[6][4];
+    private int[][] tiSimSimTriggerCount = new int[6][4];
+    private int[][] tiHardwareSimTriggerCount = new int[6][4];
+    private int[][] tiMatchedSimSimTriggers = new int[6][4];
+    private int[][] tiMatchedHardwareSimTriggers = new int[6][4];
+    
+    // Store the LCIO collection names for the needed objects.
+    private String hitCollectionName = "EcalCalHits";
+    private String bankCollectionName = "TriggerBank";
+    private String clusterCollectionName = "EcalClusters";
+    private String simTriggerCollectionName = "SimTriggers";
+    
+    // Trigger modules for performing trigger analysis.
+    //private int activeTrigger = -1;
+    private boolean[] tiFlags = new boolean[6];
+    private TriggerModule[] singlesTrigger = new TriggerModule[2];
+    private TriggerModule[] pairsTrigger = new TriggerModule[2];
+    private boolean[][] singlesCutsEnabled = new boolean[2][3];
+    private boolean[][] pairCutsEnabled = new boolean[2][7];
+    private boolean[] singlesTriggerEnabled = new boolean[2];
+    private boolean[] pairTriggerEnabled = new boolean[2];
+    
+    // Verification settings.
+    private int nsa = 100;
+    private int nsb = 20;
+    private int windowWidth = 200;
+    private int hitAcceptance = 1;
+    private int noiseThreshold = 50;
+    private double energyAcceptance = 0.003;
+    private int localWindowThreshold = 1000000000;
+    
+    // Verbose settings.
+    private boolean verbose = false;
+    private boolean printClusterFail = true;
+    private boolean printSinglesTriggerEfficiencyFail = true;
+    private boolean printSinglesTriggerInternalFail = true;
+    private boolean printPairTriggerEfficiencyFail = true;
+    private boolean printPairTriggerInternalFail = true;
+    private int     statPrintInterval = Integer.MAX_VALUE;
+    
+    // Cut index arrays for trigger verification.
+    private static final int ENERGY_MIN   = TriggerDiagnosticUtil.SINGLES_ENERGY_MIN;
+    private static final int ENERGY_MAX   = TriggerDiagnosticUtil.SINGLES_ENERGY_MAX;
+    private static final int HIT_COUNT    = TriggerDiagnosticUtil.SINGLES_HIT_COUNT;
+    private static final int ENERGY_SUM   = TriggerDiagnosticUtil.PAIR_ENERGY_SUM;
+    private static final int ENERGY_DIFF  = TriggerDiagnosticUtil.PAIR_ENERGY_DIFF;
+    private static final int ENERGY_SLOPE = TriggerDiagnosticUtil.PAIR_ENERGY_SLOPE;
+    private static final int COPLANARITY  = TriggerDiagnosticUtil.PAIR_COPLANARITY;
+    
+    // Create trigger index labels.
+    public static final int SINGLES0 = 0;
+    public static final int SINGLES1 = 1;
+    public static final int PAIR0    = 2;
+    public static final int PAIR1    = 3;
+    public static final int PULSER   = 4;
+    public static final int COSMIC   = 5;
+    
+    // Store the TI flag type labels.
+    private static final int TI_GENERAL     = 0;
+    private static final int TI_HIERARCHICAL = 1;
+    
+    // Track the total run time.
+    private long startTime = -1;
+    private long endTime = -1;
+    
+    /**
+     * Define the trigger modules. This should be replaced by parsing
+     * the DAQ configuration at some point.
+     */
+    @Override
+    public void startOfData() {
+        // By default, all triggers and cuts are enabled.
+        for(int i = 0; i < 2; i++) {
+            // Enable the triggers.
+            pairTriggerEnabled[i] = true;
+            singlesTriggerEnabled[i] = true;
+            
+            // Enable the singles cuts.
+            for(int j = 0; j < singlesCutsEnabled.length; j++) {
+                singlesCutsEnabled[i][j] = true;
+            }
+            
+            // Enable the pair cuts.
+            for(int j = 0; j < pairCutsEnabled.length; j++) {
+                pairCutsEnabled[i][j] = true;
+            }
+        }
+        
+        // If the DAQ configuration should be read, attach a listener
+        // to track when it updates.
+        ConfigurationManager.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                // Get the DAQ configuration.
+                DAQConfig daq = ConfigurationManager.getInstance();
+                
+                // Load the DAQ settings from the configuration manager.
+                singlesTrigger[0].loadDAQConfiguration(daq.getSSPConfig().getSingles1Config());
+                singlesTrigger[1].loadDAQConfiguration(daq.getSSPConfig().getSingles2Config());
+                pairsTrigger[0].loadDAQConfiguration(daq.getSSPConfig().getPair1Config());
+                pairsTrigger[1].loadDAQConfiguration(daq.getSSPConfig().getPair2Config());
+                nsa = daq.getFADCConfig().getNSA();
+                nsb = daq.getFADCConfig().getNSB();
+                windowWidth = daq.getFADCConfig().getWindowWidth();
+                
+                // Get the trigger configurations from the DAQ.
+                SinglesTriggerConfig[] singles = { daq.getSSPConfig().getSingles1Config(),
+                        daq.getSSPConfig().getSingles2Config() };
+                PairTriggerConfig[] pairs = { daq.getSSPConfig().getPair1Config(),
+                        daq.getSSPConfig().getPair2Config() };
+                
+                // Update the enabled/disabled statuses.
+                for(int i = 0; i < 2; i++) {
+                    // Set the trigger enabled status.
+                    pairTriggerEnabled[i] = pairs[i].isEnabled();
+                    singlesTriggerEnabled[i] = singles[i].isEnabled();
+                    
+                    // Set the singles cut statuses.
+                    singlesCutsEnabled[i][ENERGY_MIN] = singles[i].getEnergyMinCutConfig().isEnabled();
+                    singlesCutsEnabled[i][ENERGY_MAX] = singles[i].getEnergyMaxCutConfig().isEnabled();
+                    singlesCutsEnabled[i][HIT_COUNT] = singles[i].getHitCountCutConfig().isEnabled();
+                    
+                    // Set the pair cut statuses.
+                    pairCutsEnabled[i][ENERGY_MIN] = pairs[i].getEnergyMinCutConfig().isEnabled();
+                    pairCutsEnabled[i][ENERGY_MAX] = pairs[i].getEnergyMaxCutConfig().isEnabled();
+                    pairCutsEnabled[i][HIT_COUNT] = pairs[i].getHitCountCutConfig().isEnabled();
+                    pairCutsEnabled[i][3 + ENERGY_SUM] = pairs[i].getEnergySumCutConfig().isEnabled();
+                    pairCutsEnabled[i][3 + ENERGY_DIFF] = pairs[i].getEnergyDifferenceCutConfig().isEnabled();
+                    pairCutsEnabled[i][3 + ENERGY_SLOPE] = pairs[i].getEnergySlopeCutConfig().isEnabled();
+                    pairCutsEnabled[i][3 + COPLANARITY] = pairs[i].getCoplanarityCutConfig().isEnabled();
+                }
+                
+                // Print a DAQ configuration settings header.
+                System.out.println();
+                System.out.println();
+                System.out.println("======================================================================");
+                System.out.println("=== DAQ Configuration Settings =======================================");
+                System.out.println("======================================================================");
+                logSettings();
+            }
+        });
+        
+        // Print the cluster verification header.
+        System.out.println();
+        System.out.println();
+        System.out.println("======================================================================");
+        System.out.println("=== Cluster/Trigger Verification Settings ============================");
+        System.out.println("======================================================================");
+        
+        // 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);
+        pairsTrigger[0].setCutValue(TriggerModule.PAIR_TIME_COINCIDENCE, 8);
+        
+        // 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);
+        pairsTrigger[1].setCutValue(TriggerModule.PAIR_TIME_COINCIDENCE, 8);
+        
+        // Print the initial settings.
+        logSettings();
+    }
+    
+    /**
+     * Gets the banks and clusters from the event.
+     */
+    @Override
+    public void process(EventHeader event) {
+        // ==========================================================
+        // ==== Event Pre-Initialization ============================
+        // ==========================================================
+        
+        // If DAQ settings are to be used, check if they are initialized
+        // yet. If not, skip the event.
+        if(!ConfigurationManager.isInitialized()) {
+            return;
+        }
+        
+        // Print the statistics every so often during a run.
+        if(events % statPrintInterval == 0) {
+            logStatistics();
+        }
+        
+        // Reset the output buffer.
+        OutputLogger.clearLog();
+        
+        // Track the times.
+        if(startTime == -1) { startTime = event.getTimeStamp(); }
+        else { endTime = event.getTimeStamp(); }
+        
+        // Output the clustering diagnostic header.
+        OutputLogger.printNewLine(2);
+        OutputLogger.println("======================================================================");
+        OutputLogger.println("==== Trigger Diagnostics Event Analysis ==============================");
+        OutputLogger.println("======================================================================");
+        
+        
+        
+        // ==========================================================
+        // ==== Obtain SSP and TI Banks =============================
+        // ==========================================================
+        
+        // If there is no bank data, this event can not be analyzed.
+        if(!event.hasCollection(GenericObject.class, bankCollectionName)) {
+            System.err.println("TriggerDiagnostics :: Skipping event; no bank data found.");
+            if(verbose) { OutputLogger.printLog(); }
+            return;
+        }
+        
+        // Get the SSP and TI banks. The TI bank stores TI bits, which
+        // are used for trigger-type efficiency values. The SSP bank
+        // contains hardware triggers and hardware clusters.
+        TIData tiBank = null;
+        SSPData sspBank = null;
+        
+        // The TI and SSP banks are stored as generic objects. They
+        // must be extracted from the list and parsed into the correct
+        // format.
+        List<GenericObject> bankList = event.get(GenericObject.class, bankCollectionName);
+        for(GenericObject obj : bankList) {
+            // If this is an SSP bank, parse it into an SSP bank object.
+            if(AbstractIntData.getTag(obj) == SSPData.BANK_TAG) {
+                sspBank = new SSPData(obj);
+            }
+            
+            // Otherwise, if this is a TI bank, convert it into the
+            // correct object format and also extract the active TI bits.
+            else if(AbstractIntData.getTag(obj) == TIData.BANK_TAG) {
+                // Parse the TI bank object.
+                tiBank = new TIData(obj);
+                
+                // Store which TI bits are active.
+                tiFlags = new boolean[6];
+                boolean activeBitRead = false;
+                if(tiBank.isPulserTrigger()) {
+                    activeBitRead = true;
+                    tiFlags[PULSER] = true;
+                    OutputLogger.println("Trigger type :: Pulser");
+                } if(tiBank.isSingle0Trigger()) {
+                    activeBitRead = true;
+                    tiFlags[SINGLES0] = true;
+                    OutputLogger.println("Trigger type :: Singles 0");
+                } if(tiBank.isSingle1Trigger()) {
+                    activeBitRead = true;
+                    tiFlags[SINGLES1] = true;
+                    OutputLogger.println("Trigger type :: Singles 1");
+                } if(tiBank.isPair0Trigger()) {
+                    activeBitRead = true;
+                    tiFlags[PAIR0] = true;
+                    OutputLogger.println("Trigger type :: Pair 0");
+                } if(tiBank.isPair1Trigger()) {
+                    activeBitRead = true;
+                    tiFlags[PAIR1] = true;
+                    OutputLogger.println("Trigger type :: Pair 1");
+                } if(tiBank.isCalibTrigger()) {
+                    activeBitRead = true;
+                    tiFlags[COSMIC] = true;
+                    OutputLogger.println("Trigger type :: Cosmic");
+                } if(!activeBitRead) {
+                    System.err.println("TriggerDiagnostics: Skipping event; no TI trigger bits are active.");
+                    if(verbose) { OutputLogger.printLog(); }
+                    return;
+                }
+            }
+        }
+        
+        // Check that all of the required objects are present.
+        if(sspBank == null) {
+            System.err.println("TriggerDiagnostics: Skipping event; no SSP bank found for this event.");
+            if(verbose) { OutputLogger.printLog(); }
+            return;
+        } if(tiBank == null) {
+            System.err.println("TriggerDiagnostics: Skipping event; no TI bank found for this event.");
+            if(verbose) { OutputLogger.printLog(); }
+            return;
+        }
+        
+        // Output the event number and information.
+        OutputLogger.printf("Event Number %d (%d)%n", sspBank.getEventNumber(), event.getEventNumber());
+        
+        // Get the hardware clusters.
+        List<SSPCluster> hardwareClusters = sspBank.getClusters();
+        
+        
+        
+        // ==========================================================
+        // ==== Obtain Simulated Clusters ===========================
+        // ==========================================================
+        
+        // If the simulated cluster collection does not exist, analysis
+        // can not be performed.
+        if(!event.hasCollection(Cluster.class, clusterCollectionName)) {
+            System.err.println("TriggerDiagnostics: Skipping event; no simulated clusters found.");
+            if(verbose) { OutputLogger.printLog(); }
+            return;
+        }
+        
+        // Get the simulated clusters.
+        List<Cluster> simulatedClusters = event.get(Cluster.class, clusterCollectionName);
+        
+        
+        
+        // ==========================================================
+        // ==== Obtain Simulated Triggers ===========================
+        // ==========================================================
+        
+        // If the simulated trigger collection does not exist, analysis
+        // can not be performed.
+        if(!event.hasCollection(SimTriggerData.class, simTriggerCollectionName)) {
+            System.err.println("TriggerDiagnostics: Skipping event; no simulated triggers found.");
+            if(verbose) { OutputLogger.printLog(); }
+            return;
+        }
+        
+        // Get the simulated trigger module.
+        List<SimTriggerData> stdList = event.get(SimTriggerData.class, "SimTriggers");
+        SimTriggerData triggerData = stdList.get(0);
+        
+        
+        
+        // ==========================================================
+        // ==== Obtain Hit Collection / Check Noise Level ===========
+        // ==========================================================
+        
+        // If the FADC hit collection does not exist, analysis can
+        // not be performed.
+        if(!event.hasCollection(CalorimeterHit.class, hitCollectionName)) {
+            System.err.println("TriggerDiagnostics: Skipping event; no FADC hits found.");
+            if(verbose) { OutputLogger.printLog(); }
+            return;
+        }
+        
+        // Get the simulated clusters.
+        List<CalorimeterHit> fadcHits = event.get(CalorimeterHit.class, hitCollectionName);
+        
+        // Check if there are more hits than the noise threshold. If so,
+        // the event quality is too low to perform meaningful analysis.
+        if(fadcHits.size() >= noiseThreshold) {
+            events++;
+            noiseEvents++;
+            System.err.println("TriggerDiagnostics: Skipping event; noise event detected.");
+            if(verbose) { OutputLogger.printLog(); }
+            return;
+        }
+        
+        
+        
+        // ==========================================================
+        // ==== Perform Event Verification ==========================
+        // ==========================================================
+        
+        // Increment the number of events processed.
+        events++;
+        
+        // Perform cluster verification.
+        ClusterDiagnosticModule clusterVerification = new ClusterDiagnosticModule(fadcHits, hardwareClusters, simulatedClusters,
+                nsa, nsb, windowWidth, hitAcceptance, energyAcceptance);
+        
+        // Create a list in which to store singles triggers.
+        List<List<SSPSinglesTrigger>> singlesTriggers = new ArrayList<List<SSPSinglesTrigger>>();
+        singlesTriggers.add(new ArrayList<SSPSinglesTrigger>());
+        singlesTriggers.add(new ArrayList<SSPSinglesTrigger>());
+        
+        // Perform the singles trigger verification.
+        SinglesTriggerDiagnosticModule[] singlesVerification = new SinglesTriggerDiagnosticModule[2];
+        for(int trigNum = 0; trigNum < 2; trigNum++) {
+            // Extract only the correct singles triggers for this trigger
+            // number from the compiled list of hardware singles triggers.
+            for(SSPSinglesTrigger trigger : sspBank.getSinglesTriggers()) {
+                if(trigger.getTriggerNumber() == trigNum) { singlesTriggers.get(trigNum).add(trigger); }
+            }
+            
+            // Get the appropriate simulated trigger objects.
+            List<SinglesTrigger<Cluster>> simSimTriggers = triggerData.getSimSoftwareClusterTriggers().getSinglesTriggers(trigNum);
+            List<SinglesTrigger<SSPCluster>> simHardwareTriggers = triggerData.getSimHardwareClusterTriggers().getSinglesTriggers(trigNum);
+            
+            // Perform singles trigger verification.
+            singlesVerification[trigNum] = new SinglesTriggerDiagnosticModule(simSimTriggers, simHardwareTriggers,
+                    singlesTriggers.get(trigNum), "Singles " + trigNum, nsa, nsb, windowWidth);
+        }
+        
+        // Create a list in which to store pair triggers.
+        List<List<SSPPairTrigger>> pairTriggers = new ArrayList<List<SSPPairTrigger>>();
+        pairTriggers.add(new ArrayList<SSPPairTrigger>());
+        pairTriggers.add(new ArrayList<SSPPairTrigger>());
+        
+        // Perform the singles trigger verification.
+        PairTriggerDiagnosticModule[] pairVerification = new PairTriggerDiagnosticModule[2];
+        for(int trigNum = 0; trigNum < 2; trigNum++) {
+            // Extract only the correct pair triggers for this trigger
+            // number from the compiled list of hardware pair triggers.
+            for(SSPPairTrigger trigger : sspBank.getPairTriggers()) {
+                if(trigger.getTriggerNumber() == trigNum) { pairTriggers.get(trigNum).add(trigger); }
+            }
+            
+            // Get the appropriate simulated trigger objects.
+            List<PairTrigger<Cluster[]>> simSimTriggers = triggerData.getSimSoftwareClusterTriggers().getPairTriggers(trigNum);
+            List<PairTrigger<SSPCluster[]>> simHardwareTriggers = triggerData.getSimHardwareClusterTriggers().getPairTriggers(trigNum);
+            
+            // Perform singles trigger verification.
+            pairVerification[trigNum] = new PairTriggerDiagnosticModule(simSimTriggers, simHardwareTriggers,
+                    pairTriggers.get(trigNum), "Pair " + trigNum, nsa, nsb, windowWidth);
+        }
+        
+        
+        
+        // ==========================================================
+        // ==== Update Statistical Information ======================
+        // ==========================================================
+        
+        // Update general event failure rate.
+        if(clusterVerification.clusterFail) { clusterFailEvents++; }
+        if(singlesVerification[0].hardwareTriggerFail || singlesVerification[1].hardwareTriggerFail
+                || singlesVerification[0].simulatedTriggerFail || singlesVerification[1].simulatedTriggerFail) {
+            singlesFailEvents++;
+        }
+        if(pairVerification[0].hardwareTriggerFail || pairVerification[1].hardwareTriggerFail
+                || pairVerification[0].simulatedTriggerFail || pairVerification[1].simulatedTriggerFail) {
+            pairFailEvents++;
+        }
+        
+        // Update the TI event types. For the general count, it does
+        // not matter how many TI bits are active.
+        for(int i = 0; i < 6; i++) {
+            if(tiFlags[i]) { tiEvents[TI_GENERAL][i]++; }
+        }
+        
+        // For the hierarchical TI count, only the "highest" order bit
+        // is considered.
+        if(tiFlags[PAIR0]) { tiEvents[TI_HIERARCHICAL][PAIR0]++; }
+        else if(tiFlags[PAIR1]) { tiEvents[TI_HIERARCHICAL][PAIR1]++; }
+        else if(tiFlags[SINGLES0]) { tiEvents[TI_HIERARCHICAL][SINGLES0]++; }
+        else if(tiFlags[SINGLES1]) { tiEvents[TI_HIERARCHICAL][SINGLES1]++; }
+        else if(tiFlags[PULSER]) { tiEvents[TI_HIERARCHICAL][PULSER]++; }
+        else if(tiFlags[COSMIC]) { tiEvents[TI_HIERARCHICAL][COSMIC]++; }
+        
+        // Update the cluster statistical information.
+        simClusterCount += clusterVerification.goodSimulatedClusterCount;
+        hardwareClusterCount += clusterVerification.hardwareClusterCount;
+        matchedClusters += clusterVerification.matchedClusters;
+        matchClusterFailPosition += clusterVerification.failedMatchPosition;
+        matchClusterFailHitCount += clusterVerification.failedMatchHitCount;
+        matchClusterFailEnergy += clusterVerification.failedMatchEnergy;
+        matchClusterFailTime += clusterVerification.failedMatchTime;
+        
+        // Update trigger statistical information.
+        for(int trigNum = 0; trigNum < 2; trigNum++) {
+            // Store trigger information globally by trigger type.
+            // Start with the singles triggers.
+            allHardwareTriggerCount[trigNum == 0 ? SINGLES0 : SINGLES1] += singlesVerification[trigNum].hardwareTriggerCount;
+            allSimSimTriggerCount[trigNum == 0 ? SINGLES0 : SINGLES1] += singlesVerification[trigNum].simSimTriggerCount;
+            allHardwareSimTriggerCount[trigNum == 0 ? SINGLES0 : SINGLES1] += singlesVerification[trigNum].hardwareSimTriggerCount;
+            allMatchedSimSimTriggers[trigNum == 0 ? SINGLES0 : SINGLES1] += singlesVerification[trigNum].matchedSimSimTriggers;
+            allMatchedHardwareSimTriggers[trigNum == 0 ? SINGLES0 : SINGLES1] += singlesVerification[trigNum].matchedHardwareSimTriggers;
+            
+            // And repeat for the pair triggers.
+            allHardwareTriggerCount[trigNum == 0 ? PAIR0 : PAIR1] += pairVerification[trigNum].hardwareTriggerCount;
+            allSimSimTriggerCount[trigNum == 0 ? PAIR0 : PAIR1] += pairVerification[trigNum].simSimTriggerCount;
+            allHardwareSimTriggerCount[trigNum == 0 ? PAIR0 : PAIR1] += pairVerification[trigNum].hardwareSimTriggerCount;
+            allMatchedSimSimTriggers[trigNum == 0 ? PAIR0 : PAIR1] += pairVerification[trigNum].matchedSimSimTriggers;
+            allMatchedHardwareSimTriggers[trigNum == 0 ? PAIR0 : PAIR1] += pairVerification[trigNum].matchedHardwareSimTriggers;
+            
+            // In addition to globally, trigger information is also
+            // stored by TI bit.
+            for(int tiBit = 0; tiBit < 6; tiBit++) {
+                // Update the singles trigger statistical information.
+                tiHardwareTriggerCount[tiBit][trigNum == 0 ? SINGLES0 : SINGLES1] += singlesVerification[trigNum].hardwareTriggerCount;
+                tiSimSimTriggerCount[tiBit][trigNum == 0 ? SINGLES0 : SINGLES1] += singlesVerification[trigNum].simSimTriggerCount;
+                tiHardwareSimTriggerCount[tiBit][trigNum == 0 ? SINGLES0 : SINGLES1] += singlesVerification[trigNum].hardwareSimTriggerCount;
+                tiMatchedSimSimTriggers[tiBit][trigNum == 0 ? SINGLES0 : SINGLES1] += singlesVerification[trigNum].matchedSimSimTriggers;
+                tiMatchedHardwareSimTriggers[tiBit][trigNum == 0 ? SINGLES0 : SINGLES1] += singlesVerification[trigNum].matchedHardwareSimTriggers;
+                
+                // Update the pair trigger statistical information.
+                tiHardwareTriggerCount[tiBit][trigNum == 0 ? PAIR0 : PAIR1] += pairVerification[trigNum].hardwareTriggerCount;
+                tiSimSimTriggerCount[tiBit][trigNum == 0 ? PAIR0 : PAIR1] += pairVerification[trigNum].simSimTriggerCount;
+                tiHardwareSimTriggerCount[tiBit][trigNum == 0 ? PAIR0 : PAIR1] += pairVerification[trigNum].hardwareSimTriggerCount;
+                tiMatchedSimSimTriggers[tiBit][trigNum == 0 ? PAIR0 : PAIR1] += pairVerification[trigNum].matchedSimSimTriggers;
+                tiMatchedHardwareSimTriggers[tiBit][trigNum == 0 ? PAIR0 : PAIR1] += pairVerification[trigNum].matchedHardwareSimTriggers;
+            }
+        }
+        
+        
+        
+        // ==========================================================
+        // ==== Perform Event Write-Out =============================
+        // ==========================================================
+        
+        if(verbose ||(clusterVerification.clusterFail && printClusterFail) ||
+                ((singlesVerification[0].hardwareTriggerFail || singlesVerification[1].hardwareTriggerFail) && printSinglesTriggerInternalFail) ||
+                ((singlesVerification[0].simulatedTriggerFail || singlesVerification[1].simulatedTriggerFail) && printSinglesTriggerEfficiencyFail) ||
+                ((pairVerification[0].hardwareTriggerFail || pairVerification[1].hardwareTriggerFail) && printPairTriggerInternalFail) ||
+                ((pairVerification[0].simulatedTriggerFail || pairVerification[1].simulatedTriggerFail) && printPairTriggerEfficiencyFail)) {
+            OutputLogger.printLog();
+        }
+        
+        
+        
+        // ==========================================================
+        // ==== Process Local Tracked Variables =====================
+        // ==========================================================
+        // TODO: Re-implement local plotting.
+        /**
+        if(localStats.getDuration() > localWindowThreshold) {
+            // Write a snapshot of the driver to the event stream.
+            List<DiagnosticSnapshot> snapshotList = new ArrayList<DiagnosticSnapshot>(2);
+            snapshotList.add(localStats.getSnapshot());
+            snapshotList.add(globalStats.getSnapshot());
+            
+            // Push the snapshot to the data stream.
+            event.put(diagnosticCollectionName, snapshotList);
+            
+            // Store values needed to calculate efficiency.
+            int[] matched = {
+                    localStats.getClusterStats().getMatches(),
+                    localStats.getTriggerStats().getSingles0Stats().getMatchedReconSimulatedTriggers(),
+                    localStats.getTriggerStats().getSingles1Stats().getMatchedReconSimulatedTriggers(),
+                    localStats.getTriggerStats().getPair0Stats().getMatchedReconSimulatedTriggers(),
+                    localStats.getTriggerStats().getPair1Stats().getMatchedReconSimulatedTriggers()
+            };
+            int[] total = {
+                    localStats.getClusterStats().getReconClusterCount(),
+                    localStats.getTriggerStats().getSingles0Stats().getReconSimulatedTriggers(),
+                    localStats.getTriggerStats().getSingles1Stats().getReconSimulatedTriggers(),
+                    localStats.getTriggerStats().getPair0Stats().getReconSimulatedTriggers(),
+                    localStats.getTriggerStats().getPair1Stats().getReconSimulatedTriggers()
+            };
+            
+            // Calculate the efficiencies and upper/lower errors.
+            double[] efficiency = new double[5];
+            for(int i = 0; i < 5; i++) {
+                efficiency[i] = 1.0 * matched[i] / total[i];
+            }
+            
+            // Get the time for the current snapshot. This is the total
+            // run time before the snapshot plus half of the snapshot.
+            long time = globalStats.getDuration() - (localStats.getDuration() / 2);
+            
+            // Add them to the appropriate cloud plot.
+            for(int i = 0; i < 5; i++) { efficiencyTimeHist[i].fill(time, efficiency[i]); }
+            
+            // Clear the local statistical data.
+            localStats.clear();
+        }
+        **/
+    }
+    
+    /**
+     * Prints the total run statistics.
+     */
+    @Override
+    public void endOfData() {
+        // Output the statistics.
+        logStatistics();
+        
+        /*
+        // Calculate the values needed for the efficiency histogram.
+        long totalTime = entryList.get(entryList.size()).time / 1000000000;
+        int entries = (int) (totalTime / (localWindowThreshold / 1000000000)) + 1;
+        
+        // Generate a histogram containing the efficiencies.
+        IHistogram1D[] efficiencyHist = new IHistogram1D[5];
+        for(int i = 0; i < 5; i++) {
+            efficiencyHist[i] = aida.histogram1D("Efficiency " + i, entries, 0.0, totalTime + (localWindowThreshold / 1000000000));
+        }
+        
+        // Input the efficiencies.
+        for(EfficiencyEntry entry : entryList) {
+            for(int i = 0; i < 5; i++) {
+                efficiencyHist[i].fill(entry.time / 1000000000, entry.efficiency[i]);
+            }
+        }
+        */
+    }
+    
+    /**
+     * Outputs all of the verification parameters currently in use by
+     * the software. A warning will be issued if the values for NSA and
+     * NSB, along with the FADC window, preclude clusters from being
+     * verified.
+     */
+    private void logSettings() {
+        // Output general settings.
+        System.out.println("Cluster Verification Settings");
+        System.out.printf("\tHit Threshold          :: %1d hit(s)%n", hitAcceptance);
+        System.out.printf("\tEnergy Threshold       :: %5.3f GeV%n",  energyAcceptance);
+        System.out.println();
+        
+        // Output window settings.
+        System.out.println("FADC Timing Window Settings");
+        System.out.printf("\tNSB                    :: %3d ns%n", nsb);
+        System.out.printf("\tNSA                    :: %3d ns%n", nsa);
+        System.out.printf("\tFADC Window            :: %3d ns%n", windowWidth);
+        
+        // Calculate the valid clustering window.
+        int start = nsb;
+        int end = windowWidth - nsa;
+        System.out.printf("\tValid Cluster Window   :: [ %3d ns, %3d ns ]%n", start, end);
+        System.out.println();
+        
+        // Output the singles trigger settings.
+        for(int i = 0; i < 2; i++) {
+            // Print the settings.
+            System.out.printf("Singles Trigger %d Settings%23s[%5b]%n", (i + 1), "", singlesTriggerEnabled[i]);
+            System.out.printf("\tCluster Energy Low     :: %.3f GeV      [%5b]%n",
+                    singlesTrigger[i].getCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_LOW), singlesCutsEnabled[i][0]);
+            System.out.printf("\tCluster Energy High    :: %.3f GeV      [%5b]%n",
+                    singlesTrigger[i].getCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_HIGH), singlesCutsEnabled[i][1]);
+            System.out.printf("\tCluster Hit Count      :: %.0f hit(s)       [%5b]%n",
+                    singlesTrigger[i].getCutValue(TriggerModule.CLUSTER_HIT_COUNT_LOW), singlesCutsEnabled[i][2]);
+            System.out.println();
+        }
+        
+        // Output the pair trigger settings.
+        for(int i = 0; i < 2; i++) {
+            System.out.printf("Pairs Trigger %d Settings%25s[%5b]%n", (i + 1), "", pairTriggerEnabled[i]);
+            System.out.printf("\tCluster Energy Low     :: %.3f GeV      [%5b]%n",
+                    pairsTrigger[i].getCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_LOW), pairCutsEnabled[i][0]);
+            System.out.printf("\tCluster Energy High    :: %.3f GeV      [%5b]%n",
+                    pairsTrigger[i].getCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_HIGH), pairCutsEnabled[i][1]);
+            System.out.printf("\tCluster Hit Count      :: %.0f hit(s)       [%5b]%n",
+                    pairsTrigger[i].getCutValue(TriggerModule.CLUSTER_HIT_COUNT_LOW), pairCutsEnabled[i][2]);
+            System.out.printf("\tPair Energy Sum Low    :: %.3f GeV      [%5b]%n",
+                    pairsTrigger[i].getCutValue(TriggerModule.PAIR_ENERGY_SUM_LOW), pairCutsEnabled[i][3]);
+            System.out.printf("\tPair Energy Sum High   :: %.3f GeV      [%5b]%n",
+                    pairsTrigger[i].getCutValue(TriggerModule.PAIR_ENERGY_SUM_HIGH), pairCutsEnabled[i][3]);
+            System.out.printf("\tPair Energy Difference :: %.3f GeV      [%5b]%n",
+                    pairsTrigger[i].getCutValue(TriggerModule.PAIR_ENERGY_DIFFERENCE_HIGH), pairCutsEnabled[i][4]);
+            System.out.printf("\tPair Energy Slope      :: %.3f GeV      [%5b]%n",
+                    pairsTrigger[i].getCutValue(TriggerModule.PAIR_ENERGY_SLOPE_LOW), pairCutsEnabled[i][5]);
+            System.out.printf("\tPair Energy Slope F    :: %.4f GeV / mm%n",
+                    pairsTrigger[i].getCutValue(TriggerModule.PAIR_ENERGY_SLOPE_F));
+            System.out.printf("\tPair Coplanarity       :: %3.0f Degrees    [%5b]%n",
+                    pairsTrigger[i].getCutValue(TriggerModule.PAIR_COPLANARITY_HIGH), pairCutsEnabled[i][6]);
+            System.out.printf("\tPair Time Coincidence  :: %2.0f ns          [%5b]%n",
+                    pairsTrigger[i].getCutValue(TriggerModule.PAIR_TIME_COINCIDENCE), true);
+            System.out.println();
+        }
+    }
+    
+    /**
+     * Summarizes the global run statistics in a log to the terminal.
+     */
+    private void logStatistics() {
+        // Print the cluster/trigger verification header.
+        System.out.println();
+        System.out.println();
+        System.out.println("======================================================================");
+        System.out.println("=== Cluster/Trigger Verification Results =============================");
+        System.out.println("======================================================================");
+        
+        // Print the general event failure rate.
+        int headSpaces = getPrintSpaces(events);
+        System.out.println("General Event Statistics:");
+        System.out.printf("\tEvent Start Time      :: %.3f s%n", (startTime / Math.pow(10, 9)));
+        System.out.printf("\tEvent End Time        :: %.3f%n", (endTime / Math.pow(10, 9)));
+        System.out.printf("\tEvent Run Time        :: %.3f%n", ((endTime - startTime) / Math.pow(10, 9)));
+        System.out.printf("\tNoise Events          :: %" + headSpaces + "d / %" + headSpaces + "d (%7.3f%%)%n",
+                noiseEvents, events, (100.0 * noiseEvents / events));
+        System.out.printf("\tCluster Events Failed :: %" + headSpaces + "d / %" + headSpaces + "d (%7.3f%%)%n",
+                clusterFailEvents, events, (100.0 * clusterFailEvents / events));
+        System.out.printf("\tSingles Events Failed :: %" + headSpaces + "d / %" + headSpaces + "d (%7.3f%%)%n",
+                singlesFailEvents, events, (100.0 * singlesFailEvents / events));
+        System.out.printf("\tPair Events Failed    :: %" + headSpaces + "d / %" + headSpaces + "d (%7.3f%%)%n",
+                pairFailEvents, events, (100.0 * pairFailEvents / events));
+        
+        // Print out how many events reported a given TI type, both in
+        // total and hierarchically.
+        System.out.println();
+        System.out.println("Event Triggering Type Verification:");
+        System.out.printf("\t%15s\t%15s\t%15s%n", "Trigger", "Total", "Hierarchical");
+        System.out.printf("\t%15s\t%15s\t%15s%n", "Pulser",    tiEvents[TI_GENERAL][PULSER],   tiEvents[TI_HIERARCHICAL][PULSER]);
+        System.out.printf("\t%15s\t%15s\t%15s%n", "Cosmic",    tiEvents[TI_GENERAL][COSMIC],   tiEvents[TI_HIERARCHICAL][COSMIC]);
+        System.out.printf("\t%15s\t%15s\t%15s%n", "Singles 0", tiEvents[TI_GENERAL][SINGLES0], tiEvents[TI_HIERARCHICAL][SINGLES0]);
+        System.out.printf("\t%15s\t%15s\t%15s%n", "Singles 1", tiEvents[TI_GENERAL][SINGLES1], tiEvents[TI_HIERARCHICAL][SINGLES1]);
+        System.out.printf("\t%15s\t%15s\t%15s%n", "Pair 0",    tiEvents[TI_GENERAL][PAIR0],    tiEvents[TI_HIERARCHICAL][PAIR0]);
+        System.out.printf("\t%15s\t%15s\t%15s%n", "Pair 1",    tiEvents[TI_GENERAL][PAIR1],    tiEvents[TI_HIERARCHICAL][PAIR1]);
+        
+        // Print the cluster verification data.
+        System.out.println();
+        System.out.println("Cluster Verification:");
+        System.out.printf("\tSimulated Clusters    :: %d%n", simClusterCount);
+        System.out.printf("\tHardware Clusters     :: %d%n", hardwareClusterCount);
+        System.out.printf("\tClusters Matched      :: %d%n", matchedClusters);
+        System.out.printf("\tFailed (Position)     :: %d%n", matchClusterFailPosition);
+        System.out.printf("\tFailed (Time)         :: %d%n", matchClusterFailTime);
+        System.out.printf("\tFailed (Energy)       :: %d%n", matchClusterFailEnergy);
+        System.out.printf("\tFailed (Hit Count)    :: %d%n", matchClusterFailHitCount);
+        if(simClusterCount == 0) {
+            System.out.printf("\tCluster Efficiency    :: N/A%n");
+        } else {
+            System.out.printf("\tCluster Efficiency    :: %7.3f%%%n",
+                    100.0 * matchedClusters / simClusterCount);
+        }
+        
+        // Print the trigger verification data.
+        int[][] trigRef = {
+                { SINGLES0, SINGLES1 },
+                { PAIR0, PAIR1 }
+        };
+        for(int triggerType = 0; triggerType < 2; triggerType++) {
+            // Get the basic trigger data.
+            int sspSimTriggers = allHardwareSimTriggerCount[trigRef[triggerType][0]] + allHardwareSimTriggerCount[trigRef[triggerType][1]];
+            int reconSimTriggers = allSimSimTriggerCount[trigRef[triggerType][0]] + allSimSimTriggerCount[trigRef[triggerType][1]];
+            int sspReportedTriggers  = allHardwareTriggerCount[trigRef[triggerType][0]] + allHardwareTriggerCount[trigRef[triggerType][1]];
+            int sspMatchedTriggers   = allMatchedHardwareSimTriggers[trigRef[triggerType][0]] + allMatchedHardwareSimTriggers[trigRef[triggerType][1]];
+            int reconMatchedTriggers = allMatchedSimSimTriggers[trigRef[triggerType][0]] + allMatchedSimSimTriggers[trigRef[triggerType][1]];
+            
+            // Print the basic trigger statistics.
+            int spaces = getPrintSpaces(sspSimTriggers, reconSimTriggers, sspReportedTriggers);
+            System.out.println();
+            if(triggerType == 0) { System.out.println("Singles Trigger Verification:"); }
+            else { System.out.println("Pair Trigger Verification:"); }
+            System.out.printf("\tHardware Cluster Sim Triggers  :: %" + spaces + "d%n", sspSimTriggers);
+            System.out.printf("\tSimulated Cluster Sim Triggers :: %" + spaces + "d%n", reconSimTriggers);
+            System.out.printf("\tHardware Reported Triggers     :: %" + spaces + "d%n", sspReportedTriggers);
+            
+            System.out.printf("\tInternal Efficiency        :: %" + spaces + "d / %" + spaces + "d ", sspMatchedTriggers, sspSimTriggers);
+            if(sspSimTriggers == 0) { System.out.printf("(N/A)%n"); }
+            else { System.out.printf("(%7.3f%%)%n", (100.0 * sspMatchedTriggers / sspSimTriggers)); }
+            
+            System.out.printf("\tTrigger Efficiency         :: %" + spaces + "d / %" + spaces + "d ", reconMatchedTriggers, reconSimTriggers);
+            if(reconSimTriggers == 0) { System.out.printf("(N/A)%n"); }
+            else { System.out.printf("(%7.3f%%)%n" , (100.0 * reconMatchedTriggers / reconSimTriggers)); }
+        }
+        
+        // Print out the trigger efficiency table.
+        System.out.println();
+        // TODO: Re-implement efficiency table.
+        //globalStats.getTriggerStats().printEfficiencyTable();
+    }
+    
+    /**
+     * Determines the number of spaces needed to render the longest of
+     * a series of integers as a string.
+     * @param vals - The series of integers.
+     * @return Returns the number of spaces needed to render the longest
+     * integer as a base-10 string.
+     */
+    private static final int getPrintSpaces(int... vals) {
+        // Track the largest value.
+        int largest = 0;
+        
+        // Iterate over the arguments and find the largest.
+        for(int val : vals) {
+            // Get the length of the string.
+            int length = TriggerDiagnosticUtil.getDigits(val);
+            
+            // If it is larger, track it.
+            if(length > largest) { largest = length; }
+        }
+        
+        // Return the longer one.
+        return largest;
+    }
+
+    public void setPrintResultsEveryNEvents(int n) {
+        statPrintInterval = n;
+    }
+    
+    public void setPrintOnClusterFailure(boolean state) {
+        printClusterFail = state;
+    }
+    
+    public void setPrintOnSinglesEfficiencyFailure(boolean state) {
+        printSinglesTriggerEfficiencyFail = state;
+    }
+    
+    public void setPrintOnSinglesSSPFailure(boolean state) {
+        printSinglesTriggerInternalFail = state;
+    }
+    
+    public void setPrintOnPairEfficiencyFailure(boolean state) {
+        printPairTriggerEfficiencyFail = state;
+    }
+    
+    public void setPrintOnPairSSPFailure(boolean state) {
+        printPairTriggerInternalFail = state;
+    }
+    
+    public void setVerbose(boolean state) {
+        verbose = state;
+    }
+    
+    public void setHitCollectionName(String hitCollectionName) {
+        this.hitCollectionName = hitCollectionName;
+    }
+    
+    public void setClusterCollectionName(String clusterCollectionName) {
+        this.clusterCollectionName = clusterCollectionName;
+    }
+    
+    public void setBankCollectionName(String bankCollectionName) {
+        this.bankCollectionName = bankCollectionName;
+    }
+    
+    public void setNoiseThresholdCount(int noiseHits) {
+        noiseThreshold = noiseHits;
+    }
+    
+    public void setHitAcceptanceWindow(int window) {
+        hitAcceptance = window;
+    }
+    
+    public void setEnergyAcceptanceWindow(double window) {
+        energyAcceptance = window;
+    }
+    
+    public void setLocalWindowThresholdMilliseconds(int localWindowThreshold) {
+        this.localWindowThreshold = localWindowThreshold;
+    }
+}

Added: java/trunk/analysis/src/main/java/org/hps/analysis/trigger/PairTriggerDiagnosticModule.java
 =============================================================================
--- java/trunk/analysis/src/main/java/org/hps/analysis/trigger/PairTriggerDiagnosticModule.java	(added)
+++ java/trunk/analysis/src/main/java/org/hps/analysis/trigger/PairTriggerDiagnosticModule.java	Wed Mar 23 00:24:36 2016
@@ -0,0 +1,259 @@
+package org.hps.analysis.trigger;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.hps.analysis.trigger.util.OutputLogger;
+import org.hps.analysis.trigger.util.PairTrigger;
+import org.hps.analysis.trigger.util.TriggerDiagnosticUtil;
+import org.hps.record.triggerbank.SSPCluster;
+import org.hps.record.triggerbank.SSPPairTrigger;
+import org.hps.record.triggerbank.TriggerModule;
+import org.lcsim.event.Cluster;
+
+public class PairTriggerDiagnosticModule {
+    int hardwareTriggerCount = 0;
+    int simSimTriggerCount = 0;
+    int hardwareSimTriggerCount = 0;
+    int matchedSimSimTriggers = 0;
+    int matchedHardwareSimTriggers = 0;
+    
+    boolean hardwareTriggerFail = false;
+    boolean simulatedTriggerFail = false;
+    
+    PairTriggerDiagnosticModule(List<PairTrigger<Cluster[]>> simClusterSimTriggers, List<PairTrigger<SSPCluster[]>> hardwareClusterSimTriggers,
+            List<SSPPairTrigger> hardwareTriggers, String triggerName, int nsa, int nsb, int windowWidth) {
+        // Output the current trigger's diagnostic header.
+        OutputLogger.printNewLine(2);
+        OutputLogger.println("======================================================================");
+        StringBuffer nameBuffer = new StringBuffer("=== " + triggerName + " ");
+        while(nameBuffer.length() < 70) { nameBuffer.append('='); }
+        OutputLogger.println(nameBuffer.toString());
+        OutputLogger.println("======================================================================");
+        
+        // Print out the event summary sub-header.
+        OutputLogger.printNewLine(2);
+        OutputLogger.println("==== Event Summary ===================================================");
+        OutputLogger.println("======================================================================");
+        
+        // List the hardware triggers. Note that the source clusters
+        // are not retained for hardware triggers. Additionally, the
+        // singles cuts are not tracked; it assumed that they passed,
+        // as the trigger should not appear otherwise.
+        OutputLogger.printNewLine(2);
+        OutputLogger.println("Hardware Triggers:");
+        for(SSPPairTrigger trigger : hardwareTriggers) {
+            OutputLogger.printf("\t%s%n", "Source Cluster Unknown");
+            OutputLogger.printf("\t\tEnergy Sum        :: [ %5b ]%n", trigger.passCutEnergySum());
+            OutputLogger.printf("\t\tEnergy Difference :: [ %5b ]%n", trigger.passCutEnergyDifference());
+            OutputLogger.printf("\t\tEnergy Slope      :: [ %5b ]%n", trigger.passCutEnergySlope());
+            OutputLogger.printf("\t\tCoplanarity       :: [ %5b ]%n", trigger.passCutCoplanarity());
+        }
+        if(simClusterSimTriggers.isEmpty()) { OutputLogger.println("\tNone"); }
+        
+        // List the triggers simulated from simulated clusters.
+        OutputLogger.printNewLine(2);
+        OutputLogger.println("Simulated Triggers (Simulated Clusters):");
+        for(PairTrigger<Cluster[]> trigger : simClusterSimTriggers) {
+            if(TriggerModule.getClusterY(trigger.getTriggerSource()[0]) < 0) {
+                OutputLogger.printf("\t%s%n", TriggerDiagnosticUtil.clusterToString(trigger.getTriggerSource()[0]));
+            } else {
+                OutputLogger.printf("\t%s%n", TriggerDiagnosticUtil.clusterToString(trigger.getTriggerSource()[1]));
+            }
+            OutputLogger.printf("\t\tEnergy Sum        :: [ %5b ]%n", trigger.getStateEnergySum());
+            OutputLogger.printf("\t\tEnergy Difference :: [ %5b ]%n", trigger.getStateEnergyDifference());
+            OutputLogger.printf("\t\tEnergy Slope      :: [ %5b ]%n", trigger.getStateEnergySlope());
+            OutputLogger.printf("\t\tCoplanarity       :: [ %5b ]%n", trigger.getStateCoplanarity());
+        }
+        if(simClusterSimTriggers.isEmpty()) { OutputLogger.println("\tNone"); }
+        
+        // List the triggers simulated from hardware clusters.
+        OutputLogger.printNewLine(2);
+        OutputLogger.println("Simulated Triggers (Hardware Clusters):");
+        for(PairTrigger<SSPCluster[]> trigger : hardwareClusterSimTriggers) {
+            if(TriggerModule.getClusterY(trigger.getTriggerSource()[0]) < 0) {
+                OutputLogger.printf("\t%s%n", TriggerDiagnosticUtil.clusterToString(trigger.getTriggerSource()[0]));
+            } else {
+                OutputLogger.printf("\t%s%n", TriggerDiagnosticUtil.clusterToString(trigger.getTriggerSource()[1]));
+            }
+            OutputLogger.printf("\t\tEnergy Sum        :: [ %5b ]%n", trigger.getStateEnergySum());
+            OutputLogger.printf("\t\tEnergy Difference :: [ %5b ]%n", trigger.getStateEnergyDifference());
+            OutputLogger.printf("\t\tEnergy Slope      :: [ %5b ]%n", trigger.getStateEnergySlope());
+            OutputLogger.printf("\t\tCoplanarity       :: [ %5b ]%n", trigger.getStateCoplanarity());
+        }
+        if(simClusterSimTriggers.isEmpty()) { OutputLogger.println("\tNone"); }
+        
+        // note the number of hardware triggers.
+        hardwareTriggerCount = hardwareTriggers.size();
+        
+        // The first verification test for the trigger is that all
+        // triggers simulated from hardware clusters were actually
+        // found by the hardware.
+        hardwareSimTriggerCount = hardwareClusterSimTriggers.size();
+        compareSimulatedToHardware(hardwareClusterSimTriggers, hardwareTriggers, SSPCluster.class);
+        
+        // The next verification test is to see that all triggers
+        // produced from simulated clusters are also observed by the
+        // hardware. However, because of pulse-clipping, it is not a
+        // meaningful test to verify every simulated cluster trigger.
+        // Simulated cluster triggers with source clusters too near the
+        // edge of the event window must be excluded.
+        List<PairTrigger<Cluster[]>> goodSimClusterSimTriggers = new ArrayList<PairTrigger<Cluster[]>>();
+        for(PairTrigger<Cluster[]> simTrigger : simClusterSimTriggers) {
+            if(TriggerDiagnosticUtil.isVerifiable(simTrigger.getTriggerSource()[0], nsa, nsb, windowWidth)
+                    && TriggerDiagnosticUtil.isVerifiable(simTrigger.getTriggerSource()[1], nsa, nsb, windowWidth)) {
+                goodSimClusterSimTriggers.add(simTrigger);
+            }
+        }
+        
+        // The trigger verification can then be performed on the good
+        // simulated cluster triggers.
+        simSimTriggerCount = goodSimClusterSimTriggers.size();
+        compareSimulatedToHardware(goodSimClusterSimTriggers, hardwareTriggers, Cluster.class);
+        
+        // Trigger verification is considered to have failed if at
+        // least one trigger of a given type can not be verified.
+        if(matchedSimSimTriggers != simSimTriggerCount) { simulatedTriggerFail = true; }
+        if(matchedHardwareSimTriggers != hardwareSimTriggerCount) { hardwareTriggerFail = true; }
+        
+        // Print out the verification results header.
+        OutputLogger.printNewLine(2);
+        OutputLogger.println("==== Trigger Verification Results Summary ============================");
+        OutputLogger.println("======================================================================");
+        
+        // Output the event statistics to the diagnostics logger.
+        OutputLogger.println();
+        OutputLogger.println("Event Statistics:");
+        OutputLogger.printf("\tHardware Triggers              :: %d%n", hardwareTriggerCount);
+        OutputLogger.printf("\tSimulated Cluster Sim Triggers :: %d%n", simSimTriggerCount);
+        OutputLogger.printf("\tHardware Cluster Sim Triggers  :: %d%n", hardwareSimTriggerCount);
+        if(simClusterSimTriggers.isEmpty()) {
+            OutputLogger.printf("\tSimulated Cluster Sim Trigger :: N/A %n");
+        } else {
+            OutputLogger.printf("\tSimulated Cluster Sim Trigger Efficiency :: %3.0f%%%n", 100.0 * matchedSimSimTriggers / simSimTriggerCount);
+        }
+        if(hardwareClusterSimTriggers.isEmpty()) {
+            OutputLogger.printf("\tHardware Cluster Sim Trigger  :: N/A %n");
+        } else {
+            OutputLogger.printf("\tHardware Cluster Sim Trigger Efficiency  :: %3.0f%%%n", 100.0 * matchedHardwareSimTriggers / hardwareSimTriggerCount);
+        }
+    }
+    
+    private <E> void compareSimulatedToHardware(List<PairTrigger<E[]>> hardwareSimTriggers,
+            List<SSPPairTrigger> hardwareTriggers, Class<E> clusterType) {
+        // Print out the matching sub-header.
+        OutputLogger.printNewLine(2);
+        if(clusterType == Cluster.class) {
+            OutputLogger.println("==== Simulated Sim Trigger to Hardware Trigger Verification ==========");
+        } else {
+            OutputLogger.println("==== Hardware Sim Trigger to Hardware Trigger Verification ===========");
+        }
+        OutputLogger.println("======================================================================");
+        
+        // Trigger matches must be one-to-one. Track which hardware
+        // triggers are already matched do that they are not matched
+        // twice.
+        Set<SSPPairTrigger> matchedTriggers = new HashSet<SSPPairTrigger>();
+        
+        // Iterate over each trigger simulated from hardware clusters.
+        // It is expected that each of these triggers will correspond
+        // to an existing hardware trigger, since, purportedly, these
+        // are the same clusters from which the hardware triggers are
+        // generated.
+        simLoop:
+        for(PairTrigger<E[]> simTrigger : hardwareSimTriggers) {
+            // Since hardware triggers do not retain the triggering
+            // cluster, the only way to compare triggers is by the
+            // time stamp and the cut results. In order to be verified,
+            // a trigger must have all of these values match. The time
+            // of a singles trigger is the time of the cluster which
+            // created it.
+            double simTime;
+            String[] clusterText = new String[2];
+            E[] pair = simTrigger.getTriggerSource();
+            if(pair instanceof Cluster[]) {
+                if(TriggerModule.getClusterYIndex(((Cluster[]) pair)[0]) < 0) {
+                    simTime = TriggerModule.getClusterTime(((Cluster[]) pair)[0]);
+                } else { simTime = TriggerModule.getClusterTime(((Cluster[]) pair)[1]); }
+                clusterText[0] = TriggerDiagnosticUtil.clusterToString(((Cluster[]) pair)[0]);
+                clusterText[1] = TriggerDiagnosticUtil.clusterToString(((Cluster[]) pair)[1]);
+            } else {
+                if(TriggerModule.getClusterYIndex(((SSPCluster[]) pair)[0]) < 0) {
+                    simTime = TriggerModule.getClusterTime(((SSPCluster[]) pair)[0]);
+                } else { simTime = TriggerModule.getClusterTime(((SSPCluster[]) pair)[1]); }
+                clusterText[0] = TriggerDiagnosticUtil.clusterToString(((SSPCluster[]) pair)[0]);
+                clusterText[1] = TriggerDiagnosticUtil.clusterToString(((SSPCluster[]) pair)[1]);
+            }
+            
+            // Output the current trigger that is being matched.
+            OutputLogger.printf("Matching Trigger t = %3.0f; Sum: %5b; Diff: %5b; Slope: %5b; Coplanarity: %5b%n\t\t%s%n\t\t%s%n",
+                    simTime, simTrigger.getStateEnergySum(), simTrigger.getStateEnergyDifference(),
+                    simTrigger.getStateEnergySlope(), simTrigger.getStateCoplanarity(), clusterText[0], clusterText[1]);
+            
+            // Iterate over the hardware triggers and look for one that
+            // matches all of the simulated trigger's values.
+            hardwareLoop:
+            for(SSPPairTrigger hardwareTrigger : hardwareTriggers) {
+                // Output the comparison hardware trigger.
+                OutputLogger.printf("\tHardwareTrigger t = %3d; Sum: %5b; Diff: %5b; Slope: %5b; Coplanarity: %5b", hardwareTrigger.getTime(),
+                        true, hardwareTrigger.passCutEnergySum(), hardwareTrigger.passCutEnergyDifference(),
+                        hardwareTrigger.passCutEnergySlope(), hardwareTrigger.passCutCoplanarity());
+                
+                // If the current trigger has already been matched,
+                // then skip over it.
+                if(matchedTriggers.contains(hardwareTrigger)) {
+                    OutputLogger.printf(" [ fail; matched     ]%n");
+                    continue hardwareLoop;
+                }
+                
+                // The triggers must occur at the same time to classify
+                // as a match.
+                if(hardwareTrigger.getTime() != simTime) {
+                    OutputLogger.printf(" [ fail; time        ]%n");
+                    continue hardwareLoop;
+                }
+                
+                // Since there is no singles cuts data for the hardware
+                // trigger, we just assume that these cuts match. Move
+                // to the pair energy sum cut.
+                if(hardwareTrigger.passCutEnergySum() != simTrigger.getStateEnergySum()) {
+                    OutputLogger.printf(" [ fail; sum         ]%n");
+                    continue hardwareLoop;
+                }
+                
+                // Next, check the energy difference cut.
+                if(hardwareTrigger.passCutEnergyDifference() != simTrigger.getStateEnergyDifference()) {
+                    OutputLogger.printf(" [ fail; difference  ]%n");
+                    continue hardwareLoop;
+                }
+                
+                // Next, check the energy slope cut.
+                if(hardwareTrigger.passCutEnergySlope() != simTrigger.getStateEnergySlope()) {
+                    OutputLogger.printf(" [ fail; slope       ]%n");
+                    continue hardwareLoop;
+                }
+                
+                // Lastly, check the coplanarity cut.
+                if(hardwareTrigger.passCutCoplanarity() != simTrigger.getStateCoplanarity()) {
+                    OutputLogger.printf(" [ fail; coplanarity ]%n");
+                    continue hardwareLoop;
+                }
+                
+                // If all three values match, then these triggers are
+                // considered a match and verified.
+                OutputLogger.printf(" [ cluster verified  ]%n");
+                matchedTriggers.add(hardwareTrigger);
+                if(clusterType == Cluster.class) { matchedSimSimTriggers++; }
+                else { matchedHardwareSimTriggers++; }
+                continue simLoop;
+            }
+            
+            // If this point is reached, all possible hardware triggers
+            // have been checked and failed to match. This trigger then
+            // fails to verify.
+            OutputLogger.println("\t\tVerification failed!");
+        }
+    }
+}

Modified: java/trunk/analysis/src/main/java/org/hps/analysis/trigger/SimTriggerData.java
 =============================================================================
--- java/trunk/analysis/src/main/java/org/hps/analysis/trigger/SimTriggerData.java	(original)
+++ java/trunk/analysis/src/main/java/org/hps/analysis/trigger/SimTriggerData.java	Wed Mar 23 00:24:36 2016
@@ -7,34 +7,35 @@
  * Class <code>SimTriggerData</code> is a container class that holds
  * simulated trigger data modules. It is intended to be placed in the
  * LCIO data stream by the <code>DataTriggerSimDriver</code> to allow
- * other classes to access triggers simulated from SSP and reconstructed
+ * other classes to access triggers simulated from hardware and software
  * cluster data.
  * 
  * @author Kyle McCarty <[log in to unmask]>
  */
 public class SimTriggerData {
-    private final SimTriggerModule<Cluster> reconTriggers;
-    private final SimTriggerModule<SSPCluster> sspTriggers;
+    private final SimTriggerModule<Cluster> softwareClusterTriggers;
+    private final SimTriggerModule<SSPCluster> hardwareClusterTriggers;
     
     /**
      * Instantiates a new <code>SimTriggerData</code> object with empty
      * trigger results modules.
      */
     SimTriggerData() {
-        reconTriggers = new SimTriggerModule<Cluster>();
-        sspTriggers = new SimTriggerModule<SSPCluster>();
+        softwareClusterTriggers = new SimTriggerModule<Cluster>();
+        hardwareClusterTriggers = new SimTriggerModule<SSPCluster>();
     }
     
     /**
      * Instantiates a new <code>SimTriggerData</code> object that will
      * contain the argument trigger modules.
-     * @param reconTriggers - The simulated reconstructed cluster
-     * triggers module.
-     * @param sspTriggers - The simulated SSP cluster triggers module.
+     * @param softwareClusterTriggers - The module containing triggers
+     * simulated from software simulated clusters.
+     * @param hardwareClusterTriggers - The module containing triggers
+     * simulated from hardware reported clusters.
      */
-    SimTriggerData(SimTriggerModule<Cluster> reconTriggers, SimTriggerModule<SSPCluster> sspTriggers) {
-        this.reconTriggers = reconTriggers;
-        this.sspTriggers = sspTriggers;
+    SimTriggerData(SimTriggerModule<Cluster> softwareClusterTriggers, SimTriggerModule<SSPCluster> hardwareClusterTriggers) {
+        this.softwareClusterTriggers = softwareClusterTriggers;
+        this.hardwareClusterTriggers = hardwareClusterTriggers;
     }
     
     /**
@@ -43,8 +44,9 @@
      * @return Returns the trigger data in a <code>SimTriggerModule</code>
      * object.
      */
+    @Deprecated
     public SimTriggerModule<SSPCluster> getSimSSPTriggers() {
-        return sspTriggers;
+        return hardwareClusterTriggers;
     }
     
     /**
@@ -53,7 +55,28 @@
      * @return Returns the trigger data in a <code>SimTriggerModule</code>
      * object.
      */
+    @Deprecated
     public SimTriggerModule<Cluster> getSimReconTriggers() {
-        return reconTriggers;
+        return softwareClusterTriggers;
+    }
+    
+    /**
+     * Gets the module containing all triggers simulated from hardware
+     * reported clusters for each of the four production triggers.
+     * @return Returns the trigger data in a <code>SimTriggerModule</code>
+     * object.
+     */
+    public SimTriggerModule<SSPCluster> getSimHardwareClusterTriggers() {
+        return hardwareClusterTriggers;
+    }
+    
+    /**
+     * Gets the module containing all triggers simulated from software
+     * simulated clusters for each of the four production triggers.
+     * @return Returns the trigger data in a <code>SimTriggerModule</code>
+     * object.
+     */
+    public SimTriggerModule<Cluster> getSimSoftwareClusterTriggers() {
+        return softwareClusterTriggers;
     }
 }

Modified: java/trunk/analysis/src/main/java/org/hps/analysis/trigger/SimTriggerModule.java
 =============================================================================
--- java/trunk/analysis/src/main/java/org/hps/analysis/trigger/SimTriggerModule.java	(original)
+++ java/trunk/analysis/src/main/java/org/hps/analysis/trigger/SimTriggerModule.java	Wed Mar 23 00:24:36 2016
@@ -51,6 +51,28 @@
     }
     
     /**
+     * Gets the simulated trigger results for the indicated singles
+     * trigger. Note that only inputs of <code>0</code> and <code>1</code>
+     * are allowed.
+     * @param triggerNumber - A value of either <code>0</code>, to
+     * obtain the singles 0 trigger results, or <code>1</code>, to
+     * obtain the singles 1 trigger results.
+     * @return Returns the trigger results as a <code>List</code> of
+     * <code>SinglesTrigger</code> objects.
+     * @throws IllegalArgumentException Occurs if the input argument
+     * is not either <code>0</code> or <code>1</code>.
+     */
+    public List<SinglesTrigger<E>> getSinglesTriggers(int triggerNumber) {
+        // Return the appropriate trigger list.
+        if(triggerNumber == 0) { return getSingles0Triggers(); }
+        else if(triggerNumber == 1) { return getSingles1Triggers(); }
+        
+        // Any other trigger number is not valid and should produce an
+        // exception.
+        throw new IllegalArgumentException("Trigger number " + triggerNumber + " is not valid.");
+    }
+    
+    /**
      * Gets the simulated trigger results for the singles 0 trigger.
      * @return Returns the trigger results as a <code>List</code> of
      * <code>SinglesTrigger</code> objects.
@@ -66,6 +88,28 @@
      */
     public List<SinglesTrigger<E>> getSingles1Triggers() {
         return singles1;
+    }
+    
+    /**
+     * Gets the simulated trigger results for the indicated pair trigger.
+     * Note that only inputs of <code>0</code> and <code>1</code> are
+     * allowed.
+     * @param triggerNumber - A value of either <code>0</code>, to
+     * obtain the pair 0 trigger results, or <code>1</code>, to obtain
+     * the pair 1 trigger results.
+     * @return Returns the trigger results as a <code>List</code> of
+     * <code>PairTrigger</code> objects.
+     * @throws IllegalArgumentException Occurs if the input argument
+     * is not either <code>0</code> or <code>1</code>.
+     */
+    public List<PairTrigger<E[]>> getPairTriggers(int triggerNumber) {
+        // Return the appropriate trigger list.
+        if(triggerNumber == 0) { return getPair0Triggers(); }
+        else if(triggerNumber == 1) { return getPair1Triggers(); }
+        
+        // Any other trigger number is not valid and should produce an
+        // exception.
+        throw new IllegalArgumentException("Trigger number " + triggerNumber + " is not valid.");
     }
     
     /**
@@ -85,4 +129,4 @@
     public List<PairTrigger<E[]>> getPair1Triggers() {
         return pair1;
     }
-}
+}

Added: java/trunk/analysis/src/main/java/org/hps/analysis/trigger/SinglesTriggerDiagnosticModule.java
 =============================================================================
--- java/trunk/analysis/src/main/java/org/hps/analysis/trigger/SinglesTriggerDiagnosticModule.java	(added)
+++ java/trunk/analysis/src/main/java/org/hps/analysis/trigger/SinglesTriggerDiagnosticModule.java	Wed Mar 23 00:24:36 2016
@@ -0,0 +1,234 @@
+package org.hps.analysis.trigger;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.hps.analysis.trigger.util.OutputLogger;
+import org.hps.analysis.trigger.util.SinglesTrigger;
+import org.hps.analysis.trigger.util.TriggerDiagnosticUtil;
+import org.hps.record.triggerbank.SSPCluster;
+import org.hps.record.triggerbank.SSPSinglesTrigger;
+import org.hps.record.triggerbank.TriggerModule;
+import org.lcsim.event.Cluster;
+
+public class SinglesTriggerDiagnosticModule {
+    int hardwareTriggerCount = 0;
+    int simSimTriggerCount = 0;
+    int hardwareSimTriggerCount = 0;
+    int matchedSimSimTriggers = 0;
+    int matchedHardwareSimTriggers = 0;
+    
+    boolean hardwareTriggerFail = false;
+    boolean simulatedTriggerFail = false;
+    
+    SinglesTriggerDiagnosticModule(List<SinglesTrigger<Cluster>> simClusterSimTriggers, List<SinglesTrigger<SSPCluster>> hardwareClusterSimTriggers,
+            List<SSPSinglesTrigger> hardwareTriggers, String triggerName, int nsa, int nsb, int windowWidth) {
+        // Output the current trigger's diagnostic header.
+        OutputLogger.printNewLine(2);
+        OutputLogger.println("======================================================================");
+        StringBuffer nameBuffer = new StringBuffer("=== " + triggerName + " ");
+        while(nameBuffer.length() < 70) { nameBuffer.append('='); }
+        OutputLogger.println(nameBuffer.toString());
+        OutputLogger.println("======================================================================");
+        
+        // Print out the event summary sub-header.
+        OutputLogger.printNewLine(2);
+        OutputLogger.println("==== Event Summary ===================================================");
+        OutputLogger.println("======================================================================");
+        
+        // Only keep simulated cluster triggers which have source clusters
+        // which occurred within the non-pulse-clipped region. Clusters
+        // from outside this region are can not be relied upon to have
+        // accurate energies, and thusly to have the correct pass/fail
+        // result for most trigger cuts.
+        List<SinglesTrigger<Cluster>> goodSimClusterSimTriggers = new ArrayList<SinglesTrigger<Cluster>>();
+        
+        // List the hardware triggers. Note that the source clusters
+        // are not retained for hardware triggers. Additionally, the
+        // seed energy cut is not tracked; it assumed that it passed,
+        // as the trigger should not appear otherwise. If clusters are
+        // appearing that should not, the error should be caught during
+        // cluster verification.
+        OutputLogger.printNewLine(2);
+        OutputLogger.println("Hardware Triggers:");
+        for(SSPSinglesTrigger trigger : hardwareTriggers) {
+            OutputLogger.printf("\t%s%n", "Source Cluster Unknown");
+            OutputLogger.printf("\t\tSeed Energy    :: [ %5b ]%n", true);
+            OutputLogger.printf("\t\tCluster Energy :: [ %5b ]%n", (trigger.passCutEnergyMin() && trigger.passCutEnergyMax()));
+            OutputLogger.printf("\t\tHit Count      :: [ %5b ]%n", trigger.passCutHitCount());
+        }
+        if(simClusterSimTriggers.isEmpty()) { OutputLogger.println("\tNone"); }
+        
+        // List the triggers simulated from simulated clusters.
+        OutputLogger.printNewLine(2);
+        OutputLogger.println("Simulated Triggers (Simulated Clusters):");
+        for(SinglesTrigger<Cluster> trigger : simClusterSimTriggers) {
+            // Output the cluster summary.
+            OutputLogger.printf("\t%s%n", TriggerDiagnosticUtil.clusterToString(trigger.getTriggerSource()));
+            OutputLogger.printf("\t\tSeed Energy    :: [ %5b ]%n", trigger.getStateSeedEnergy());
+            OutputLogger.printf("\t\tCluster Energy :: [ %5b ]%n", trigger.getStateClusterEnergy());
+            OutputLogger.printf("\t\tHit Count      :: [ %5b ]%n", trigger.getStateHitCount());
+            
+            // Restrict the triggers to only those that are outside
+            // the pulse clipping region.
+            if(TriggerDiagnosticUtil.isVerifiable(trigger.getTriggerSource(), nsa, nsb, windowWidth)) {
+                goodSimClusterSimTriggers.add(trigger);
+                OutputLogger.printf("\t\tVerifiable     :: [ %5b ]%n", true);
+            } else { OutputLogger.printf("\t\tVerifiable     :: [ %5b ]%n", false); }
+            
+        }
+        if(simClusterSimTriggers.isEmpty()) { OutputLogger.println("\tNone"); }
+        
+        // List the triggers simulated from hardware clusters.
+        OutputLogger.printNewLine(2);
+        OutputLogger.println("Simulated Triggers (Hardware Clusters):");
+        for(SinglesTrigger<SSPCluster> trigger : hardwareClusterSimTriggers) {
+            OutputLogger.printf("\t%s%n", TriggerDiagnosticUtil.clusterToString(trigger.getTriggerSource()));
+            OutputLogger.printf("\t\tSeed Energy    :: [ %5b ]%n", trigger.getStateSeedEnergy());
+            OutputLogger.printf("\t\tCluster Energy :: [ %5b ]%n", trigger.getStateClusterEnergy());
+            OutputLogger.printf("\t\tHit Count      :: [ %5b ]%n", trigger.getStateHitCount());
+        }
+        if(simClusterSimTriggers.isEmpty()) { OutputLogger.println("\tNone"); }
+        
+        // note the number of hardware triggers.
+        hardwareTriggerCount = hardwareTriggers.size();
+        
+        // The first verification test for the trigger is that all
+        // triggers simulated from hardware clusters were actually
+        // found by the hardware.
+        hardwareSimTriggerCount = hardwareClusterSimTriggers.size();
+        compareSimulatedToHardware(hardwareClusterSimTriggers, hardwareTriggers, SSPCluster.class);
+        
+        // The trigger verification can then be performed on the good
+        // simulated cluster triggers.
+        simSimTriggerCount = goodSimClusterSimTriggers.size();
+        compareSimulatedToHardware(goodSimClusterSimTriggers, hardwareTriggers, Cluster.class);
+        
+        // Trigger verification is considered to have failed if at
+        // least one trigger of a given type can not be verified.
+        if(matchedSimSimTriggers != simSimTriggerCount) { simulatedTriggerFail = true; }
+        if(matchedHardwareSimTriggers != hardwareSimTriggerCount) { hardwareTriggerFail = true; }
+        
+        // Print out the verification results header.
+        OutputLogger.printNewLine(2);
+        OutputLogger.println("==== Trigger Verification Results Summary ============================");
+        OutputLogger.println("======================================================================");
+        
+        // Output the event statistics to the diagnostics logger.
+        OutputLogger.println();
+        OutputLogger.println("Event Statistics:");
+        OutputLogger.printf("\tHardware Triggers              :: %d%n", hardwareTriggerCount);
+        OutputLogger.printf("\tSimulated Cluster Sim Triggers :: %d%n", simSimTriggerCount);
+        OutputLogger.printf("\tHardware Cluster Sim Triggers  :: %d%n", hardwareSimTriggerCount);
+        if(simClusterSimTriggers.isEmpty()) {
+            OutputLogger.printf("\tSimulated Cluster Sim Trigger :: N/A %n");
+        } else {
+            OutputLogger.printf("\tSimulated Cluster Sim Trigger Efficiency :: %3.0f%%%n", 100.0 * matchedSimSimTriggers / simSimTriggerCount);
+        }
+        if(hardwareClusterSimTriggers.isEmpty()) {
+            OutputLogger.printf("\tHardware Cluster Sim Trigger  :: N/A %n");
+        } else {
+            OutputLogger.printf("\tHardware Cluster Sim Trigger Efficiency  :: %3.0f%%%n", 100.0 * matchedHardwareSimTriggers / hardwareSimTriggerCount);
+        }
+    }
+    
+    private <E> void compareSimulatedToHardware(List<SinglesTrigger<E>> hardwareSimTriggers,
+            List<SSPSinglesTrigger> hardwareTriggers, Class<E> clusterType) {
+        // Print out the matching sub-header.
+        OutputLogger.printNewLine(2);
+        if(clusterType == Cluster.class) {
+            OutputLogger.println("==== Simulated Sim Trigger to Hardware Trigger Verification ==========");
+        } else {
+            OutputLogger.println("==== Hardware Sim Trigger to Hardware Trigger Verification ===========");
+        }
+        OutputLogger.println("======================================================================");
+        
+        // Trigger matches must be one-to-one. Track which hardware
+        // triggers are already matched do that they are not matched
+        // twice.
+        Set<SSPSinglesTrigger> matchedTriggers = new HashSet<SSPSinglesTrigger>();
+        
+        // Iterate over each trigger simulated from hardware clusters.
+        // It is expected that each of these triggers will correspond
+        // to an existing hardware trigger, since, purportedly, these
+        // are the same clusters from which the hardware triggers are
+        // generated.
+        simLoop:
+        for(SinglesTrigger<E> simTrigger : hardwareSimTriggers) {
+            // Since hardware triggers do not retain the triggering
+            // cluster, the only way to compare triggers is by the
+            // time stamp and the cut results. In order to be verified,
+            // a trigger must have all of these values match. The time
+            // of a singles trigger is the time of the cluster which
+            // created it.
+            double simTime;
+            String clusterText;
+            E cluster = simTrigger.getTriggerSource();
+            if(cluster instanceof Cluster) {
+                simTime = TriggerModule.getClusterTime((Cluster) cluster);
+                clusterText = TriggerDiagnosticUtil.clusterToString((Cluster) cluster);
+            } else {
+                simTime = TriggerModule.getClusterTime((SSPCluster) cluster);
+                clusterText = TriggerDiagnosticUtil.clusterToString((SSPCluster) cluster);
+            }
+            
+            // Output the current trigger that is being matched.
+            OutputLogger.printf("Matching Trigger t = %3.0f; Seed: %5b; Cluster: %5b; Hit: %5b%n\t\t%s%n",
+                    simTime, simTrigger.getStateSeedEnergy(), simTrigger.getStateClusterEnergy(), simTrigger.getStateHitCount(),
+                    clusterText);
+            
+            // Iterate over the hardware triggers and look for one that
+            // matches all of the simulated trigger's values.
+            hardwareLoop:
+            for(SSPSinglesTrigger hardwareTrigger : hardwareTriggers) {
+                // Output the comparison hardware trigger.
+                OutputLogger.printf("\tHardwareTrigger t = %3d; Seed: %5b; Cluster: %5b; Hit: %5b", hardwareTrigger.getTime(),
+                        true, (hardwareTrigger.passCutEnergyMin() && hardwareTrigger.passCutEnergyMax()), hardwareTrigger.passCutHitCount());
+                
+                // If the current trigger has already been matched,
+                // then skip over it.
+                if(matchedTriggers.contains(hardwareTrigger)) {
+                    OutputLogger.printf(" [ fail; matched    ]%n");
+                    continue hardwareLoop;
+                }
+                
+                // The triggers must occur at the same time to classify
+                // as a match.
+                if(hardwareTrigger.getTime() != simTime) {
+                    OutputLogger.printf(" [ fail; time       ]%n");
+                    continue hardwareLoop;
+                }
+                
+                // Since there is no seed hit information for the
+                // hardware cluster, we just assume that this cut
+                // matches. Instead, move to the cluster total energy
+                // cut.
+                if((hardwareTrigger.passCutEnergyMin() && hardwareTrigger.passCutEnergyMax()) != simTrigger.getStateClusterEnergy()) {
+                    OutputLogger.printf(" [ fail; energy     ]%n");
+                    continue hardwareLoop;
+                }
+                
+                // Lastly, check if the cluster hit count cut matches.
+                if(hardwareTrigger.passCutHitCount() != simTrigger.getStateHitCount()) {
+                    OutputLogger.printf(" [ fail; hit count  ]%n");
+                    continue hardwareLoop;
+                }
+                
+                // If all three values match, then these triggers are
+                // considered a match and verified.
+                OutputLogger.printf(" [ cluster verified ]%n");
+                matchedTriggers.add(hardwareTrigger);
+                if(clusterType == Cluster.class) { matchedSimSimTriggers++; }
+                else { matchedHardwareSimTriggers++; }
+                continue simLoop;
+            }
+            
+            // If this point is reached, all possible hardware triggers
+            // have been checked and failed to match. This trigger then
+            // fails to verify.
+            OutputLogger.println("\t\tVerification failed!");
+        }
+    }
+}

Modified: java/trunk/analysis/src/main/java/org/hps/analysis/trigger/util/PairTrigger.java
 =============================================================================
--- java/trunk/analysis/src/main/java/org/hps/analysis/trigger/util/PairTrigger.java	(original)
+++ java/trunk/analysis/src/main/java/org/hps/analysis/trigger/util/PairTrigger.java	Wed Mar 23 00:24:36 2016
@@ -163,4 +163,4 @@
                 getStateEnergySumHigh() ? 1 : 0, getStateEnergyDifference() ? 1 : 0,
                 getStateEnergySlope() ? 1 : 0, getStateCoplanarity() ? 1 : 0);
     }
-}
+}

Modified: java/trunk/analysis/src/main/java/org/hps/analysis/trigger/util/SinglesTrigger.java
 =============================================================================
--- java/trunk/analysis/src/main/java/org/hps/analysis/trigger/util/SinglesTrigger.java	(original)
+++ java/trunk/analysis/src/main/java/org/hps/analysis/trigger/util/SinglesTrigger.java	Wed Mar 23 00:24:36 2016
@@ -150,4 +150,4 @@
                 getStateClusterEnergyLow() ? 1 : 0, getStateClusterEnergyHigh() ? 1 : 0,
                 getStateHitCount() ? 1 : 0);
     }
-}
+}

Modified: java/trunk/analysis/src/main/java/org/hps/analysis/trigger/util/Trigger.java
 =============================================================================
--- java/trunk/analysis/src/main/java/org/hps/analysis/trigger/util/Trigger.java	(original)
+++ java/trunk/analysis/src/main/java/org/hps/analysis/trigger/util/Trigger.java	Wed Mar 23 00:24:36 2016
@@ -149,4 +149,4 @@
     protected boolean supportsCut(String cut) {
         return passMap.containsKey(cut);
     }
-}
+}

Top of Message | Previous Page | Permalink

Advanced Options


Options

Log In

Log In

Get Password

Get Password


Search Archives

Search Archives


Subscribe or Unsubscribe

Subscribe or Unsubscribe


Archives

November 2017
August 2017
July 2017
January 2017
December 2016
November 2016
October 2016
September 2016
August 2016
July 2016
June 2016
May 2016
April 2016
March 2016
February 2016
January 2016
December 2015
November 2015
October 2015
September 2015
August 2015
July 2015
June 2015
May 2015
April 2015
March 2015
February 2015
January 2015
December 2014
November 2014
October 2014
September 2014
August 2014
July 2014
June 2014
May 2014
April 2014
March 2014
February 2014
January 2014
December 2013
November 2013

ATOM RSS1 RSS2



LISTSERV.SLAC.STANFORD.EDU

Secured by F-Secure Anti-Virus CataList Email List Search Powered by the LISTSERV Email List Manager

Privacy Notice, Security Notice and Terms of Use