Author: [log in to unmask] Date: Thu Feb 12 13:00:39 2015 New Revision: 2123 Log: PairTrigger now supports the ability to track whether a pair passed the time cut. TriggerDiagnosticDriver now contains a more robust cluster matching algorithm and additional framework toward an implementation of trigger matching. Modified: java/trunk/users/src/main/java/org/hps/users/kmccarty/PairTrigger.java java/trunk/users/src/main/java/org/hps/users/kmccarty/TriggerDiagnosticDriver.java Modified: java/trunk/users/src/main/java/org/hps/users/kmccarty/PairTrigger.java ============================================================================= --- java/trunk/users/src/main/java/org/hps/users/kmccarty/PairTrigger.java (original) +++ java/trunk/users/src/main/java/org/hps/users/kmccarty/PairTrigger.java Thu Feb 12 13:00:39 2015 @@ -9,6 +9,7 @@ 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 @@ -27,6 +28,7 @@ addValidCut(PAIR_ENERGY_DIFFERENCE_HIGH); addValidCut(PAIR_ENERGY_SLOPE_LOW); addValidCut(PAIR_COPLANARITY_HIGH); + addValidCut(PAIR_TIME_COINCIDENCE); } /** @@ -85,12 +87,21 @@ } /** + * 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 getStateEnergySumLow(boolean state) { + public void setStateEnergySumLow(boolean state) { setCutState(PAIR_ENERGY_SUM_LOW, state); } @@ -100,7 +111,7 @@ * @param state - <code>true</code> indicates that the cut conditions * were met and <code>false</code> that they were not. */ - public void getStateEnergySumHigh(boolean state) { + public void setStateEnergySumHigh(boolean state) { setCutState(PAIR_ENERGY_SUM_HIGH, state); } @@ -110,7 +121,7 @@ * @param state - <code>true</code> indicates that the cut conditions * were met and <code>false</code> that they were not. */ - public void getStateEnergyDifference(boolean state) { + public void setStateEnergyDifference(boolean state) { setCutState(PAIR_ENERGY_DIFFERENCE_HIGH, state); } @@ -120,7 +131,7 @@ * @param state - <code>true</code> indicates that the cut conditions * were met and <code>false</code> that they were not. */ - public void getStateEnergySlope(boolean state) { + public void setStateEnergySlope(boolean state) { setCutState(PAIR_ENERGY_SLOPE_LOW, state); } @@ -130,7 +141,17 @@ * @param state - <code>true</code> indicates that the cut conditions * were met and <code>false</code> that they were not. */ - public void getStateCoplanarity(boolean state) { + 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); + } } Modified: java/trunk/users/src/main/java/org/hps/users/kmccarty/TriggerDiagnosticDriver.java ============================================================================= --- java/trunk/users/src/main/java/org/hps/users/kmccarty/TriggerDiagnosticDriver.java (original) +++ java/trunk/users/src/main/java/org/hps/users/kmccarty/TriggerDiagnosticDriver.java Thu Feb 12 13:00:39 2015 @@ -6,12 +6,16 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.logging.Level; +import java.util.logging.Logger; import java.util.Set; import org.hps.readout.ecal.TriggerModule; import org.hps.readout.ecal.triggerbank.AbstractIntData; import org.hps.readout.ecal.triggerbank.SSPCluster; import org.hps.readout.ecal.triggerbank.SSPData; +import org.hps.readout.ecal.triggerbank.SSPSinglesTrigger; +import org.hps.readout.ecal.triggerbank.SSPTrigger; import org.hps.readout.ecal.triggerbank.TIData; import org.hps.recon.ecal.CalorimeterHitUtilities; import org.lcsim.detector.converter.compact.EcalCrystal; @@ -21,6 +25,7 @@ import org.lcsim.event.GenericObject; import org.lcsim.geometry.Detector; import org.lcsim.util.Driver; +import org.lcsim.util.log.LogUtil; public class TriggerDiagnosticDriver extends Driver { // Store the LCIO collection names for the needed objects. @@ -32,8 +37,8 @@ private SSPData sspBank; private List<Cluster> reconClusters; private List<SSPCluster> sspClusters; - private List<List<PairTrigger<Cluster>>> reconPairsTriggers = new ArrayList<List<PairTrigger<Cluster>>>(2); - private List<List<PairTrigger<SSPCluster>>> sspPairsTriggers = new ArrayList<List<PairTrigger<SSPCluster>>>(2); + private List<List<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); @@ -41,12 +46,26 @@ private TriggerModule[] singlesTrigger = new TriggerModule[2]; private TriggerModule[] pairsTrigger = new TriggerModule[2]; - // Store internal variables. - private double energyAcceptance = 0.05; + // Output text logger. + private static Level logLevel = Level.FINEST; + private static Logger logger = LogUtil.create(TriggerDiagnosticDriver.class); + + // Verification settings. + private int nsa = 100; + private int nsb = 20; + private int windowWidth = 200; + private int hitAcceptance = 1; + private double energyAcceptance = 0.03; + private boolean performClusterVerification = true; + + // Efficiency tracking variables. + private int reconClustersFound = 0; + private int reconClustersMatched = 0; /* @Override public void detectorChanged(Detector detector) { + if(detector == null) { return; } for(EcalCrystal crystal : detector.getSubdetector("Ecal").getDetectorElement().findDescendants(EcalCrystal.class)) { System.out.println(crystal.getIdentifier().getValue()); CalorimeterHit tempHit = CalorimeterHitUtilities.create(1.000, 10.0, crystal.getIdentifier().getValue()); @@ -89,6 +108,7 @@ 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(); @@ -101,14 +121,32 @@ 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>>()); + reconPairsTriggers.add(new ArrayList<PairTrigger<Cluster[]>>()); + sspPairsTriggers.add(new ArrayList<PairTrigger<SSPCluster[]>>()); reconSinglesTriggers.add(new ArrayList<SinglesTrigger<Cluster>>()); sspSinglesTriggers.add(new ArrayList<SinglesTrigger<SSPCluster>>()); } + + // Set the logger level. + logger.setLevel(logLevel); + + // Print the initial settings. + logSettings(); + } + + @Override + public void endOfData() { + System.out.println("\n==== Efficiency Report ==============================================="); + System.out.println("======================================================================\n"); + System.out.println("==== Cluster Verification ================"); + System.out.printf("\tValid Clusters Reconstructed :: %d%n", reconClustersFound); + System.out.printf("\tReconstructed Clusters Matched :: %d%n", reconClustersMatched); + System.out.printf("\tClustering Efficiency :: %3.2f %%%n", (100.0 * reconClustersMatched / reconClustersFound)); + } /** @@ -116,10 +154,24 @@ */ @Override public void process(EventHeader event) { + // ========================================================== + // ==== Obtain Reconstructed Clusters ======================= + // ========================================================== + // Get the reconstructed clusters. if(event.hasCollection(Cluster.class, clusterCollectionName)) { reconClusters = event.get(Cluster.class, clusterCollectionName); - } + logger.fine(String.format("%d reconstructed clusters found.", reconClusters.size())); + } else { + reconClusters = new ArrayList<Cluster>(0); + logger.warning(String.format("No reconstructed clusters were found for collection \"%s\" in this event.", clusterCollectionName)); + } + + + + // ========================================================== + // ==== Obtain SSP and TI Banks ============================= + // ========================================================== // Get the SSP clusters. if(event.hasCollection(GenericObject.class, bankCollectionName)) { @@ -131,83 +183,139 @@ // If this is an SSP bank, parse it. if(AbstractIntData.getTag(obj) == SSPData.BANK_TAG) { sspBank = new SSPData(obj); + logger.finer("Read SSP bank."); } // Otherwise, if this is a TI bank, parse it. else if(AbstractIntData.getTag(obj) == TIData.BANK_TAG) { tiBank = new TIData(obj); + logger.finer("Read TI bank."); } } // If there is an SSP bank, get the list of SSP clusters. if(sspBank != null) { sspClusters = sspBank.getClusters(); - } - } - - // Check that all of the collections and objects are present. - boolean allPresent = true; + logger.fine(String.format("%d SSP clusters found.", sspClusters.size())); + } + } + + + + // ========================================================== + // ==== Establish Event Integrity =========================== + // ========================================================== + + // Check that all of the required objects are present. if(sspBank == null) { - System.out.println("SSP bank not found!"); - allPresent = false; + logger.warning("No SSP bank found for this event. No verification will be performed."); + return; } if(tiBank == null) { - System.out.println("TI bank not found!"); - allPresent = false; - } if(sspClusters == null) { - System.out.println("SSP clusters not found!"); - allPresent = false; - } if(reconClusters == null) { - System.out.println("Reconstructed clusters not found!"); - allPresent = false; - } - - // Do nothing further if an object is missing. - if(!allPresent) { return; } + logger.warning("No TI bank found for this event. No verification will be performed."); + return; + } + + + + // ========================================================== + // ==== Perform Detailed Event Logging ====================== + // ========================================================== // Otherwise, print out the two cluster collections. - System.out.printf("Summary for Event %d at time %d%n", event.getEventNumber(), event.getTimeStamp()); - System.out.println("Reconstructed Clusters:"); + logger.finest(String.format("Summary for Event %d at time %d", event.getEventNumber(), event.getTimeStamp())); + logger.finest("Reconstructed Clusters:"); for(Cluster cluster : reconClusters) { - System.out.println("\t" + reconClusterToString(cluster)); - } - - System.out.println("SSP Clusters:"); + logger.finest("\t" + reconClusterToString(cluster)); + } + + logger.finest("SSP Clusters:"); for(SSPCluster cluster : sspClusters) { - System.out.println("\t" + sspClusterToString(cluster)); - } + logger.finest("\t" + sspClusterToString(cluster)); + } + + + + // ========================================================== + // ==== Perform Event Verification ========================== + // ========================================================== // Perform the cluster verification step. - verifyClusters(); + if(performClusterVerification) { verifyClusters(); } // Construct lists of triggers for the SSP clusters and the // reconstructed clusters. - constructTriggers(); - - System.out.println("\n\n"); - } - + constructSinglesTriggers(); + constructPairTriggers(); + verifyTriggers(); + } + + /** + * 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 verifyClusters() { + // ========================================================== + // ==== Cluster Verification Initialization ================= + // ========================================================== + // Track which clusters match and whether a given cluster // has been matched or not. Set<Cluster> reconClusterSet = new HashSet<Cluster>(reconClusters.size()); Set<SSPCluster> sspClusterSet = new HashSet<SSPCluster>(sspClusters.size()); Map<Cluster, SSPCluster> pairMap = new HashMap<Cluster, SSPCluster>(reconClusters.size()); + // Store which clusters were rejected due to the cluster + // window size. + Set<Cluster> badClusters = new HashSet<Cluster>(); + + + + // ========================================================== + // ==== Cluster Matching ==================================== + // ========================================================== + // Iterate over the reconstructed clusters and check whether // there is a matching SSP cluster or not. + reconLoop: for(Cluster reconCluster : reconClusters) { // Get the cluster's x- and y- indices. int ix = reconCluster.getCalorimeterHits().get(0).getIdentifierFieldValue("ix"); int iy = reconCluster.getCalorimeterHits().get(0).getIdentifierFieldValue("iy"); + // Require that the clusters are far enough way + // from the edges of the time window to ensure + // that they will not have the FADC pulse cut. + for(CalorimeterHit hit : reconCluster.getCalorimeterHits()) { + if(hit.getTime() <= nsb || hit.getTime() >= (windowWidth - nsa)) { + badClusters.add(reconCluster); + logger.finer(String.format("Cluster %s is out-of-time due to hit (%3d, %3d) at time %3.0f.", + reconClusterPositionString(reconCluster), + hit.getIdentifierFieldValue("ix"), hit.getIdentifierFieldValue("iy"), + hit.getTime())); + continue reconLoop; + } + } + + // Increment the number of reconstructed clusters found that + // are within the allowed time window. + reconClustersFound++; + // Look for an unmatched cluster with the same indices. matchLoop: for(SSPCluster sspCluster : sspClusters) { - // TODO: Implement time cut on cluster matching. if(sspCluster.getXIndex() == ix && sspCluster.getYIndex() == iy) { // If this SSP cluster is already matched, it can // not be used again. if(sspClusterSet.contains(sspCluster)) { + logger.finer(String.format("Cluster %s fails to match SSP cluster %s; SSP cluster already matched.", + reconClusterPositionString(reconCluster), sspClusterPositionString(sspCluster))); continue matchLoop; } @@ -218,45 +326,131 @@ sspCluster.getEnergy() * (1 + energyAcceptance) }; // If the energies are within range, consider this - // a matched cluster pair. They must also have the - // same hit count. - // TODO: Fix hit inconsistency bug. - if(reconCluster.getEnergy() >= energy[0] && reconCluster.getEnergy() <= energy[1] - && reconCluster.getCalorimeterHits().size() == sspCluster.getHitCount()) { - // Add the two clusters to the matched sets. - sspClusterSet.add(sspCluster); - reconClusterSet.add(reconCluster); - - // Map the two clusters together. - pairMap.put(reconCluster, sspCluster); - - // Skip to the next recon cluster. - continue matchLoop; + // a matched cluster pair. + if(reconCluster.getEnergy() >= energy[0] && reconCluster.getEnergy() <= energy[1]) { + // Check that the clusters have the same hit + // count. If the allowHitCountVariance setting + // is enabled, this may differ by +/- 1 hit. + int reconHits = reconCluster.getCalorimeterHits().size(); + int sspHits = sspCluster.getHitCount(); + if((reconHits + hitAcceptance >= sspHits) && (reconHits - hitAcceptance <= sspHits)) { + // Add the two clusters to the matched sets. + sspClusterSet.add(sspCluster); + reconClusterSet.add(reconCluster); + + // Map the two clusters together. + pairMap.put(reconCluster, sspCluster); + + // Log the match. + logger.finer(String.format("Cluster %s matches SSP cluster %s", + reconClusterPositionString(reconCluster), sspClusterPositionString(sspCluster))); + + // Increment the number of reconstructed + // clusters matched. + reconClustersMatched++; + + // Skip to the next recon cluster. + continue matchLoop; + } else { + logger.finer(String.format("Cluster %s fails to match SSP cluster %s; hits not within threshold.", + reconClusterPositionString(reconCluster), sspClusterPositionString(sspCluster))); + } // End match hits check + } else { + logger.finer(String.format("Cluster %s fails to match SSP cluster %s; Energy not within %3.1f%%.", + reconClusterPositionString(reconCluster), sspClusterPositionString(sspCluster), + (energyAcceptance * 100.0))); + } // End match energy check + } // End match indices check + } // End matchLoop + } // End reconLoop + + + + // ========================================================== + // ==== Event Summary Readout =============================== + // ========================================================== + + // Output the matched clusters. + logger.finest("Matched Clusters:"); + if(!pairMap.isEmpty()) { + for(Entry<Cluster, SSPCluster> pair : pairMap.entrySet()) { + logger.finest(String.format("\t%s --> %s%n", reconClusterToString(pair.getKey()), sspClusterToString(pair.getValue()))); + } + } else { logger.finest("\tNone"); } + + // Output unmatched reconstructed clusters and SSP clusters. + logger.finest("\nUnmatched Clusters:"); + if(sspClusterSet.size() != sspClusters.size() || reconClusterSet.size() != reconClusters.size()) { + // Output the SSP clusters that were not matched. + for(SSPCluster sspCluster : sspClusters) { + if(!sspClusterSet.contains(sspCluster)) { + logger.finest(String.format("\tSSP :: %s", sspClusterToString(sspCluster))); + } + } + + // Output the recon clusters that were not matched. + for(Cluster reconCluster : reconClusters) { + if(!reconClusterSet.contains(reconCluster) && !badClusters.contains(reconCluster)) { + logger.finest(String.format("\tRecon :: %s", reconClusterToString(reconCluster))); + } + } + } else { logger.finest("\tNone"); } + + // Output the reconstructed clusters that were out-of-time. + logger.finest("\nOut-of-time Recon Clusters:"); + if(!badClusters.isEmpty()) { + for(Cluster badCluster : badClusters) { + logger.finest(String.format("\tRecon :: %s", reconClusterToString(badCluster))); + } + } else { logger.finest("\tNone"); } + + // Output the event efficiency. + logger.fine(String.format("Event Efficiency: %.1f", (100.0 * pairMap.size() / (reconClusters.size() - badClusters.size())))); + } + + private void verifyTriggers() { + // Get the list of triggers reported by the SSP. + List<SSPTrigger> sspTriggers = sspBank.getTriggers(); + + // Iterate over the triggers. + System.out.println("SSP Bank Singles Triggers:"); + for(SSPTrigger sspTrigger : sspTriggers) { + // If the trigger is a singles trigger, convert it. + if(sspTrigger instanceof SSPSinglesTrigger) { + // Cast the trigger to a singles trigger. + SSPSinglesTrigger sspSingles = (SSPSinglesTrigger) sspTrigger; + int triggerNum = sspSingles.isFirstTrigger() ? 0 : 1; + boolean matchedTrigger = false; + + // Iterate over the SSP cluster simulated triggers and + // look for a cluster that matches. + matchLoop: + for(SinglesTrigger<SSPCluster> simTrigger : sspSinglesTriggers.get(triggerNum)) { + if(compareSSPSinglesTriggers(sspSingles, simTrigger)) { + matchedTrigger = true; + break matchLoop; } } - } - } // End matchLoop - - // Output the cluster matches and note which clusters failed - // to be paired. These may suggest an error. - System.out.println("Matched Clusters:"); - for(Entry<Cluster, SSPCluster> pair : pairMap.entrySet()) { - System.out.printf("\t%s --> %s%n", reconClusterToString(pair.getKey()), sspClusterToString(pair.getValue())); - } - System.out.println("Unmatched Clusters:"); - for(SSPCluster sspCluster : sspClusters){ - if(!sspClusterSet.contains(sspCluster)) { - System.out.printf("\tSSP :: %s%n", sspClusterToString(sspCluster)); - } - } - for(Cluster reconCluster : reconClusters){ - if(!reconClusterSet.contains(reconCluster)) { - System.out.printf("\tRecon :: %s%n", reconClusterToString(reconCluster)); - } - } - } - - private void constructTriggers() { + + System.out.printf("\tTrigger %d :: %3d :: EClusterLow: %d; EClusterHigh %d; HitCount: %d :: Matched: %5b%n", + (triggerNum + 1), sspSingles.getTime(), + sspSingles.passCutEnergyMin() ? 1 : 0, sspSingles.passCutEnergyMax() ? 1 : 0, + sspSingles.passCutHitCount() ? 1 : 0, matchedTrigger); + } + } + + System.out.println("SSP Clusters:"); + for(SSPCluster cluster : sspClusters) { + System.out.println("\t" + sspClusterToString(cluster)); + } + } + + /** + * Generates and stores the singles triggers for both reconstructed + * and SSP clusters. + */ + private void constructSinglesTriggers() { + System.out.println("SSP Cluster Singles Triggers:"); // Run the SSP clusters through the singles trigger to determine // whether they pass it or not. for(SSPCluster cluster : sspClusters) { @@ -282,11 +476,16 @@ // Store the trigger. sspSinglesTriggers.get(triggerNum).add(trigger); + + System.out.printf("\tTrigger %d :: %s :: EClusterLow: %d; EClusterHigh %d; HitCount: %d%n", + (triggerNum + 1), sspClusterPositionString(cluster), passClusterLow ? 1 : 0, + passClusterHigh ? 1 : 0, passHitCount ? 1 : 0); } } // Run the reconstructed clusters through the singles trigger // to determine whether they pass it or not. + System.out.println("Recon Cluster Singles Triggers:"); for(Cluster cluster : reconClusters) { for(int triggerNum = 0; triggerNum < 2; triggerNum++) { // For a cluster to have formed it is assumed to have passed @@ -310,7 +509,210 @@ // Store the trigger. reconSinglesTriggers.get(triggerNum).add(trigger); - } + + System.out.printf("\tTrigger %d :: %s :: EClusterLow: %d; EClusterHigh %d; HitCount: %d%n", + (triggerNum + 1), reconClusterPositionString(cluster), passClusterLow ? 1 : 0, + passClusterHigh ? 1 : 0, passHitCount ? 1 : 0); + } + } + } + + /** + * 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. + System.out.println("Recon Cluster Pairs Triggers:"); + for(Cluster[] reconPair : reconPairs) { + for(int triggerIndex = 0; triggerIndex < 2; triggerIndex++) { + // 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); + 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); + + System.out.printf("\tTrigger %d :: %s, %s :: EClusterLow: %d; EClusterHigh %d; HitCount: %d; ESumLow: %d, ESumHigh: %d, EDiff: %d, ESlope: %d, Coplanarity: %d, TimeDiff: %d%n", + (triggerIndex + 1), reconClusterPositionString(reconPair[0]), + reconClusterPositionString(reconPair[1]), passClusterLow ? 1 : 0, + passClusterHigh ? 1 : 0, passHitCount ? 1 : 0, passPairEnergySumLow ? 1 : 0, + passPairEnergySumHigh ? 1 : 0, passPairEnergyDifference ? 1 : 0, + passPairEnergySlope ? 1 : 0, passPairCoplanarity ? 1 : 0, + passTimeCoincidence ? 1 : 0); + } + } + + for(SSPCluster[] sspPair : sspPairs) { + for(int triggerIndex = 0; triggerIndex < 2; triggerIndex++) { + // 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); + + // TODO: Implement the pair energy slope cut and the and + // pair coplanarity cut once they are supported by + // TriggerModule. + boolean passPairEnergySlope = false; + boolean passPairCoplanarity = false; + + boolean passTimeCoincidence = pairsTrigger[triggerIndex].pairTimeCoincidenceCut(sspPair); + + // Create a trigger from the results. + PairTrigger<SSPCluster[]> trigger = new PairTrigger<SSPCluster[]>(sspPair); + 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. + logger.config("Cluster Verification Settings"); + logger.config(String.format("\tEnergy Threshold :: %1.2f", energyAcceptance)); + logger.config(String.format("\tHit Threshold :: %1d", hitAcceptance)); + + // Output window settings. + logger.config("FADC Timing Window Settings"); + logger.config(String.format("\tNSB :: %3d ns", nsb)); + logger.config(String.format("\tNSA :: %3d ns", nsa)); + logger.config(String.format("\tFADC Window :: %3d ns", windowWidth)); + + // Calculate the valid clustering window. + int start = nsb; + int end = windowWidth - nsa; + if(start < end) { + logger.config(String.format("\tValid Cluster Window :: [ %3d ns, %3d ns ]", start, end)); + performClusterVerification = true; + } else { + logger.warning("\tNSB, NSA, and FADC window preclude a valid cluster verification window."); + logger.warning("\tCluster verification will not be performed!"); + performClusterVerification = false; + } + + // Output the singles trigger settings. + for(int i = 0; i < 2; i++) { + logger.config(String.format("Singles Trigger %d Settings", (i + 1))); + logger.config(String.format("\tCluster Energy Low :: %.3f GeV", singlesTrigger[i].getCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_LOW))); + logger.config(String.format("\tCluster Energy High :: %.3f GeV", singlesTrigger[i].getCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_HIGH))); + logger.config(String.format("\tCluster Hit Count :: %.0f hits", singlesTrigger[i].getCutValue(TriggerModule.CLUSTER_HIT_COUNT_LOW))); + } + + // Output the pair trigger settings. + for(int i = 0; i < 2; i++) { + logger.config(String.format("Pairs Trigger %d Settings", (i + 1))); + logger.config(String.format("\tCluster Energy Low :: %.3f GeV", pairsTrigger[i].getCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_LOW))); + logger.config(String.format("\tCluster Energy High :: %.3f Gen", pairsTrigger[i].getCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_HIGH))); + logger.config(String.format("\tCluster Hit Count :: %.0f hits", pairsTrigger[i].getCutValue(TriggerModule.CLUSTER_HIT_COUNT_LOW))); + logger.config(String.format("\tPair Energy Sum Low :: %.3f GeV", pairsTrigger[i].getCutValue(TriggerModule.PAIR_ENERGY_SUM_LOW))); + logger.config(String.format("\tPair Energy Sum Low :: %.3f GeV", pairsTrigger[i].getCutValue(TriggerModule.PAIR_ENERGY_SUM_HIGH))); + logger.config(String.format("\tPair Energy Difference :: %.3f GeV", pairsTrigger[i].getCutValue(TriggerModule.PAIR_ENERGY_DIFFERENCE_HIGH))); + logger.config(String.format("\tPair Energy Slope :: %.3f GeV", pairsTrigger[i].getCutValue(TriggerModule.PAIR_ENERGY_SLOPE_LOW))); + logger.config(String.format("\tPair Energy Slope F :: %.3f GeV / mm", pairsTrigger[i].getCutValue(TriggerModule.PAIR_ENERGY_SLOPE_F))); + logger.config(String.format("\tPair Coplanarity :: %.0f Degrees", pairsTrigger[i].getCutValue(TriggerModule.PAIR_COPLANARITY_HIGH))); + logger.config(String.format("\tPair Time Coincidence :: %.0f ns", pairsTrigger[i].getCutValue(TriggerModule.PAIR_TIME_COINCIDENCE))); } } @@ -327,4 +729,49 @@ cluster.getXIndex(), cluster.getYIndex(), cluster.getEnergy(), cluster.getHitCount(), cluster.getTime()); } + + private static final String reconClusterPositionString(Cluster cluster) { + return String.format("(%3d, %3d)", + cluster.getCalorimeterHits().get(0).getIdentifierFieldValue("ix"), + cluster.getCalorimeterHits().get(0).getIdentifierFieldValue("iy")); + } + + private static final String sspClusterPositionString(SSPCluster cluster) { + return String.format("(%3d, %3d)", cluster.getXIndex(), cluster.getYIndex()); + } + + /** + * 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; + } }