Print

Print


Author: [log in to unmask]
Date: Thu Mar 19 10:30:08 2015
New Revision: 2491

Log:
Updated trigger diagnostic functionality and fixed a statistics tracking bug. Additionally, added in support for diagnostic trigger plots and their management.

Added:
    java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerPlotsModule.java
Modified:
    java/trunk/analysis/src/main/java/org/hps/analysis/trigger/TriggerDiagnosticDriver.java
    java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerMatchEvent.java
    java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerMatchStatus.java
    java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerMatchedPair.java
    java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerStatModule.java

Modified: java/trunk/analysis/src/main/java/org/hps/analysis/trigger/TriggerDiagnosticDriver.java
 =============================================================================
--- java/trunk/analysis/src/main/java/org/hps/analysis/trigger/TriggerDiagnosticDriver.java	(original)
+++ java/trunk/analysis/src/main/java/org/hps/analysis/trigger/TriggerDiagnosticDriver.java	Thu Mar 19 10:30:08 2015
@@ -22,6 +22,7 @@
 import org.hps.analysis.trigger.event.TriggerEfficiencyModule;
 import org.hps.analysis.trigger.event.TriggerMatchEvent;
 import org.hps.analysis.trigger.event.TriggerMatchStatus;
+import org.hps.analysis.trigger.event.TriggerPlotsModule;
 import org.hps.analysis.trigger.util.OutputLogger;
 import org.hps.analysis.trigger.util.Pair;
 import org.hps.analysis.trigger.util.PairTrigger;
@@ -125,7 +126,14 @@
 	private static final int ENERGY_SLOPE = TriggerDiagnosticUtil.PAIR_ENERGY_SLOPE;
 	private static final int COPLANARITY  = TriggerDiagnosticUtil.PAIR_COPLANARITY;
     
+	// Cut names for logging.
+	private static final String[][] cutNames = {
+			{ "E_min", "E_max", "hit count", "null" },
+			{ "E_sum", "E_diff", "E_slope", "coplanar" }
+	};
+	
 	// Temporary AIDA Plots
+	private TriggerPlotsModule globalTriggerPlots = new TriggerPlotsModule(0, 0);
 	private static final int RECON   = 0;
 	private static final int SSP     = 1;
 	private static final int ALL     = 0;
@@ -134,58 +142,57 @@
 	private AIDA aida = AIDA.defaultInstance();
 	private IHistogram1D[][] clusterHitPlot = {
 			{
-				aida.histogram1D("Trigger Diagnostics :: Recon Cluster Hit Count (All)",     9, 0.5, 9.5),
-				aida.histogram1D("Trigger Diagnostics :: Recon Cluster Hit Count (Matched)", 9, 0.5, 9.5),
-				aida.histogram1D("Trigger Diagnostics :: Recon Cluster Hit Count (Failed)",  9, 0.5, 9.5)
+				aida.histogram1D("cluster/Recon Cluster Hit Count (All)",     9, 0.5, 9.5),
+				aida.histogram1D("cluster/Recon Cluster Hit Count (Matched)", 9, 0.5, 9.5),
+				aida.histogram1D("cluster/Recon Cluster Hit Count (Failed)",  9, 0.5, 9.5)
 			},
 			{
-				aida.histogram1D("Trigger Diagnostics :: SSP Cluster Hit Count (All)",     9, 0.5, 9.5),
-				aida.histogram1D("Trigger Diagnostics :: SSP Cluster Hit Count (Matched)", 9, 0.5, 9.5),
-				aida.histogram1D("Trigger Diagnostics :: SSP Cluster Hit Count (Failed)",  9, 0.5, 9.5)
+				aida.histogram1D("cluster/SSP Cluster Hit Count (All)",     9, 0.5, 9.5),
+				aida.histogram1D("cluster/SSP Cluster Hit Count (Matched)", 9, 0.5, 9.5),
+				aida.histogram1D("cluster/SSP Cluster Hit Count (Failed)",  9, 0.5, 9.5)
 			}
 	};
 	private IHistogram1D[][] clusterEnergyPlot = {
 			{
-				aida.histogram1D("Trigger Diagnostics :: Recon Cluster Energy (All)",     300, 0.0, 3.0),
-				aida.histogram1D("Trigger Diagnostics :: Recon Cluster Energy (Matched)", 300, 0.0, 3.0),
-				aida.histogram1D("Trigger Diagnostics :: Recon Cluster Energy (Failed)",  300, 0.0, 3.0)
+				aida.histogram1D("cluster/Recon Cluster Energy (All)",     300, 0.0, 3.0),
+				aida.histogram1D("cluster/Recon Cluster Energy (Matched)", 300, 0.0, 3.0),
+				aida.histogram1D("cluster/Recon Cluster Energy (Failed)",  300, 0.0, 3.0)
 			},
 			{
-				aida.histogram1D("Trigger Diagnostics :: SSP Cluster Energy (All)",     300, 0.0, 3.0),
-				aida.histogram1D("Trigger Diagnostics :: SSP Cluster Energy (Matched)", 300, 0.0, 3.0),
-				aida.histogram1D("Trigger Diagnostics :: SSP Cluster Energy (Failed)",  300, 0.0, 3.0)
+				aida.histogram1D("cluster/SSP Cluster Energy (All)",     300, 0.0, 3.0),
+				aida.histogram1D("cluster/SSP Cluster Energy (Matched)", 300, 0.0, 3.0),
+				aida.histogram1D("cluster/SSP Cluster Energy (Failed)",  300, 0.0, 3.0)
 			}
 	};
 	private IHistogram1D[][] clusterTimePlot = {
 			{
-				aida.histogram1D("Trigger Diagnostics :: Recon Cluster Time (All)",     115, 0, 460),
-				aida.histogram1D("Trigger Diagnostics :: Recon Cluster Time (Matched)", 115, 0, 460),
-				aida.histogram1D("Trigger Diagnostics :: Recon Cluster Time (Failed)",  115, 0, 460)
+				aida.histogram1D("cluster/Recon Cluster Time (All)",     115, 0, 460),
+				aida.histogram1D("cluster/Recon Cluster Time (Matched)", 115, 0, 460),
+				aida.histogram1D("cluster/Recon Cluster Time (Failed)",  115, 0, 460)
 			},
 			{
-				aida.histogram1D("Trigger Diagnostics :: SSP Cluster Time (All)",     115, 0, 460),
-				aida.histogram1D("Trigger Diagnostics :: SSP Cluster Time (Matched)", 115, 0, 460),
-				aida.histogram1D("Trigger Diagnostics :: SSP Cluster Time (Failed)",  115, 0, 460)
+				aida.histogram1D("cluster/SSP Cluster Time (All)",     115, 0, 460),
+				aida.histogram1D("cluster/SSP Cluster Time (Matched)", 115, 0, 460),
+				aida.histogram1D("cluster/SSP Cluster Time (Failed)",  115, 0, 460)
 			}
 	};
 	private IHistogram2D[][] clusterPositionPlot = {
 			{
-				aida.histogram2D("Trigger Diagnostics :: Recon Cluster Position (All)",     47, -23.5, 23.5, 11, -5.5, 5.5),
-				aida.histogram2D("Trigger Diagnostics :: Recon Cluster Position (Matched)", 47, -23.5, 23.5, 11, -5.5, 5.5),
-				aida.histogram2D("Trigger Diagnostics :: Recon Cluster Position (Failed)",  47, -23.5, 23.5, 11, -5.5, 5.5)
+				aida.histogram2D("cluster/Recon Cluster Position (All)",     47, -23.5, 23.5, 11, -5.5, 5.5),
+				aida.histogram2D("cluster/Recon Cluster Position (Matched)", 47, -23.5, 23.5, 11, -5.5, 5.5),
+				aida.histogram2D("cluster/Recon Cluster Position (Failed)",  47, -23.5, 23.5, 11, -5.5, 5.5)
 			},
 			{
-				aida.histogram2D("Trigger Diagnostics :: SSP Cluster Position (All)",     47, -23.5, 23.5, 11, -5.5, 5.5),
-				aida.histogram2D("Trigger Diagnostics :: SSP Cluster Position (Matched)", 47, -23.5, 23.5, 11, -5.5, 5.5),
-				aida.histogram2D("Trigger Diagnostics :: SSP Cluster Position (Failed)",  47, -23.5, 23.5, 11, -5.5, 5.5)
+				aida.histogram2D("cluster/SSP Cluster Position (All)",     47, -23.5, 23.5, 11, -5.5, 5.5),
+				aida.histogram2D("cluster/SSP Cluster Position (Matched)", 47, -23.5, 23.5, 11, -5.5, 5.5),
+				aida.histogram2D("cluster/SSP Cluster Position (Failed)",  47, -23.5, 23.5, 11, -5.5, 5.5)
 			}
 	};
 	private IHistogram2D[] energyhitDiffPlot = {
-		aida.histogram2D("Trigger Diagnostics :: Recon-SSP Energy-Hit Difference (All)",     60, -0.060, 0.060, 6, -3, 3),
-		aida.histogram2D("Trigger Diagnostics :: Recon-SSP Energy-Hit Difference (Matched)", 60, -0.060, 0.060, 6, -3, 3),
-		aida.histogram2D("Trigger Diagnostics :: Recon-SSP Energy-Hit Difference (Failed)",  60, -0.060, 0.060, 6, -3, 3)
+		aida.histogram2D("cluster/Recon-SSP Energy-Hit Difference (All)",     21, -0.010, 0.010, 6, -3, 3),
+		aida.histogram2D("cluster/Recon-SSP Energy-Hit Difference (Matched)", 21, -0.010, 0.010, 6, -3, 3),
+		aida.histogram2D("cluster/Recon-SSP Energy-Hit Difference (Failed)",  21, -0.010, 0.010, 6, -3, 3)
 	};
