Author: [log in to unmask] Date: Tue Oct 20 18:57:00 2015 New Revision: 3864 Log: Added work-in-progress Moller/trident analysis driver. Added: java/trunk/users/src/main/java/org/hps/users/kmccarty/TriggerProcessAnalysisDriver.java (with props) Modified: java/trunk/users/src/main/java/org/hps/users/kmccarty/plots/formatter/TriggerPlotsFormat.java Added: java/trunk/users/src/main/java/org/hps/users/kmccarty/TriggerProcessAnalysisDriver.java ============================================================================= --- java/trunk/users/src/main/java/org/hps/users/kmccarty/TriggerProcessAnalysisDriver.java (added) +++ java/trunk/users/src/main/java/org/hps/users/kmccarty/TriggerProcessAnalysisDriver.java Tue Oct 20 18:57:00 2015 @@ -0,0 +1,390 @@ +package org.hps.users.kmccarty; + +import hep.aida.IHistogram1D; +import hep.aida.IHistogram2D; +import hep.physics.vec.VecOp; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.hps.record.triggerbank.TriggerModule; +import org.lcsim.event.Cluster; +import org.lcsim.event.EventHeader; +import org.lcsim.event.ReconstructedParticle; +import org.lcsim.util.Driver; +import org.lcsim.util.aida.AIDA; + +public class TriggerProcessAnalysisDriver extends Driver { + private int eventsProcessed = 0; + private int møllersProcessed = 0; + private int tridentsProcessed = 0; + private double timeCoincidence = 2.5; + private AIDA aida = AIDA.defaultInstance(); + private String clusterCollectionName = "EcalClustersCorr"; + private String particleCollectionName = "FinalStateParticles"; + + // Define trident cluster-track matched condition plots. + private IHistogram1D trctmInvariantMass = aida.histogram1D("Tridents CTMatched/Invariant Mass", 140, 0.0, 0.070); + private IHistogram1D trctmInstancesInEvent = aida.histogram1D("Tridents CTMatched/Instances in Event", 9, 0.5, 9.5); + private IHistogram1D trctmEnergySum1D = aida.histogram1D("Tridents CTMatched/Cluster Energy Sum", 150, 0.000, 1.500); + private IHistogram1D trctmMomentumSum1D = aida.histogram1D("Tridents CTMatched/Track Momentum Sum", 150, 0.000, 1.500); + private IHistogram1D trctmElectronEnergy = aida.histogram1D("Tridents CTMatched/Electron Cluster Energy", 150, 0.000, 1.500); + private IHistogram1D trctmElectronMomentum = aida.histogram1D("Tridents CTMatched/Electron Track Momentum", 150, 0.000, 1.500); + private IHistogram1D trctmPositronEnergy = aida.histogram1D("Tridents CTMatched/Positron Cluster Energy", 150, 0.000, 1.500); + private IHistogram1D trctmPositronMomentum = aida.histogram1D("Tridents CTMatched/Positron Track Momentum", 150, 0.000, 1.500); + private IHistogram1D trctmTimeCoincidence = aida.histogram1D("Tridents CTMatched/Time Coincidence", 100, -4, 4); + private IHistogram2D trctmClusterPosition = aida.histogram2D("Tridents CTMatched/Cluster Seed Position", 46, -23, 23, 11, -5.5, 5.5); + private IHistogram2D trctmEnergySum2D = aida.histogram2D("Tridents CTMatched/Cluster Energy Sum 2D", 300, 0.000, 1.500, 300, 0.000, 1.500); + private IHistogram2D trctmMomentumSum2D = aida.histogram2D("Tridents CTMatched/Track Momentum Sum 2D", 300, 0.000, 1.500, 300, 0.000, 1.500); + + // Define the Møller cluster-track matched condition plots. + private IHistogram1D møctmInvariantMass = aida.histogram1D("Møller CTMatched/Invariant Mass", 140, 0.0, 0.070); + private IHistogram1D møctmInstancesInEvent = aida.histogram1D("Møller CTMatched/Instances in Event", 9, 0.5, 9.5); + private IHistogram1D møctmEnergySum1D = aida.histogram1D("Møller CTMatched/Cluster Energy Sum", 150, 0.000, 1.500); + private IHistogram1D møctmMomentumSum1D = aida.histogram1D("Møller CTMatched/Track Momentum Sum", 150, 0.000, 1.500); + private IHistogram1D møctmElectronEnergy = aida.histogram1D("Møller CTMatched/Electron Cluster Energy", 150, 0.000, 1.500); + private IHistogram1D møctmElectronMomentum = aida.histogram1D("Møller CTMatched/Electron Track Momentum", 150, 0.000, 1.500); + private IHistogram1D møctmTimeCoincidence = aida.histogram1D("Møller CTMatched/Time Coincidence", 100, -4, 4); + private IHistogram2D møctmClusterPosition = aida.histogram2D("Møller CTMatched/Cluster Seed Position", 46, -23, 23, 11, -5.5, 5.5); + private IHistogram2D møctmEnergySum2D = aida.histogram2D("Møller CTMatched/Cluster Energy Sum 2D", 300, 0.000, 1.500, 300, 0.000, 1.500); + private IHistogram2D møctmMomentumSum2D = aida.histogram2D("Møller CTMatched/Track Momentum Sum 2D", 300, 0.000, 1.500, 300, 0.000, 1.500); + + @Override + public void endOfData() { + // Calculate the scaling factor for Hertz. + double scale = 19000.0 / eventsProcessed; + + System.out.println("Processed " + eventsProcessed + " events."); + System.out.println("Processed " + møllersProcessed + " Møller events"); + System.out.println("\tAcceptance :: " + (100.0 * møllersProcessed / eventsProcessed) + "%"); + System.out.println("\tRate :: " + (møllersProcessed * scale) + "Hz"); + System.out.println("Processed " + tridentsProcessed + " trident events"); + System.out.println("\tAcceptance :: " + (100.0 * tridentsProcessed / eventsProcessed) + "%"); + System.out.println("\tRate :: " + (tridentsProcessed * scale) + "Hz"); + + // Scale the cluster-track matched Møller plots. + møctmInvariantMass.scale(scale); + møctmInstancesInEvent.scale(scale); + møctmEnergySum1D.scale(scale); + møctmMomentumSum1D.scale(scale); + møctmElectronEnergy.scale(scale); + møctmElectronMomentum.scale(scale); + møctmTimeCoincidence.scale(scale); + møctmClusterPosition.scale(scale); + møctmEnergySum2D.scale(scale); + møctmMomentumSum2D.scale(scale); + + // Scale the cluster-track matched trident plots. + trctmInvariantMass.scale(scale); + trctmInstancesInEvent.scale(scale); + trctmEnergySum1D.scale(scale); + trctmMomentumSum1D.scale(scale); + trctmElectronEnergy.scale(scale); + trctmElectronMomentum.scale(scale); + trctmPositronEnergy.scale(scale); + trctmPositronMomentum.scale(scale); + trctmTimeCoincidence.scale(scale); + trctmClusterPosition.scale(scale); + trctmEnergySum2D.scale(scale); + trctmMomentumSum2D.scale(scale); + } + + @Override + public void process(EventHeader event) { + // Check whether the SVT was active in this event and, if so, + // skip it. + final String[] flagNames = { "svt_bias_good", "svt_burstmode_noise_good", "svt_position_good" }; + boolean svtGood = true; + for(int i = 0; i < flagNames.length; i++) { + int[] flag = event.getIntegerParameters().get(flagNames[i]); + if(flag == null || flag[0] == 0) { + svtGood = false; + } + } + if(!svtGood) { return; } + + // Track the number of events with good SVT. + eventsProcessed++; + + // Check if the event has a collection of tracks. If it exists, + // extract it. Otherwise, skip the event. + if(!event.hasCollection(ReconstructedParticle.class, particleCollectionName)) { + return; + } + List<ReconstructedParticle> trackList = event.get(ReconstructedParticle.class, particleCollectionName); + + // Check if the event has a collection of clusters. If it + // exists, extract it. Otherwise, skip the event. + if(!event.hasCollection(Cluster.class, clusterCollectionName)) { + return; + } + List<Cluster> clusterList = event.get(Cluster.class, clusterCollectionName); + + // Get cluster-track matched top/bottom pairs. + List<ReconstructedParticle[]> ctMatchedPairs = getTopBottomTracksCTMatched(trackList); + + // Get the trident and Møller tracks for the matched track + // and cluster pair condition sets. + List<ReconstructedParticle[]> møllers = getMøllerTracksCTMatched(ctMatchedPairs); + List<ReconstructedParticle[]> tridents = getTridentTracksCTMatched(ctMatchedPairs); + + // Track how many events had tridents and Møllers. + if(!møllers.isEmpty()) { møllersProcessed++; } + if(!tridents.isEmpty()) { tridentsProcessed++; } + + // Produce Møller cluster-track matched plots. + møctmInstancesInEvent.fill(møllers.size()); + for(ReconstructedParticle[] pair : møllers) { + // Get the track clusters. + Cluster[] trackClusters = { pair[0].getClusters().get(0), pair[1].getClusters().get(0) }; + + // Populate the cluster plots. + møctmElectronEnergy.fill(trackClusters[0].getEnergy()); + møctmElectronEnergy.fill(trackClusters[1].getEnergy()); + møctmEnergySum1D.fill(TriggerModule.getValueEnergySum(trackClusters)); + møctmEnergySum2D.fill(trackClusters[0].getEnergy(), trackClusters[1].getEnergy()); + møctmTimeCoincidence.fill(TriggerModule.getClusterTime(trackClusters[0]) - TriggerModule.getClusterTime(trackClusters[1])); + møctmClusterPosition.fill(TriggerModule.getClusterXIndex(trackClusters[0]), TriggerModule.getClusterYIndex(trackClusters[0])); + møctmClusterPosition.fill(TriggerModule.getClusterXIndex(trackClusters[1]), TriggerModule.getClusterYIndex(trackClusters[1])); + + // Populate the momentum plots. + møctmInvariantMass.fill(getInvariantMass(pair)); + møctmElectronMomentum.fill(pair[0].getMomentum().magnitude()); + møctmElectronMomentum.fill(pair[1].getMomentum().magnitude()); + møctmMomentumSum1D.fill(VecOp.add(pair[0].getMomentum(), pair[1].getMomentum()).magnitude()); + møctmMomentumSum2D.fill(pair[0].getMomentum().magnitude(), pair[1].getMomentum().magnitude()); + } + + // Produce trident cluster-track matched plots. + trctmInstancesInEvent.fill(tridents.size()); + for(ReconstructedParticle[] pair : tridents) { + // Get the electron and positron tracks. + ReconstructedParticle electronTrack = pair[pair[0].getCharge() < 0 ? 0 : 1]; + ReconstructedParticle positronTrack = pair[pair[0].getCharge() > 0 ? 0 : 1]; + + // Get the track clusters. + Cluster electronCluster = electronTrack.getClusters().get(0); + Cluster positronCluster = positronTrack.getClusters().get(0); + Cluster[] trackClusters = { pair[0].getClusters().get(0), pair[1].getClusters().get(0) }; + + // Populate the cluster plots. + trctmElectronEnergy.fill(electronCluster.getEnergy()); + trctmPositronEnergy.fill(positronCluster.getEnergy()); + trctmEnergySum1D.fill(TriggerModule.getValueEnergySum(trackClusters)); + trctmEnergySum2D.fill(pair[0].getEnergy(), pair[1].getEnergy()); + trctmTimeCoincidence.fill(TriggerModule.getClusterTime(trackClusters[0]) - TriggerModule.getClusterTime(trackClusters[1])); + trctmClusterPosition.fill(TriggerModule.getClusterXIndex(trackClusters[0]), TriggerModule.getClusterYIndex(trackClusters[0])); + trctmClusterPosition.fill(TriggerModule.getClusterXIndex(trackClusters[1]), TriggerModule.getClusterYIndex(trackClusters[1])); + + // Populate the momentum plots. + trctmInvariantMass.fill(getInvariantMass(pair)); + trctmElectronMomentum.fill(electronTrack.getMomentum().magnitude()); + trctmPositronMomentum.fill(positronTrack.getMomentum().magnitude()); + trctmMomentumSum1D.fill(VecOp.add(pair[0].getMomentum(), pair[1].getMomentum()).magnitude()); + trctmMomentumSum2D.fill(pair[0].getMomentum().magnitude(), pair[1].getMomentum().magnitude()); + } + } + + /** + * Produces pairs of tracks. The track pairs are required to be + * matched to a cluster and the associated clusters must form a + * top/bottom pair. If more than one track points to the same + * cluster, only the first track is retained. + * @param trackList - A list of all tracks. + * @return Returns a list of track pairs meeting the aforementioned + * conditions. + */ + private static final List<ReconstructedParticle[]> getTopBottomTracksCTMatched(List<ReconstructedParticle> trackList) { + // Track clusters that have already been seen to prevent clusters + // that have duplicate tracks from reappearing. + Set<Cluster> clusterSet = new HashSet<Cluster>(); + + // Separate the tracks into top and bottom tracks based on + // the track cluster. Filter out tracks with no clusters. + List<ReconstructedParticle> topTracks = new ArrayList<ReconstructedParticle>(); + List<ReconstructedParticle> botTracks = new ArrayList<ReconstructedParticle>(); + trackLoop: + for(ReconstructedParticle track : trackList) { + // Check if the track has a cluster. If not, skip it. + if(track.getClusters().isEmpty()) { + continue trackLoop; + } + + // If the track doesn't have actual tracks, skip it. + if(track.getTracks().isEmpty()) { + continue trackLoop; + } + + // Check if the track cluster has already seen. + Cluster trackCluster = track.getClusters().get(0); + if(clusterSet.contains(trackCluster)) { + continue trackLoop; + } + + // If the track has a unique cluster, add it to the proper + // list based on the cluster y-index. + clusterSet.add(trackCluster); + if(TriggerModule.getClusterYIndex(trackCluster) > 0) { + topTracks.add(track); + } else { + botTracks.add(track); + } + } + + // Form all top/bottom pairs with the unique tracks. + List<ReconstructedParticle[]> pairList = new ArrayList<ReconstructedParticle[]>(); + for(ReconstructedParticle topTrack : topTracks) { + for(ReconstructedParticle botTrack : botTracks) { + pairList.add(new ReconstructedParticle[] { topTrack, botTrack }); + } + } + + // Return the result. + return pairList; + } + + /** + * Gets a list track pairs that meet the trident condition defined + * using tracks with matched calorimeter clusters. A pair meets the + * cluster/track matched trident condition is it meets the following: + * <ul><li>Both tracks have matched clusters.</li> + * <li>Has one positive track.</li> + * <li>Has one negative track.</li> + * <li>Clusters have a time coincidence of 2.5 ns or less.</li> + * <li>The electron momentum is below 900 MeV.</li></ul> + * @param pairList - A <code>List</code> collection of parameterized + * type <code>ReconstructedParticle[]</code> containing all valid + * top/bottom pairs of tracks with matched clusters. These will be + * tested to see if they meet the process criteria. + * @return Returns a list containing pairs of tracks that meet the + * trident condition. + */ + private final List<ReconstructedParticle[]> getTridentTracksCTMatched(List<ReconstructedParticle[]> pairList) { + // Store the set of track pairs that meet the trident condition. + List<ReconstructedParticle[]> tridentTracks = new ArrayList<ReconstructedParticle[]>(); + + // Loop over the filtered pair list and apply the trident + // condition test. + tridentLoop: + for(ReconstructedParticle[] pair : pairList) { + // There must be one positive and one negative track. + ReconstructedParticle electron = null; + ReconstructedParticle positron = null; + if(pair[0].getCharge() > 0) { positron = pair[0]; } + else if(pair[1].getCharge() > 0) { positron = pair[1]; } + if(pair[0].getCharge() < 0) { electron = pair[0]; } + else if(pair[1].getCharge() < 0) { electron = pair[1]; } + if(electron == null || positron == null) { + continue tridentLoop; + } + + // Make sure that the clusters are not the same. This should + // not actually ever be possible... + if(pair[0].getClusters().get(0) == pair[1].getClusters().get(0)) { + continue tridentLoop; + } + + // The clusters must within a limited time window. + Cluster[] trackClusters = { pair[0].getClusters().get(0), pair[1].getClusters().get(0) }; + if(TriggerModule.getValueTimeCoincidence(trackClusters) > timeCoincidence) { + continue tridentLoop; + } + + // Require that the electron in the pair have an energy + // below 900 MeV to exclude elastic electrons. + if(electron.getMomentum().magnitude() >= 0.900) { + continue tridentLoop; + } + + // If all the above conditions are met, the pair is to be + // considered a trident pair. Add it to the list. + tridentTracks.add(pair); + } + + // Return the list of pairs that passed the condition. + return tridentTracks; + } + + /** + * Gets a list track pairs that meet the Møller condition defined + * using tracks with matched calorimeter clusters. A pair meets the + * cluster/track matched Møller condition is it meets the following: + * <ul><li>Both tracks have matched clusters.</li> + * <li>Both tracks are negative.</li> + * <li>Clusters have a time coincidence of 2.5 ns or less.</li> + * <li>The electron momenta are below 900 MeV.</li> + * <li>The momentum sum of the tracks is in the range <code>800 MeV + * ⤠p1 + p2 ⤠1500 MeV</li></ul> + * @param pairList - A <code>List</code> collection of parameterized + * type <code>ReconstructedParticle[]</code> containing all valid + * top/bottom pairs of tracks with matched clusters. These will be + * tested to see if they meet the process criteria. + * @return Returns a list containing pairs of tracks that meet the + * Møller condition. + */ + private final List<ReconstructedParticle[]> getMøllerTracksCTMatched(List<ReconstructedParticle[]> pairList) { + // Store the set of track pairs that meet the Møller condition. + List<ReconstructedParticle[]> møllerTracks = new ArrayList<ReconstructedParticle[]>(); + + // Loop over the filtered pair list and apply the Møller + // condition test. + møllerLoop: + for(ReconstructedParticle[] pair : pairList) { + // Both tracks must be negatively charged. + if(pair[0].getCharge() > 0 || pair[1].getCharge() > 0) { + continue møllerLoop; + } + + // The clusters must within a limited time window. + Cluster[] trackClusters = { pair[0].getClusters().get(0), pair[1].getClusters().get(0) }; + if(TriggerModule.getValueTimeCoincidence(trackClusters) > timeCoincidence) { + continue møllerLoop; + } + + // Require that the electrons in the pair have energies + // below 900 MeV to exclude elastic electrons. + if(pair[0].getMomentum().magnitude() > 0.900 || pair[1].getMomentum().magnitude() > 0.900) { + continue møllerLoop; + } + + // Require that the energy of the pair be within a range + // that is sufficiently "Møller-like." + double momentumSum = VecOp.add(pair[0].getMomentum(), pair[1].getMomentum()).magnitude(); + if(momentumSum < 0.800 || momentumSum > 1.500) { + continue møllerLoop; + } + + // If all the above conditions are met, the pair is to be + // considered a trident pair. Add it to the list. + møllerTracks.add(pair); + } + + // Return the list of pairs that passed the condition. + return møllerTracks; + } + + /** + * Calculates the approximate invariant mass for a pair of tracks + * from their momentum. This assumes that the particles are either + * electrons or positrons, and thusly have a sufficiently small + * mass term that it can be safely excluded. + * @param pair - The track pair for which to calculate the invariant + * mass. + * @return Returns the approximate invariant mass in units of GeV. + */ + private static final double getInvariantMass(ReconstructedParticle[] pair) { + // Get the momentum squared. + double p2 = Math.pow(pair[0].getMomentum().magnitude() + pair[1].getMomentum().magnitude(), 2); + + // Get the remaining terms. + double xPro = pair[0].getMomentum().x() + pair[1].getMomentum().x(); + double yPro = pair[0].getMomentum().y() + pair[1].getMomentum().y(); + double zPro = pair[0].getMomentum().z() + pair[1].getMomentum().z(); + + // Calculate the invariant mass. + return Math.sqrt(p2 - Math.pow(xPro, 2) - Math.pow(yPro, 2) - Math.pow(zPro, 2)); + } +} +// [log in to unmask] Modified: java/trunk/users/src/main/java/org/hps/users/kmccarty/plots/formatter/TriggerPlotsFormat.java ============================================================================= --- java/trunk/users/src/main/java/org/hps/users/kmccarty/plots/formatter/TriggerPlotsFormat.java (original) +++ java/trunk/users/src/main/java/org/hps/users/kmccarty/plots/formatter/TriggerPlotsFormat.java Tue Oct 20 18:57:00 2015 @@ -57,22 +57,23 @@ */ public static void main(String[] args) throws IOException { // Define the root directory for the plots. - String rootDir = "D:\\cygwin64\\home\\Kyle\\aprime-plots\\"; + String rootDir = "D:\\cygwin64\\home\\Kyle\\beam-plots\\base\\"; + //String rootDir = "D:\\cygwin64\\home\\Kyle\\aprime-plots\\base\\readout-plots\\"; // Define the new name of the file containing the trigger plots. String[] plotFile = { - //rootDir + "background-ana_triggerPlots.aida" - rootDir + "15-MeV\\compiled-plots.aida", - rootDir + "20-MeV\\compiled-plots.aida", - rootDir + "30-MeV\\compiled-plots.aida", - rootDir + "40-MeV\\compiled-plots.aida", - rootDir + "50-MeV\\compiled-plots.aida" + rootDir + "compiled-plots.aida" + //rootDir + "15-MeV\\compiled-plots.aida", + //rootDir + "20-MeV\\compiled-plots.aida", + //rootDir + "30-MeV\\compiled-plots.aida", + //rootDir + "40-MeV\\compiled-plots.aida", + //rootDir + "50-MeV\\compiled-plots.aida" }; // Define the names of each plot. This will be used for the // legend in the case of multiple plots. String[] treeName = { - //"Background", + "Background", "15 MeV A'", "20 MeV A'", "30 MeV A'", @@ -82,13 +83,13 @@ // Define the color style for the plots. ColorStyle[] dataColorStyle = { - //ColorStyle.GREY, + ColorStyle.GREY, ColorStyle.MS_GREEN, ColorStyle.MS_BLUE, ColorStyle.MS_ORANGE, ColorStyle.MS_RED, + ColorStyle.TEAL, ColorStyle.CRIMSON, - ColorStyle.TEAL, ColorStyle.FOREST };