Author: [log in to unmask] Date: Thu Mar 5 10:59:19 2015 New Revision: 2266 Log: Trigger diagnostic package is being relocated to an official location now that it is approaching release status. Added: java/trunk/analysis/src/main/java/org/hps/analysis/trigger/ java/trunk/analysis/src/main/java/org/hps/analysis/trigger/DiagSnapshot.java java/trunk/analysis/src/main/java/org/hps/analysis/trigger/TriggerDiagnosticDriver.java java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/ java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/ClusterMatchEvent.java java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/ClusterMatchStatus.java java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/ClusterMatchedPair.java java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/ClusterStatModule.java java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerEfficiencyModule.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 java/trunk/analysis/src/main/java/org/hps/analysis/trigger/util/ java/trunk/analysis/src/main/java/org/hps/analysis/trigger/util/ComponentUtils.java java/trunk/analysis/src/main/java/org/hps/analysis/trigger/util/OutputLogger.java java/trunk/analysis/src/main/java/org/hps/analysis/trigger/util/Pair.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 java/trunk/analysis/src/main/java/org/hps/analysis/trigger/util/TriggerDiagnosticUtil.java Added: java/trunk/analysis/src/main/java/org/hps/analysis/trigger/DiagSnapshot.java ============================================================================= --- java/trunk/analysis/src/main/java/org/hps/analysis/trigger/DiagSnapshot.java (added) +++ java/trunk/analysis/src/main/java/org/hps/analysis/trigger/DiagSnapshot.java Thu Mar 5 10:59:19 2015 @@ -0,0 +1,169 @@ +package org.hps.analysis.trigger; + +import org.hps.analysis.trigger.event.ClusterMatchStatus; +import org.hps.analysis.trigger.event.ClusterStatModule; +import org.hps.analysis.trigger.event.TriggerEfficiencyModule; +import org.hps.analysis.trigger.event.TriggerMatchStatus; +import org.hps.analysis.trigger.event.TriggerStatModule; +import org.hps.analysis.trigger.util.TriggerDiagnosticUtil; + + +/** + * Class <code>DiagSnapshot</code> creates a snapshot of the trigger + * diagnostics at a specific time that can be passed to other classes. + * It is entirely static and will not change after creation. + * + * @author Kyle McCarty <[log in to unmask]> + */ +public class DiagSnapshot { + public final ClusterStatModule clusterRunStatistics; + public final ClusterStatModule clusterLocalStatistics; + public final SinglesTriggerStatModule singlesRunStatistics; + public final SinglesTriggerStatModule singlesLocalStatistics; + public final PairTriggerStatModule pairRunStatistics; + public final PairTriggerStatModule pairLocalStatistics; + public final TriggerEfficiencyModule efficiencyRunStatistics; + public final TriggerEfficiencyModule efficiencyLocalStatistics; + + /** + * Instantiates a new snapshot. The snapshot creates a copy of the + * statistical information stored in the status objects and makes + * a copy of this information available to other classes. + * @param localCluster - The local cluster data object. + * @param globalCluster - The run cluster data object. + * @param localSingles - The local singles trigger data object. + * @param globalSingles - The run singles trigger data object. + * @param localPair - The local pair trigger data object. + * @param globalPair - The run pair trigger data object. + */ + DiagSnapshot(ClusterMatchStatus localCluster, ClusterMatchStatus globalCluster, + TriggerMatchStatus localSingles, TriggerMatchStatus globalSingles, + TriggerMatchStatus localPair, TriggerMatchStatus globalPair, + TriggerEfficiencyModule localEfficiency, TriggerEfficiencyModule globalEfficiency) { + clusterRunStatistics = globalCluster.cloneStatModule(); + clusterLocalStatistics = localCluster.cloneStatModule(); + singlesRunStatistics = new SinglesTriggerStatModule(globalSingles); + singlesLocalStatistics = new SinglesTriggerStatModule(localSingles); + pairRunStatistics = new PairTriggerStatModule(globalPair); + pairLocalStatistics = new PairTriggerStatModule(localPair); + efficiencyRunStatistics = globalEfficiency.clone(); + efficiencyLocalStatistics = localEfficiency.clone(); + } + + /** + * + * Class <code>SinglesTriggerStatModule</code> is a wrapper for the + * generic <code>TriggerStatModule</code> that provides specific + * methods to obtain cut results rather than needing to reference + * a cut index. + * + * @author Kyle McCarty <[log in to unmask]> + */ + public class SinglesTriggerStatModule extends TriggerStatModule { + /** + * Instantiates a <code>SinglesTriggerStatModule</code> with + * statistics cloned from the base object. + * @param base - The source for the statistical data. + */ + SinglesTriggerStatModule(TriggerStatModule base) { + super(base); + } + + /** + * Gets the number of times the cluster energy upper bound cut + * failed to match. + * @param triggerNumber - The trigger for which to get the value. + * @return Returns the number of times the cut failed as an + * <code>int</code> primitive. + */ + public int getEMaxFailures(int triggerNumber) { + return getCutFailures(triggerNumber, TriggerDiagnosticUtil.SINGLES_ENERGY_MAX); + } + + /** + * Gets the number of times the cluster energy lower bound cut + * failed to match. + * @param triggerNumber - The trigger for which to get the value. + * @return Returns the number of times the cut failed as an + * <code>int</code> primitive. + */ + public int getEMinFailures(int triggerNumber) { + return getCutFailures(triggerNumber, TriggerDiagnosticUtil.SINGLES_ENERGY_MIN); + } + + /** + * Gets the number of times the cluster hit count cut failed + * to match. + * @param triggerNumber - The trigger for which to get the value. + * @return Returns the number of times the cut failed as an + * <code>int</code> primitive. + */ + public int getHitCountFailures(int triggerNumber) { + return getCutFailures(triggerNumber, TriggerDiagnosticUtil.SINGLES_HIT_COUNT); + } + } + + /** + * + * Class <code>PairTriggerStatModule</code> is a wrapper for the + * generic <code>TriggerStatModule</code> that provides specific + * methods to obtain cut results rather than needing to reference + * a cut index. + * + * @author Kyle McCarty <[log in to unmask]> + */ + public class PairTriggerStatModule extends TriggerStatModule { + /** + * Instantiates a <code>PairTriggerStatModule</code> with + * statistics cloned from the base object. + * @param base - The source for the statistical data. + */ + PairTriggerStatModule(TriggerStatModule base) { + super(base); + } + + /** + * Gets the number of times the pair energy sum cut failed to + * match. + * @param triggerNumber - The trigger for which to get the value. + * @return Returns the number of times the cut failed as an + * <code>int</code> primitive. + */ + public int getESumFailures(int triggerNumber) { + return getCutFailures(triggerNumber, TriggerDiagnosticUtil.PAIR_ENERGY_SUM); + } + + /** + * Gets the number of times the pair energy difference cut failed + * to match. + * @param triggerNumber - The trigger for which to get the value. + * @return Returns the number of times the cut failed as an + * <code>int</code> primitive. + */ + public int getEDiffFailures(int triggerNumber) { + return getCutFailures(triggerNumber, TriggerDiagnosticUtil.PAIR_ENERGY_DIFF); + } + + /** + * Gets the number of times the pair energy slope cut failed + * to match. + * @param triggerNumber - The trigger for which to get the value. + * @return Returns the number of times the cut failed as an + * <code>int</code> primitive. + */ + public int getESlopeFailures(int triggerNumber) { + return getCutFailures(triggerNumber, TriggerDiagnosticUtil.PAIR_ENERGY_SLOPE); + } + + /** + * Gets the number of times the pair coplanarity cut failed to + * match. + * @param triggerNumber - The trigger for which to get the value. + * @return Returns the number of times the cut failed as an + * <code>int</code> primitive. + */ + public int getCoplanarityFailures(int triggerNumber) { + return getCutFailures(triggerNumber, TriggerDiagnosticUtil.PAIR_COPLANARITY); + } + } +} Added: java/trunk/analysis/src/main/java/org/hps/analysis/trigger/TriggerDiagnosticDriver.java ============================================================================= --- java/trunk/analysis/src/main/java/org/hps/analysis/trigger/TriggerDiagnosticDriver.java (added) +++ java/trunk/analysis/src/main/java/org/hps/analysis/trigger/TriggerDiagnosticDriver.java Thu Mar 5 10:59:19 2015 @@ -0,0 +1,1995 @@ +package org.hps.analysis.trigger; + +import java.awt.Point; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import org.hps.analysis.trigger.event.ClusterMatchEvent; +import org.hps.analysis.trigger.event.ClusterMatchStatus; +import org.hps.analysis.trigger.event.ClusterMatchedPair; +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.util.OutputLogger; +import org.hps.analysis.trigger.util.Pair; +import org.hps.analysis.trigger.util.PairTrigger; +import org.hps.analysis.trigger.util.SinglesTrigger; +import org.hps.analysis.trigger.util.Trigger; +import org.hps.analysis.trigger.util.TriggerDiagnosticUtil; +import org.hps.readout.ecal.TriggerModule; +import org.hps.readout.ecal.daqconfig.ConfigurationManager; +import org.hps.readout.ecal.daqconfig.DAQConfig; +import org.hps.readout.ecal.triggerbank.AbstractIntData; +import org.hps.readout.ecal.triggerbank.SSPCluster; +import org.hps.readout.ecal.triggerbank.SSPData; +import org.hps.readout.ecal.triggerbank.SSPNumberedTrigger; +import org.hps.readout.ecal.triggerbank.SSPPairTrigger; +import org.hps.readout.ecal.triggerbank.SSPSinglesTrigger; +import org.hps.readout.ecal.triggerbank.SSPTrigger; +import org.hps.readout.ecal.triggerbank.TIData; +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 TriggerDiagnosticDriver extends Driver { + // Store the LCIO collection names for the needed objects. + private String hitCollectionName = "EcalCalHits"; + private String bankCollectionName = "TriggerBank"; + private String clusterCollectionName = "EcalClusters"; + private String diagnosticCollectionName = "DiagnosticSnapshot"; + + // Store the lists of parsed objects. + private TIData tiBank; + private SSPData sspBank; + private List<Cluster> reconClusters = new ArrayList<Cluster>(); + private List<SSPCluster> sspClusters; + private List<List<PairTrigger<Cluster[]>>> reconPairsTriggers = new ArrayList<List<PairTrigger<Cluster[]>>>(2); + private List<List<PairTrigger<SSPCluster[]>>> sspPairsTriggers = new ArrayList<List<PairTrigger<SSPCluster[]>>>(2); + private List<List<SinglesTrigger<Cluster>>> reconSinglesTriggers = new ArrayList<List<SinglesTrigger<Cluster>>>(2); + private List<List<SinglesTrigger<SSPCluster>>> sspSinglesTriggers = new ArrayList<List<SinglesTrigger<SSPCluster>>>(2); + + // Trigger modules for performing trigger analysis. + private int activeTrigger = -1; + private TriggerModule[] singlesTrigger = new TriggerModule[2]; + private TriggerModule[] pairsTrigger = new TriggerModule[2]; + + // Verification settings. + private int nsa = 100; + private int nsb = 20; + private int windowWidth = 200; + private int hitAcceptance = 1; + private int noiseThreshold = 50; + private long localWindowStart = 0; + private boolean readDAQConfig = false; + private double energyAcceptance = 0.03; + private int localWindowThreshold = 30000; + private boolean performClusterVerification = true; + private boolean performSinglesTriggerVerification = true; + private boolean performPairTriggerVerification = true; + + // Efficiency tracking variables. + private ClusterMatchStatus clusterRunStats = new ClusterMatchStatus(); + private ClusterMatchStatus clusterLocalStats = new ClusterMatchStatus(); + private TriggerEfficiencyModule efficiencyRunStats = new TriggerEfficiencyModule(); + private TriggerEfficiencyModule efficiencyLocalStats = new TriggerEfficiencyModule(); + private TriggerMatchStatus[] triggerRunStats = { new TriggerMatchStatus(), new TriggerMatchStatus() }; + private TriggerMatchStatus[] triggerLocalStats = { new TriggerMatchStatus(), new TriggerMatchStatus() }; + + private int failedClusterEvents = 0; + private int failedSinglesEvents = 0; + private int failedPairEvents = 0; + private int totalEvents = 0; + private int noiseEvents = 0; + + // Verbose settings. + private boolean clusterFail = false; + private boolean singlesEfficiencyFail = false; + private boolean singlesInternalFail = false; + private boolean pairEfficiencyFail = false; + private boolean pairInternalFail = false; + private boolean verbose = false; + private boolean printClusterFail = true; + private boolean printSinglesTriggerEfficiencyFail = true; + private boolean printSinglesTriggerInternalFail = true; + private boolean printPairTriggerEfficiencyFail = true; + private boolean printPairTriggerInternalFail = true; + + // 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; + + /** + * Define the trigger modules. This should be replaced by parsing + * the DAQ configuration at some point. + */ + @Override + public void startOfData() { + // If the DAQ configuration should be read, attach a listener + // to track when it updates. + if(readDAQConfig) { + 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(); + + // 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("======================================================================"); + + // Set the FADC settings. + nsa = 100; + nsb = 20; + windowWidth = 400; + + /* + // Define the first singles trigger. + singlesTrigger[0] = new TriggerModule(); + singlesTrigger[0].setCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_LOW, 0.010); + singlesTrigger[0].setCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_HIGH, 8.191); + singlesTrigger[0].setCutValue(TriggerModule.CLUSTER_HIT_COUNT_LOW, 2); + + // Define the second singles trigger. + singlesTrigger[1] = new TriggerModule(); + singlesTrigger[1].setCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_LOW, 0.010); + singlesTrigger[1].setCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_HIGH, 0.050); + singlesTrigger[1].setCutValue(TriggerModule.CLUSTER_HIT_COUNT_LOW, 2); + + // Define the first pairs trigger. + pairsTrigger[0] = new TriggerModule(); + pairsTrigger[0].setCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_LOW, 0.020); + pairsTrigger[0].setCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_HIGH, 0.055); + pairsTrigger[0].setCutValue(TriggerModule.CLUSTER_HIT_COUNT_LOW, 1); + pairsTrigger[0].setCutValue(TriggerModule.PAIR_ENERGY_SUM_LOW, 0.010); + pairsTrigger[0].setCutValue(TriggerModule.PAIR_ENERGY_SUM_HIGH, 2.000); + pairsTrigger[0].setCutValue(TriggerModule.PAIR_ENERGY_DIFFERENCE_HIGH, 1.200); + pairsTrigger[0].setCutValue(TriggerModule.PAIR_ENERGY_SLOPE_LOW, 0.400); + pairsTrigger[0].setCutValue(TriggerModule.PAIR_ENERGY_SLOPE_F, 0.0055); + pairsTrigger[0].setCutValue(TriggerModule.PAIR_COPLANARITY_HIGH, 40); + pairsTrigger[0].setCutValue(TriggerModule.PAIR_TIME_COINCIDENCE, 16); + + // Define the second pairs trigger. + pairsTrigger[1] = new TriggerModule(); + pairsTrigger[1].setCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_LOW, 0.010); + pairsTrigger[1].setCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_HIGH, 1.800); + pairsTrigger[1].setCutValue(TriggerModule.CLUSTER_HIT_COUNT_LOW, 2); + pairsTrigger[1].setCutValue(TriggerModule.PAIR_ENERGY_SUM_LOW, 0.020); + pairsTrigger[1].setCutValue(TriggerModule.PAIR_ENERGY_SUM_HIGH, 2.000); + pairsTrigger[1].setCutValue(TriggerModule.PAIR_ENERGY_DIFFERENCE_HIGH, 1.200); + pairsTrigger[1].setCutValue(TriggerModule.PAIR_ENERGY_SLOPE_LOW, 0.400); + pairsTrigger[1].setCutValue(TriggerModule.PAIR_ENERGY_SLOPE_F, 0.0055); + pairsTrigger[1].setCutValue(TriggerModule.PAIR_COPLANARITY_HIGH, 40); + pairsTrigger[1].setCutValue(TriggerModule.PAIR_TIME_COINCIDENCE, 16); + */ + + // 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); + + // Instantiate the triggers lists. + for(int triggerNum = 0; triggerNum < 2; triggerNum++) { + reconPairsTriggers.add(new ArrayList<PairTrigger<Cluster[]>>()); + sspPairsTriggers.add(new ArrayList<PairTrigger<SSPCluster[]>>()); + reconSinglesTriggers.add(new ArrayList<SinglesTrigger<Cluster>>()); + sspSinglesTriggers.add(new ArrayList<SinglesTrigger<SSPCluster>>()); + } + + // Print the initial settings. + logSettings(); + } + + /** + * Prints the total run statistics. + */ + @Override + public void endOfData() { + // 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. + System.out.println("Event Failure Rate:"); + System.out.printf("\tNoise Events :: %d / %d (%7.3f%%)%n", + noiseEvents, totalEvents, (100.0 * noiseEvents / totalEvents)); + System.out.printf("\tCluster Events Failed :: %d / %d (%7.3f%%)%n", + failedClusterEvents, totalEvents, (100.0 * failedClusterEvents / totalEvents)); + System.out.printf("\tSingles Events Failed :: %d / %d (%7.3f%%)%n", + failedSinglesEvents, totalEvents, (100.0 * failedSinglesEvents / totalEvents)); + System.out.printf("\tPair Events Failed :: %d / %d (%7.3f%%)%n", + failedPairEvents, totalEvents, (100.0 * failedPairEvents / totalEvents)); + + // Print the cluster verification data. + System.out.println("Cluster Verification:"); + System.out.printf("\tRecon Clusters :: %d%n", clusterRunStats.getReconClusterCount()); + System.out.printf("\tSSP Clusters :: %d%n", clusterRunStats.getSSPClusterCount()); + System.out.printf("\tClusters Matched :: %d%n", clusterRunStats.getMatches()); + System.out.printf("\tFailed (Position) :: %d%n", clusterRunStats.getPositionFailures()); + System.out.printf("\tFailed (Energy) :: %d%n", clusterRunStats.getEnergyFailures()); + System.out.printf("\tFailed (Hit Count) :: %d%n", clusterRunStats.getHitCountFailures()); + if(clusterRunStats.getReconClusterCount() == 0) { System.out.printf("\tCluster Efficiency :: N/A%n"); } + else { System.out.printf("\tCluster Efficiency :: %7.3f%%%n", 100.0 * clusterRunStats.getMatches() / clusterRunStats.getReconClusterCount()); } + + // Print the trigger verification data. + for(int triggerType = 0; triggerType < 2; triggerType++) { + int spaces = getPrintSpaces(triggerRunStats[triggerType].getSSPSimTriggerCount(), + triggerRunStats[triggerType].getReconTriggerCount(), triggerRunStats[triggerType].getSSPBankTriggerCount(), + triggerRunStats[triggerType].getMatchedSSPTriggers(), triggerRunStats[triggerType].getMatchedReconTriggers()); + System.out.println(); + if(triggerType == 0) { System.out.println("Singles Trigger Verification:"); } + else { System.out.println("Pair Trigger Verification:"); } + System.out.printf("\tSSP Cluster Sim Triggers :: %" + spaces + "d%n", triggerRunStats[triggerType].getSSPSimTriggerCount()); + System.out.printf("\tRecon Cluster Sim Triggers :: %" + spaces + "d%n", triggerRunStats[triggerType].getReconTriggerCount()); + System.out.printf("\tSSP Reported Triggers :: %" + spaces + "d%n", triggerRunStats[triggerType].getSSPBankTriggerCount()); + System.out.printf("\tExtra Reported Triggers :: %" + spaces + "d%n", triggerRunStats[triggerType].getExtraSSPBankTriggers()); + + System.out.printf("\tInternal Efficiency :: %" + spaces + "d / %" + spaces + "d ", + triggerRunStats[triggerType].getMatchedSSPTriggers(), triggerRunStats[triggerType].getSSPSimTriggerCount()); + if(triggerRunStats[triggerType].getSSPSimTriggerCount() == 0) { System.out.printf("(N/A)%n"); } + else { + System.out.printf("(%7.3f%%)%n", (100.0 * triggerRunStats[triggerType].getMatchedSSPTriggers() / triggerRunStats[triggerType].getSSPSimTriggerCount())); + } + + System.out.printf("\tTrigger Efficiency :: %" + spaces + "d / %" + spaces + "d ", + triggerRunStats[triggerType].getMatchedReconTriggers(), triggerRunStats[triggerType].getReconTriggerCount()); + if(triggerRunStats[triggerType].getReconTriggerCount() == 0) { System.out.printf("(N/A)%n"); } + else { System.out.printf("(%7.3f%%)%n" , (100.0 * triggerRunStats[triggerType].getMatchedReconTriggers() / triggerRunStats[triggerType].getReconTriggerCount())); } + + // Print the individual cut performances. + int halfSSPTriggers = triggerRunStats[triggerType].getSSPSimTriggerCount() / 2; + if(triggerType == 0) { + for(int triggerNum = 0; triggerNum < 2; triggerNum++) { + System.out.println(); + System.out.printf("\tTrigger %d Individual Cut Failure Rate:%n", (triggerNum + 1)); + if(triggerRunStats[0].getSSPSimTriggerCount() == 0) { + System.out.printf("\t\tCluster Energy Lower Bound :: %" + spaces + "d / %" + spaces + "d%n", + triggerRunStats[0].getCutFailures(triggerNum, ENERGY_MIN), halfSSPTriggers); + System.out.printf("\t\tCluster Energy Upper Bound :: %" + spaces + "d / %" + spaces + "d%n", + triggerRunStats[0].getCutFailures(triggerNum, ENERGY_MAX), halfSSPTriggers); + System.out.printf("\t\tCluster Hit Count :: %" + spaces + "d / %" + spaces + "d%n", + triggerRunStats[0].getCutFailures(triggerNum, HIT_COUNT), halfSSPTriggers); + } else { + System.out.printf("\t\tCluster Energy Lower Bound :: %" + spaces + "d / %" + spaces + "d (%7.3f%%)%n", + triggerRunStats[0].getCutFailures(triggerNum, ENERGY_MIN), halfSSPTriggers, + (100.0 * triggerRunStats[0].getCutFailures(triggerNum, ENERGY_MIN) / halfSSPTriggers)); + System.out.printf("\t\tCluster Energy Upper Bound :: %" + spaces + "d / %" + spaces + "d (%7.3f%%)%n", + triggerRunStats[0].getCutFailures(triggerNum, ENERGY_MAX), halfSSPTriggers, + (100.0 * triggerRunStats[0].getCutFailures(triggerNum, ENERGY_MAX) / halfSSPTriggers)); + System.out.printf("\t\tCluster Hit Count :: %" + spaces + "d / %" + spaces + "d (%7.3f%%)%n", + triggerRunStats[0].getCutFailures(triggerNum, HIT_COUNT), halfSSPTriggers, + (100.0 * triggerRunStats[0].getCutFailures(triggerNum, HIT_COUNT) / halfSSPTriggers)); + } + } + } else { + for(int triggerNum = 0; triggerNum < 2; triggerNum++) { + System.out.println(); + System.out.printf("\tTrigger %d Individual Cut Failure Rate:%n", (triggerNum + 1)); + if(triggerRunStats[1].getSSPSimTriggerCount() == 0) { + System.out.printf("\t\tPair Energy Sum :: %" + spaces + "d / %" + spaces + "d%n", + triggerRunStats[1].getCutFailures(triggerNum, ENERGY_SUM), halfSSPTriggers); + System.out.printf("\t\tPair Energy Difference :: %" + spaces + "d / %" + spaces + "d%n", + triggerRunStats[1].getCutFailures(triggerNum, ENERGY_DIFF), halfSSPTriggers); + System.out.printf("\t\tPair Energy Slope :: %" + spaces + "d / %" + spaces + "d%n", + triggerRunStats[1].getCutFailures(triggerNum, ENERGY_SLOPE), halfSSPTriggers); + System.out.printf("\t\tPair Coplanarity :: %" + spaces + "d / %" + spaces + "d%n", + triggerRunStats[1].getCutFailures(triggerNum, COPLANARITY), halfSSPTriggers); + } else { + System.out.printf("\t\tPair Energy Sum :: %" + spaces + "d / %" + spaces + "d (%7.3f%%)%n", + triggerRunStats[1].getCutFailures(triggerNum, ENERGY_SUM), halfSSPTriggers, + (100.0 * triggerRunStats[1].getCutFailures(triggerNum, ENERGY_SUM) / halfSSPTriggers)); + System.out.printf("\t\tPair Energy Difference :: %" + spaces + "d / %" + spaces + "d (%7.3f%%)%n", + triggerRunStats[1].getCutFailures(triggerNum, ENERGY_DIFF), halfSSPTriggers, + (100.0 * triggerRunStats[1].getCutFailures(triggerNum, ENERGY_DIFF) / halfSSPTriggers)); + System.out.printf("\t\tPair Energy Slope :: %" + spaces + "d / %" + spaces + "d (%7.3f%%)%n", + triggerRunStats[1].getCutFailures(triggerNum, ENERGY_SLOPE), halfSSPTriggers, + (100.0 * triggerRunStats[1].getCutFailures(triggerNum, ENERGY_SLOPE) / halfSSPTriggers)); + System.out.printf("\t\tPair Coplanarity :: %" + spaces + "d / %" + spaces + "d (%7.3f%%)%n", + triggerRunStats[1].getCutFailures(triggerNum, COPLANARITY), halfSSPTriggers, + (100.0 * triggerRunStats[1].getCutFailures(triggerNum, COPLANARITY) / halfSSPTriggers)); + } + } + } + } + + this.efficiencyRunStats.printModule(); + } + + /** + * Gets the banks and clusters from the event. + */ + @Override + public void process(EventHeader event) { + // ========================================================== + // ==== Initialize the Event ================================ + // ========================================================== + + // If DAQ settings are to be used, check if they are initialized + // yet. If not, skip the event. + if(readDAQConfig) { + if(!ConfigurationManager.isInitialized()) { + return; + } + } + + // Print the verification header. + OutputLogger.printNewLine(2); + OutputLogger.println("======================================================================"); + OutputLogger.println("==== Cluster/Trigger Verification ===================================="); + OutputLogger.println("======================================================================"); + + // Increment the total event count. + totalEvents++; + + // Reset the output buffer and print flags. + clusterFail = false; + singlesInternalFail = false; + singlesEfficiencyFail = false; + pairInternalFail = false; + pairEfficiencyFail = false; + + + + // ========================================================== + // ==== Obtain SSP and TI Banks ============================= + // ========================================================== + + // Get the SSP clusters. + if(event.hasCollection(GenericObject.class, bankCollectionName)) { + // Get the bank list. + List<GenericObject> bankList = event.get(GenericObject.class, bankCollectionName); + + // Search through the banks and get the SSP and TI banks. + for(GenericObject obj : bankList) { + // If this is an SSP bank, parse it. + if(AbstractIntData.getTag(obj) == SSPData.BANK_TAG) { + sspBank = new SSPData(obj); + } + + // Otherwise, if this is a TI bank, parse it. + else if(AbstractIntData.getTag(obj) == TIData.BANK_TAG) { + tiBank = new TIData(obj); + + if(tiBank.isPulserTrigger()) { + OutputLogger.println("Trigger type :: Pulser"); + activeTrigger = TriggerDiagnosticUtil.TRIGGER_PULSER; + } else if(tiBank.isSingle0Trigger()) { + OutputLogger.println("Trigger type :: Singles 1"); + activeTrigger = TriggerDiagnosticUtil.TRIGGER_SINGLES_1; + } else if(tiBank.isSingle1Trigger()) { + OutputLogger.println("Trigger type :: Singles 2"); + activeTrigger = TriggerDiagnosticUtil.TRIGGER_SINGLES_2; + } else if(tiBank.isPair0Trigger()) { + OutputLogger.println("Trigger type :: Pair 1"); + activeTrigger = TriggerDiagnosticUtil.TRIGGER_PAIR_1; + } else if(tiBank.isPair1Trigger()) { + OutputLogger.println("Trigger type :: Pair 2"); + activeTrigger = TriggerDiagnosticUtil.TRIGGER_PAIR_2; + } else if(tiBank.isCalibTrigger()) { + OutputLogger.println("Trigger type :: Cosmic"); + activeTrigger = TriggerDiagnosticUtil.TRIGGER_COSMIC; + } + } + } + + // If there is an SSP bank, get the list of SSP clusters. + if(sspBank != null) { + sspClusters = sspBank.getClusters(); + if(sspClusters.size() == 1) { + OutputLogger.println("1 SSP cluster found."); + } else { + OutputLogger.printf("%d SSP clusters found.%n", sspClusters.size()); + } + } + } + + + + // ========================================================== + // ==== Establish Event Integrity =========================== + // ========================================================== + + // Check that all of the required objects are present. + if(sspBank == null) { + OutputLogger.println("No SSP bank found for this event. No verification will be performed."); + if(verbose) { OutputLogger.printLog(); } + return; + } if(tiBank == null) { + OutputLogger.println("No TI bank found for this event. No verification will be performed."); + if(verbose) { OutputLogger.printLog(); } + return; + } + + + + // ========================================================== + // ==== Check the Noise Level =============================== + // ========================================================== + + // Check if there are hits. + if(event.hasCollection(CalorimeterHit.class, hitCollectionName)) { + // Check if there are more hits than the noise threshold. + if(event.get(CalorimeterHit.class, hitCollectionName).size() >= noiseThreshold) { + noiseEvents++; + OutputLogger.println("Noise event detected. Skipping event..."); + if(verbose) { OutputLogger.printLog(); } + return; + } + } + + + + // ========================================================== + // ==== Obtain Reconstructed Clusters ======================= + // ========================================================== + + // Clear the list of triggers from previous events. + for(int triggerNum = 0; triggerNum < 2; triggerNum++) { + sspSinglesTriggers.get(triggerNum).clear(); + reconSinglesTriggers.get(triggerNum).clear(); + sspPairsTriggers.get(triggerNum).clear(); + reconPairsTriggers.get(triggerNum).clear(); + } + + // Get the reconstructed clusters. + if(event.hasCollection(Cluster.class, clusterCollectionName)) { + // Get the reconstructed clusters. + List<Cluster> allClusters = event.get(Cluster.class, clusterCollectionName); + + // Keep only the clusters that can be verified. + OutputLogger.println(); + OutputLogger.println("Process cluster for verifiability:"); + reconClusters.clear(); + for(Cluster reconCluster : allClusters) { + // Check that the cluster is within the safe region of the + // FADC readout window. If it is not, it will likely have + // inaccurate energy or hit values and may not produce the + // expected results. + OutputLogger.printf("\t%s", TriggerDiagnosticUtil.clusterToString(reconCluster)); + if(isVerifiable(reconCluster)) { + reconClusters.add(reconCluster); + OutputLogger.println(" [ verifiable ]"); + } else { OutputLogger.println(" [ unverifiable ]"); } + + } + + // Output the number of verifiable clusters found. + if(reconClusters.size() == 1) { OutputLogger.println("1 verifiable reconstructed cluster found."); } + else { OutputLogger.printf("%d verifiable reconstructed clusters found.%n", reconClusters.size()); } + + // Output the number of unverifiable clusters found. + int unverifiableClusters = allClusters.size() - reconClusters.size(); + if(unverifiableClusters == 1) { OutputLogger.println("1 unverifiable reconstructed cluster found."); } + else { OutputLogger.printf("%d unverifiable reconstructed clusters found.%n", unverifiableClusters); } + } else { + reconClusters = new ArrayList<Cluster>(0); + OutputLogger.printf("No reconstructed clusters were found for collection \"%s\" in this event.%n", clusterCollectionName); + } + + + + // ========================================================== + // ==== Perform Event Verification ========================== + // ========================================================== + + // Perform the cluster verification step. + if(performClusterVerification) { clusterVerification(); } + + // Construct lists of triggers for the SSP clusters and the + // reconstructed clusters. + if(performSinglesTriggerVerification) { + constructSinglesTriggers(); + singlesTriggerVerification(); + } + if(performPairTriggerVerification) { + constructPairTriggers(); + pairTriggerVerification(); + } + + // Track how many events failed due to each type of verification. + if(clusterFail) { failedClusterEvents++; } + if(pairInternalFail || pairEfficiencyFail) { failedPairEvents++; } + if(singlesInternalFail || singlesEfficiencyFail) { failedSinglesEvents++; } + + + + // ========================================================== + // ==== Perform Event Write-Out ============================= + // ========================================================== + + if(verbose ||(clusterFail && printClusterFail) || + (singlesInternalFail && printSinglesTriggerInternalFail) || + (singlesEfficiencyFail && printSinglesTriggerEfficiencyFail) || + (pairInternalFail && printPairTriggerInternalFail) || + (pairEfficiencyFail && printPairTriggerEfficiencyFail)) { + OutputLogger.printLog(); + } + + + + // ========================================================== + // ==== Process Local Tracked Variables ===================== + // ========================================================== + if(Calendar.getInstance().getTimeInMillis() - localWindowStart > localWindowThreshold) { + // Write a snapshot of the driver to the event stream. + DiagSnapshot snapshot = new DiagSnapshot(clusterLocalStats, clusterRunStats, + triggerLocalStats[0], triggerRunStats[0], triggerLocalStats[1], + triggerRunStats[1], efficiencyRunStats, efficiencyLocalStats); + + // Push the snapshot to the data stream. + List<DiagSnapshot> snapshotCollection = new ArrayList<DiagSnapshot>(1); + snapshotCollection.add(snapshot); + event.put(diagnosticCollectionName, snapshotCollection); + + // Clear the local statistical data. + clusterLocalStats.clear(); + triggerLocalStats[0].clear(); + triggerLocalStats[1].clear(); + efficiencyLocalStats.clear(); + + // Update the last write time. + localWindowStart = Calendar.getInstance().getTimeInMillis(); + } + } + + 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 setReadDAQConfig(boolean state) { + readDAQConfig = state; + } + + /** + * Attempts to match all reconstructed clusters that are safely + * within the integration window with clusters reported by the SSP. + * Method also tracks the ratio of valid reconstructed clusters to + * matches found.<br/> + * <br/> + * Note that unmatched SSP clusters are ignored. Since these may + * or may not correspond to reconstructed clusters that occur in + * the forbidden time region, it is impossible to say whether or + * not these legitimately failed to match or not. + */ + private void clusterVerification() { + // ========================================================== + // ==== Initialize Cluster Verification ===================== + // ========================================================== + + // Print the cluster verification header. + OutputLogger.printNewLine(2); + OutputLogger.println("======================================================================"); + OutputLogger.println("=== Cluster Verification ============================================="); + OutputLogger.println("======================================================================"); + + // Track the number of cluster pairs that were matched and that + // failed by failure type. + ClusterMatchEvent event = new ClusterMatchEvent(); + + + + // ========================================================== + // ==== Produce the Cluster Position Mappings =============== + // ========================================================== + + // Create maps to link cluster position to the list of clusters + // that were found at that location. + Map<Point, List<Cluster>> reconClusterMap = new HashMap<Point, List<Cluster>>(reconClusters.size()); + Map<Point, List<SSPCluster>> sspClusterMap = new HashMap<Point, List<SSPCluster>>(reconClusters.size()); + + // Populate the reconstructed cluster map. + for(Cluster reconCluster : reconClusters) { + // Get the cluster position. + Point position = new Point(TriggerDiagnosticUtil.getXIndex(reconCluster), + TriggerDiagnosticUtil.getYIndex(reconCluster)); + + // Get the list for this cluster position. + List<Cluster> reconList = reconClusterMap.get(position); + if(reconList == null) { + reconList = new ArrayList<Cluster>(); + reconClusterMap.put(position, reconList); + } + + // Add the cluster to the list. + reconList.add(reconCluster); + } + + // Populate the SSP cluster map. + for(SSPCluster sspCluster : sspClusters) { + // Get the cluster position. + Point position = new Point(sspCluster.getXIndex(), sspCluster.getYIndex()); + + // Get the list for this cluster position. + List<SSPCluster> sspList = sspClusterMap.get(position); + if(sspList == null) { + sspList = new ArrayList<SSPCluster>(); + sspClusterMap.put(position, sspList); + } + + // Add the cluster to the list. + sspList.add(sspCluster); + } + + + + // ========================================================== + // ==== Perform Cluster Matching ============================ + // ========================================================== + + // For each reconstructed cluster, attempt to match the clusters + // with SSP clusters at the same position. + positionLoop: + for(Entry<Point, List<Cluster>> clusterSet : reconClusterMap.entrySet()) { + // Get the reconstructed and SSP clusters at this position. + List<Cluster> reconList = clusterSet.getValue(); + List<SSPCluster> sspList = sspClusterMap.get(clusterSet.getKey()); + + // Print the crystal position header. + OutputLogger.println(); + OutputLogger.printf("Considering clusters at (%3d, %3d)%n", clusterSet.getKey().x, clusterSet.getKey().y); + + // If there are no SSP clusters, then matching fails by + // reason of position. The remainder of the loop may be + // skipped, since there is nothing to check. + if(sspList == null || sspList.isEmpty()) { + clusterFail = true; + for(Cluster cluster : reconList) { + event.pairFailPosition(cluster, null); + } + continue positionLoop; + } + + // If there are more reconstructed clusters than there are + // SSP clusters, than a number equal to the difference must + // fail by means of positions. + if(sspList.size() < reconList.size()) { clusterFail = true; } + + // Get all possible permutations of SSP clusters. + List<List<Pair<Cluster, SSPCluster>>> permutations = getPermutations(reconList, sspList); + + // Print the information for this crystal position. + OutputLogger.printf("\tRecon Clusters :: %d%n", reconList.size()); + OutputLogger.printf("\tSSP Clusters :: %d%n", sspList.size()); + OutputLogger.printf("\tPermutations :: %d%n", permutations.size()); + + // Track the plotted values for the current best permutation. + ClusterMatchEvent bestPerm = null; + + // Iterate over the permutations and find the permutation + // that produces the best possible result when compared to + // the reconstructed clusters. + int permIndex = 0; + for(List<Pair<Cluster, SSPCluster>> pairs : permutations) { + // Update the current permutation number. + permIndex++; + + // Track the plot values for this permutation. + ClusterMatchEvent perm = new ClusterMatchEvent(); + + // Try to match each pair. + pairLoop: + for(Pair<Cluster, SSPCluster> pair : pairs) { + // Print the current reconstructed/SSP cluster pair. + OutputLogger.printf("\tP%d :: %s --> %s", permIndex, + pair.getFirstElement() == null ? "None" : TriggerDiagnosticUtil.clusterToString(pair.getFirstElement()), + pair.getSecondElement() == null ? "None" : TriggerDiagnosticUtil.clusterToString(pair.getSecondElement())); + + // If either cluster in the pair is null, there + // are not enough clusters to perform this match. + if(pair.getFirstElement() == null || pair.getSecondElement() == null) { + OutputLogger.printf(" [ %18s ]%n", "failure: unpaired"); + perm.pairFailPosition(pair.getFirstElement(), pair.getSecondElement()); + continue pairLoop; + } + + // Check if the reconstructed cluster has an energy + // within the allotted threshold of the SSP cluster. + if(pair.getSecondElement().getEnergy() >= pair.getFirstElement().getEnergy() * (1 - energyAcceptance) && + pair.getSecondElement().getEnergy() <= pair.getFirstElement().getEnergy() * (1 + energyAcceptance)) { + // Check that the hit count of the reconstructed + // is within the allotted threshold of the SSP + // cluster. + if(pair.getSecondElement().getHitCount() >= pair.getFirstElement().getCalorimeterHits().size() - hitAcceptance && + pair.getSecondElement().getHitCount() <= pair.getFirstElement().getCalorimeterHits().size() + hitAcceptance) { + // Designate the pair as a match. + perm.pairMatch(pair.getFirstElement(), pair.getSecondElement()); + OutputLogger.printf(" [ %18s ]%n", "success: matched"); + } else { + perm.pairFailHitCount(pair.getFirstElement(), pair.getSecondElement()); + OutputLogger.printf(" [ %18s ]%n", "failure: hit count"); + } // End hit count check. + } else { + perm.pairFailEnergy(pair.getFirstElement(), pair.getSecondElement()); + OutputLogger.printf(" [ %18s ]%n", "failure: energy"); + } // End energy check. + } // End Pair Loop + + // Print the results of the permutation. + OutputLogger.printf("\t\tPermutation Matched :: %d%n", perm.getMatches()); + OutputLogger.printf("\t\tPermutation Energy :: %d%n", perm.getEnergyFailures()); + OutputLogger.printf("\t\tPermutation Hit Count :: %d%n", perm.getHitCountFailures()); + + // Check whether the results from this permutation + // exceed the quality of the last best results. A + // greater number of matches is always better. If the + // matches are the same, select the one with fewer + // failures due to energy. + bestPerm = getBestPermutation(bestPerm, perm); + } // End Permutation Loop + + // Print the final results for the position. + OutputLogger.printf("\tPosition Matched :: %d%n", bestPerm.getMatches()); + OutputLogger.printf("\tPosition Energy :: %d%n", bestPerm.getEnergyFailures()); + OutputLogger.printf("\tPosition Hit Count :: %d%n", bestPerm.getHitCountFailures()); + + // Add the results from the best-matched permutation + // to the event efficiency results. + event.addEvent(bestPerm); + } // End Crystal Position Loop + + // Add the event results to the global results. + clusterRunStats.addEvent(event, reconClusters, sspClusters); + clusterLocalStats.addEvent(event, reconClusters, sspClusters); + + + + // ========================================================== + // ==== Output Event Summary ================================ + // ========================================================== + + // Print the valid reconstructed clusters and populate their + // distribution graphs. + OutputLogger.println(); + OutputLogger.println("Verified Reconstructed Clusters:"); + if(!reconClusters.isEmpty()) { + for(Cluster reconCluster : reconClusters) { + OutputLogger.printf("\t%s%n", TriggerDiagnosticUtil.clusterToString(reconCluster)); + } + } else { OutputLogger.println("\tNone"); } + + // Print the SSP clusters and populate their distribution graphs. + OutputLogger.println("SSP Clusters:"); + if(!sspClusters.isEmpty()) { + for(SSPCluster sspCluster : sspClusters) { + OutputLogger.printf("\t%s%n", TriggerDiagnosticUtil.clusterToString(sspCluster)); + } + } else { OutputLogger.println("\tNone"); } + + // Print the matched clusters. + OutputLogger.println("Matched Clusters:"); + if(event.getMatchedPairs().size() != 0) { + // Iterate over the matched pairs. + for(ClusterMatchedPair pair : event.getMatchedPairs()) { + // If the pair is a match, print it out. + if(pair.isMatch()) { + OutputLogger.printf("\t%s --> %s%n", + TriggerDiagnosticUtil.clusterToString(pair.getReconstructedCluster()), + TriggerDiagnosticUtil.clusterToString(pair.getSSPCluster())); + } + } + } + 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(); + OutputLogger.println("Event Statistics:"); + OutputLogger.printf("\tRecon Clusters :: %d%n", reconClusters.size()); + OutputLogger.printf("\tClusters Matched :: %d%n", event.getMatches()); + OutputLogger.printf("\tFailed (Position) :: %d%n", failPosition); + OutputLogger.printf("\tFailed (Energy) :: %d%n", event.getEnergyFailures()); + OutputLogger.printf("\tFailed (Hit Count) :: %d%n", event.getHitCountFailures()); + OutputLogger.printf("\tCluster Efficiency :: %3.0f%%%n", 100.0 * event.getMatches() / reconClusters.size()); + + // Note whether there was a cluster match failure. + if(event.getMatches() - reconClusters.size() != 0) { + clusterFail = true; + } + } + + /** + * Checks triggers simulated on SSP clusters against the SSP bank's + * reported triggers to verify that the trigger is correctly applying + * cuts to the clusters it sees. Additionally compares triggers + * simulated on reconstructed clusters to measure trigger efficiency. + */ + private void singlesTriggerVerification() { + // Create lists of generic triggers. + List<List<? extends Trigger<?>>> sspTriggerList = new ArrayList<List<? extends Trigger<?>>>(2); + List<List<? extends Trigger<?>>> reconTriggerList = new ArrayList<List<? extends Trigger<?>>>(2); + + // Convert the simulated triggers to generic versions and add + // them to the generic list. + for(int triggerNum = 0; triggerNum < 2; triggerNum++) { + // Get the generic trigger list. + List<? extends Trigger<?>> sspTriggers = sspSinglesTriggers.get(triggerNum); + List<? extends Trigger<?>> reconTriggers = reconSinglesTriggers.get(triggerNum); + + // Add it to the generic list. + sspTriggerList.add(sspTriggers); + reconTriggerList.add(reconTriggers); + } + + // Run generic trigger verification. + triggerVerification(sspTriggerList, reconTriggerList, true); + } + + /** + * Checks triggers simulated on SSP clusters against the SSP bank's + * reported triggers to verify that the trigger is correctly applying + * cuts to the clusters it sees. Additionally compares triggers + * simulated on reconstructed clusters to measure trigger efficiency. + */ + private void pairTriggerVerification() { + // Create lists of generic triggers. + List<List<? extends Trigger<?>>> sspTriggerList = new ArrayList<List<? extends Trigger<?>>>(2); + List<List<? extends Trigger<?>>> reconTriggerList = new ArrayList<List<? extends Trigger<?>>>(2); + + // Convert the simulated triggers to generic versions and add + // them to the generic list. + for(int triggerNum = 0; triggerNum < 2; triggerNum++) { + // Get the generic trigger list. + List<? extends Trigger<?>> sspTriggers = sspPairsTriggers.get(triggerNum); + List<? extends Trigger<?>> reconTriggers = reconPairsTriggers.get(triggerNum); + + // Add it to the generic list. + sspTriggerList.add(sspTriggers); + reconTriggerList.add(reconTriggers); + } + + // Run generic trigger verification. + triggerVerification(sspTriggerList, reconTriggerList, false); + } + + /** + * Performs trigger verification for both trigger types. + * @param sspTriggerList - The list of SSP triggers. + * @param reconTriggerList - The list of reconstructed triggers. + * @param isSingles - Whether or not this is a singles trigger + * verification. + */ + private void triggerVerification(List<List<? extends Trigger<?>>> sspTriggerList, + List<List<? extends Trigger<?>>> reconTriggerList, boolean isSingles) { + + // ========================================================== + // ==== Initialize Trigger Verification ===================== + // ========================================================== + + // Print the cluster verification header. + OutputLogger.println(); + OutputLogger.println(); + OutputLogger.println("======================================================================"); + if(isSingles) { OutputLogger.println("=== Singles Trigger Verification ====================================="); } + else { OutputLogger.println("=== Pair Trigger Verification ========================================"); } + OutputLogger.println("======================================================================"); + + // Track the number of triggers seen and the number found. + TriggerMatchEvent event = new TriggerMatchEvent(); + + // ========================================================== + // ==== Output Event Summary ================================ + // ========================================================== + + // Get the list of triggers reported by the SSP. + List<? extends SSPNumberedTrigger> sspTriggers; + if(isSingles) { sspTriggers = sspBank.getSinglesTriggers(); } + else { sspTriggers = sspBank.getPairTriggers(); } + + // Output the SSP cluster singles triggers. + OutputLogger.println(); + OutputLogger.println("SSP Cluster " + (isSingles ? "Singles" : "Pair") + " Triggers"); + for(int triggerNum = 0; triggerNum < 2; triggerNum++) { + for(Trigger<?> simTrigger : sspTriggerList.get(triggerNum)) { + OutputLogger.printf("\tTrigger %d :: %s :: %s%n", + (triggerNum + 1), triggerPositionString(simTrigger), + simTrigger.toString()); + } + } + if(sspTriggerList.get(0).size() + sspTriggerList.get(1).size() == 0) { + OutputLogger.println("\tNone"); + } + + // Output the reconstructed cluster singles triggers. + OutputLogger.println("Reconstructed Cluster " + (isSingles ? "Singles" : "Pair") + " Triggers"); + for(int triggerNum = 0; triggerNum < 2; triggerNum++) { + for(Trigger<?> simTrigger : reconTriggerList.get(triggerNum)) { + OutputLogger.printf("\tTrigger %d :: %s :: %s%n", + (triggerNum + 1), triggerPositionString(simTrigger), + simTrigger.toString()); + } + } + if(reconTriggerList.get(0).size() + reconTriggerList.get(1).size() == 0) { + OutputLogger.println("\tNone"); + } + + // Output the SSP reported triggers. + OutputLogger.println("SSP Reported " + (isSingles ? "Singles" : "Pair") + " Triggers"); + for(SSPTrigger sspTrigger : sspTriggers) { + OutputLogger.printf("\t%s%n", sspTrigger.toString()); + } + if(sspTriggers.size() == 0) { OutputLogger.println("\tNone"); } + + + + // ========================================================== + // ==== SSP Internal Logic Verification ===================== + // ========================================================== + + // Track which SSP triggers have been matched to avoid matching + // multiple reconstructed SSP cluster triggers to the same SSP + // trigger. + Set<SSPNumberedTrigger> sspTriggerSet = new HashSet<SSPNumberedTrigger>(); + Set<Trigger<?>> simTriggerSet = new HashSet<Trigger<?>>(); + + // Track the number of SSP reported triggers that are found in + // excess of the SSP simulated triggers. + int sspReportedExtras = sspTriggers.size() - (sspTriggerList.get(0).size() + sspTriggerList.get(1).size()); + if(sspReportedExtras > 0) { + if(isSingles) { singlesInternalFail = true; } + else { pairInternalFail = true; } + } + + // Iterate over the triggers. + OutputLogger.println(); + OutputLogger.println("SSP Reported Trigger --> SSP Cluster Trigger Match Status"); + for(SSPNumberedTrigger sspTrigger : sspTriggers) { + // Get the trigger information. + int triggerNum = sspTrigger.isFirstTrigger() ? 0 : 1; + boolean matchedTrigger = false; + + // Iterate over the SSP cluster simulated triggers and + // look for a trigger that matches. + matchLoop: + for(Trigger<?> simTrigger : sspTriggerList.get(triggerNum)) { + // If the current SSP trigger has already been + // matched, skip it. + if(sspTriggerSet.contains(sspTrigger)) { continue matchLoop; } + + // Otherwise, check whether the reconstructed SSP + // cluster trigger matches the SSP trigger. + if(compareTriggers(sspTrigger, simTrigger)) { + matchedTrigger = true; + sspTriggerSet.add(sspTrigger); + simTriggerSet.add(simTrigger); + event.matchedSSPPair(simTrigger, sspTrigger); + break matchLoop; + } + + OutputLogger.printf("\t%s :: Matched: %5b%n", sspTrigger.toString(), matchedTrigger); + } + } + + // Iterate over the unmatched simulated triggers again and the + // unmatched SSP reported trigger that most closely matches it. + simLoop: + for(int triggerNum = 0; triggerNum < 2; triggerNum++) { + for(Trigger<?> simTrigger : sspTriggerList.get(triggerNum)) { + // Check whether this trigger has already been matched + // or not. If it has been matched, skip it. + if(simTriggerSet.contains(simTrigger)) { continue simLoop; } + + // Get the trigger time for the simulated trigger. + double simTime = getTriggerTime(simTrigger); + + // Track the match statistics for each reported trigger + // so that the closest match may be found. + int numMatched = -1; + boolean[] matchedCut = null; + SSPNumberedTrigger bestMatch = null; + + // Iterate over the reported triggers to find a match. + reportedLoop: + for(SSPNumberedTrigger sspTrigger : sspTriggers) { + // If the two triggers have different times, this + // trigger should be skipped. + if(sspTrigger.getTime() != simTime) { + continue reportedLoop; + } + + // If this reported trigger has been matched then + // it should be skipped. + if(sspTriggerSet.contains(sspTrigger)) { continue reportedLoop; } + + // Check each of the cuts. + boolean[] tempMatchedCut = triggerCutMatch(simTrigger, sspTrigger); + + // Check each cut and see if this is a closer match + // than the previous best match. + int tempNumMatched = 0; + for(boolean passed : tempMatchedCut) { if(passed) { tempNumMatched++; } } + + // If the number of matched cuts exceeds the old + // best result, this becomes the new best result. + if(tempNumMatched > numMatched) { + numMatched = tempNumMatched; + matchedCut = tempMatchedCut; + bestMatch = sspTrigger; + } + } + + // If there was no match found, it means that there were + // no triggers that were both unmatched and at the same + // time as this simulated trigger. + + if(bestMatch == null) { + if(isSingles) { singlesInternalFail = true; } + else { pairInternalFail = true; } + } else { + event.matchedSSPPair(simTrigger, bestMatch, matchedCut); + } + } + } + + + + // ========================================================== + // ==== Trigger Efficiency ================================== + // ========================================================== + + // Reset the SSP matched trigger set. + sspTriggerSet.clear(); + + // Iterate over the reconstructed cluster singles triggers. + OutputLogger.println(); + OutputLogger.println("Recon Cluster Trigger --> SSP Reported Trigger Match Status"); + for(int triggerNum = 0; triggerNum < 2; triggerNum++) { + for(Trigger<?> simTrigger : reconTriggerList.get(triggerNum)) { + + OutputLogger.printf("\tTrigger %d :: %s :: %s%n", (triggerNum + 1), + triggerPositionString(simTrigger), simTrigger.toString()); + + // Iterate over the SSP reported triggers and compare + // them to the reconstructed cluster simulated trigger. + matchLoop: + for(SSPNumberedTrigger sspTrigger : sspTriggers) { + OutputLogger.printf("\t\t\t%s", sspTrigger.toString()); + + // Only compare triggers if they are from the + // same trigger source. + if((triggerNum == 0 && sspTrigger.isSecondTrigger()) + || (triggerNum == 1 && sspTrigger.isFirstTrigger())) { + OutputLogger.print(" [ fail; source ]%n"); + continue matchLoop; + } + + // Only compare the singles trigger if it was + // not already matched to another trigger. + if(sspTriggerSet.contains(sspTrigger)) { + OutputLogger.print(" [ fail; matched ]%n"); + continue matchLoop; + } + + // 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++) { + if(!matchedCuts[cutIndex]) { + OutputLogger.printf(" [ fail; %-9s ]%n", cutNames[typeIndex][cutIndex]); + continue matchLoop; + } + } + + // If all the trigger flags match, then the + // triggers are a match. + sspTriggerSet.add(sspTrigger); + event.matchedReconPair(simTrigger, sspTrigger); + OutputLogger.print(" [ success ]%n"); + break matchLoop; + } + } + } + + + + // ========================================================== + // ==== Output Event Results ================================ + // ========================================================== + + // Get the number of SSP and reconstructed cluster simulated + // triggers. + + int sspSimTriggers = sspTriggerList.get(0).size() + sspTriggerList.get(1).size(); + int reconSimTriggers = reconTriggerList.get(0).size() + reconTriggerList.get(1).size(); + int halfSimTriggers = sspSimTriggers / 2; + + // Print event statistics. + OutputLogger.println(); + OutputLogger.println("Event Statistics:"); + 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()); + if(sspSimTriggers == 0) { + OutputLogger.printf("\tInternal Efficiency :: %d / %d (N/A)%n", + event.getMatchedSSPTriggers(), sspSimTriggers); + } else { + OutputLogger.printf("\tInternal Efficiency :: %d / %d (%3.0f%%)%n", + event.getMatchedSSPTriggers(), sspSimTriggers, (100.0 * event.getMatchedSSPTriggers() / sspSimTriggers)); + } + if(reconSimTriggers == 0) { + OutputLogger.printf("\tTrigger Efficiency :: %d / %d (N/A)%n", + event.getMatchedReconTriggers(), reconSimTriggers); + } else { + OutputLogger.printf("\tTrigger Efficiency :: %d / %d (%3.0f%%)%n", + event.getMatchedReconTriggers(), reconSimTriggers, (100.0 * event.getMatchedReconTriggers() / reconSimTriggers)); + } + + // Print the individual cut performances. + if(isSingles) { + OutputLogger.println(); + for(int triggerNum = 0; triggerNum < 2; triggerNum++) { + OutputLogger.printf("Trigger %d Individual Cut Failure Rate:%n", (triggerNum + 1)); + if(sspSimTriggers == 0) { + OutputLogger.printf("\tCluster Energy Lower Bound :: %d / %d%n", event.getCutFailures(triggerNum, ENERGY_MIN), halfSimTriggers); + OutputLogger.printf("\tCluster Energy Upper Bound :: %d / %d%n", event.getCutFailures(triggerNum, ENERGY_MAX), halfSimTriggers); + OutputLogger.printf("\tCluster Hit Count :: %d / %d%n", event.getCutFailures(triggerNum, HIT_COUNT), halfSimTriggers); + } else { + OutputLogger.printf("\tCluster Energy Lower Bound :: %d / %d (%3.0f%%)%n", + event.getCutFailures(triggerNum, ENERGY_MIN), halfSimTriggers, (100.0 * event.getCutFailures(triggerNum, ENERGY_MIN) / halfSimTriggers)); + OutputLogger.printf("\tCluster Energy Upper Bound :: %d / %d (%3.0f%%)%n", + event.getCutFailures(triggerNum, ENERGY_MAX), halfSimTriggers, (100.0 * event.getCutFailures(triggerNum, ENERGY_MAX) / halfSimTriggers)); + OutputLogger.printf("\tCluster Hit Count :: %d / %d (%3.0f%%)%n", + event.getCutFailures(triggerNum, HIT_COUNT), halfSimTriggers, (100.0 * event.getCutFailures(triggerNum, HIT_COUNT) / halfSimTriggers)); + } + OutputLogger.printf("\tExcess Reported Triggers :: %d%n", sspReportedExtras / 2); + } + + // Update the global trigger tracking variables. + triggerRunStats[0].addEvent(event, reconTriggerList, sspTriggerList, sspTriggers); + triggerLocalStats[0].addEvent(event, reconTriggerList, sspTriggerList, sspTriggers); + efficiencyRunStats.addSinglesTriggers(activeTrigger, reconTriggerList); + efficiencyLocalStats.addSinglesTriggers(activeTrigger, reconTriggerList); + efficiencyRunStats.addEvent(activeTrigger, event); + efficiencyLocalStats.addEvent(activeTrigger, event); + } else { + for(int triggerNum = 0; triggerNum < 2; triggerNum++) { + OutputLogger.println(); + OutputLogger.printf("Trigger %d Individual Cut Failure Rate:%n", (triggerNum + 1)); + if(sspSimTriggers == 0) { + OutputLogger.printf("\tPair Energy Sum :: %d / %d%n", event.getCutFailures(triggerNum, ENERGY_SUM), halfSimTriggers); + OutputLogger.printf("\tPair Energy Difference :: %d / %d%n", event.getCutFailures(triggerNum, ENERGY_DIFF), halfSimTriggers); + OutputLogger.printf("\tPair Energy Slope :: %d / %d%n", event.getCutFailures(triggerNum, ENERGY_SLOPE), halfSimTriggers); + OutputLogger.printf("\tPair Coplanarity :: %d / %d%n", event.getCutFailures(triggerNum, COPLANARITY), halfSimTriggers); + } else { + OutputLogger.printf("\tPair Energy Sum :: %d / %d (%3.0f%%)%n", + event.getCutFailures(triggerNum, ENERGY_SUM), halfSimTriggers, (100.0 * event.getCutFailures(triggerNum, ENERGY_SUM) / halfSimTriggers)); + OutputLogger.printf("\tPair Energy Difference :: %d / %d (%3.0f%%)%n", + event.getCutFailures(triggerNum, ENERGY_DIFF), halfSimTriggers, (100.0 * event.getCutFailures(triggerNum, ENERGY_DIFF) / halfSimTriggers)); + OutputLogger.printf("\tPair Energy Slope :: %d / %d (%3.0f%%)%n", + event.getCutFailures(triggerNum, ENERGY_SLOPE), halfSimTriggers, (100.0 * event.getCutFailures(triggerNum, ENERGY_SLOPE) / halfSimTriggers)); + OutputLogger.printf("\tPair Coplanarity :: %d / %d (%3.0f%%)%n", + event.getCutFailures(triggerNum, COPLANARITY), halfSimTriggers, (100.0 * event.getCutFailures(triggerNum, COPLANARITY) / halfSimTriggers)); + } + OutputLogger.printf("\tExcess Reported Triggers :: %d%n", sspReportedExtras / 2); + } + + // Update the global trigger tracking variables. + triggerRunStats[1].addEvent(event, reconTriggerList, sspTriggerList, sspTriggers); + triggerLocalStats[1].addEvent(event, reconTriggerList, sspTriggerList, sspTriggers); + efficiencyRunStats.addPairTriggers(activeTrigger, reconTriggerList); + efficiencyLocalStats.addSinglesTriggers(activeTrigger, reconTriggerList); + efficiencyRunStats.addEvent(activeTrigger, event); + efficiencyLocalStats.addEvent(activeTrigger, event); + } + + // Note whether the was a trigger match failure. + if((event.getMatchedReconTriggers() - reconSimTriggers != 0) || (event.getMatchedSSPTriggers() - sspSimTriggers != 0)) { + if(isSingles) { singlesEfficiencyFail = true; } + else { pairEfficiencyFail = true; } + } + } + + /** + * Generates and stores the singles triggers for both reconstructed + * and SSP clusters. + */ + private void constructSinglesTriggers() { + // Run the SSP clusters through the singles trigger to determine + // whether they pass it or not. + for(SSPCluster cluster : sspClusters) { + for(int triggerNum = 0; triggerNum < 2; triggerNum++) { + // For a cluster to have formed it is assumed to have passed + // the cluster seed energy cuts. This can not be verified + // since the SSP bank does not report individual hit. + boolean passSeedLow = true; + boolean passSeedHigh = true; + + // The remaining cuts may be acquired from trigger module. + boolean passClusterLow = singlesTrigger[triggerNum].clusterTotalEnergyCutLow(cluster); + boolean passClusterHigh = singlesTrigger[triggerNum].clusterTotalEnergyCutHigh(cluster); + boolean passHitCount = singlesTrigger[triggerNum].clusterHitCountCut(cluster); + + // Make a trigger to store the results. + SinglesTrigger<SSPCluster> trigger = new SinglesTrigger<SSPCluster>(cluster, triggerNum); + trigger.setStateSeedEnergyLow(passSeedLow); + trigger.setStateSeedEnergyHigh(passSeedHigh); + trigger.setStateClusterEnergyLow(passClusterLow); + trigger.setStateClusterEnergyHigh(passClusterHigh); + trigger.setStateHitCount(passHitCount); + + // Store the trigger. + sspSinglesTriggers.get(triggerNum).add(trigger); + } + } + + // Run the reconstructed clusters through the singles trigger + // to determine whether they pass it or not. + for(Cluster cluster : reconClusters) { + // Simulate each of the cluster singles triggers. + for(int triggerNum = 0; triggerNum < 2; triggerNum++) { + // For a cluster to have formed it is assumed to have passed + // the cluster seed energy cuts. This can not be verified + // since the SSP bank does not report individual hit. + boolean passSeedLow = true; + boolean passSeedHigh = true; + + // The remaining cuts may be acquired from trigger module. + boolean passClusterLow = singlesTrigger[triggerNum].clusterTotalEnergyCutLow(cluster); + boolean passClusterHigh = singlesTrigger[triggerNum].clusterTotalEnergyCutHigh(cluster); + boolean passHitCount = singlesTrigger[triggerNum].clusterHitCountCut(cluster); + + // Make a trigger to store the results. + SinglesTrigger<Cluster> trigger = new SinglesTrigger<Cluster>(cluster, triggerNum); + trigger.setStateSeedEnergyLow(passSeedLow); + trigger.setStateSeedEnergyHigh(passSeedHigh); + trigger.setStateClusterEnergyLow(passClusterLow); + trigger.setStateClusterEnergyHigh(passClusterHigh); + trigger.setStateHitCount(passHitCount); + + // Store the trigger. + reconSinglesTriggers.get(triggerNum).add(trigger); + } + } + } + + /** + * Generates and stores the pair triggers for both reconstructed + * and SSP clusters. + */ + private void constructPairTriggers() { + // Store cluster pairs. + List<Cluster> topReconClusters = new ArrayList<Cluster>(); + List<Cluster> bottomReconClusters = new ArrayList<Cluster>(); + List<Cluster[]> reconPairs = new ArrayList<Cluster[]>(); + List<SSPCluster> topSSPClusters = new ArrayList<SSPCluster>(); + List<SSPCluster> bottomSSPClusters = new ArrayList<SSPCluster>(); + List<SSPCluster[]> sspPairs = new ArrayList<SSPCluster[]>(); + + // Split the clusters into lists of top and bottom clusters. + for(Cluster reconCluster : reconClusters) { + if(reconCluster.getCalorimeterHits().get(0).getIdentifierFieldValue("iy") > 0) { + topReconClusters.add(reconCluster); + } else { + bottomReconClusters.add(reconCluster); + } + } + for(SSPCluster sspCluster : sspClusters) { + if(sspCluster.getYIndex() > 0) { + topSSPClusters.add(sspCluster); + } else { + bottomSSPClusters.add(sspCluster); + } + } + + // Form all possible top/bottom cluster pairs. + for(Cluster topReconCluster : topReconClusters) { + for(Cluster bottomReconCluster : bottomReconClusters) { + Cluster[] reconPair = new Cluster[2]; + reconPair[0] = topReconCluster; + reconPair[1] = bottomReconCluster; + reconPairs.add(reconPair); + } + } + for(SSPCluster topSSPCluster : topSSPClusters) { + for(SSPCluster bottomSSPCluster : bottomSSPClusters) { + SSPCluster[] sspPair = new SSPCluster[2]; + sspPair[0] = topSSPCluster; + sspPair[1] = bottomSSPCluster; + sspPairs.add(sspPair); + } + } + + // Simulate the pair triggers and record the results. + for(Cluster[] reconPair : reconPairs) { + // Simulate each of the cluster pair triggers. + reconTriggerLoop: + for(int triggerIndex = 0; triggerIndex < 2; triggerIndex++) { + // Check that the pair passes the time coincidence cut. + // If it does not, it is not a valid pair and should be + // destroyed. + if(!pairsTrigger[triggerIndex].pairTimeCoincidenceCut(reconPair)) { + continue reconTriggerLoop; + } + + // For a cluster to have formed it is assumed to have passed + // the cluster seed energy cuts. This can not be verified + // since the SSP bank does not report individual hit. + boolean passSeedLow = true; + boolean passSeedHigh = true; + + // The remaining cuts may be acquired from trigger module. + boolean passClusterLow = pairsTrigger[triggerIndex].clusterTotalEnergyCutLow(reconPair[0]) + && pairsTrigger[triggerIndex].clusterTotalEnergyCutLow(reconPair[1]); + boolean passClusterHigh = pairsTrigger[triggerIndex].clusterTotalEnergyCutHigh(reconPair[0]) + && pairsTrigger[triggerIndex].clusterTotalEnergyCutHigh(reconPair[1]); + boolean passHitCount = pairsTrigger[triggerIndex].clusterHitCountCut(reconPair[0]) + && pairsTrigger[triggerIndex].clusterHitCountCut(reconPair[1]); + boolean passPairEnergySumLow = pairsTrigger[triggerIndex].pairEnergySumCutLow(reconPair); + boolean passPairEnergySumHigh = pairsTrigger[triggerIndex].pairEnergySumCutHigh(reconPair); + boolean passPairEnergyDifference = pairsTrigger[triggerIndex].pairEnergyDifferenceCut(reconPair); + boolean passPairEnergySlope = pairsTrigger[triggerIndex].pairEnergySlopeCut(reconPair); + boolean passPairCoplanarity = pairsTrigger[triggerIndex].pairCoplanarityCut(reconPair); + boolean passTimeCoincidence = pairsTrigger[triggerIndex].pairTimeCoincidenceCut(reconPair); + + // Create a trigger from the results. + PairTrigger<Cluster[]> trigger = new PairTrigger<Cluster[]>(reconPair, triggerIndex); + trigger.setStateSeedEnergyLow(passSeedLow); + trigger.setStateSeedEnergyHigh(passSeedHigh); + trigger.setStateClusterEnergyLow(passClusterLow); + trigger.setStateClusterEnergyHigh(passClusterHigh); + trigger.setStateHitCount(passHitCount); + trigger.setStateEnergySumLow(passPairEnergySumLow); + trigger.setStateEnergySumHigh(passPairEnergySumHigh); + trigger.setStateEnergyDifference(passPairEnergyDifference); + trigger.setStateEnergySlope(passPairEnergySlope); + trigger.setStateCoplanarity(passPairCoplanarity); + trigger.setStateTimeCoincidence(passTimeCoincidence); + + // Add the trigger to the list. + reconPairsTriggers.get(triggerIndex).add(trigger); + } + } + + for(SSPCluster[] sspPair : sspPairs) { + pairTriggerLoop: + for(int triggerIndex = 0; triggerIndex < 2; triggerIndex++) { + // Check that the pair passes the time coincidence cut. + // If it does not, it is not a valid pair and should be + // destroyed. + if(!pairsTrigger[triggerIndex].pairTimeCoincidenceCut(sspPair)) { + continue pairTriggerLoop; + } + + // For a cluster to have formed it is assumed to have passed + // the cluster seed energy cuts. This can not be verified + // since the SSP bank does not report individual hit. + boolean passSeedLow = true; + boolean passSeedHigh = true; + + // The remaining cuts may be acquired from trigger module. + boolean passClusterLow = pairsTrigger[triggerIndex].clusterTotalEnergyCutLow(sspPair[0]) + && pairsTrigger[triggerIndex].clusterTotalEnergyCutLow(sspPair[1]); + boolean passClusterHigh = pairsTrigger[triggerIndex].clusterTotalEnergyCutHigh(sspPair[0]) + && pairsTrigger[triggerIndex].clusterTotalEnergyCutHigh(sspPair[1]); + boolean passHitCount = pairsTrigger[triggerIndex].clusterHitCountCut(sspPair[0]) + && pairsTrigger[triggerIndex].clusterHitCountCut(sspPair[1]); + boolean passPairEnergySumLow = pairsTrigger[triggerIndex].pairEnergySumCutLow(sspPair); + boolean passPairEnergySumHigh = pairsTrigger[triggerIndex].pairEnergySumCutHigh(sspPair); + boolean passPairEnergyDifference = pairsTrigger[triggerIndex].pairEnergyDifferenceCut(sspPair); + boolean passPairEnergySlope = pairsTrigger[triggerIndex].pairEnergySlopeCut(sspPair); + boolean passPairCoplanarity = pairsTrigger[triggerIndex].pairCoplanarityCut(sspPair); + boolean passTimeCoincidence = pairsTrigger[triggerIndex].pairTimeCoincidenceCut(sspPair); + + // Create a trigger from the results. + PairTrigger<SSPCluster[]> trigger = new PairTrigger<SSPCluster[]>(sspPair, triggerIndex); + trigger.setStateSeedEnergyLow(passSeedLow); + trigger.setStateSeedEnergyHigh(passSeedHigh); + trigger.setStateClusterEnergyLow(passClusterLow); + trigger.setStateClusterEnergyHigh(passClusterHigh); + trigger.setStateHitCount(passHitCount); + trigger.setStateEnergySumLow(passPairEnergySumLow); + trigger.setStateEnergySumHigh(passPairEnergySumHigh); + trigger.setStateEnergyDifference(passPairEnergyDifference); + trigger.setStateEnergySlope(passPairEnergySlope); + trigger.setStateCoplanarity(passPairCoplanarity); + trigger.setStateTimeCoincidence(passTimeCoincidence); + + // Add the trigger to the list. + sspPairsTriggers.get(triggerIndex).add(trigger); + } + } + } + + /** + * 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("\tEnergy Threshold :: %1.2f%%%n", energyAcceptance); + System.out.printf("\tHit Threshold :: %1d%n", hitAcceptance); + + // 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; + if(start < end) { + System.out.printf("\tValid Cluster Window :: [ %3d ns, %3d ns ]%n", start, end); + performClusterVerification = true; + } else { + System.out.println("\tNSB, NSA, and FADC window preclude a valid cluster verification window."); + System.out.println("\tCluster verification will not be performed!"); + performClusterVerification = false; + } + + // Output the singles trigger settings. + for(int i = 0; i < 2; i++) { + System.out.printf("Singles Trigger %d Settings%n", (i + 1)); + System.out.printf("\tCluster Energy Low :: %.3f GeV%n", singlesTrigger[i].getCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_LOW)); + System.out.printf("\tCluster Energy High :: %.3f GeV%n", singlesTrigger[i].getCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_HIGH)); + System.out.printf("\tCluster Hit Count :: %.0f hits%n", singlesTrigger[i].getCutValue(TriggerModule.CLUSTER_HIT_COUNT_LOW)); + } + + // Output the pair trigger settings. + for(int i = 0; i < 2; i++) { + System.out.printf("Pairs Trigger %d Settings%n", (i + 1)); + System.out.printf("\tCluster Energy Low :: %.3f GeV%n", pairsTrigger[i].getCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_LOW)); + System.out.printf("\tCluster Energy High :: %.3f GeV%n", pairsTrigger[i].getCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_HIGH)); + System.out.printf("\tCluster Hit Count :: %.0f hits%n", pairsTrigger[i].getCutValue(TriggerModule.CLUSTER_HIT_COUNT_LOW)); + System.out.printf("\tPair Energy Sum Low :: %.3f GeV%n", pairsTrigger[i].getCutValue(TriggerModule.PAIR_ENERGY_SUM_LOW)); + System.out.printf("\tPair Energy Sum Low :: %.3f GeV%n", pairsTrigger[i].getCutValue(TriggerModule.PAIR_ENERGY_SUM_HIGH)); + System.out.printf("\tPair Energy Difference :: %.3f GeV%n", pairsTrigger[i].getCutValue(TriggerModule.PAIR_ENERGY_DIFFERENCE_HIGH)); + System.out.printf("\tPair Energy Slope :: %.3f GeV%n", pairsTrigger[i].getCutValue(TriggerModule.PAIR_ENERGY_SLOPE_LOW)); + System.out.printf("\tPair Energy Slope F :: %.3f GeV / mm%n", pairsTrigger[i].getCutValue(TriggerModule.PAIR_ENERGY_SLOPE_F)); + System.out.printf("\tPair Coplanarity :: %.0f Degrees%n", pairsTrigger[i].getCutValue(TriggerModule.PAIR_COPLANARITY_HIGH)); + System.out.printf("\tPair Time Coincidence :: %.0f ns%n", pairsTrigger[i].getCutValue(TriggerModule.PAIR_TIME_COINCIDENCE)); + } + } + + /** + * Checks whether all of the hits in a cluster are within the safe + * region of the FADC output window. + * @param reconCluster - The cluster to check. + * @return Returns <code>true</code> if the cluster is safe and + * returns <code>false</code> otherwise. + */ + private final boolean isVerifiable(Cluster reconCluster) { + return TriggerDiagnosticUtil.isVerifiable(reconCluster, nsa, nsb, windowWidth); + } + + /** + * Compares an SSP trigger with a simulated trigger. Note that only + * certain class combinations are supported. Triggers of the type + * <code>SSPSinglesTrigger</code> may be compared with triggers of + * the type <code>SinglesTrigger<SSPCluster></code> and triggers of + * the type <code>SSPPairTrigger</code> may be compared to either + * <code>PairTrigger<SSPCluster[]></code> triggers objects. + * @param bankTrigger - The SSP bank trigger. + * @param simTrigger - The simulated trigger. + * @return Returns <code>true</code> if the triggers are valid + * matches and <code>false</code> if they are not. + * @throws IllegalArgumentException Occurs if the trigger types + * are not of a supported type. + */ + @SuppressWarnings("unchecked") + private static final boolean compareTriggers(SSPNumberedTrigger bankTrigger, Trigger<?> simTrigger) throws IllegalArgumentException { + // Get the classes of the arguments. This is used to check the + // generic type of the Trigger<?> object, and means that the + // "unchecked" warnings can be safely ignored. + Object source = simTrigger.getTriggerSource(); + + // If the combination of classes is supported, pass the triggers + // to the appropriate handler. + if(bankTrigger instanceof SSPSinglesTrigger && simTrigger instanceof SinglesTrigger && source instanceof SSPCluster) { + return compareSSPSinglesTriggers((SSPSinglesTrigger) bankTrigger, (SinglesTrigger<SSPCluster>) simTrigger); + } else if(bankTrigger instanceof SSPPairTrigger && simTrigger instanceof PairTrigger && source instanceof SSPCluster[]) { + return compareSSPPairTriggers((SSPPairTrigger) bankTrigger, (PairTrigger<SSPCluster[]>) simTrigger); + } + + // Otherwise, the trigger combination is not supported. Produce + // and exception. + throw new IllegalArgumentException(String.format("Trigger type \"%s\" can not be compared to trigger type \"%s\" with source type \"%s\".", + bankTrigger.getClass().getSimpleName(), simTrigger.getClass().getSimpleName(), source.getClass().getSimpleName())); + } + + /** + * Compares a trigger from the SSP bank to a trigger simulated on + * an SSP cluster. + * @param bankTrigger - The trigger from the SSP bank. + * @param simTrigger - The trigger from the simulation. + * @return Returns <code>true</code> if the triggers match and + * <code>false</code> if they do not. + */ + private static final boolean compareSSPSinglesTriggers(SSPSinglesTrigger bankTrigger, SinglesTrigger<SSPCluster> simTrigger) { + // The bank trigger and simulated trigger must have the same + // time. This is equivalent to the time of the triggering cluster. + if(bankTrigger.getTime() != simTrigger.getTriggerSource().getTime()) { + return false; + } + + // If the time stamp is the same, check that the trigger flags + // are all the same. Start with cluster energy low. + if(bankTrigger.passCutEnergyMin() != simTrigger.getStateClusterEnergyLow()) { + return false; + } + + // Check cluster energy high. + if(bankTrigger.passCutEnergyMax() != simTrigger.getStateClusterEnergyHigh()) { + return false; + } + + // Check cluster hit count. + if(bankTrigger.passCutHitCount() != simTrigger.getStateHitCount()) { + return false; + } + + // If all of the tests are successful, the triggers match. + return true; + } + + /** + * Compares a trigger from the SSP bank to a trigger simulated on + * an SSP cluster. + * @param bankTrigger - The trigger from the SSP bank. + * @param simTrigger - The trigger from the simulation. + * @return Returns <code>true</code> if the triggers match and + * <code>false</code> if they do not. + */ + private static final boolean compareSSPPairTriggers(SSPPairTrigger bankTrigger, PairTrigger<SSPCluster[]> simTrigger) { + // Get the time of the bottom cluster in the pair. + int simTime = 0; + if(simTrigger.getTriggerSource()[0].getYIndex() < 0) { + simTime = simTrigger.getTriggerSource()[0].getTime(); + } else { + simTime = simTrigger.getTriggerSource()[1].getTime(); + } + + // The bank trigger and simulated trigger must have the same + // time. This is equivalent to the time of the triggering cluster. + if(bankTrigger.getTime() != simTime) { return false; } + + // If the time stamp is the same, check that the trigger flags + // are all the same. Start with energy sum. + if(bankTrigger.passCutEnergySum() != simTrigger.getStateEnergySum()) { + return false; + } + + // Check pair energy difference. + if(bankTrigger.passCutEnergyDifference() != simTrigger.getStateEnergyDifference()) { + return false; + } + + // Check pair energy slope. + if(bankTrigger.passCutEnergySlope() != simTrigger.getStateEnergySlope()) { + return false; + } + + // Check pair coplanarity. + if(bankTrigger.passCutCoplanarity() != simTrigger.getStateCoplanarity()) { + return false; + } + + // If all of the tests are successful, the triggers match. + return true; + } + + /** + * Generates a <code>List</code> collection that contains a set + * of <code>ArrayList</code> collections representing a unique + * permutation of the entries in the argument. + * @param values - A collection of the entries to be permuted. + * @return Returns a list of lists representing the permutations. + */ + private static final List<List<Pair<Cluster, SSPCluster>>> getPermutations(List<Cluster> reconClusters, List<SSPCluster> sspClusters) { + // Store the SSP cluster permutations. + List<List<SSPCluster>> permList = new ArrayList<List<SSPCluster>>(); + + // Make sure that the two lists are the same size. + int reconSize = reconClusters.size(); + int sspSize = sspClusters.size(); + while(sspClusters.size() < reconClusters.size()) { + sspClusters.add(null); + } + while(reconClusters.size() < sspClusters.size()) { + reconClusters.add(null); + } + + // Get the SSP cluster permutations. + permute(new ArrayList<SSPCluster>(0), sspClusters, permList); + + // Create pairs from the permutations. + List<List<Pair<Cluster, SSPCluster>>> pairList = new ArrayList<List<Pair<Cluster, SSPCluster>>>(); + for(List<SSPCluster> permutation : permList) { + List<Pair<Cluster, SSPCluster>> pairs = new ArrayList<Pair<Cluster, SSPCluster>>(reconClusters.size()); + + for(int clusterIndex = 0; (clusterIndex < reconClusters.size() && clusterIndex < permutation.size()); clusterIndex++) { + pairs.add(new Pair<Cluster, SSPCluster>(reconClusters.get(clusterIndex), permutation.get(clusterIndex))); + } + + pairList.add(pairs); + } + + // Remove the extra values. + for(int i = sspClusters.size() - 1; i >= sspSize; i--) { sspClusters.remove(i); } + for(int i = reconClusters.size() - 1; i >= reconSize; i--) { reconClusters.remove(i); } + + // Return the pairs. + return pairList; + } + + /** + * Recursive method for permuting all entries in the argument + * collection <code>remainingValues</code> into the argument + * <code>permutedValues</code> values. Completed permutations are + * placed in the argument <code>permList</code>. + * @param permutedValues - List to store entries that have already + * been permuted. + * @param remainingValues - List to store entries that need to be + * permuted. + * @param permList - List to store completed permutations. + */ + private static final void permute(List<SSPCluster> permutedValues, List<SSPCluster> remainingValues, List<List<SSPCluster>> permList) { + // If the list of entries that still need to be sorted is empty, + // then there is nothing to sort. Just return and empty list. + if(remainingValues.isEmpty()) { return; } + + // If there is only one value left in the list of entries that + // still need to be sorted, then just add it to the permutation + // list and return it. + else if(remainingValues.size() <= 1) { + // Add the last entry. + permutedValues.add(remainingValues.get(0)); + + // Add the permutation to the list of completed permutations. + permList.add(permutedValues); + } + + // Otherwise, continue to get all possible permutations. + else { + // Iterate over the entries that have not been permuted. + for(int i = 0; i < remainingValues.size(); i++) { + // Make new lists to contain the permutations. + List<SSPCluster> newPermList = new ArrayList<SSPCluster>(permutedValues.size() + 1); + List<SSPCluster> newRemainList = new ArrayList<SSPCluster>(remainingValues.size()); + + // Copy the current permuted entries to the new list + // and one value from the list of entries that have + // not been permuted yet. + newPermList.addAll(permutedValues); + newPermList.add(remainingValues.get(i)); + + // The new list of entries that have not been permuted + // should be identical, except it should now be missing + // the entry that was moved. + for(int index = 0; index < remainingValues.size(); index++) { + if(index != i) { newRemainList.add(remainingValues.get(index)); } + } + + // Repeat the process with the new lists. + permute(newPermList, newRemainList, permList); + } + } + } + + /** + * Compares two cluster matching events and finds the one that has + * the better results. Note that this will only return results that + * make sense if both of the events represent different permutations + * of the same set of clusters. Comparing events with different sets + * of clusters will produce meaningless results. + * @param firstEvent - The first cluster matching event, + * @param secondEvent - The second cluster matching event. + * @return Returns the cluster matching event that is better. + */ + private static final ClusterMatchEvent getBestPermutation(ClusterMatchEvent firstEvent, ClusterMatchEvent secondEvent) { + // If both permutations are null, return that. + if(firstEvent == null && secondEvent == null) { + return null; + } + + // If one permutation is null, it is not the best. + if(firstEvent == null) { return secondEvent; } + else if(secondEvent == null) { return firstEvent; } + + // A permutation is better if it has more matches. + if(firstEvent.getMatches() > secondEvent.getMatches()) { return firstEvent; } + else if(secondEvent.getMatches() > firstEvent.getMatches()) { return secondEvent; } + + // Otherwise, the permutation with the least energy failures is + // the better permutation. + if(firstEvent.getEnergyFailures() < secondEvent.getEnergyFailures()) { return firstEvent; } + else if(secondEvent.getEnergyFailures() < firstEvent.getEnergyFailures()) { return secondEvent; } + + // If both these values are the same, then the events are identical. + return firstEvent; + } + + /** + * 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; + } + + /** + * Gets the position of the source of a <code>Trigger</code> object + * as text. This method only supports trigger sources of the types + * <code>SSPCluster</code>, <code>Cluster</code>, and arrays of size + * two of either type. + * @param trigger - The trigger from which to obtain the source. + * @return Returns the source of the trigger as a <code>String</code> + * object. + * @throws IllegalArgumentException Occurs if the source of the + * trigger is not any of the supported types. + */ + private static final String triggerPositionString(Trigger<?> trigger) throws IllegalArgumentException { + // Get the trigger source. + Object source = trigger.getTriggerSource(); + + // Handle valid trigger sources. + if(source instanceof SSPCluster) { + return TriggerDiagnosticUtil.clusterPositionString((SSPCluster) source); + } else if(source instanceof Cluster) { + return TriggerDiagnosticUtil.clusterPositionString((Cluster) source); + } else if(source instanceof SSPCluster[]) { + SSPCluster[] sourcePair = (SSPCluster[]) source; + if(sourcePair.length == 2) { + return String.format("%s, %s", TriggerDiagnosticUtil.clusterPositionString(sourcePair[0]), + TriggerDiagnosticUtil.clusterPositionString(sourcePair[1])); + } + } else if(source instanceof Cluster[]) { + Cluster[] sourcePair = (Cluster[]) source; + if(sourcePair.length == 2) { + return String.format("%s, %s", TriggerDiagnosticUtil.clusterPositionString(sourcePair[0]), + TriggerDiagnosticUtil.clusterPositionString(sourcePair[1])); + } + } + + // Otherwise, the source type is unrecognized. Throw an error. + throw new IllegalArgumentException(String.format("Trigger source type \"%s\" is not supported.", + trigger.getTriggerSource().getClass().getSimpleName())); + } + + /** + * Gets the time of a simulated trigger object. Method supports + * triggers with source objects of type <code>SSPCluster</code>, + * <code>Cluster</code>, and arrays of size two composed of either + * object type. + * @param trigger - The trigger. + * @return Returns the time at which the trigger occurred. + * @throws IllegalArgumentException Occurs if the trigger source + * is not a supported type. + */ + private static final double getTriggerTime(Trigger<?> trigger) throws IllegalArgumentException { + // Get the trigger source. + Object source = trigger.getTriggerSource(); + + // Get the trigger time for supported trigger types. + if(source instanceof SSPCluster) { + return ((SSPCluster) source).getTime(); + } else if(source instanceof Cluster) { + return TriggerDiagnosticUtil.getClusterTime((Cluster) source); + } else if(source instanceof SSPCluster[]) { + // Get the pair. + SSPCluster[] sourcePair = (SSPCluster[]) source; + + // Get the time of the bottom cluster. + if(sourcePair.length == 2) { + if(sourcePair[0].getYIndex() < 0) { return sourcePair[0].getTime(); } + else if(sourcePair[1].getYIndex() < 0) { return sourcePair[1].getTime(); } + else { throw new IllegalArgumentException("Cluster pairs must be formed of a top/bottom pair."); } + } + else { throw new IllegalArgumentException("Cluster pairs must be of size 2."); } + } else if(source instanceof Cluster[]) { + // Get the pair. + Cluster[] sourcePair = (Cluster[]) source; + int[] iy = { + TriggerDiagnosticUtil.getYIndex(sourcePair[0]), + TriggerDiagnosticUtil.getYIndex(sourcePair[1]) + }; + + // Get the time of the bottom cluster. + if(sourcePair.length == 2) { + if(iy[0] < 0) { return TriggerDiagnosticUtil.getClusterTime(sourcePair[0]); } + else if(iy[1] < 0) { return TriggerDiagnosticUtil.getClusterTime(sourcePair[1]); } + else { throw new IllegalArgumentException("Cluster pairs must be formed of a top/bottom pair."); } + } + else { throw new IllegalArgumentException("Cluster pairs must be of size 2."); } + } + + // If the source type is unrecognized, throw an exception. + throw new IllegalArgumentException(String.format("Trigger source type \"%\" is not supported.", + source.getClass().getSimpleName())); + } + + /** + * Checks if a simulated trigger and an SSP trigger match. Note + * that only certain types can be compared. These are: + * <ul><li><code>SinglesTrigger<?> --> SSPSinglesTrigger</code></li> + * <li><code>PairTrigger<?> --> SSPPairTrigger</code></li></ul> + * @param simTrigger - The simulated trigger. + * @param sspTrigger - The SSP bank trigger. + * @return Returns an array of <code>boolean</code> primitives that + * indicate which cuts passed and which failed. + */ + private static final boolean[] triggerCutMatch(Trigger<?> simTrigger, SSPTrigger sspTrigger) { + // Check that the cuts match for supported trigger types. + if(simTrigger instanceof SinglesTrigger && sspTrigger instanceof SSPSinglesTrigger) { + // Create an array to store the cut checks. + boolean[] cutMatch = new boolean[3]; + + // Cast the triggers. + SinglesTrigger<?> simSingles = (SinglesTrigger<?>) simTrigger; + SSPSinglesTrigger sspSingles = (SSPSinglesTrigger) sspTrigger; + + // Perform the check. + cutMatch[ENERGY_MIN] = (simSingles.getStateClusterEnergyLow() == sspSingles.passCutEnergyMin()); + cutMatch[ENERGY_MAX] = (simSingles.getStateClusterEnergyHigh() == sspSingles.passCutEnergyMax()); + cutMatch[HIT_COUNT] = (simSingles.getStateHitCount() == sspSingles.passCutHitCount()); + + // Return the match array. + return cutMatch; + } else if(simTrigger instanceof PairTrigger && sspTrigger instanceof SSPPairTrigger) { + // Create an array to store the cut checks. + boolean[] cutMatch = new boolean[4]; + + // Cast the triggers. + PairTrigger<?> simPair = (PairTrigger<?>) simTrigger; + SSPPairTrigger sspPair = (SSPPairTrigger) sspTrigger; + + // Perform the check. + cutMatch[ENERGY_SUM] = (simPair.getStateEnergySum() == sspPair.passCutEnergySum()); + cutMatch[ENERGY_DIFF] = (simPair.getStateEnergyDifference() == sspPair.passCutEnergyDifference()); + cutMatch[ENERGY_SLOPE] = (simPair.getStateEnergySlope() == sspPair.passCutEnergySlope()); + cutMatch[COPLANARITY] = (simPair.getStateCoplanarity() == sspPair.passCutCoplanarity()); + + // Return the match array. + return cutMatch; + } + + // If this point is reached, the triggers are not of a supported + // type for cut comparison. Produce an exception. + throw new IllegalArgumentException(String.format("Triggers of type \"%s\" can not be cut-matched with triggers of type \"%s\".", + simTrigger.getClass().getSimpleName(), sspTrigger.getClass().getSimpleName())); + } +} Added: java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/ClusterMatchEvent.java ============================================================================= --- java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/ClusterMatchEvent.java (added) +++ java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/ClusterMatchEvent.java Thu Mar 5 10:59:19 2015 @@ -0,0 +1,145 @@ +package org.hps.analysis.trigger.event; + +import java.util.ArrayList; +import java.util.List; + +import org.hps.readout.ecal.triggerbank.SSPCluster; +import org.hps.analysis.trigger.util.TriggerDiagnosticUtil; +import org.lcsim.event.Cluster; + +/** + * Class <code>ClusterMatchEvent</code> tracks reconstructed/SSP cluster + * pairs for the purpose of cluster matching. It maintains a list of + * all pairs that have been seen as well as their match states. It can + * additionally provide the total number of each type of match state. + * + * @author Kyle McCarty + */ +public class ClusterMatchEvent { + // Track the number of state instances. + private int matched = 0; + private int failPosition = 0; + private int failEnergy = 0; + private int failHitCount = 0; + + // Store all of the pairs. + private List<ClusterMatchedPair> pairList = new ArrayList<ClusterMatchedPair>(); + + /** + * Fuses another <code>ClusterMatchEvent</code> with this object. + * The other event's cluster pairs and states will be added to those + * already in this event. + * @param event - The event to fuse. + */ + public void addEvent(ClusterMatchEvent event) { + // If the event is null, do nothing. + if(event == null) { return; } + + // Iterate over the new event's matched pairs and add them into + // this event's statistics. + for(ClusterMatchedPair cmp : event.pairList) { + // Add the current pair to this pair list. + pairList.add(cmp); + + // Increment the statistics counters based on the pair state. + if(cmp.isMatch()) { matched++; } + if(cmp.isPositionFailState()) { failPosition++; } + if(cmp.isEnergyFailState()) { failEnergy++; } + if(cmp.isHitCountFailState()) { failHitCount++; } + } + } + + /** + * Gets the number of cluster pairs stored in this event that are + * marked with energy fail states. + * @return Returns the number of instances of this state as an + * <code>int</code> primitive. + */ + public int getEnergyFailures() { + return failEnergy; + } + + /** + * Gets the number of cluster pairs stored in this event that are + * marked with hit count fail states. + * @return Returns the number of instances of this state as an + * <code>int</code> primitive. + */ + public int getHitCountFailures() { + return failHitCount; + } + + /** + * Gets the number of cluster pairs stored in this event that are + * marked with match states. + * @return Returns the number of instances of this state as an + * <code>int</code> primitive. + */ + public List<ClusterMatchedPair> getMatchedPairs() { + return pairList; + } + + /** + * Gets the number of cluster pairs stored in this event that are + * marked with position fail states. + * @return Returns the number of instances of this state as an + * <code>int</code> primitive. + */ + public int getMatches() { + return matched; + } + + /** + * Gets the number of cluster pairs stored in this event that are + * marked with position fail states. + * @return Returns the number of instances of this state as an + * <code>int</code> primitive. + */ + public int getPositionFailures() { + return failPosition; + } + + /** + * Adds a reconstructed/SSP cluster pair and marks it as having an + * energy fail state. + * @param reconCluster - The reconstructed cluster. + * @param sspCluster - The SSP cluster. + */ + public void pairFailEnergy(Cluster reconCluster, SSPCluster sspCluster) { + failEnergy++; + pairList.add(new ClusterMatchedPair(reconCluster, sspCluster, TriggerDiagnosticUtil.CLUSTER_STATE_FAIL_ENERGY)); + } + + /** + * Adds a reconstructed/SSP cluster pair and marks it as having a + * hit count fail state. + * @param reconCluster - The reconstructed cluster. + * @param sspCluster - The SSP cluster. + */ + public void pairFailHitCount(Cluster reconCluster, SSPCluster sspCluster) { + failHitCount++; + pairList.add(new ClusterMatchedPair(reconCluster, sspCluster, TriggerDiagnosticUtil.CLUSTER_STATE_FAIL_HIT_COUNT)); + } + + /** + * Adds a reconstructed/SSP cluster pair and marks it as having a + * position fail state. + * @param reconCluster - The reconstructed cluster. + * @param sspCluster - The SSP cluster. + */ + public void pairFailPosition(Cluster reconCluster, SSPCluster sspCluster) { + failPosition++; + pairList.add(new ClusterMatchedPair(reconCluster, sspCluster, TriggerDiagnosticUtil.CLUSTER_STATE_FAIL_POSITION)); + } + + /** + * Adds a reconstructed/SSP cluster pair and marks it as having a + * match state. + * @param reconCluster - The reconstructed cluster. + * @param sspCluster - The SSP cluster. + */ + public void pairMatch(Cluster reconCluster, SSPCluster sspCluster) { + matched++; + pairList.add(new ClusterMatchedPair(reconCluster, sspCluster, TriggerDiagnosticUtil.CLUSTER_STATE_MATCHED)); + } +} Added: java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/ClusterMatchStatus.java ============================================================================= --- java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/ClusterMatchStatus.java (added) +++ java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/ClusterMatchStatus.java Thu Mar 5 10:59:19 2015 @@ -0,0 +1,200 @@ +package org.hps.analysis.trigger.event; + +import java.awt.Point; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.hps.analysis.trigger.util.TriggerDiagnosticUtil; +import org.hps.readout.ecal.triggerbank.SSPCluster; +import org.lcsim.event.Cluster; + +/** + * Tracks the status of cluster matching for the purposes of trigger + * verification. + * + * @author Kyle McCarty + */ +public class ClusterMatchStatus extends ClusterStatModule { + // Plot binning values. + private static final int TIME_BIN = 4; + private static final double ENERGY_BIN = 0.01; + private static final int TIME_BIN_HALF = TIME_BIN / 2; + private static final double ENERGY_BIN_HALF = ENERGY_BIN / 2; + + // Track plotting values for reconstructed and SSP clusters. + private Map<Integer, Integer> sspHitCountBins = new HashMap<Integer, Integer>(); + private Map<Integer, Integer> reconHitCountBins = new HashMap<Integer, Integer>(); + private Map<Point, Integer> sspPositionBins = new HashMap<Point, Integer>(); + private Map<Point, Integer> reconPositionBins = new HashMap<Point, Integer>(); + private Map<Integer, Integer> sspEnergyBins = new HashMap<Integer, Integer>(); + private Map<Integer, Integer> reconEnergyBins = new HashMap<Integer, Integer>(); + + // Track plotting values for cluster matching results. + private Map<Point, Integer> failPositionBins = new HashMap<Point, Integer>(); + private Map<Point, Integer> allHenergyBins = new HashMap<Point, Integer>(); + private Map<Point, Integer> failHenergyBins = new HashMap<Point, Integer>(); + private Map<Integer, Integer> allTimeBins = new HashMap<Integer, Integer>(); + private Map<Integer, Integer> failTimeBins = new HashMap<Integer, Integer>(); + + public void addEvent(ClusterMatchEvent event, List<Cluster> reconClusters, List<SSPCluster> sspClusters) { + // Update the number of reconstructed and SSP clusters + // that have been seen so far. + int sspCount = sspClusters == null ? 0 : sspClusters.size(); + int reconCount = reconClusters == null ? 0 : reconClusters.size(); + this.sspClusters += sspCount; + this.reconClusters += reconCount; + + // Update the pair state information. + matches += event.getMatches(); + failEnergy += event.getEnergyFailures(); + failHitCount += event.getHitCountFailures(); + failPosition += event.getPositionFailures(); + + // In the special case that there are no SSP clusters, no pairs + // will be listed. All possible fails are known to have failed + // due to position. + if(sspClusters == null || sspClusters.isEmpty()) { + failPosition += (reconClusters == null ? 0 : reconClusters.size()); + } + + // Update the plotting information for reconstructed clusters. + for(Cluster cluster : reconClusters) { + // Update the hit count bin data. + Integer hitCountCount = reconHitCountBins.get(cluster.getCalorimeterHits().size()); + if(hitCountCount == null) { reconHitCountBins.put(cluster.getCalorimeterHits().size(), 1); } + else { reconHitCountBins.put(cluster.getCalorimeterHits().size(), hitCountCount + 1); } + + // Update the position bin data. + Point clusterPosition = TriggerDiagnosticUtil.getClusterPosition(cluster); + Integer positionCount = reconPositionBins.get(clusterPosition); + if(positionCount == null) { reconPositionBins.put(clusterPosition, 1); } + else { reconPositionBins.put(clusterPosition, positionCount + 1); } + + // Update the energy bin data. + int energyBin = (int) Math.floor(cluster.getEnergy() / ENERGY_BIN); + Integer energyCount = reconEnergyBins.get(energyBin); + if(energyCount == null) { reconEnergyBins.put(energyBin, 1); } + else { reconEnergyBins.put(energyBin, energyCount + 1); } + } + + // Update the plotting information for SSP clusters. + for(SSPCluster cluster : sspClusters) { + // Update the hit count bin data. + Integer hitCountCount = sspHitCountBins.get(cluster.getHitCount()); + if(hitCountCount == null) { sspHitCountBins.put(cluster.getHitCount(), 1); } + else { sspHitCountBins.put(cluster.getHitCount(), hitCountCount + 1); } + + // Update the position bin data. + Point clusterPosition = TriggerDiagnosticUtil.getClusterPosition(cluster); + Integer positionCount = sspPositionBins.get(clusterPosition); + if(positionCount == null) { sspPositionBins.put(clusterPosition, 1); } + else { sspPositionBins.put(clusterPosition, positionCount + 1); } + + // Update the energy bin data. + int energyBin = (int) Math.floor(cluster.getEnergy() / ENERGY_BIN); + Integer energyCount = sspEnergyBins.get(energyBin); + if(energyCount == null) { sspEnergyBins.put(energyBin, 1); } + else { sspEnergyBins.put(energyBin, energyCount + 1); } + } + + // Update the plotting information for SSP/reconstructed cluster + // pairs. + pairLoop: + for(ClusterMatchedPair pair : event.getMatchedPairs()) { + // If one of the pairs is null, then it is unmatched cluster + // and may be skipped. + if(pair.getReconstructedCluster() == null || pair.getSSPCluster() == null) { + continue pairLoop; + } + + // Populate the bins for the "all" plots. + // Update the match time plots. + int timeBin = (int) Math.floor(TriggerDiagnosticUtil.getClusterTime(pair.getReconstructedCluster()) / TIME_BIN); + Integer timeCount = allTimeBins.get(timeBin); + if(timeCount == null) { allTimeBins.put(timeBin, 1); } + else { allTimeBins.put(timeBin, timeCount + 1); } + + // Update the energy/hit difference plots. + int hitBin = getHitCountDifference(pair.getSSPCluster(), pair.getReconstructedCluster()); + int energyBin = (int) Math.floor(getEnergyPercentDifference(pair.getSSPCluster(), pair.getReconstructedCluster()) / ENERGY_BIN); + Point henergyBin = new Point(hitBin, energyBin); + Integer henergyCount = allHenergyBins.get(henergyBin); + if(henergyCount == null) { allHenergyBins.put(henergyBin, 1); } + else { allHenergyBins.put(henergyBin, henergyCount + 1); } + + // Populate the bins for the "fail" plots. + if(!pair.isMatch()) { + // Update the failed cluster position bins. + Point clusterPosition = TriggerDiagnosticUtil.getClusterPosition(pair.getReconstructedCluster()); + Integer positionCount = failPositionBins.get(clusterPosition); + if(positionCount == null) { failPositionBins.put(clusterPosition, 1); } + else { failPositionBins.put(clusterPosition, positionCount + 1); } + + // Update the failed match time plots. + timeCount = failTimeBins.get(timeBin); + if(timeCount == null) { failTimeBins.put(timeBin, 1); } + else { failTimeBins.put(timeBin, timeCount + 1); } + + // Update the failed energy/hit difference plots. + henergyCount = failHenergyBins.get(henergyBin); + if(henergyCount == null) { failHenergyBins.put(henergyBin, 1); } + else { failHenergyBins.put(henergyBin, henergyCount + 1); } + } + } + } + + /** + * Clears all statistical information and resets the object of its + * default, empty state. + */ + @Override + public void clear() { + // Clear statistical data. + super.clear(); + + // Clear plot collections. + sspHitCountBins.clear(); + reconHitCountBins.clear(); + sspPositionBins.clear(); + reconPositionBins.clear(); + sspEnergyBins.clear(); + reconEnergyBins.clear(); + failPositionBins.clear(); + allHenergyBins.clear(); + failHenergyBins.clear(); + allTimeBins.clear(); + failTimeBins.clear(); + } + + /** + * Gets a copy of the statistical data stored in the object. + * @return Returns the data in a <code>ClusterStatModule</code> + * object. + */ + public ClusterStatModule cloneStatModule() { + return new ClusterStatModule(this); + } + + /** + * Solves the equation <code>|E_ssp / E_recon|</code>. + * @param sspCluster - The SSP cluster. + * @param reconCluster - The reconstructed cluster. + * @return Returns the solution to the equation as a <code>double + * </code> primitive. + */ + private static final double getEnergyPercentDifference(SSPCluster sspCluster, Cluster reconCluster) { + return Math.abs((sspCluster.getEnergy() / reconCluster.getEnergy())); + } + + /** + * Gets the difference in hit count between an SSP cluster and a + * reconstructed cluster. + * @param sspCluster - The SSP cluster. + * @param reconCluster - The reconstructed cluster. + * @return Returns the difference as an <code>int</code> primitive. + */ + private static final int getHitCountDifference(SSPCluster sspCluster, Cluster reconCluster) { + return sspCluster.getHitCount() - TriggerDiagnosticUtil.getHitCount(reconCluster); + } +} Added: java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/ClusterMatchedPair.java ============================================================================= --- java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/ClusterMatchedPair.java (added) +++ java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/ClusterMatchedPair.java Thu Mar 5 10:59:19 2015 @@ -0,0 +1,117 @@ +package org.hps.analysis.trigger.event; + +import org.hps.analysis.trigger.util.Pair; +import org.hps.analysis.trigger.util.TriggerDiagnosticUtil; +import org.hps.readout.ecal.triggerbank.SSPCluster; +import org.lcsim.event.Cluster; + +/** + * Class <code>ClusterMatchedPair</code> stores a reconstructed cluster + * and an SSP bank reported cluster which have been compared for the + * purpose of cluster matching. It also tracks what the match state of + * the two clusters is. + * + * @author Kyle McCarty <[log in to unmask]> + */ +public class ClusterMatchedPair extends Pair<Cluster, SSPCluster> { + // CLass variables. + private final byte state; + + /** + * Instantiates a new <code>ClusterMatchedPair</code> object from + * the two indicated clusters and marks their match state. + * @param reconCluster - The reconstructed cluster. + * @param sspCluster - The SSP cluster. + * @param state - The pair match state. + */ + public ClusterMatchedPair(Cluster reconCluster, SSPCluster sspCluster, byte state) { + // Set the cluster pairs. + super(reconCluster, sspCluster); + + // If the state is defined, set it. Otherwise, it is unknown. + if(state == TriggerDiagnosticUtil.CLUSTER_STATE_MATCHED + || state == TriggerDiagnosticUtil.CLUSTER_STATE_FAIL_POSITION + || state == TriggerDiagnosticUtil.CLUSTER_STATE_FAIL_ENERGY + || state == TriggerDiagnosticUtil.CLUSTER_STATE_FAIL_HIT_COUNT) { + this.state = state; + } else { + this.state = TriggerDiagnosticUtil.CLUSTER_STATE_FAIL_UNKNOWN; + } + } + + /** + * Gets the reconstructed cluster of the pair. + * @return Returns the reconstructed cluster a <code>Cluster</cod> + * object. + */ + public Cluster getReconstructedCluster() { + return getFirstElement(); + } + + /** + * Gets the SSP cluster of the pair. + * @return Returns the SSP cluster as an <code>SSPCluster</code> + * object. + */ + public SSPCluster getSSPCluster() { + return getSecondElement(); + } + + /** + * Gets the raw state identifier. + * @return Returns the state identifier as a <code>byte</code> + * primitive. Valid identifiers are defined in the class + * <code>TriggerDiagnosticUtil</code>. + */ + public byte getState() { + return state; + } + + /** + * Indicates whether the recon/SSP pair failed to not being close + * enough in energy. + * @return Returns <code>true</code> if the pair match state is an + * energy fail state and <code>false</code> otherwise. + */ + public boolean isEnergyFailState() { + return (state == TriggerDiagnosticUtil.CLUSTER_STATE_FAIL_ENERGY); + } + + /** + * Indicates whether the recon/SSP pair failed to match due to not + * being close enough in hit count. + * @return Returns <code>true</code> if the pair match state is a + * hit count fail state and <code>false</code> otherwise. + */ + public boolean isHitCountFailState() { + return (state == TriggerDiagnosticUtil.CLUSTER_STATE_FAIL_HIT_COUNT); + } + + /** + * Indicates whether the recon/SSP pair matched. + * @return Returns <code>true</code> if the pair match state is a + * match state and <code>false</code> otherwise. + */ + public boolean isMatch() { + return (state == TriggerDiagnosticUtil.CLUSTER_STATE_MATCHED); + } + + /** + * Indicates whether the recon/SSP pair failed to match due to a + * the cluster positions not aligning. + * @return Returns <code>true</code> if the pair match state is a + * position fail state and <code>false</code> otherwise. + */ + public boolean isPositionFailState() { + return (state == TriggerDiagnosticUtil.CLUSTER_STATE_FAIL_POSITION); + } + + /** + * Indicates whether the recon/SSP pair has no known match state. + * @return Returns <code>true</code> if the pair match state is + * unknown and <code>false</code> otherwise. + */ + public boolean isUnknownState() { + return (state == TriggerDiagnosticUtil.CLUSTER_STATE_FAIL_UNKNOWN); + } +} Added: java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/ClusterStatModule.java ============================================================================= --- java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/ClusterStatModule.java (added) +++ java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/ClusterStatModule.java Thu Mar 5 10:59:19 2015 @@ -0,0 +1,109 @@ +package org.hps.analysis.trigger.event; + +/** + * Class <code>ClusterStatModule</code> stores the statistical data + * for trigger diagnostic cluster matching. + * + * @author Kyle McCarty <[log in to unmask]> + */ +public class ClusterStatModule { + // Track cluster statistics. + protected int sspClusters = 0; + protected int reconClusters = 0; + protected int matches = 0; + protected int failEnergy = 0; + protected int failPosition = 0; + protected int failHitCount = 0; + + /** + * Instantiates a <code>ClusterStatModule</code> with no statistics + * stored. + */ + ClusterStatModule() { } + + /** + * Instantiates a <code>ClusterStatModule</code> with no statistics + * cloned from the base object. + * @param base - The source for the statistical data. + */ + ClusterStatModule(ClusterStatModule base) { + // Copy the statistical data into it. + sspClusters = base.sspClusters; + reconClusters = base.reconClusters; + matches = base.matches; + failEnergy = base.failEnergy; + failPosition = base.failPosition; + failHitCount = base.failHitCount; + } + + /** + * Clears all statistical information and resets the object of its + * default, empty state. + */ + void clear() { + sspClusters = 0; + reconClusters = 0; + matches = 0; + failEnergy = 0; + failPosition = 0; + failHitCount = 0; + } + + /** + * Gets the number of cluster pairs stored in this event that are + * marked with energy fail states. + * @return Returns the number of instances of this state as an + * <code>int</code> primitive. + */ + public int getEnergyFailures() { + return failEnergy; + } + + /** + * Gets the number of cluster pairs stored in this event that are + * marked with hit count fail states. + * @return Returns the number of instances of this state as an + * <code>int</code> primitive. + */ + public int getHitCountFailures() { + return failHitCount; + } + + /** + * Gets the number of cluster pairs stored in this event that are + * marked with position fail states. + * @return Returns the number of instances of this state as an + * <code>int</code> primitive. + */ + public int getMatches() { + return matches; + } + + /** + * Gets the number of cluster pairs stored in this event that are + * marked with position fail states. + * @return Returns the number of instances of this state as an + * <code>int</code> primitive. + */ + public int getPositionFailures() { + return failPosition; + } + + /** + * Gets the total number of verifiable reconstructed clusters seen. + * @return Returns the cluster count as an <code>int</code> + * primitive. + */ + public int getReconClusterCount() { + return reconClusters; + } + + /** + * Gets the total number of SSP bank clusters seen. + * @return Returns the cluster count as an <code>int</code> + * primitive. + */ + public int getSSPClusterCount() { + return sspClusters; + } +} Added: java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerEfficiencyModule.java ============================================================================= --- java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerEfficiencyModule.java (added) +++ java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerEfficiencyModule.java Thu Mar 5 10:59:19 2015 @@ -0,0 +1,264 @@ +package org.hps.analysis.trigger.event; + +import java.util.List; + +import org.hps.analysis.trigger.util.ComponentUtils; +import org.hps.analysis.trigger.util.Pair; +import org.hps.analysis.trigger.util.PairTrigger; +import org.hps.analysis.trigger.util.SinglesTrigger; +import org.hps.readout.ecal.triggerbank.SSPNumberedTrigger; +import org.hps.analysis.trigger.util.Trigger; +import org.hps.analysis.trigger.util.TriggerDiagnosticUtil; + +public class TriggerEfficiencyModule { + // Store the statistics. + protected int[][] triggersSeenByType = new int[6][6]; + protected int[][] triggersMatchedByType = new int[6][6]; + + /** + * Adds the number of matched triggers from the event to the total + * seen for each of the appropriate trigger types. + * @param eventTriggerType - The trigger type ID for the trigger that + * caused the event readout. + * @param event - A trigger statistical event. + */ + public void addEvent(int eventTriggerType, TriggerMatchEvent event) { + // Iterate over the matched triggers and track how many were + // found of each trigger type. + List<Pair<Trigger<?>, SSPNumberedTrigger>> pairList = event.getMatchedReconPairs(); + for(Pair<Trigger<?>, SSPNumberedTrigger> pair : pairList) { + // Update the appropriate counter based on the trigger type. + int triggerType = getTriggerType(pair.getFirstElement()); + triggersMatchedByType[eventTriggerType][triggerType]++; + } + } + + /** + * Adds singles triggers to the list of triggers seen. + * @param eventTriggerType - The trigger type ID for the event + * trigger type. + * @param singlesTriggers - A list of size two containing the + * triggers seen for each of the two singles triggers. + */ + public void addSinglesTriggers(int eventTriggerType, List<List<? extends Trigger<?>>> singlesTriggers) { + // Note the trigger type. + int[] triggerType = { TriggerDiagnosticUtil.TRIGGER_SINGLES_1, TriggerDiagnosticUtil.TRIGGER_SINGLES_2 }; + + // Track the total number of singles triggers seen. + addTriggers(eventTriggerType, singlesTriggers, triggerType); + } + + /** + * Adds pair triggers to the list of triggers seen. + * @param eventTriggerType - The trigger type ID for the event + * trigger type. + * @param pairTriggers - A list of size two containing the + * triggers seen for each of the two pair triggers. + */ + public void addPairTriggers(int eventTriggerType, List<List<? extends Trigger<?>>> pairTriggers) { + // Note the trigger type. + int[] triggerType = { TriggerDiagnosticUtil.TRIGGER_PAIR_1, TriggerDiagnosticUtil.TRIGGER_PAIR_2 }; + + // Track the total number of singles triggers seen. + addTriggers(eventTriggerType, pairTriggers, triggerType); + } + + /** + * Clears the data stored in the module. + */ + public void clear() { + triggersSeenByType = new int[6][6]; + triggersMatchedByType = new int[6][6]; + } + + @Override + public TriggerEfficiencyModule clone() { + // Create a new module. + TriggerEfficiencyModule clone = new TriggerEfficiencyModule(); + + // Clone the data. + clone.triggersMatchedByType = triggersMatchedByType.clone(); + clone.triggersSeenByType = triggersSeenByType.clone(); + + // Return the clone. + return clone; + } + + /** + * Gets the number of triggers matched in events that were caused + * by trigger <code>eventTriggerID</code> for <code>seenTriggerID + * </code> trigger. + * @param eventTriggerID - The trigger that caused the event. + * @param seenTriggerID - The trigger that was seen in the event. + * @return Returns the number of matches as an <code>int</code>. + */ + public int getTriggersMatched(int eventTriggerID, int seenTriggerID) { + return triggersMatchedByType[eventTriggerID][seenTriggerID]; + } + + /** + * Gets the number of triggers seen in events that were caused + * by trigger <code>eventTriggerID</code> for <code>seenTriggerID + * </code> trigger. + * @param eventTriggerID - The trigger that caused the event. + * @param seenTriggerID - The trigger that was seen in the event. + * @return Returns the number of triggers as an <code>int</code>. + */ + public int getTriggersSeen(int eventTriggerID, int seenTriggerID) { + return triggersSeenByType[eventTriggerID][seenTriggerID]; + } + + /** + * Prints the trigger statistics to the terminal as a table. + */ + public void printModule() { + // Define constant spacing variables. + int columnSpacing = 3; + + // Define table headers. + String sourceName = "Source"; + String seenName = "Trigger Efficiency"; + + // Get the longest column header name. + int longestHeader = -1; + for(String triggerName : TriggerDiagnosticUtil.TRIGGER_NAME) { + longestHeader = ComponentUtils.max(longestHeader, triggerName.length()); + } + longestHeader = ComponentUtils.max(longestHeader, sourceName.length()); + + // Determine the spacing needed to display the largest numerical + // cell value. + int numWidth = -1; + int longestCell = -1; + for(int eventTriggerID = 0; eventTriggerID < 6; eventTriggerID++) { + for(int seenTriggerID = 0; seenTriggerID < 6; seenTriggerID++) { + int valueSize = ComponentUtils.getDigits(triggersSeenByType[eventTriggerID][seenTriggerID]); + int cellSize = valueSize * 2 + 3; + if(cellSize > longestCell) { + longestCell = cellSize; + numWidth = valueSize; + } + } + } + + // The total column width can then be calculated from the + // longer of the header and cell values. + int columnWidth = ComponentUtils.max(longestCell, longestHeader); + + // Calculate the total width of the table value header columns. + int headerTotalWidth = (TriggerDiagnosticUtil.TRIGGER_NAME.length * columnWidth) + + ((TriggerDiagnosticUtil.TRIGGER_NAME.length - 1) * columnSpacing); + + // Write the table header. + String spacingText = ComponentUtils.getChars(' ', columnSpacing); + System.out.println(ComponentUtils.getChars(' ', columnWidth) + spacingText + + getCenteredString(seenName, headerTotalWidth)); + + // Create the format strings for the cell values. + String headerFormat = "%-" + columnWidth + "s" + spacingText; + String cellFormat = "%" + numWidth + "d / %" + numWidth + "d"; + String nullText = getCenteredString(ComponentUtils.getChars('-', numWidth) + " / " + + ComponentUtils.getChars('-', numWidth), columnWidth) + spacingText; + + // Print the column headers. + System.out.printf(headerFormat, sourceName); + for(String header : TriggerDiagnosticUtil.TRIGGER_NAME) { + System.out.print(getCenteredString(header, columnWidth) + spacingText); + } + System.out.println(); + + // Write out the value columns. + for(int eventTriggerID = 0; eventTriggerID < 6; eventTriggerID++) { + // Print out the row header. + System.out.printf(headerFormat, TriggerDiagnosticUtil.TRIGGER_NAME[eventTriggerID]); + + // Print the cell values. + for(int seenTriggerID = 0; seenTriggerID < 6; seenTriggerID++) { + if(seenTriggerID == eventTriggerID) { System.out.print(nullText); } + else { + String cellText = String.format(cellFormat, triggersMatchedByType[eventTriggerID][seenTriggerID], + triggersSeenByType[eventTriggerID][seenTriggerID]); + System.out.print(getCenteredString(cellText, columnWidth) + spacingText); + } + } + + // Start a new line. + System.out.println(); + } + } + + /** + * Adds triggers in a generic way to the number of triggers seen. + * @param eventTriggerType - The trigger type ID for the event + * trigger type. + * @param triggerList - A list of size two containing the + * triggers seen for each of the two triggers of its type. + * @param triggerTypeID - The two trigger IDs corresponding to the + * list entries. + */ + private void addTriggers(int eventTriggerType, List<List<? extends Trigger<?>>> triggerList, int[] triggerTypeID) { + // Track the total number of singles triggers seen. + for(int triggerNum = 0; triggerNum < 2; triggerNum++) { + List<? extends Trigger<?>> triggers = triggerList.get(triggerNum); + triggersSeenByType[eventTriggerType][triggerTypeID[triggerNum]] += triggers.size(); + } + } + + /** + * Produces a <code>String</code> of the indicated length with the + * text <code>value</code> centered in the middle. Extra length is + * filled through spaces before and after the text. + * @param value - The text to display. + * @param width - The number of spaces to include. + * @return Returns a <code>String</code> of the specified length, + * or the argument text if it is longer. + */ + private static final String getCenteredString(String value, int width) { + // The method can not perform as intended if the argument text + // exceeds the requested string length. Just return the text. + if(width <= value.length()) { + return value; + } + + // Otherwise, get the amount of buffering needed to center the + // text and add it around the text to produce the string. + else { + int buffer = (width - value.length()) / 2; + return ComponentUtils.getChars(' ', buffer) + value + + ComponentUtils.getChars(' ', width - buffer - value.length()); + } + } + + /** + * Gets the trigger type identifier from a trigger object. + * @param trigger - A trigger. + * @return Returns the trigger type ID of the argument trigger. + */ + private static final int getTriggerType(Trigger<?> trigger) { + // Choose the appropriate trigger type ID based on the class + // of the trigger. + if(trigger instanceof PairTrigger) { + // Use the trigger number to determine which of the two + // triggers this is. Note that this assumes that the trigger + // number is stored as either 0 or 1. + if(trigger.getTriggerNumber() == 0) { + return TriggerDiagnosticUtil.TRIGGER_PAIR_1; + } else { + return TriggerDiagnosticUtil.TRIGGER_PAIR_2; + } + } else if(trigger instanceof SinglesTrigger) { + // Use the trigger number to determine which of the two + // triggers this is. Note that this assumes that the trigger + // number is stored as either 0 or 1. + if(trigger.getTriggerNumber() == 0) { + return TriggerDiagnosticUtil.TRIGGER_SINGLES_1; + } else { + return TriggerDiagnosticUtil.TRIGGER_SINGLES_2; + } + } + + // If the trigger type is not supported, throw an exception. + throw new IllegalArgumentException(String.format("Trigger type \"%s\" is not supported.", + trigger.getClass().getSimpleName())); + } +} Added: 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 (added) +++ java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerMatchEvent.java Thu Mar 5 10:59:19 2015 @@ -0,0 +1,172 @@ +package org.hps.analysis.trigger.event; + +import java.util.ArrayList; +import java.util.List; + +import org.hps.analysis.trigger.util.Pair; +import org.hps.analysis.trigger.util.Trigger; +import org.hps.readout.ecal.triggerbank.SSPNumberedTrigger; + +/** + * Tracks trigger pairs that were matched within an event. This can also + * track simulated SSP cluster pairs along with specifically which cuts + * passed and which did not. + * + * @author Kyle McCarty <[log in to unmask]> + */ +public class TriggerMatchEvent { + // Track trigger matching statistics. + private int[] sspInternalMatched = new int[2]; + private int[] reconTriggersMatched = new int[2]; + private int[][] triggerComp = new int[4][2]; + + // Track the matched trigger pairs. + private List<TriggerMatchedPair> sspPairList = new ArrayList<TriggerMatchedPair>(); + private List<Pair<Trigger<?>, SSPNumberedTrigger>> reconPairList = new ArrayList<Pair<Trigger<?>, SSPNumberedTrigger>>(); + + /** + * Gets the number of times a cut of the given cut ID failed when + * SSP simulated triggers were compared to SSP bank triggers. + * @param triggerNumber - The trigger for which to get the value. + * @param cutID - The ID of the cut. + * @return Returns the number of times the cut failed as an + * <code>int</code> primitive. + */ + public int getCutFailures(int triggerNumber, int cutID) { + // Validate the arguments. + 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)); + } + + // Return the requested cut value. + return triggerComp[cutID][triggerNumber]; + } + + /** + * Gets the number of reconstructed cluster triggers that were + * matched successfully. + * @return Returns the value as an <code>int</code> primitive. + */ + public int getMatchedReconTriggers() { + return reconPairList.size(); + } + + /** + * Gets the number of reconstructed cluster triggers that were + * matched successfully for a specific trigger. + * @param triggerNumber - The trigger number. + * @return Returns the value as an <code>int</code> primitive. + */ + public int getMatchedReconTriggers(int triggerNumber) { + // Validate the arguments. + if(triggerNumber !=0 && triggerNumber != 1) { + throw new IndexOutOfBoundsException("Trigger number must be 0 or 1."); + } + + // Return the trigger count. + return reconTriggersMatched[triggerNumber]; + } + + /** + * Gets the number of simulated SSP cluster triggers that were + * matched successfully. + * @return Returns the value as an <code>int</code> primitive. + */ + public int getMatchedSSPTriggers() { + return sspPairList.size(); + } + + /** + * Gets the number of simulated SSP cluster triggers that were + * matched successfully for a specific trigger. + * @param triggerNumber - The trigger number. + * @return Returns the value as an <code>int</code> primitive. + */ + public int getMatchedSSPTriggers(int triggerNumber) { + // Validate the arguments. + if(triggerNumber !=0 && triggerNumber != 1) { + throw new IndexOutOfBoundsException("Trigger number must be 0 or 1."); + } + + // Return the trigger count. + return sspInternalMatched[triggerNumber]; + } + + /** + * Gets a list containing all reconstructed cluster triggers and + * their matched SSP bank triggers. + * @return Returns the trigger pairs as a <code>List</code> + * collection of <code>Pair</code> objects. + */ + public List<Pair<Trigger<?>, SSPNumberedTrigger>> getMatchedReconPairs() { + return reconPairList; + } + + /** + * Adds a reconstructed trigger and SSP bank trigger pair that is + * marked as matched for all trigger cuts. + * @param reconTrigger - The reconstructed cluster trigger. + * @param sspTrigger - The SSP bank trigger. + */ + public void matchedReconPair(Trigger<?> reconTrigger, SSPNumberedTrigger sspTrigger) { + reconTriggersMatched[sspTrigger.isFirstTrigger() ? 0 : 1]++; + reconPairList.add(new Pair<Trigger<?>, SSPNumberedTrigger>(reconTrigger, sspTrigger)); + } + + /** + * Adds a simulated SSP trigger and SSP bank trigger pair that is + * marked as matched for all trigger cuts. + * @param simTrigger - The simulated SSP cluster trigger. + * @param sspTrigger - The SSP bank trigger. + */ + public void matchedSSPPair(Trigger<?> simTrigger, SSPNumberedTrigger sspTrigger) { + // A null SSP trigger means that no match was found. This is + // treated as a failure due to time, which is not tracked. + if(sspTrigger != null) { + sspInternalMatched[sspTrigger.isFirstTrigger() ? 0 : 1]++; + sspPairList.add(new TriggerMatchedPair(simTrigger, sspTrigger, new boolean[] { true, true, true, true, true })); + } + } + + /** + * Adds a simulated SSP trigger and SSP bank trigger pair along + * with the cuts that matched and did not. + * @param simTrigger - The simulated SSP cluster trigger. + * @param sspTrigger - The SSP bank trigger. + * @param cutsMatched - An array indicating which cuts matched and + * which did not. + */ + public void matchedSSPPair(Trigger<?> simTrigger, SSPNumberedTrigger sspTrigger, boolean[] cutsMatched) { + // Store the full cut array. + boolean[] cutArray = cutsMatched; + + // If the array is size 3, it is the a singles trigger. Update + // it to an array of size 4 for compatibility. + if(cutsMatched.length == 3) { + boolean[] tempArray = { true, true, true, true }; + for(int cutIndex = 0; cutIndex < cutsMatched.length; cutIndex++) { + tempArray[cutIndex] = cutsMatched[cutIndex]; + } + cutArray = tempArray; + } + + // Add the trigger pair to the list. + TriggerMatchedPair triggerPair = new TriggerMatchedPair(simTrigger, sspTrigger, cutArray); + sspPairList.add(triggerPair); + + // 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]) { + triggerComp[cutIndex][triggerNum]++; + isMatched = false; + } + } + + // If all the cuts are true, then the trigger pair is a match. + if(isMatched) { sspInternalMatched[triggerNum]++; } + } +} Added: 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 (added) +++ java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerMatchStatus.java Thu Mar 5 10:59:19 2015 @@ -0,0 +1,65 @@ +package org.hps.analysis.trigger.event; + +import java.util.List; + +import org.hps.analysis.trigger.util.Trigger; +import org.hps.readout.ecal.triggerbank.SSPNumberedTrigger; + +/** + * Tracks the trigger diagnostic statistics for trigger matching. + * + * @author Kyle McCarty <[log in to unmask]> + */ +public class TriggerMatchStatus extends TriggerStatModule { + /** + * Adds the statistical data stored in a trigger comparison event + * into this status tracking module. + * @param event - The event object. + * @param reconTriggers - A list of reconstructed cluster triggers. + * @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, + List<List<? extends Trigger<?>>> sspSimTriggers, List<? extends SSPNumberedTrigger> sspBankTriggers) { + // Check if there are more bank triggers than there are + // simulated SSP triggers. + int sspTriggerDiff = sspBankTriggers.size() - sspSimTriggers.size(); + if(sspTriggerDiff > 0) { + reportedExtras += sspTriggerDiff; + } + + // Increment the number of triggers of each type that have been + // seen so far. + sspTriggers += sspSimTriggers.get(0).size() + sspSimTriggers.get(1).size(); + this.reconTriggers += reconTriggers.get(0).size() + reconTriggers.get(1).size(); + reportedTriggers += sspBankTriggers.size(); + + // Increment the count for each cut failure type. + for(int triggerNum = 0; triggerNum < 2; triggerNum++) { + for(int cutIndex = 0; cutIndex < 4; cutIndex++) { + triggerComp[cutIndex][triggerNum] += event.getCutFailures(triggerNum, cutIndex); + } + } + + // Increment the total triggers found. + for(int triggerNum = 0; triggerNum < 2; triggerNum++) { + sspInternalMatched[triggerNum] += event.getMatchedSSPTriggers(triggerNum); + reconTriggersMatched[triggerNum] += event.getMatchedReconTriggers(triggerNum); + } + } + + @Override + public void clear() { + // Clear the statistics module. + super.clear(); + } + + /** + * Gets a copy of the statistical data stored in the object. + * @return Returns the data in a <code>TriggerStatModule</code> + * object. + */ + public TriggerStatModule cloneStatModule() { + return new TriggerStatModule(this); + } +} Added: 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 (added) +++ java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerMatchedPair.java Thu Mar 5 10:59:19 2015 @@ -0,0 +1,140 @@ +package org.hps.analysis.trigger.event; + +import org.hps.analysis.trigger.util.Pair; +import org.hps.analysis.trigger.util.PairTrigger; +import org.hps.analysis.trigger.util.SinglesTrigger; +import org.hps.analysis.trigger.util.Trigger; +import org.hps.readout.ecal.triggerbank.SSPNumberedTrigger; +import org.hps.readout.ecal.triggerbank.SSPPairTrigger; +import org.hps.readout.ecal.triggerbank.SSPSinglesTrigger; + +/** + * Stores a pair of one simulated trigger and one SSP bank trigger that + * have been matched, along with which cuts are the same and which are + * different. + * + * @author Kyle McCarty <[log in to unmask]> + */ +public class TriggerMatchedPair extends Pair<Trigger<?>, SSPNumberedTrigger> { + // Cut IDs. + public static final int SINGLES_ENERGY_MIN = 0; + public static final int SINGLES_ENERGY_MAX = 1; + public static final int SINGLES_HIT_COUNT = 2; + public static final int PAIR_ENERGY_SUM = 0; + public static final int PAIR_ENERGY_DIFF = 1; + public static final int PAIR_ENERGY_SLOPE = 2; + public static final int PAIR_COPLANARITY = 3; + + // Store the trigger result. + private boolean[] matchedCut = new boolean[4]; + + /** + * Creates a matched trigger pair of a simulated trigger and an SSP + * bank trigger. + * @param firstElement - The simulated trigger. + * @param secondElement - The SSP bank trigger. + * @param cutsMatched - An array indicating which cuts match and + * which do not. + */ + public TriggerMatchedPair(Trigger<?> firstElement, SSPNumberedTrigger secondElement, boolean[] cutsMatched) { + // Store the trigger objects. + super(firstElement, secondElement); + + // Set the matched cuts. + if(cutsMatched.length != 4) { + throw new IllegalArgumentException(String.format("The matched cuts array must be of size 4. Seen size = %d.", cutsMatched.length)); + } else { + matchedCut[0] = cutsMatched[0]; + matchedCut[1] = cutsMatched[1]; + matchedCut[2] = cutsMatched[2]; + matchedCut[3] = cutsMatched[3]; + } + } + + /** + * Gets whether the cut of the given cut ID passed the trigger. + * @param cutID - The cut ID. + * @return Returns <code>true</code> if the cut passed and + * <code>false</code> otherwise. + */ + public boolean getCutResult(int cutID) { + if(cutID > matchedCut.length || cutID < 0) { + throw new IndexOutOfBoundsException(String.format("Cut ID \"%d\" is not valid.", cutID)); + } else { + return matchedCut[cutID]; + } + } + + /** + * Gets the simulated trigger from the pair. + * @return Returns the simulated trigger as an object of the generic + * type <code>Trigger<?></code>. + */ + public Trigger<?> getSimulatedTrigger() { + return getFirstElement(); + } + + /** + * Gets the type of cluster on which the trigger was simulated. + * @return Returns the type of cluster as a <code>Class<?></code> + * object. + */ + public Class<?> getSimulatedTriggerType() { + return getFirstElement().getTriggerSource().getClass(); + } + + /** + * Gets the SSP bank trigger from the pair. + * @return Returns the SSP bank trigger as an object of the generic + * type <code>SSPNumberedTrigger</code>. + */ + public SSPNumberedTrigger getSSPTrigger() { + return getSecondElement(); + } + + /** + * Returns whether the triggers in this pair are from the first + * trigger or not. + * @return Returns <code>true</code> if the triggers are from the + * first trigger and <code>false</code> otherwise. + */ + public boolean isFirstTrigger() { + return getSecondElement().isFirstTrigger(); + } + + /** + * Indicates whether this is a pair of pair triggers. + * @return Returns <code>true</code> if the triggers are pair + * triggers and <code>false</code> otherwise. + */ + public boolean isPairTrigger() { + if(getFirstElement() instanceof PairTrigger && getSecondElement() instanceof SSPPairTrigger) { + return true; + } else { + return false; + } + } + + /** + * Returns whether the triggers in this pair are from the second + * trigger or not. + * @return Returns <code>true</code> if the triggers are from the + * second trigger and <code>false</code> otherwise. + */ + public boolean isSecondTrigger() { + return getSecondElement().isSecondTrigger(); + } + + /** + * Indicates whether this is a pair of singles triggers. + * @return Returns <code>true</code> if the triggers are singles + * triggers and <code>false</code> otherwise. + */ + public boolean isSinglesTrigger() { + if(getFirstElement() instanceof SinglesTrigger && getSecondElement() instanceof SSPSinglesTrigger) { + return true; + } else { + return false; + } + } +} Added: 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 (added) +++ java/trunk/analysis/src/main/java/org/hps/analysis/trigger/event/TriggerStatModule.java Thu Mar 5 10:59:19 2015 @@ -0,0 +1,166 @@ +package org.hps.analysis.trigger.event; + +/** + * Class <code>TriggerStatModule</code> stores the statistical data + * for trigger diagnostic trigger matching. + * + * @author Kyle McCarty <[log in to unmask]> + */ +public class TriggerStatModule { + // Track trigger verification statistical information. + protected int sspTriggers = 0; + protected int reconTriggers = 0; + protected int reportedTriggers = 0; + protected int reportedExtras = 0; + protected int[] sspInternalMatched = new int[2]; + protected int[] reconTriggersMatched = new int[2]; + protected int[][] triggerComp = new int[4][2]; + + + /** + * Instantiates a <code>TriggerStatModule</code> with no statistics + * stored. + */ + TriggerStatModule() { } + + /** + * Instantiates a <code>TriggerStatModule</code> with no statistics + * cloned from the base object. + * @param base - The source for the statistical data. + */ + protected TriggerStatModule(TriggerStatModule base) { + // Copy all of the values to the clone. + sspTriggers = base.sspTriggers; + reconTriggers = base.reconTriggers; + reportedTriggers = base.reportedTriggers; + reportedExtras = base.reportedExtras; + for(int i = 0; i < sspInternalMatched.length; i++) { + sspInternalMatched[i] = base.sspInternalMatched[i]; + } + for(int i = 0; i < reconTriggersMatched.length; i++) { + reconTriggersMatched[i] = base.reconTriggersMatched[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]; + } + } + } + + /** + * Clears all the tracked values and resets them to the default + * empty value. + */ + void clear() { + sspTriggers = 0; + reconTriggers = 0; + reportedTriggers = 0; + reportedExtras = 0; + sspInternalMatched = new int[2]; + reconTriggersMatched = new int[2]; + triggerComp = new int[4][2]; + } + + /** + * Gets the number of times a cut of the given cut ID failed when + * SSP simulated triggers were compared to SSP bank triggers. + * @param triggerNumber - The trigger for which to get the value. + * @param cutID - The ID of the cut. + * @return Returns the number of times the cut failed as an + * <code>int</code> primitive. + */ + public int getCutFailures(int triggerNumber, int cutID) { + // Validate the arguments. + 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)); + } + + // Return the requested cut value. + return triggerComp[cutID][triggerNumber]; + } + + /** + * 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. + */ + public int getExtraSSPBankTriggers() { + return reportedExtras; + } + + /** + * Gets the number of reconstructed triggers that matched bank SSP + * triggers for both triggers. + * @return Returns the value as an <code>int</code> primitive. + */ + public int getMatchedReconTriggers() { + return reconTriggersMatched[0] + reconTriggersMatched[1]; + } + + /** + * Gets the number of reconstructed triggers that matched bank SSP + * triggers. + * @param triggerNumber - The trigger for which to get the value. + * @return Returns the value as an <code>int</code> primitive. + */ + public int getMatchedReconTriggers(int triggerNumber) { + // Validate the arguments. + if(triggerNumber !=0 && triggerNumber != 1) { + throw new IndexOutOfBoundsException("Trigger number must be 0 or 1."); + } + + // Return the trigger count. + return reconTriggersMatched[triggerNumber]; + } + + /** + * Gets the number of simulated SSP triggers that matched bank SSP + * triggers for both triggers. + * @return Returns the value as an <code>int</code> primitive. + */ + public int getMatchedSSPTriggers() { + return sspInternalMatched[0] + sspInternalMatched[1]; + } + + /** + * Gets the number of simulated SSP triggers that matched bank SSP + * triggers. + * @param triggerNumber - The trigger for which to get the value. + * @return Returns the value as an <code>int</code> primitive. + */ + public int getMatchedSSPTriggers(int triggerNumber) { + // Validate the arguments. + if(triggerNumber !=0 && triggerNumber != 1) { + throw new IndexOutOfBoundsException("Trigger number must be 0 or 1."); + } + + // Return the trigger count. + return sspInternalMatched[triggerNumber]; + } + + /** + * Gets the number of reconstructed cluster triggers seen. + * @return Returns the value as an <code>int</code> primitive. + */ + public int getReconTriggerCount() { + return reconTriggers; + } + + /** + * Gets the number of simulated SSP cluster triggers seen. + * @return Returns the value as an <code>int</code> primitive. + */ + public int getSSPSimTriggerCount() { + return sspTriggers; + } + + /** + * Gets the number of triggers reported by the SSP bank. + * @return Returns the value as an <code>int</code> primitive. + */ + public int getSSPBankTriggerCount() { + return reportedTriggers; + } +} Added: java/trunk/analysis/src/main/java/org/hps/analysis/trigger/util/ComponentUtils.java ============================================================================= --- java/trunk/analysis/src/main/java/org/hps/analysis/trigger/util/ComponentUtils.java (added) +++ java/trunk/analysis/src/main/java/org/hps/analysis/trigger/util/ComponentUtils.java Thu Mar 5 10:59:19 2015 @@ -0,0 +1,126 @@ +package org.hps.analysis.trigger.util; + +import java.awt.Component; + +import org.hps.analysis.trigger.util.TriggerDiagnosticUtil; + +/** + * Class <code>ComponentUtils</code> is a list of utility methods used + * by the trigger diagnostic GUI. + * + * @author Kyle McCarty <[log in to unmask]> + */ +public class ComponentUtils { + /** The default spacing used between a horizontal edge of one + * component and the horizontal edge of another. */ + static final int hinternal = 10; + /** The default spacing used between a vertical edge of one + * component and the vertical edge of another. */ + static final int vinternal = 10; + /** The default spacing used between a horizontal edge of one + * component and the edge of its parent component. */ + static final int hexternal = 0; + /** The default spacing used between a vertical edge of one + * component and the edge of its parent component. */ + static final int vexternal = 0; + + /** + * Gets a <code>String</code> composed of a number of instances of + * character <code>c</code> equal to <code>number</code>. + * @param c - The character to repeat. + * @param number - The number of repetitions. + * @return Returns the repeated character as a <code>String</code>. + */ + public static final String getChars(char c, int number) { + // Create a buffer to store the characters in. + StringBuffer s = new StringBuffer(); + + // Add the indicated number of instances. + for(int i = 0; i < number; i++) { + s.append(c); + } + + // Return the string. + return s.toString(); + } + + /** + * Gets the number of digits in the base-10 String representation + * of an integer primitive. Negative signs are not included in the + * digit count. + * @param value - The value of which to obtain the length. + * @return Returns the number of digits in the String representation + * of the argument value. + */ + public static final int getDigits(int value) { + return TriggerDiagnosticUtil.getDigits(value); + } + + /** + * Gets the maximum value from a list of values. + * @param values - The values to compare. + * @return Returns the largest of the argument values. + * @throws IllegalArgumentException Occurs if no values are given. + */ + public static final int max(int... values) throws IllegalArgumentException { + // Throw an error if no arguments are provided. + if(values == null || values.length == 0) { + throw new IllegalArgumentException("Can not determine maximum value from a list of 0 values."); + } + + // If there is only one value, return it. + if(values.length == 1) { return values[0]; } + + // Otherwise, get the largest value. + int largest = Integer.MIN_VALUE; + for(int value : values) { + if(value > largest) { largest = value; } + } + + // Return the result. + return largest; + } + + /** + * Gets the x-coordinate immediately to the right of the given + * component. + * @param c - The component of which to find the edge. + * @return Returns the x-coordinate as an <code>int</code> value. + */ + static final int getNextX(Component c) { + return getNextX(c, 0); + } + + /** + * Gets the x-coordinate a given distance to the right edge of the + * argument component. + * @param c - The component of which to find the edge. + * @param spacing - The additional spacing past the edge of the + * component to add. + * @return Returns the x-coordinate as an <code>int</code> value. + */ + static final int getNextX(Component c, int spacing) { + return c.getX() + c.getWidth() + spacing; + } + + /** + * Gets the y-coordinate immediately below the given component. + * @param c - The component of which to find the edge. + * @return Returns the y-coordinate as an <code>int</code> value. + */ + static final int getNextY(Component c) { + return getNextY(c, 0); + } + + /** + * Gets the y-coordinate a given distance below the bottom edge + * of the argument component. + * @param c - The component of which to find the edge. + * @param spacing - The additional spacing past the edge of the + * component to add. + * @return Returns the y-coordinate as an <code>int</code> value. + */ + static final int getNextY(Component c, int spacing) { + return c.getY() + c.getHeight() + spacing; + } +} Added: java/trunk/analysis/src/main/java/org/hps/analysis/trigger/util/OutputLogger.java ============================================================================= --- java/trunk/analysis/src/main/java/org/hps/analysis/trigger/util/OutputLogger.java (added) +++ java/trunk/analysis/src/main/java/org/hps/analysis/trigger/util/OutputLogger.java Thu Mar 5 10:59:19 2015 @@ -0,0 +1,30 @@ +package org.hps.analysis.trigger.util; + +public class OutputLogger { + private static StringBuffer outputBuffer = new StringBuffer(); + + public static final void printf(String text, Object... args) { + outputBuffer.append(String.format(text, args)); + } + + public static final void println() { printf(String.format("%n")); } + + public static final void println(String text) { printf(String.format("%s%n", text)); } + + public static final void print(String text) { printf(text); } + + public static final void printLog() { + System.out.println(outputBuffer.toString()); + clearLog(); + } + + public static final void printNewLine() { println(); } + + public static final void printNewLine(int quantity) { + for(int i = 0; i < quantity; i++) { println(); } + } + + public static final void clearLog() { + outputBuffer = new StringBuffer(); + } +} Added: java/trunk/analysis/src/main/java/org/hps/analysis/trigger/util/Pair.java ============================================================================= --- java/trunk/analysis/src/main/java/org/hps/analysis/trigger/util/Pair.java (added) +++ java/trunk/analysis/src/main/java/org/hps/analysis/trigger/util/Pair.java Thu Mar 5 10:59:19 2015 @@ -0,0 +1,39 @@ +package org.hps.analysis.trigger.util; + +/** + * Class <code>Pair</code> represents a pair of two objects. + * + * @author Kyle McCarty <[log in to unmask]> + * @param <E> - The object type of the first element in the pair. + * @param <F> - The object type of the second element in the pair. + */ +public class Pair<E, F> { + private final E firstObject; + private final F secondObject; + + /** + * Creates a pair of the two indicated objects. + * @param firstObject - The first object. + * @param secondObject - The second object. + */ + public Pair(E firstElement, F secondElement) { + this.firstObject = firstElement; + this.secondObject = secondElement; + } + + /** + * Gets the first element of the pair. + * @return Returns the first element. + */ + public E getFirstElement() { + return firstObject; + } + + /** + * Gets the second element of the pair. + * @return Returns the second element. + */ + public F getSecondElement() { + return secondObject; + } +} Added: 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 (added) +++ java/trunk/analysis/src/main/java/org/hps/analysis/trigger/util/PairTrigger.java Thu Mar 5 10:59:19 2015 @@ -0,0 +1,166 @@ +package org.hps.analysis.trigger.util; + +import org.hps.readout.ecal.TriggerModule; + +public class PairTrigger<E> extends SinglesTrigger<E> { + // Define the supported trigger cuts. + private static final String PAIR_ENERGY_SUM_LOW = TriggerModule.PAIR_ENERGY_SUM_LOW; + private static final String PAIR_ENERGY_SUM_HIGH = TriggerModule.PAIR_ENERGY_SUM_HIGH; + private static final String PAIR_ENERGY_DIFFERENCE_HIGH = TriggerModule.PAIR_ENERGY_DIFFERENCE_HIGH; + private static final String PAIR_ENERGY_SLOPE_LOW = TriggerModule.PAIR_ENERGY_SLOPE_LOW; + private static final String PAIR_COPLANARITY_HIGH = TriggerModule.PAIR_COPLANARITY_HIGH; + private static final String PAIR_TIME_COINCIDENCE = "pairTimeCoincidence"; + + /** + * Instantiates a new <code>PairTrigger</code> with all cut + * states set to <code>false</code> and with the trigger source + * defined according to the specified object. + * @param source - The object from which the trigger cut states + * are derived. + */ + public PairTrigger(E source, int triggerNum) { + // Instantiate the superclass. + super(source, triggerNum); + + // Add the supported cuts types. + addValidCut(PAIR_ENERGY_SUM_LOW); + addValidCut(PAIR_ENERGY_SUM_HIGH); + addValidCut(PAIR_ENERGY_DIFFERENCE_HIGH); + addValidCut(PAIR_ENERGY_SLOPE_LOW); + addValidCut(PAIR_COPLANARITY_HIGH); + addValidCut(PAIR_TIME_COINCIDENCE); + } + + /** + * Gets whether the pair energy sum lower bound cut was met. + * @return Returns <code>true</code> if the cut was met and + * <code>false</code> otherwise. + */ + public boolean getStateEnergySumLow() { + return getCutState(PAIR_ENERGY_SUM_LOW); + } + + /** + * Gets whether the pair energy sum upper bound cut was met. + * @return Returns <code>true</code> if the cut was met and + * <code>false</code> otherwise. + */ + public boolean getStateEnergySumHigh() { + return getCutState(PAIR_ENERGY_SUM_HIGH); + } + + /** + * Gets whether both the pair energy sum upper and lower bound cuts + * were met. + * @return Returns <code>true</code> if the cuts were met and + * <code>false</code> otherwise. + */ + public boolean getStateEnergySum() { + return getCutState(PAIR_ENERGY_SUM_HIGH); + } + + /** + * Gets whether the pair energy difference cut was met. + * @return Returns <code>true</code> if the cut was met and + * <code>false</code> otherwise. + */ + public boolean getStateEnergyDifference() { + return getCutState(PAIR_ENERGY_SUM_HIGH); + } + + /** + * Gets whether the pair energy slope cut was met. + * @return Returns <code>true</code> if the cut was met and + * <code>false</code> otherwise. + */ + public boolean getStateEnergySlope() { + return getCutState(PAIR_ENERGY_SLOPE_LOW); + } + + /** + * Gets whether the pair coplanarity cut was met. + * @return Returns <code>true</code> if the cut was met and + * <code>false</code> otherwise. + */ + public boolean getStateCoplanarity() { + return getCutState(PAIR_COPLANARITY_HIGH); + } + + /** + * Gets whether the time coincidence cut was met. + * @return Returns <code>true</code> if the cut was met and + * <code>false</code> otherwise. + */ + public boolean getStateTimeCoincidence() { + return getCutState(PAIR_TIME_COINCIDENCE); + } + + /** + * Sets whether the conditions for the pair energy sum lower bound + * cut were met. + * @param state - <code>true</code> indicates that the cut conditions + * were met and <code>false</code> that they were not. + */ + public void setStateEnergySumLow(boolean state) { + setCutState(PAIR_ENERGY_SUM_LOW, state); + } + + /** + * Sets whether the conditions for the pair energy sum upper bound + * cut were met. + * @param state - <code>true</code> indicates that the cut conditions + * were met and <code>false</code> that they were not. + */ + public void setStateEnergySumHigh(boolean state) { + setCutState(PAIR_ENERGY_SUM_HIGH, state); + } + + /** + * Sets whether the conditions for the pair energy difference cut + * were met. + * @param state - <code>true</code> indicates that the cut conditions + * were met and <code>false</code> that they were not. + */ + public void setStateEnergyDifference(boolean state) { + setCutState(PAIR_ENERGY_DIFFERENCE_HIGH, state); + } + + /** + * Sets whether the conditions for the pair energy slope cut were + * met. + * @param state - <code>true</code> indicates that the cut conditions + * were met and <code>false</code> that they were not. + */ + public void setStateEnergySlope(boolean state) { + setCutState(PAIR_ENERGY_SLOPE_LOW, state); + } + + /** + * Sets whether the conditions for the pair coplanarity cut were + * met. + * @param state - <code>true</code> indicates that the cut conditions + * were met and <code>false</code> that they were not. + */ + public void setStateCoplanarity(boolean state) { + setCutState(PAIR_COPLANARITY_HIGH, state); + } + + /** + * Sets whether the conditions for the time coincidence cut were + * met. + * @param state - <code>true</code> indicates that the cut conditions + * were met and <code>false</code> that they were not. + */ + public void setStateTimeCoincidence(boolean state) { + setCutState(PAIR_TIME_COINCIDENCE, state); + } + + @Override + public String toString() { + return String.format("EClusterLow: %d; EClusterHigh %d; HitCount: %d; ESumLow: %d, ESumHigh: %d, EDiff: %d, ESlope: %d, Coplanarity: %d", + getStateClusterEnergyLow() ? 1 : 0, getStateClusterEnergyHigh() ? 1 : 0, + getStateHitCount() ? 1 : 0, getStateEnergySumLow() ? 1 : 0, + getStateEnergySumHigh() ? 1 : 0, getStateEnergyDifference() ? 1 : 0, + getStateEnergySlope() ? 1 : 0, getStateCoplanarity() ? 1 : 0); + } +} Added: 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 (added) +++ java/trunk/analysis/src/main/java/org/hps/analysis/trigger/util/SinglesTrigger.java Thu Mar 5 10:59:19 2015 @@ -0,0 +1,153 @@ +package org.hps.analysis.trigger.util; + +import org.hps.readout.ecal.TriggerModule; + +public class SinglesTrigger<E> extends Trigger<E> { + // Define the supported trigger cuts. + private static final String CLUSTER_HIT_COUNT_LOW = TriggerModule.CLUSTER_HIT_COUNT_LOW; + private static final String CLUSTER_SEED_ENERGY_LOW = TriggerModule.CLUSTER_SEED_ENERGY_LOW; + private static final String CLUSTER_SEED_ENERGY_HIGH = TriggerModule.CLUSTER_SEED_ENERGY_HIGH; + private static final String CLUSTER_TOTAL_ENERGY_LOW = TriggerModule.CLUSTER_TOTAL_ENERGY_LOW; + private static final String CLUSTER_TOTAL_ENERGY_HIGH = TriggerModule.CLUSTER_TOTAL_ENERGY_HIGH; + + /** + * Instantiates a new <code>SinglesTrigger</code> with all cut + * states set to <code>false</code> and with the trigger source + * defined according to the specified object. + * @param source - The object from which the trigger cut states + * are derived. + */ + public SinglesTrigger(E source, int triggerNum) { + // Instantiate the superclass. + super(source, triggerNum); + + // Add the supported cuts types. + addValidCut(CLUSTER_HIT_COUNT_LOW); + addValidCut(CLUSTER_SEED_ENERGY_LOW); + addValidCut(CLUSTER_SEED_ENERGY_HIGH); + addValidCut(CLUSTER_TOTAL_ENERGY_LOW); + addValidCut(CLUSTER_TOTAL_ENERGY_HIGH); + } + + /** + * Gets whether the cluster hit count cut was met. + * @return Returns <code>true</code> if the cut was met and + * <code>false</code> otherwise. + */ + public boolean getStateHitCount() { + return getCutState(CLUSTER_HIT_COUNT_LOW); + } + + /** + * Gets whether the cluster seed energy lower bound cut was met. + * @return Returns <code>true</code> if the cut was met and + * <code>false</code> otherwise. + */ + public boolean getStateSeedEnergyLow() { + return getCutState(CLUSTER_SEED_ENERGY_LOW); + } + + /** + * Gets whether the cluster seed energy upper bound cut was met. + * @return Returns <code>true</code> if the cut was met and + * <code>false</code> otherwise. + */ + public boolean getStateSeedEnergyHigh() { + return getCutState(CLUSTER_SEED_ENERGY_HIGH); + } + + /** + * Gets whether both the cluster seed energy upper and lower bound + * cuts were met. + * @return Returns <code>true</code> if the cuts were met and + * <code>false</code> otherwise. + */ + public boolean getStateSeedEnergy() { + return getCutState(CLUSTER_SEED_ENERGY_LOW) && getCutState(CLUSTER_SEED_ENERGY_HIGH); + } + + /** + * Gets whether the cluster total energy lower bound cut was met. + * @return Returns <code>true</code> if the cut was met and + * <code>false</code> otherwise. + */ + public boolean getStateClusterEnergyLow() { + return getCutState(CLUSTER_TOTAL_ENERGY_LOW); + } + + /** + * Gets whether the cluster total energy upper bound cut was met. + * @return Returns <code>true</code> if the cut was met and + * <code>false</code> otherwise. + */ + public boolean getStateClusterEnergyHigh() { + return getCutState(CLUSTER_TOTAL_ENERGY_HIGH); + } + + /** + * Gets whether both the cluster total energy upper and lower bound + * cuts were met. + * @return Returns <code>true</code> if the cuts were met and + * <code>false</code> otherwise. + */ + public boolean getStateClusterEnergy() { + return getCutState(CLUSTER_TOTAL_ENERGY_LOW) && getCutState(CLUSTER_TOTAL_ENERGY_HIGH); + } + + /** + * Sets whether the conditions for the cluster hit count cut were + * met. + * @param state - <code>true</code> indicates that the cut conditions + * were met and <code>false</code> that they were not. + */ + public void setStateHitCount(boolean state) { + setCutState(CLUSTER_HIT_COUNT_LOW, state); + } + + /** + * Sets whether the conditions for the cluster seed energy lower + * bound cut were met. + * @param state - <code>true</code> indicates that the cut conditions + * were met and <code>false</code> that they were not. + */ + public void setStateSeedEnergyLow(boolean state) { + setCutState(CLUSTER_SEED_ENERGY_LOW, state); + } + + /** + * Sets whether the conditions for the cluster seed energy upper + * bound cut were met. + * @param state - <code>true</code> indicates that the cut conditions + * were met and <code>false</code> that they were not. + */ + public void setStateSeedEnergyHigh(boolean state) { + setCutState(CLUSTER_SEED_ENERGY_HIGH, state); + } + + /** + * Sets whether the conditions for the cluster total energy lower + * bound cut were met. + * @param state - <code>true</code> indicates that the cut conditions + * were met and <code>false</code> that they were not. + */ + public void setStateClusterEnergyLow(boolean state) { + setCutState(CLUSTER_TOTAL_ENERGY_LOW, state); + } + + /** + * Sets whether the conditions for the cluster total energy upper + * bound cut were met. + * @param state - <code>true</code> indicates that the cut conditions + * were met and <code>false</code> that they were not. + */ + public void setStateClusterEnergyHigh(boolean state) { + setCutState(CLUSTER_TOTAL_ENERGY_HIGH, state); + } + + @Override + public String toString() { + return String.format("EClusterLow: %d; EClusterHigh %d; HitCount: %d", + getStateClusterEnergyLow() ? 1 : 0, getStateClusterEnergyHigh() ? 1 : 0, + getStateHitCount() ? 1 : 0); + } +} Added: 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 (added) +++ java/trunk/analysis/src/main/java/org/hps/analysis/trigger/util/Trigger.java Thu Mar 5 10:59:19 2015 @@ -0,0 +1,152 @@ +package org.hps.analysis.trigger.util; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +/** + * Class <code>Trigger</code> stores a set cut states indicating whether + * specific cut conditions associated with a trigger were met or not as + * well as the state of the overall trigger. It is the responsibility of + * implementing classes to specify the supported cut states and also + * to define when the trigger conditions are met. + * + * @author Kyle McCarty <[log in to unmask]> + */ +public abstract class Trigger<E> { + // Track whether the trigger conditions were met. + private boolean passTrigger = false; + // Store the cut condition states. + private Map<String, Boolean> passMap = new HashMap<String, Boolean>(); + // Store the cluster associated with the trigger. + private final E source; + // Store the trigger number. + private final int triggerNum; + + /** + * Creates a new <code>Trigger</code> object with the argument + * specifying the object from whence the trigger state is derived. + * @param source - The trigger source object. + */ + protected Trigger(E source) { + this(source, -1); + } + + /** + * Creates a new <code>Trigger</code> object with the argument + * specifying the object from whence the trigger state is derived. + * @param source - The trigger source object. + * @param triggerNum - The number of the trigger. + */ + protected Trigger(E source, int triggerNum) { + this.source = source; + this.triggerNum = triggerNum; + } + + /** + * Adds a cut to the set of cuts tracked by this trigger. + * @param cut - The identifier for the cut. + */ + protected void addValidCut(String cut) { + passMap.put(cut, new Boolean(false)); + } + + /** + * Gets the state of the specified cut. + * @param cut - The identifier for the cut. + * @return Returns <code>true</code> if the conditions for the + * specified cut were met and <code>false</code> otherwise. + * @throws IllegalArgumentException Occurs if the specified cut + * is not supported by the object. + */ + protected boolean getCutState(String cut) throws IllegalArgumentException { + if(passMap.containsKey(cut)) { + return passMap.get(cut); + } else { + throw new IllegalArgumentException(String.format("Trigger cut \"%s\" is not a supported trigger cut.", cut)); + } + } + + /** + * Gets the number of the trigger. If the trigger has no number, + * it will return <code>-1</code>. + * @return Returns the trigger number as an <code>int</code>. + */ + public int getTriggerNumber() { + return triggerNum; + } + + /** + * Gets the object to which the trigger cuts are applied. + * @return Returns the trigger source object. + */ + public E getTriggerSource() { return source; } + + /** + * Gets whether the conditions for the trigger were met. + * @return Returns <code>true</code> if the conditions for the + * trigger were met and <code>false</code> if they were not. + */ + public boolean getTriggerState() { + return passTrigger; + } + + /** + * Removes a cut from the set of cuts tracked by the trigger. + * @param cut - The identifier for the cut. + */ + protected void removeValidCut(String cut) { + passMap.remove(cut); + } + + /** + * Checks whether the all of the trigger cut conditions were met. + * @return Returns <code>true</code> if all of the cut conditions + * were met and <code>false</code> otherwise. + */ + private boolean isValidTrigger() { + // Iterate over all of the cuts and look for any that have not + // been met. + for(Entry<String, Boolean> cut : passMap.entrySet()) { + if(!cut.getValue()) { return false; } + } + + // If there are no cut conditions that have not been met, then + // the trigger is valid. + return true; + } + + /** + * Sets whether the conditions for the specified cut were met. + * @param cut - The identifier for the cut. + * @param state - <code>true</code> indicates that the conditions + * for the cut were met and <code>false</code> that they were not. + * @throws IllegalArgumentException Occurs if the specified cut + * is not supported by the object. + */ + protected void setCutState(String cut, boolean state) throws IllegalArgumentException { + if(passMap.containsKey(cut)) { + // Set the cut state. + passMap.put(cut, state); + + // If the cut state is true, then all cut conditions may have + // been met. Check whether this is true and, if so, set the + // trigger state accordingly. + if(state && isValidTrigger()) { passTrigger = true; } + else { passTrigger = false; } + } else { + throw new IllegalArgumentException(String.format("Trigger cut \"%s\" is not a supported trigger cut.", cut)); + } + } + + /** + * Indicates whether the specified cut state is tracked by this + * object or not. + * @param cut - The identifier for the cut. + * @return Returns <code>true</code> if the cut state is tracked + * by this object and <code>false</code> otherwise. + */ + protected boolean supportsCut(String cut) { + return passMap.containsKey(cut); + } +} Added: java/trunk/analysis/src/main/java/org/hps/analysis/trigger/util/TriggerDiagnosticUtil.java ============================================================================= --- java/trunk/analysis/src/main/java/org/hps/analysis/trigger/util/TriggerDiagnosticUtil.java (added) +++ java/trunk/analysis/src/main/java/org/hps/analysis/trigger/util/TriggerDiagnosticUtil.java Thu Mar 5 10:59:19 2015 @@ -0,0 +1,220 @@ +package org.hps.analysis.trigger.util; + +import java.awt.Point; + +import org.hps.readout.ecal.triggerbank.SSPCluster; +import org.lcsim.event.CalorimeterHit; +import org.lcsim.event.Cluster; + +/** + * Class <code>TriggerDiagnosticUtil</code> contains a series of + * utility methods that are used at various points throughout the + * trigger diagnostic package. + * + * @author Kyle McCarty <[log in to unmask]> + */ +public class TriggerDiagnosticUtil { + // Cluster match state variables. + public static final byte CLUSTER_STATE_MATCHED = 0; + public static final byte CLUSTER_STATE_FAIL_POSITION = 1; + public static final byte CLUSTER_STATE_FAIL_ENERGY = 2; + public static final byte CLUSTER_STATE_FAIL_HIT_COUNT = 3; + public static final byte CLUSTER_STATE_FAIL_UNKNOWN = 4; + + // Trigger match cut IDs. + public static final int SINGLES_ENERGY_MIN = 0; + public static final int SINGLES_ENERGY_MAX = 1; + public static final int SINGLES_HIT_COUNT = 2; + public static final int PAIR_ENERGY_SUM = 0; + public static final int PAIR_ENERGY_DIFF = 1; + public static final int PAIR_ENERGY_SLOPE = 2; + public static final int PAIR_COPLANARITY = 3; + + // Trigger type variables. + public static final int TRIGGER_PULSER = 0; + public static final int TRIGGER_COSMIC = 1; + public static final int TRIGGER_SINGLES_1 = 2; + public static final int TRIGGER_SINGLES_2 = 3; + public static final int TRIGGER_PAIR_1 = 4; + public static final int TRIGGER_PAIR_2 = 5; + public static final String[] TRIGGER_NAME = { "Pulser", "Cosmic", "Singles 1", "Singles 2", "Pair 1", "Pair 2" }; + + /** + * Convenience method that writes the position of a cluster in the + * form (ix, iy). + * @param cluster - The cluster. + * @return Returns the cluster position as a <code>String</code>. + */ + public static final String clusterPositionString(Cluster cluster) { + return String.format("(%3d, %3d)", + cluster.getCalorimeterHits().get(0).getIdentifierFieldValue("ix"), + cluster.getCalorimeterHits().get(0).getIdentifierFieldValue("iy")); + } + + /** + * Convenience method that writes the position of a cluster in the + * form (ix, iy). + * @param cluster - The cluster. + * @return Returns the cluster position as a <code>String</code>. + */ + public static final String clusterPositionString(SSPCluster cluster) { + return String.format("(%3d, %3d)", cluster.getXIndex(), cluster.getYIndex()); + } + + /** + * Convenience method that writes the information in a cluster to + * a <code>String</code>. + * @param cluster - The cluster. + * @return Returns the cluster information as a <code>String</code>. + */ + public static final String clusterToString(Cluster cluster) { + return String.format("Cluster at (%3d, %3d) with %.3f GeV and %d hits at %4.0f ns.", + cluster.getCalorimeterHits().get(0).getIdentifierFieldValue("ix"), + cluster.getCalorimeterHits().get(0).getIdentifierFieldValue("iy"), + cluster.getEnergy(), cluster.getCalorimeterHits().size(), + cluster.getCalorimeterHits().get(0).getTime()); + } + + /** + * Convenience method that writes the information in a cluster to + * a <code>String</code>. + * @param cluster - The cluster. + * @return Returns the cluster information as a <code>String</code>. + */ + public static final String clusterToString(SSPCluster cluster) { + return String.format("Cluster at (%3d, %3d) with %.3f GeV and %d hits at %4d ns.", + cluster.getXIndex(), cluster.getYIndex(), cluster.getEnergy(), + cluster.getHitCount(), cluster.getTime()); + } + + /** + * Gets the x/y-indices of the cluster. + * @param cluster - The cluster of which to obtain the indices. + * @return Returns the indices as a <code>Point</code> object. + */ + public static final Point getClusterPosition(Cluster cluster) { + return new Point(getXIndex(cluster), getYIndex(cluster)); + } + + /** + * Gets the x/y-indices of the cluster. + * @param cluster - The cluster of which to obtain the indices. + * @return Returns the indices as a <code>Point</code> object. + */ + public static final Point getClusterPosition(SSPCluster cluster) { + return new Point(cluster.getXIndex(), cluster.getYIndex()); + } + + /** + * Gets the time stamp of the cluster in nanoseconds. + * @param cluster - The cluster. + * @return Returns the time-stamp. + */ + public static final double getClusterTime(Cluster cluster) { + return cluster.getCalorimeterHits().get(0).getTime(); + } + + /** + * Gets the time stamp of the cluster in nanoseconds. + * @param cluster - The cluster. + * @return Returns the time-stamp. + */ + public static final int getClusterTime(SSPCluster cluster) { + return cluster.getTime(); + } + + /** + * Gets the number of digits in the base-10 String representation + * of an integer primitive. Negative signs are not included in the + * digit count. + * @param value - The value of which to obtain the length. + * @return Returns the number of digits in the String representation + * of the argument value. + */ + public static final int getDigits(int value) { + if(value < 0) { return Integer.toString(value).length() - 1; } + else { return Integer.toString(value).length(); } + } + + /** + * Gets the number of hits in a cluster. + * @param cluster - The cluster. + * @return Returns the number of hits in the cluster. + */ + public static final int getHitCount(Cluster cluster) { + return cluster.getCalorimeterHits().size(); + } + + /** + * Gets the number of hits in a cluster. + * @param cluster - The cluster. + * @return Returns the number of hits in the cluster. + */ + public static final int getHitCount(SSPCluster cluster) { + return cluster.getHitCount(); + } + + /** + * Gets the x-index of the cluster's seed hit. + * @param cluster - The cluster. + * @return Returns the x-index. + */ + public static final int getXIndex(Cluster cluster) { + return cluster.getCalorimeterHits().get(0).getIdentifierFieldValue("ix"); + } + + /** + * Gets the x-index of the cluster's seed hit. + * @param cluster - The cluster. + * @return Returns the x-index. + */ + public static final int getXIndex(SSPCluster cluster) { + return cluster.getXIndex(); + } + + /** + * Gets the y-index of the cluster's seed hit. + * @param cluster - The cluster. + * @return Returns the y-index. + */ + public static final int getYIndex(Cluster cluster) { + return cluster.getCalorimeterHits().get(0).getIdentifierFieldValue("iy"); + } + + /** + * Gets the y-index of the cluster's seed hit. + * @param cluster - The cluster. + * @return Returns the y-index. + */ + public static final int getYIndex(SSPCluster cluster) { + return cluster.getYIndex(); + } + + /** + * Checks whether all of the hits in a cluster are within the safe + * region of the FADC output window. + * @param reconCluster - The cluster to check. + * @return Returns <code>true</code> if the cluster is safe and + * returns <code>false</code> otherwise. + */ + public static final boolean isVerifiable(Cluster reconCluster, int nsa, int nsb, int windowWidth) { + // Iterate over the hits in the cluster. + for(CalorimeterHit hit : reconCluster.getCalorimeterHits()) { + // Check that none of the hits are within the disallowed + // region of the FADC readout window. + if(hit.getTime() <= nsb || hit.getTime() >= (windowWidth - nsa)) { + return false; + } + + // Also check to make sure that the cluster does not have + // any negative energy hits. These are, obviously, wrong. + if(hit.getCorrectedEnergy() < 0.0) { + return false; + } + } + + // If all of the cluster hits pass the time cut, the cluster + // is valid. + return true; + } +}