-	
 	
 	/**
 	 * Define the trigger modules. This should be replaced by parsing
@@ -218,6 +225,10 @@
 				public void actionPerformed(ActionEvent e) {
 					// Get the DAQ configuration.
 					DAQConfig daq = ConfigurationManager.getInstance();
+					
+					// Update the plotting energy slope values.
+					globalTriggerPlots.setEnergySlopeParamF(0, daq.getSSPConfig().getPair1Config().getEnergySlopeCutConfig().getParameterF());
+					globalTriggerPlots.setEnergySlopeParamF(1, daq.getSSPConfig().getPair2Config().getEnergySlopeCutConfig().getParameterF());
 					
 					// Load the DAQ settings from the configuration manager.
 					singlesTrigger[0].loadDAQConfiguration(daq.getSSPConfig().getSingles1Config());
@@ -346,7 +357,33 @@
 		System.out.printf("\tPair Events Failed    :: %d / %d (%7.3f%%)%n",
 				failedPairEvents, totalEvents, (100.0 * failedPairEvents / totalEvents));
 		
+		// Print out how many events were triggered by a type along
+		// with how many were verified.
+		System.out.println();
+		System.out.println("Event Failure Rate:");
+		System.out.printf("\tSingles Trigger 1     :: %d / %d",
+				triggerRunStats[0].getEventsOfTypeSeen(0), triggerRunStats[0].getEventsOfType(0));
+		if(triggerRunStats[0].getEventsOfType(0) != 0) {
+			System.out.printf(" %d / %d (%7.3f%%)%n", (100.0 * triggerRunStats[0].getEventsOfTypeSeen(0) / triggerRunStats[0].getEventsOfTypeSeen(0)));
+		} else { System.out.println(); }
+		System.out.printf("\tSingles Trigger 2     :: %d / %d",
+				triggerRunStats[0].getEventsOfTypeSeen(1), triggerRunStats[0].getEventsOfType(1));
+		if(triggerRunStats[0].getEventsOfType(0) != 0) {
+			System.out.printf(" %d / %d (%7.3f%%)%n", (100.0 * triggerRunStats[0].getEventsOfTypeSeen(1) / triggerRunStats[0].getEventsOfTypeSeen(1)));
+		} else { System.out.println(); }
+		System.out.printf("\tPair Trigger 1        :: %d / %d",
+				triggerRunStats[1].getEventsOfTypeSeen(0), triggerRunStats[0].getEventsOfType(0));
+		if(triggerRunStats[1].getEventsOfType(0) != 0) {
+			System.out.printf(" %d / %d (%7.3f%%)%n", (100.0 * triggerRunStats[1].getEventsOfTypeSeen(0) / triggerRunStats[1].getEventsOfTypeSeen(0)));
+		} else { System.out.println(); }
+		System.out.printf("\tPair Trigger 2        :: %d / %d",
+				triggerRunStats[1].getEventsOfTypeSeen(1), triggerRunStats[0].getEventsOfType(1));
+		if(triggerRunStats[1].getEventsOfType(0) != 0) {
+			System.out.printf(" %d / %d (%7.3f%%)%n", (100.0 * triggerRunStats[1].getEventsOfTypeSeen(1) / triggerRunStats[1].getEventsOfTypeSeen(1)));
+		} else { System.out.println(); }
+		
 		// Print the cluster verification data.
+		System.out.println();
 		System.out.println("Cluster Verification:");
 		System.out.printf("\tRecon Clusters        :: %d%n", clusterRunStats.getReconClusterCount());
 		System.out.printf("\tSSP Clusters          :: %d%n", clusterRunStats.getSSPClusterCount());
@@ -389,6 +426,7 @@
 				System.out.println();
 				System.out.printf("\tTrigger %d Individual Cut Failure Rate:%n", (triggerNum + 1));
 				if(sspTriggerCount == 0) {
+					System.out.printf("\t\tUmatched Triggers          :: %" + spaces + "d%n", triggerRunStats[0].getUnmatchedTriggers(triggerNum));
 						System.out.printf("\t\tCluster Energy Lower Bound :: %" + spaces + "d / %" + spaces + "d%n",
 								triggerRunStats[0].getCutFailures(triggerNum, ENERGY_MIN), sspTriggerCount);
 						System.out.printf("\t\tCluster Energy Upper Bound :: %" + spaces + "d / %" + spaces + "d%n",
@@ -396,6 +434,7 @@
 						System.out.printf("\t\tCluster Hit Count          :: %" + spaces + "d / %" + spaces + "d%n",
 								triggerRunStats[0].getCutFailures(triggerNum, HIT_COUNT), sspTriggerCount);
 					} else {
+						System.out.printf("\t\tUmatched Triggers          :: %" + spaces + "d%n", triggerRunStats[0].getUnmatchedTriggers(triggerNum));
 						System.out.printf("\t\tCluster Energy Lower Bound :: %" + spaces + "d / %" + spaces + "d (%7.3f%%)%n",
 								triggerRunStats[0].getCutFailures(triggerNum, ENERGY_MIN), sspTriggerCount,
 								(100.0 * triggerRunStats[0].getCutFailures(triggerNum, ENERGY_MIN) / sspTriggerCount));
@@ -413,6 +452,7 @@
 					System.out.println();
 					System.out.printf("\tTrigger %d Individual Cut Failure Rate:%n", (triggerNum + 1));
 					if(sspTriggerCount == 0) {
+						System.out.printf("\t\tUmatched Triggers          :: %" + spaces + "d%n", triggerRunStats[1].getUnmatchedTriggers(triggerNum));
 						System.out.printf("\t\tPair Energy Sum            :: %" + spaces + "d / %" + spaces + "d%n",
 								triggerRunStats[1].getCutFailures(triggerNum, ENERGY_SUM), sspTriggerCount);
 						System.out.printf("\t\tPair Energy Difference     :: %" + spaces + "d / %" + spaces + "d%n",
@@ -422,6 +462,7 @@
 						System.out.printf("\t\tPair Coplanarity           :: %" + spaces + "d / %" + spaces + "d%n",
 								triggerRunStats[1].getCutFailures(triggerNum, COPLANARITY), sspTriggerCount);
 					} else {
+						System.out.printf("\t\tUmatched Triggers          :: %" + spaces + "d%n", triggerRunStats[1].getUnmatchedTriggers(triggerNum));
 						System.out.printf("\t\tPair Energy Sum            :: %" + spaces + "d / %" + spaces + "d (%7.3f%%)%n",
 								triggerRunStats[1].getCutFailures(triggerNum, ENERGY_SUM), sspTriggerCount,
 								(100.0 * triggerRunStats[1].getCutFailures(triggerNum, ENERGY_SUM) / sspTriggerCount));
@@ -555,6 +596,9 @@
 					} else if(tiBank.isCalibTrigger()) {
 						OutputLogger.println("Trigger type :: Cosmic");
 						activeTrigger = TriggerDiagnosticUtil.TRIGGER_COSMIC;
+					} else {
+						System.err.println("TriggerDiagnosticDriver: Skipping event; no TI trigger source found.");
+						return;
 					}
 				}
 			}
@@ -568,6 +612,12 @@
 					OutputLogger.printf("%d SSP clusters found.%n", sspClusters.size());
 				}
 			}
+		}
+		
+		// Make sure that both an SSP bank and a TI bank were found.
+		if(tiBank == null || sspBank == null) {
+			System.err.println("TriggerDiagnosticDriver :: SEVERE WARNING :: TI bank or SSP bank missing from event!");
+			return;
 		}
 		
 		
@@ -858,12 +908,6 @@
 			}
 		}
 		 else { OutputLogger.println("\tNone"); }
-		
-		// Get the number of position failures.
-		//int failPosition = event.getPositionFailures();
-		//if(sspClusters == null || sspClusters.isEmpty()) {
-		//	failPosition = (reconClusters == null ? 0 : reconClusters.size());
-		//}
 		
 		// Print event statistics.
 		OutputLogger.println();
@@ -1372,11 +1416,11 @@
 		
 		// Iterate over the triggers.
 		OutputLogger.println();
-		OutputLogger.println("Matching SSP Reported Triggers to SSP Simulated Trigger:");
+		OutputLogger.println("Matching SSP Reported Triggers to SSP Simulated Triggers:");
 		for(SSPNumberedTrigger sspTrigger : sspTriggers) {
 			// Get the trigger information.
 			int triggerNum = sspTrigger.isFirstTrigger() ? 0 : 1;
-			OutputLogger.printf("Considering %s%n", sspTrigger.toString());
+			OutputLogger.printf("\t%s%n", sspTrigger.toString());
 			
 			// Iterate over the SSP cluster simulated triggers and
 			// look for a trigger that matches.
@@ -1384,7 +1428,7 @@
 			for(Trigger<?> simTrigger : sspTriggerList.get(triggerNum)) {
 				// VERBOSE :: Output the trigger being considered for
 				//            matching.
-				OutputLogger.printf("\tTrigger %d :: %s :: %3.0f :: %s ",
+				OutputLogger.printf("\t\tTrigger %d :: %s :: %3.0f :: %s ",
 						(triggerNum + 1), triggerPositionString(simTrigger),
 						getTriggerTime(simTrigger), simTrigger.toString());
 				
@@ -1407,7 +1451,8 @@
 				boolean[] matchedCuts = triggerCutMatch(simTrigger, sspTrigger);
 				for(int i = 0; i < matchedCuts.length; i++) {
 					if(!matchedCuts[i]) {
-						OutputLogger.printf("[ %-15s ]%n", String.format("failed; cut %d", i));
+						int typeIndex = isSingles ? 0 : 1;
+						OutputLogger.printf("[ %-15s ]%n", String.format("failed; %s", cutNames[typeIndex][i]));
 						continue matchLoop;
 					}
 				}
@@ -1422,14 +1467,34 @@
 			}
 		}
 		
+		for(int triggerNum = 0; triggerNum < 2; triggerNum++) {
+			for(Trigger<?> simTrigger : sspTriggerList.get(triggerNum)) {
+				globalTriggerPlots.sawTrigger(simTrigger);
+				if(simTriggerSet.contains(simTrigger)) {
+					globalTriggerPlots.matchedTrigger(simTrigger);
+				} else {
+					globalTriggerPlots.failedTrigger(simTrigger);
+				}
+			}
+		}
+		
 		// Iterate over the unmatched simulated triggers again and the
 		// unmatched SSP reported trigger that most closely matches it.
-		simLoop:
+		OutputLogger.println();
+		OutputLogger.println("Matching Failed SSP Reported Triggers to Remaining SSP Simulated Triggers:");
 		for(int triggerNum = 0; triggerNum < 2; triggerNum++) {
+			simLoop:
 			for(Trigger<?> simTrigger : sspTriggerList.get(triggerNum)) {
+				OutputLogger.printf("\tTrigger %d :: %s :: %3.0f :: %s%n",
+						(triggerNum + 1), triggerPositionString(simTrigger),
+						getTriggerTime(simTrigger), simTrigger.toString());
+				
 				// Check whether this trigger has already been matched
 				// or not. If it has been matched, skip it.
-				if(simTriggerSet.contains(simTrigger)) { continue simLoop; }
+				if(simTriggerSet.contains(simTrigger)) {
+					OutputLogger.println("\t\tSkipping; already matched successfully");
+					continue simLoop;
+				}
 				
 				// Get the trigger time for the simulated trigger.
 				double simTime = getTriggerTime(simTrigger);
@@ -1440,18 +1505,27 @@
 				boolean[] matchedCut = null;
 				SSPNumberedTrigger bestMatch = null;
 				
+				// Store the readout for the best match.
+				String bestMatchText = null;
+				
 				// Iterate over the reported triggers to find a match.
 				reportedLoop:
 				for(SSPNumberedTrigger sspTrigger : sspTriggers) {
+					OutputLogger.printf("\t\t%s ", sspTrigger.toString());
+					
 					// If the two triggers have different times, this
 					// trigger should be skipped.
 					if(sspTrigger.getTime() != simTime) {
+						OutputLogger.printf("[ %-15s ]%n", "failed; time");
 						continue reportedLoop;
 					}
 					
 					// If this reported trigger has been matched then
 					// it should be skipped.
-					if(sspTriggerSet.contains(sspTrigger)) { continue reportedLoop; }
+					if(sspTriggerSet.contains(sspTrigger)) {
+						OutputLogger.printf("[ %-15s ]%n", "failed; matched");
+						continue reportedLoop;
+					}
 					
 					// Check each of the cuts.
 					boolean[] tempMatchedCut = triggerCutMatch(simTrigger, sspTrigger);
@@ -1460,6 +1534,7 @@
 					// than the previous best match.
 					int tempNumMatched = 0;
 					for(boolean passed : tempMatchedCut) { if(passed) { tempNumMatched++; } }
+					OutputLogger.printf("[ %-15s ]%n", String.format("maybe; %d failed", tempNumMatched));
 					
 					// If the number of matched cuts exceeds the old
 					// best result, this becomes the new best result.
@@ -1467,6 +1542,7 @@
 						numMatched = tempNumMatched;
 						matchedCut = tempMatchedCut;
 						bestMatch = sspTrigger;
+						bestMatchText = String.format("%s%n", sspTrigger.toString());
 					}
 				}
 				
@@ -1476,8 +1552,17 @@
 				if(bestMatch == null) {
 					if(isSingles) { singlesInternalFail = true; }
 					else { pairInternalFail = true; }
+					event.matchedSSPPair(simTrigger, bestMatch, matchedCut);
+					OutputLogger.printf("\t\tTrigger %d :: %s :: %3.0f :: %s",
+							(triggerNum + 1), triggerPositionString(simTrigger),
+							getTriggerTime(simTrigger), simTrigger.toString());
+					OutputLogger.println(" --> No Valid Match Found");
 				} else {
 					event.matchedSSPPair(simTrigger, bestMatch, matchedCut);
+					OutputLogger.printf("\t\tTrigger %d :: %s :: %3.0f :: %s",
+							(triggerNum + 1), triggerPositionString(simTrigger),
+							getTriggerTime(simTrigger), simTrigger.toString());
+					OutputLogger.println(" --> " + bestMatchText);
 				}
 			}
 		}
@@ -1496,12 +1581,31 @@
 		OutputLogger.println("Recon Cluster Trigger --> SSP Reported Trigger Match Status");
 		for(int triggerNum = 0; triggerNum < 2; triggerNum++) {
 			for(Trigger<?> simTrigger : reconTriggerList.get(triggerNum)) {
+				// If the trigger number and type align with the event
+				// trigger type, mark that it was seen.
+				if(triggerNum == 1) {
+					if(activeTrigger == TriggerDiagnosticUtil.TRIGGER_SINGLES_1 && isSingles) {
+						event.setSawEventType(true);
+					} else if(activeTrigger == TriggerDiagnosticUtil.TRIGGER_PAIR_1 && !isSingles) {
+						event.setSawEventType(true);
+					}
+				} else {
+					if(activeTrigger == TriggerDiagnosticUtil.TRIGGER_SINGLES_2 && isSingles) {
+						event.setSawEventType(true);
+					} else if(activeTrigger == TriggerDiagnosticUtil.TRIGGER_PAIR_2 && !isSingles) {
+						event.setSawEventType(true);
+					}
+				}
 				
 				OutputLogger.printf("\tTrigger %d :: %s :: %s%n", (triggerNum + 1),
 						triggerPositionString(simTrigger), simTrigger.toString());
 				
+				// TEMP :: Populate the recon ALL pairs plots.
+				globalTriggerPlots.sawTrigger(simTrigger);
+				
 				// Iterate over the SSP reported triggers and compare
 				// them to the reconstructed cluster simulated trigger.
+				boolean matched = false;
 				matchLoop:
 				for(SSPNumberedTrigger sspTrigger : sspTriggers) {
 					OutputLogger.printf("\t\t\t%s", sspTrigger.toString());
@@ -1522,10 +1626,6 @@
 					}
 					
 					// Test each cut.
-					String[][] cutNames = {
-							{ "E_min", "E_max", "hit count", "null" },
-							{ "E_sum", "E_diff", "E_slope", "coplanar" }
-					};
 					int typeIndex = isSingles ? 0 : 1;
 					boolean[] matchedCuts = triggerCutMatch(simTrigger, sspTrigger);
 					for(int cutIndex = 0; cutIndex < matchedCuts.length; cutIndex++) {
@@ -1540,8 +1640,12 @@
 					sspTriggerSet.add(sspTrigger);
 					event.matchedReconPair(simTrigger, sspTrigger);
 					OutputLogger.print(" [ success         ]%n");
+					globalTriggerPlots.matchedTrigger(simTrigger);
+					matched = true;
 					break matchLoop;
 				}
+				
+				if(!matched) { globalTriggerPlots.failedTrigger(simTrigger); }
 			}
 		}
 		
@@ -1560,6 +1664,12 @@
 		// Print event statistics.
 		OutputLogger.println();
 		OutputLogger.println("Event Statistics:");
+		OutputLogger.printf("\tSaw Triggering Event Type  :: ");
+		if(activeTrigger == TriggerDiagnosticUtil.TRIGGER_COSMIC || activeTrigger == TriggerDiagnosticUtil.TRIGGER_PULSER) {
+			OutputLogger.println("Unsupported for Cosmic/Pulser");
+		} else {
+			OutputLogger.println("" + event.sawEventType());
+		}
 		OutputLogger.printf("\tSSP Cluster Sim Triggers   :: %d%n", sspSimTriggers);
 		OutputLogger.printf("\tRecon Cluster Sim Triggers :: %d%n", reconSimTriggers);
 		OutputLogger.printf("\tSSP Reported Triggers      :: %d%n", sspTriggers.size());
@@ -1602,8 +1712,8 @@
 			}
 			
 			// Update the global trigger tracking variables.
-			triggerRunStats[0].addEvent(event, reconTriggerList, sspTriggerList, sspTriggers);
-			triggerLocalStats[0].addEvent(event, reconTriggerList, sspTriggerList, sspTriggers);
+			triggerRunStats[0].addEvent(activeTrigger, event, reconTriggerList, sspTriggerList, sspTriggers);
+			triggerLocalStats[0].addEvent(activeTrigger, event, reconTriggerList, sspTriggerList, sspTriggers);
 			efficiencyRunStats.addSinglesTriggers(activeTrigger, reconTriggerList);
 			efficiencyLocalStats.addSinglesTriggers(activeTrigger, reconTriggerList);
 			efficiencyRunStats.addEvent(activeTrigger, event);
@@ -1635,8 +1745,8 @@
 			}
 			
 			// Update the global trigger tracking variables.
-			triggerRunStats[1].addEvent(event, reconTriggerList, sspTriggerList, sspTriggers);
-			triggerLocalStats[1].addEvent(event, reconTriggerList, sspTriggerList, sspTriggers);
+			triggerRunStats[1].addEvent(activeTrigger, event, reconTriggerList, sspTriggerList, sspTriggers);
+			triggerLocalStats[1].addEvent(activeTrigger, event, reconTriggerList, sspTriggerList, sspTriggers);
 			efficiencyRunStats.addPairTriggers(activeTrigger, reconTriggerList);
 			efficiencyLocalStats.addSinglesTriggers(activeTrigger, reconTriggerList);
 			efficiencyRunStats.addEvent(activeTrigger, event);

Modified: java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerMatchEvent.java
 =============================================================================
--- java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerMatchEvent.java	(original)
+++ java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerMatchEvent.java	Thu Mar 19 10:30:08 2015
@@ -19,10 +19,14 @@
 	private int[] sspInternalMatched = new int[2];
 	private int[] reconTriggersMatched = new int[2];
 	private int[][] triggerComp = new int[4][2];
+	private int[] unmatchedTriggers = new int[2];
 	
 	// Track the matched trigger pairs.
 	private List<TriggerMatchedPair> sspPairList = new ArrayList<TriggerMatchedPair>();
 	private List<Pair<Trigger<?>, SSPNumberedTrigger>> reconPairList = new ArrayList<Pair<Trigger<?>, SSPNumberedTrigger>>();
+	
+	// Track whether the event triggering type was seen.
+	private boolean sawEventType = false;
 	
 	/**
 	 * Gets the number of times a cut of the given cut ID failed when
@@ -34,7 +38,7 @@
 	 */
 	public int getCutFailures(int triggerNumber, int cutID) {
 		// Validate the arguments.
-		if(triggerNumber !=0 && triggerNumber != 1) {
+		if(triggerNumber != 0 && triggerNumber != 1) {
 			throw new IndexOutOfBoundsException("Trigger number must be 0 or 1.");
 		} if(cutID < 0 || cutID > 3) {
 			throw new IndexOutOfBoundsException(String.format("Cut ID \"%d\" is not valid.", cutID));
@@ -42,6 +46,23 @@
 		
 		// Return the requested cut value.
 		return triggerComp[cutID][triggerNumber];
+	}
+	
+	/**
+	 * Gets the number of triggers for this trigger for which there
+	 * were no matches.
+	 * @param triggerNum - The trigger for which to get the value.
+	 * @return Returns the number of triggers that failed to match as
+	 * an <code>int</code>.
+	 */
+	public int getUnmatchedTriggers(int triggerNum) {
+		// Validate the arguments.
+		if(triggerNum != 0 && triggerNum != 1) {
+			throw new IndexOutOfBoundsException("Trigger number must be 0 or 1.");
+		}
+		
+		// Return the requested cut value.
+		return unmatchedTriggers[triggerNum];
 	}
 	
 	/**
@@ -139,7 +160,9 @@
 		// singles triggers use arrays of size 3. If the array is not
 		// of the appropriate size, resize it.
 		boolean[] cutArray;
-		if(cutsMatched.length == 4) {
+		if(cutsMatched == null) {
+			cutArray = new boolean[] { false, false, false, false };
+		} else if(cutsMatched.length == 4) {
 			cutArray = cutsMatched;
 		} else {
 			cutArray = new boolean[] { cutsMatched[0], cutsMatched[1], cutsMatched[2], true };
@@ -149,11 +172,15 @@
 		TriggerMatchedPair triggerPair = new TriggerMatchedPair(simTrigger, sspTrigger, cutArray);
 		sspPairList.add(triggerPair);
 		
+		// If the argument cut array was null, then this trigger was
+		// not actually matched. Track this.
+		int triggerNum = triggerPair.isFirstTrigger() ? 0 : 1;
+		if(cutsMatched == null) { unmatchedTriggers[triggerNum]++; }
+		
 		// Track which cuts have failed.
 		boolean isMatched = true;
-		int triggerNum = triggerPair.isFirstTrigger() ? 0 : 1;
-		for(int cutIndex = 0; cutIndex < cutsMatched.length; cutIndex++) {
-			if(!cutsMatched[cutIndex]) {
+		for(int cutIndex = 0; cutIndex < cutArray.length; cutIndex++) {
+			if(!cutArray[cutIndex]) {
 				triggerComp[cutIndex][triggerNum]++;
 				isMatched = false;
 			}
@@ -162,4 +189,24 @@
 		// If all the cuts are true, then the trigger pair is a match.
 		if(isMatched) { sspInternalMatched[triggerNum]++; }
 	}
+	
+	/**
+	 * Indicates whether an event of the type that caused the event
+	 * readout was seen.
+	 * @return Returns <code>true</code> if an event was seen and
+	 * <code>false</code> otherwise.
+	 */
+	public boolean sawEventType() {
+		return sawEventType;
+	}
+	
+	/**
+	 * Sets whether a simulated trigger of the type that caused the
+	 * event readout was seen.
+	 * @param state - <code>true</code> indicates that the trigger type
+	 * was seen and <code>false</code> that it was not.
+	 */
+	public void setSawEventType(boolean state) {
+		sawEventType = state;
+	}
 }

Modified: java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerMatchStatus.java
 =============================================================================
--- java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerMatchStatus.java	(original)
+++ java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerMatchStatus.java	Thu Mar 19 10:30:08 2015
@@ -3,6 +3,7 @@
 import java.util.List;
 
 import org.hps.analysis.trigger.util.Trigger;
+import org.hps.analysis.trigger.util.TriggerDiagnosticUtil;
 import org.hps.recon.ecal.triggerbank.SSPNumberedTrigger;
 
 /**
@@ -23,8 +24,15 @@
 	 * @param sspSimTriggers - A list of simulated SSP cluster triggers.
 	 * @param sspBankTriggers - A list of SSP bank triggers.
 	 */
-	public void addEvent(TriggerMatchEvent event, List<List<? extends Trigger<?>>> reconTriggers,
+	public void addEvent(int eventType, TriggerMatchEvent event, List<List<? extends Trigger<?>>> reconTriggers,
 			List<List<? extends Trigger<?>>> sspSimTriggers, List<? extends SSPNumberedTrigger> sspBankTriggers) {
+		// Increment the event type count.
+		if(eventType == TriggerDiagnosticUtil.TRIGGER_SINGLES_1 || eventType == TriggerDiagnosticUtil.TRIGGER_PAIR_1) {
+			triggerTypesSeen[0]++;
+		} else if(eventType == TriggerDiagnosticUtil.TRIGGER_SINGLES_2 || eventType == TriggerDiagnosticUtil.TRIGGER_PAIR_2) {
+			triggerTypesSeen[1]++;
+		}
+		
 		// Check if there are more bank triggers than there are
 		// simulated SSP triggers.
 		int sspTriggerDiff = sspBankTriggers.size() - sspSimTriggers.size();
@@ -46,6 +54,8 @@
 		
 		// Increment the count for each cut failure type.
 		for(int triggerNum = 0; triggerNum < 2; triggerNum++) {
+			unmatchedTriggers[triggerNum] += event.getUnmatchedTriggers(triggerNum);
+			
 			for(int cutIndex = 0; cutIndex < 4; cutIndex++) {
 				triggerComp[cutIndex][triggerNum] += event.getCutFailures(triggerNum, cutIndex);
 			}
@@ -55,6 +65,12 @@
 		for(int triggerNum = 0; triggerNum < 2; triggerNum++) {
 			sspInternalMatched[triggerNum] += event.getMatchedSSPTriggers(triggerNum);
 			reconTriggersMatched[triggerNum] += event.getMatchedReconTriggers(triggerNum);
+		}
+		
+		// Check if a trigger of the right time was found.
+		// Get the trigger number and type.
+		if(event.sawEventType()) {
+			triggerTypesFound[eventType]++;
 		}
 	}
 	

Modified: java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerMatchedPair.java
 =============================================================================
--- java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerMatchedPair.java	(original)
+++ java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerMatchedPair.java	Thu Mar 19 10:30:08 2015
@@ -80,7 +80,11 @@
 	 * object.
 	 */
 	public Class<?> getSimulatedTriggerType() {
-		return getFirstElement().getTriggerSource().getClass();
+		if(getFirstElement() != null) {
+			return getFirstElement().getTriggerSource().getClass();
+		} else {
+			return null;
+		}
 	}
 	
 	/**
@@ -99,7 +103,11 @@
 	 * first trigger and <code>false</code> otherwise.
 	 */
 	public boolean isFirstTrigger() {
-		return getSecondElement().isFirstTrigger();
+		if(getSecondElement() != null) {
+			return getSecondElement().isFirstTrigger();
+		} else {
+			return getFirstElement().getTriggerNumber() == 0 ? true : false;
+		}
 	}
 	
 	/**
@@ -108,7 +116,7 @@
 	 * triggers and <code>false</code> otherwise.
 	 */
 	public boolean isPairTrigger() {
-		if(getFirstElement() instanceof PairTrigger && getSecondElement() instanceof SSPPairTrigger) {
+		if(getFirstElement() instanceof PairTrigger || getSecondElement() instanceof SSPPairTrigger) {
 			return true;
 		} else {
 			return false;
@@ -122,7 +130,7 @@
 	 * second trigger and <code>false</code> otherwise.
 	 */
 	public boolean isSecondTrigger() {
-		return getSecondElement().isSecondTrigger();
+		return !isFirstTrigger();
 	}
 	
 	/**
@@ -131,7 +139,7 @@
 	 * triggers and <code>false</code> otherwise.
 	 */
 	public boolean isSinglesTrigger() {
-		if(getFirstElement() instanceof SinglesTrigger && getSecondElement() instanceof SSPSinglesTrigger) {
+		if(getFirstElement() instanceof SinglesTrigger || getSecondElement() instanceof SSPSinglesTrigger) {
 			return true;
 		} else {
 			return false;

Added: java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerPlotsModule.java
 =============================================================================
--- java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerPlotsModule.java	(added)
+++ java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerPlotsModule.java	Thu Mar 19 10:30:08 2015
@@ -0,0 +1,241 @@
+package org.hps.analysis.trigger.event;
+
+import org.hps.analysis.trigger.util.Trigger;
+import org.hps.recon.ecal.triggerbank.SSPCluster;
+import org.hps.recon.ecal.triggerbank.TriggerModule;
+import org.lcsim.event.Cluster;
+import org.lcsim.util.aida.AIDA;
+
+import hep.aida.IHistogram1D;
+
+/**
+ * Class <code>TriggerPlotsModule</code> handles the plotting of singles
+ * and pair trigger values.
+ * 
+ * @author Kyle McCarty
+ */
+public class TriggerPlotsModule {
+	// Reference variables.
+	private static final int RECON   = 0;
+	private static final int SSP     = 1;
+	private static final int ALL     = 0;
+	private static final int MATCHED = 1;
+	private static final int FAILED  = 2;
+	
+	// Class variables.
+	private final double[] energySlopeParamF;
+	
+	// Plots.
+	private AIDA aida = AIDA.defaultInstance();
+	private IHistogram1D[][][] singlesClusterEnergyPlot = new IHistogram1D[2][2][3];
+	private IHistogram1D[][][] singlesHitCountPlot = new IHistogram1D[2][2][3];
+	
+	private IHistogram1D[][][] pairClusterEnergyPlot = new IHistogram1D[2][2][3];
+	private IHistogram1D[][][] pairHitCountPlot = new IHistogram1D[2][2][3];
+	private IHistogram1D[][][] pairTimePlot = new IHistogram1D[2][2][3];
+	private IHistogram1D[][][] pairSumPlot = new IHistogram1D[2][2][3];
+	private IHistogram1D[][][] pairDiffPlot = new IHistogram1D[2][2][3];
+	private IHistogram1D[][][] pairSlopePlot = new IHistogram1D[2][2][3];
+	private IHistogram1D[][][] pairCoplanarityPlot = new IHistogram1D[2][2][3];
+	
+	/**
+	 * Instantiates a new <code>TriggerPlotsModule</code> that will use
+	 * the indicated values for the energy slope conversion factor when
+	 * plotting energy slope values. Plots will be attached to the
+	 * default AIDA instance.
+	 * @param trigger0F - The energy slope conversion factor for the
+	 * first trigger.
+	 * @param trigger1F - The energy slope conversion factor for the
+	 * second trigger.
+	 */
+	public TriggerPlotsModule(double trigger0F, double trigger1F) {
+		// Store the energy slope parameter.
+		energySlopeParamF = new double[2];
+		energySlopeParamF[0] = trigger0F;
+		energySlopeParamF[1] = trigger1F;
+		
+		// Define type string values.
+		String[] sourceType = { "Recon", "SSP" };
+		String[] resultType = { "All", "Matched", "Failed" };
+		
+		// Instantiate the trigger result plots for each trigger.
+		for(int triggerNum = 0; triggerNum < 2; triggerNum++) {
+			// Get the directory for the current triggers.
+			String pairDir = "Pair Trigger " + triggerNum;
+			String singlesDir = "Singles Trigger " + triggerNum;
+			
+			// Instantiate the trigger result plots for each type of
+			// trigger source object.
+			for(int source = 0; source < 2; source++) {
+				// Instantiate the trigger result plots for each type
+				// of trigger match result.
+				for(int result = 0; result < 3; result++) {
+					// Instantiate the singles trigger plots.
+					singlesClusterEnergyPlot[triggerNum][source][result] = aida.histogram1D(String.format("%s/%s Singles Hit Count (%s)",
+							singlesDir, sourceType[source], resultType[result]), 9, 0.5, 9.5);
+					singlesHitCountPlot[triggerNum][source][result] = aida.histogram1D(String.format("%s/%s Singles Cluster Energy (%s)",
+							singlesDir, sourceType[source], resultType[result]), 300, 0.0, 3.0);
+					
+					// Instantiate the pair trigger plots.
+					pairHitCountPlot[triggerNum][source][result] = aida.histogram1D(String.format("%s/%s Pair Hit Count (%s)",
+							pairDir, sourceType[source], resultType[result]), 9, 0.5, 9.5);
+					pairClusterEnergyPlot[triggerNum][source][result] = aida.histogram1D(String.format("%s/%s Pair Cluster Energy (%s)",
+							pairDir, sourceType[source], resultType[result]), 300, 0.0, 3.0);
+					pairTimePlot[triggerNum][source][result] = aida.histogram1D(String.format("%s/%s Pair Time Coincidence (%s)",
+							pairDir, sourceType[source], resultType[result]), 8, 0, 32);
+					pairSumPlot[triggerNum][source][result] = aida.histogram1D(String.format("%s/%s Pair Energy Sum (%s)",
+							pairDir, sourceType[source], resultType[result]), 300, 0.0, 3.0);
+					pairDiffPlot[triggerNum][source][result] = aida.histogram1D(String.format("%s/%s Pair Energy Difference (%s)",
+							pairDir, sourceType[source], resultType[result]), 300, 0.0, 3.0);
+					pairSlopePlot[triggerNum][source][result] = aida.histogram1D(String.format("%s/%s Pair Energy Slope (%s)",
+							pairDir, sourceType[source], resultType[result]), 300, 0.0, 3.0);
+					pairCoplanarityPlot[triggerNum][source][result] = aida.histogram1D(String.format("%s/%s Pair Coplanarity (%s)",
+							pairDir, sourceType[source], resultType[result]), 180, 0, 180);
+				}
+			}
+		}
+	}
+	
+	/**
+	 * Populates the "all" plots of the appropriate type with the cut
+	 * results from the argument trigger.
+	 * @param trigger - The trigger from which to populate the plots.
+	 */
+	public void sawTrigger(Trigger<?> trigger) {
+		processTrigger(trigger, ALL);
+	}
+	
+	/**
+	 * Populates the "matched" plots of the appropriate type with the
+	 * cut results from the argument trigger.
+	 * @param trigger - The trigger from which to populate the plots.
+	 */
+	public void matchedTrigger(Trigger<?> trigger) {
+		processTrigger(trigger, MATCHED);
+	}
+	
+	/**
+	 * Populates the "failed" plots of the appropriate type with the
+	 * cut results from the argument trigger.
+	 * @param trigger - The trigger from which to populate the plots.
+	 */
+	public void failedTrigger(Trigger<?> trigger) {
+		processTrigger(trigger, FAILED);
+	}
+	
+	public void setEnergySlopeParamF(int triggerNum, double value) {
+		// Make sure that the trigger number is valid.
+		if(triggerNum < 0 || triggerNum > 1) {
+			throw new IllegalArgumentException(String.format("Trigger number %d is not valid.", triggerNum));
+		}
+		
+		// Set the parameter.
+		energySlopeParamF[triggerNum] = value;
+	}
+	
+	/**
+	 * Populates the indicated type of plots of the appropriate type
+	 * for the argument trigger.
+	 * @param trigger - The trigger from which to populate the plots.
+	 * @param plotType - The type of plot to populate. This must be one
+	 * of <code>ALL</code>, <code>MATCHED</code>, or <code>FAILED</code>.
+	 */
+	private void processTrigger(Trigger<?> trigger, int plotType) {
+		// Get the trigger number and source.
+		Object source = trigger.getTriggerSource();
+		int triggerNum = trigger.getTriggerNumber();
+		
+		// Populate the plots using the appropriate method.
+		if(source instanceof Cluster) {
+			processSingles(triggerNum, plotType, (Cluster) source);
+		}
+		else if(source instanceof SSPCluster) {
+			processSingles(triggerNum, plotType, (SSPCluster) source);
+		}
+		else if(source instanceof Cluster[]) {
+			processPair(triggerNum, plotType, (Cluster[]) source);
+		}
+		else if(source instanceof SSPCluster[]) {
+			processPair(triggerNum, plotType, (SSPCluster[]) source);
+		}
+		
+		// If the trigger source is unsupported, produce an error.
+		else {
+			throw new IllegalArgumentException(String.format("Trigger source \"%s\" is not supported.", source.getClass().getSimpleName()));
+		}
+	}
+	
+	/**
+	 * Populates the trigger singles plots for the indicated type for
+	 * reconstructed clusters.
+	 * @param triggerNum - The trigger number of the source trigger.
+	 * @param plotType - The type of plot to populate. This must be one
+	 * of <code>ALL</code>, <code>MATCHED</code>, or <code>FAILED</code>.
+	 * @param pair - The triggering cluster.
+	 */
+	private void processSingles(int triggerNum, int plotType, Cluster cluster) {
+		// Fill the cluster singles plots.
+		singlesHitCountPlot[triggerNum][RECON][plotType].fill(TriggerModule.getValueClusterHitCount(cluster));
+		singlesClusterEnergyPlot[triggerNum][RECON][plotType].fill(TriggerModule.getValueClusterTotalEnergy(cluster));
+	}
+	
+	/**
+	 * Populates the trigger singles plots for the indicated type for SSP
+	 * clusters.
+	 * @param triggerNum - The trigger number of the source trigger.
+	 * @param plotType - The type of plot to populate. This must be one
+	 * of <code>ALL</code>, <code>MATCHED</code>, or <code>FAILED</code>.
+	 * @param pair - The triggering cluster.
+	 */
+	private void processSingles(int triggerNum, int plotType, SSPCluster cluster) {
+		// Fill the cluster singles plots.
+		singlesHitCountPlot[triggerNum][SSP][plotType].fill(TriggerModule.getValueClusterHitCount(cluster));
+		singlesClusterEnergyPlot[triggerNum][SSP][plotType].fill(TriggerModule.getValueClusterTotalEnergy(cluster));
+	}
+	
+	/**
+	 * Populates the trigger pair plots for the indicated type for
+	 * reconstructed cluster pairs.
+	 * @param triggerNum - The trigger number of the source trigger.
+	 * @param plotType - The type of plot to populate. This must be one
+	 * of <code>ALL</code>, <code>MATCHED</code>, or <code>FAILED</code>.
+	 * @param pair - The triggering pair.
+	 */
+	private void processPair(int triggerNum, int plotType, Cluster[] pair) {
+		// Fill the cluster singles plots.
+		pairHitCountPlot[triggerNum][RECON][plotType].fill(TriggerModule.getValueClusterHitCount(pair[0]));
+		pairHitCountPlot[triggerNum][RECON][plotType].fill(TriggerModule.getValueClusterHitCount(pair[1]));
+		pairClusterEnergyPlot[triggerNum][RECON][plotType].fill(TriggerModule.getValueClusterTotalEnergy(pair[0]));
+		pairClusterEnergyPlot[triggerNum][RECON][plotType].fill(TriggerModule.getValueClusterTotalEnergy(pair[1]));
+		
+		// Fill the cluster pair plots.
+		pairTimePlot[triggerNum][RECON][plotType].fill(TriggerModule.getValueTimeCoincidence(pair));
+		pairSumPlot[triggerNum][RECON][plotType].fill(TriggerModule.getValueEnergySum(pair));
+		pairDiffPlot[triggerNum][RECON][plotType].fill(TriggerModule.getValueEnergyDifference(pair));
+		pairSlopePlot[triggerNum][RECON][plotType].fill(TriggerModule.getValueEnergySlope(pair, energySlopeParamF[triggerNum]));
+		pairCoplanarityPlot[triggerNum][RECON][plotType].fill(TriggerModule.getValueCoplanarity(pair));
+	}
+	
+	/**
+	 * Populates the trigger pair plots for the indicated type for SSP
+	 * cluster pairs.
+	 * @param triggerNum - The trigger number of the source trigger.
+	 * @param plotType - The type of plot to populate. This must be one
+	 * of <code>ALL</code>, <code>MATCHED</code>, or <code>FAILED</code>.
+	 * @param pair - The triggering pair.
+	 */
+	private void processPair(int triggerNum, int plotType, SSPCluster[] pair) {
+		// Fill the cluster singles plots.
+		pairHitCountPlot[triggerNum][SSP][plotType].fill(TriggerModule.getValueClusterHitCount(pair[0]));
+		pairHitCountPlot[triggerNum][SSP][plotType].fill(TriggerModule.getValueClusterHitCount(pair[1]));
+		pairClusterEnergyPlot[triggerNum][SSP][plotType].fill(TriggerModule.getValueClusterTotalEnergy(pair[0]));
+		pairClusterEnergyPlot[triggerNum][SSP][plotType].fill(TriggerModule.getValueClusterTotalEnergy(pair[1]));
+		
+		// Fill the cluster pair plots.
+		pairTimePlot[triggerNum][SSP][plotType].fill(TriggerModule.getValueTimeCoincidence(pair));
+		pairSumPlot[triggerNum][SSP][plotType].fill(TriggerModule.getValueEnergySum(pair));
+		pairDiffPlot[triggerNum][SSP][plotType].fill(TriggerModule.getValueEnergyDifference(pair));
+		pairSlopePlot[triggerNum][SSP][plotType].fill(TriggerModule.getValueEnergySlope(pair, energySlopeParamF[triggerNum]));
+		pairCoplanarityPlot[triggerNum][SSP][plotType].fill(TriggerModule.getValueCoplanarity(pair));
+	}
+}

Modified: java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerStatModule.java
 =============================================================================
--- java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerStatModule.java	(original)
+++ java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerStatModule.java	Thu Mar 19 10:30:08 2015
@@ -15,7 +15,9 @@
 	protected int[] sspInternalMatched = new int[2];
 	protected int[] reconTriggersMatched = new int[2];
 	protected int[][] triggerComp = new int[4][2];
-	
+	protected int[] unmatchedTriggers = new int[2];
+	protected int[] triggerTypesSeen = new int[2];
+	protected int[] triggerTypesFound = new int[2];
 	
 	/**
 	 * Instantiates a <code>TriggerStatModule</code> with no statistics
@@ -40,6 +42,13 @@
 		for(int i = 0; i < reconTriggersMatched.length; i++) {
 			reconTriggersMatched[i] = base.reconTriggersMatched[i];
 		}
+		for(int i = 0; i < triggerTypesSeen.length; i++) {
+			triggerTypesSeen[i] = base.triggerTypesSeen[i];
+			triggerTypesFound[i] = base.triggerTypesFound[i];
+		}
+		for(int i = 0; i < unmatchedTriggers.length; i++) {
+			unmatchedTriggers[i] = base.unmatchedTriggers[i];
+		}
 		for(int i = 0; i < triggerComp.length; i++) {
 			for(int j = 0; j < triggerComp[i].length; j++) {
 				triggerComp[i][j] = base.triggerComp[i][j];
@@ -82,6 +91,40 @@
 	}
 	
 	/**
+	 * Gets the number of events that were readout due to a trigger
+	 * of the indicated type.
+	 * @param triggerType - The type of trigger.
+	 * @return Returns the number of events readout because of the
+	 * trigger type as an <code>int</code>.
+	 */
+	public int getEventsOfType(int triggerNum) {
+		// Make sure that the trigger type is defined.
+		if(triggerNum < 0 || triggerNum > 1) {
+			throw new IndexOutOfBoundsException(String.format("Trigger number \"%d\" is not valid.", triggerNum));
+		}
+		
+		// Return the number of events that were trigger by this type.
+		return triggerTypesSeen[triggerNum];
+	}
+	
+	/**
+	 * Gets the number of events that were readout due to a trigger
+	 * of the indicated type where a reconstructed cluster simulated
+	 * trigger of that type existed.
+	 * @param triggerType - The type of trigger.
+	 * @return Returns the number of events as an <code>int</code>.
+	 */
+	public int getEventsOfTypeSeen(int triggerNum) {
+		// Make sure that the trigger type is defined.
+		if(triggerNum < 0 || triggerNum > 1) {
+			throw new IndexOutOfBoundsException(String.format("Trigger number \"%d\" is not valid.", triggerNum));
+		}
+		
+		// Return the number of events that were trigger by this type.
+		return triggerTypesFound[triggerNum];
+	}
+	
+	/**
 	 * Gets the number of SSP bank triggers that were reported in excess
 	 * of the number of simulated SSP triggers seen.
 	 * @return Returns the value as an <code>int</code> primitive.
@@ -163,4 +206,21 @@
 	public int getSSPBankTriggerCount() {
 		return reportedTriggers;
 	}
+	
+	/**
+	 * Gets the number of triggers for this trigger for which there
+	 * were no matches.
+	 * @param triggerNum - The trigger for which to get the value.
+	 * @return Returns the number of triggers that failed to match as
+	 * an <code>int</code>.
+	 */
+	public int getUnmatchedTriggers(int triggerNum) {
+		// Validate the arguments.
+		if(triggerNum != 0 && triggerNum != 1) {
+			throw new IndexOutOfBoundsException("Trigger number must be 0 or 1.");
+		}
+		
+		// Return the requested cut value.
+		return unmatchedTriggers[triggerNum];
+	}
 }