Author: [log in to unmask] Date: Thu Jun 18 17:48:35 2015 New Revision: 3165 Log: Added two personal analysis drivers. Added: java/trunk/users/src/main/java/org/hps/users/kmccarty/InvariantMassPairDriver.java java/trunk/users/src/main/java/org/hps/users/kmccarty/MTEAnalysis.java Added: java/trunk/users/src/main/java/org/hps/users/kmccarty/InvariantMassPairDriver.java ============================================================================= --- java/trunk/users/src/main/java/org/hps/users/kmccarty/InvariantMassPairDriver.java (added) +++ java/trunk/users/src/main/java/org/hps/users/kmccarty/InvariantMassPairDriver.java Thu Jun 18 17:48:35 2015 @@ -0,0 +1,254 @@ +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.List; + +import org.hps.recon.ecal.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 InvariantMassPairDriver extends Driver { + private int[] events = new int[3]; + private TriggerModule[] trigger = new TriggerModule[2]; + + private String gtpClusterCollectionName = "EcalClustersGTP"; + private String particleCollectionName = "FinalStateParticles"; + private String reconParticleCollectionName = "UnconstrainedV0Candidates"; + + private AIDA aida = AIDA.defaultInstance(); + private IHistogram1D electronEnergyHist = aida.histogram1D("Trident Analysis/Electron Energy", 150, 0.000, 1.500); + private IHistogram1D positronEnergyHist = aida.histogram1D("Trident Analysis/Positron Energy", 150, 0.000, 1.500); + private IHistogram1D pairEnergyHist = aida.histogram1D("Trident Analysis/Energy Sum Distribution", 220, 0.00, 2.200); + private IHistogram2D pair2DEnergyHist = aida.histogram2D("Trident Analysis/2D Energy Distribution", 55, 0, 1.1, 55, 0, 1.1); + private IHistogram1D pair1MassHist = aida.histogram1D("Trident Analysis/Particle Invariant Mass (1 Hit)", 240, 0.000, 0.120); + private IHistogram1D pair1ModMassHist = aida.histogram1D("Trident Analysis/Particle Invariant Mass (2 Hit)", 240, 0.000, 0.120); + + @Override + public void startOfData() { + // Instantiate the pair 1 trigger. + trigger[0] = new TriggerModule(); + trigger[0].setCutValue(TriggerModule.CLUSTER_HIT_COUNT_LOW, 1); + trigger[0].setCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_LOW, 0.054); + trigger[0].setCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_HIGH, 0.630); + trigger[0].setCutValue(TriggerModule.PAIR_COPLANARITY_HIGH, 30); + trigger[0].setCutValue(TriggerModule.PAIR_ENERGY_DIFFERENCE_HIGH, 0.540); + trigger[0].setCutValue(TriggerModule.PAIR_ENERGY_SUM_LOW, 0.180); + trigger[0].setCutValue(TriggerModule.PAIR_ENERGY_SUM_HIGH, 0.860); + trigger[0].setCutValue(TriggerModule.PAIR_ENERGY_SLOPE_LOW, 0.600); + trigger[0].setCutValue(TriggerModule.PAIR_ENERGY_SLOPE_F, 0.0055); + trigger[0].setCutValue(TriggerModule.PAIR_TIME_COINCIDENCE, 12); + + // Instantiate the pair 1 trigger with a hit count cut of two. + trigger[1] = new TriggerModule(); + trigger[1].setCutValue(TriggerModule.CLUSTER_HIT_COUNT_LOW, 2); + trigger[0].setCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_LOW, 0.054); + trigger[0].setCutValue(TriggerModule.CLUSTER_TOTAL_ENERGY_HIGH, 0.630); + trigger[0].setCutValue(TriggerModule.PAIR_COPLANARITY_HIGH, 30); + trigger[0].setCutValue(TriggerModule.PAIR_ENERGY_DIFFERENCE_HIGH, 0.540); + trigger[0].setCutValue(TriggerModule.PAIR_ENERGY_SUM_LOW, 0.180); + trigger[0].setCutValue(TriggerModule.PAIR_ENERGY_SUM_HIGH, 0.860); + trigger[0].setCutValue(TriggerModule.PAIR_ENERGY_SLOPE_LOW, 0.600); + trigger[1].setCutValue(TriggerModule.PAIR_ENERGY_SLOPE_F, 0.0055); + trigger[0].setCutValue(TriggerModule.PAIR_TIME_COINCIDENCE, 12); + } + + @Override + public void endOfData() { + System.out.printf("Pair 1 :: %d / %d%n", events[0], events[2]); + System.out.printf("Pair 1 Mod :: %d / %d%n", events[1], events[2]); + } + + @Override + public void process(EventHeader event) { + // Get a list of all tracks in the event. + List<ReconstructedParticle> trackList = event.get(ReconstructedParticle.class, particleCollectionName); + + // Plot the energies of the electrons and positrons. + for(ReconstructedParticle track : trackList) { + // Positive tracks are assumed to be positrons. + if(track.getCharge() > 0) { + positronEnergyHist.fill(track.getMomentum().magnitude()); + } + + // Negative tracks are assumed to be electrons. + else if(track.getCharge() < 0) { + electronEnergyHist.fill(track.getMomentum().magnitude()); + } + } + + // Get track pairs. + List<ReconstructedParticle[]> trackPairList = getTrackPairs(trackList); + + // Populate the pair plots. + trackPairLoop: + for(ReconstructedParticle[] trackPair : trackPairList) { + // Note the polarity of the tracks. + boolean[] trackIsPositive = { + trackPair[0].getCharge() > 0, + trackPair[1].getCharge() > 0 + }; + + // Require that one track be positive and one be negative. + if(!(trackIsPositive[0] ^ trackIsPositive[1])) { + continue trackPairLoop; + } + + // Populate the track pair plots. + pairEnergyHist.fill(VecOp.add(trackPair[0].getMomentum(), trackPair[1].getMomentum()).magnitude()); + if(trackIsPositive[0]) { + pair2DEnergyHist.fill(trackPair[0].getMomentum().magnitude(), trackPair[1].getMomentum().magnitude()); + } else { + pair2DEnergyHist.fill(trackPair[1].getMomentum().magnitude(), trackPair[0].getMomentum().magnitude()); + } + } + + // Check that the event has a collection of GTP clusters. + if(!event.hasCollection(Cluster.class, gtpClusterCollectionName)) { + return; + } + + // Increment the total event count. + events[2]++; + + // Get the GTP clusters. + List<Cluster> clusters = event.get(Cluster.class, gtpClusterCollectionName); + + // Get the list of top/bottom pairs. + List<Cluster[]> pairs = getClusterPairs(clusters); + + // Iterate over the pairs and determine if any cluster passes + // pair 1 trigger or the pair 1 modified trigger. + boolean passedPair1 = false; + boolean passedPair1Mod = false; + pairLoop: + for(Cluster[] pair : pairs) { + // Check the cluster energy cut. + if(!trigger[0].clusterTotalEnergyCut(pair[0])) { continue pairLoop; } + if(!trigger[0].clusterTotalEnergyCut(pair[1])) { continue pairLoop; } + + // Check the pair cuts. + if(!trigger[0].pairCoplanarityCut(pair)) { continue pairLoop; } + if(!trigger[0].pairEnergyDifferenceCut(pair)) { continue pairLoop; } + if(!trigger[0].pairEnergySumCut(pair)) { continue pairLoop; } + if(!trigger[0].pairEnergySlopeCut(pair)) { continue pairLoop; } + + // Check if the pair passes the singles 0 hit count cut. + if(trigger[0].clusterHitCountCut(pair[0]) && trigger[0].clusterHitCountCut(pair[1])) { + // Note that a pair passed the pair 1 trigger. + passedPair1 = true; + + // Check whether the pair passed the modified pair 1 + // trigger hit count cut. + if(trigger[1].clusterHitCountCut(pair[0]) && trigger[1].clusterHitCountCut(pair[1])) { + passedPair1Mod = true; + } + } else { continue pairLoop; } + } + + // If no pair passed the pair 1 cut, nothing further need be done. + if(!passedPair1) { return; } + + // Otherwise, increment the "passed pair 1" count and the + // "passed pair 1 mod" count, if appropriate. + events[0]++; + if(passedPair1Mod) { events[1]++; } + + // Get the collection of reconstructed V0 candidates. + List<ReconstructedParticle> candidateList = event.get(ReconstructedParticle.class, reconParticleCollectionName); + + // Populate the invariant mass plot. + candidateLoop: + for(ReconstructedParticle particle : candidateList) { + // Check that it has component particles that meet the + // trident condition. + boolean seenPositive = false; + boolean seenNegative = false; + for(ReconstructedParticle track : particle.getParticles()) { + // Exactly one track must be negative. Its energy is + // disallowed from exceeding 900 MeV. + if(track.getCharge() < 0) { + // Reject a second negative particle. + if(seenNegative) { continue candidateLoop; } + + // Otherwise, note that one has been seen. + seenNegative = true; + + // Reject electrons with a momentum exceeding 900 MeV. + if(track.getMomentum().magnitude() > 0.900) { + continue candidateLoop; + } + } + + // Exactly one track must be positive. Its energy is + // not constrained. + else if(track.getCharge() > 0) { + // Reject a second positive particle. + if(seenPositive) { continue candidateLoop; } + + // Otherwise, note that one has been seen. + seenPositive = true; + } + + // Lastly, reject any particle that produced a photon. + else { continue candidateLoop; } + } + + // Populate the plots. + pair1MassHist.fill(particle.getMass()); + if(passedPair1Mod) { pair1ModMassHist.fill(particle.getMass()); } + } + } + + /** + * Creates a list of top/bottom cluster pairs. + * @param clusters - A <code>List</code> collection of objects of + * type <code>Cluster</code>. + * @return Returns a <code>List</code> collection of 2-entry arrays + * of <code>Cluster</code> objects representing top/bottom cluster + * pairs. The first entry is always the top cluster. + */ + private static final List<Cluster[]> getClusterPairs(List<Cluster> clusters) { + // Separate the clusters into top and bottom clusters. + List<Cluster> topList = new ArrayList<Cluster>(); + List<Cluster> botList = new ArrayList<Cluster>(); + for(Cluster cluster : clusters) { + if(cluster.getCalorimeterHits().get(0).getIdentifierFieldValue("iy") > 0) { + topList.add(cluster); + } + else { botList.add(cluster); } + } + + // Create a list of all top/bottom pairs. + List<Cluster[]> pairs = new ArrayList<Cluster[]>(); + for(Cluster topCluster : topList) { + for(Cluster botCluster : botList) { + pairs.add(new Cluster[] { topCluster, botCluster }); + } + } + + // Return the list of cluster pairs. + return pairs; + } + + private static final List<ReconstructedParticle[]> getTrackPairs(List<ReconstructedParticle> trackList) { + // Create an empty list for the pairs. + List<ReconstructedParticle[]> pairs = new ArrayList<ReconstructedParticle[]>(); + + // Add all possible pairs of tracks. + for(int i = 0; i < trackList.size(); i++) { + for(int j = i + 1; j < trackList.size(); j++) { + pairs.add(new ReconstructedParticle[] { trackList.get(i), trackList.get(j) }); + } + } + + // Return the list of tracks. + return pairs; + } +} Added: java/trunk/users/src/main/java/org/hps/users/kmccarty/MTEAnalysis.java ============================================================================= --- java/trunk/users/src/main/java/org/hps/users/kmccarty/MTEAnalysis.java (added) +++ java/trunk/users/src/main/java/org/hps/users/kmccarty/MTEAnalysis.java Thu Jun 18 17:48:35 2015 @@ -0,0 +1,202 @@ +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.List; + +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 MTEAnalysis extends Driver { + // Define track LCIO information. + private String particleCollectionName = "FinalStateParticles"; + private static final AIDA aida = AIDA.defaultInstance(); + private IHistogram1D[] chargedTracksPlot = { + aida.histogram1D("MTE Analysis/Møller Event Tracks", 10, 0, 10), + aida.histogram1D("MTE Analysis/Trident Event Tracks", 10, 0, 10), + aida.histogram1D("MTE Analysis/Elastic Event Tracks", 10, 0, 10) + }; + private IHistogram1D[] energyPlot = { + aida.histogram1D("MTE Analysis/Møller Energy Sum Distribution", 220, 0, 2.2), + aida.histogram1D("MTE Analysis/Trident Energy Sum Distribution", 220, 0, 2.2), + aida.histogram1D("MTE Analysis/Elastic Energy Distribution", 110, 0, 1.5) + }; + private IHistogram1D[] electronPlot = { + aida.histogram1D("MTE Analysis/Møller Electron Energy Distribution", 220, 0, 2.2), + aida.histogram1D("MTE Analysis/Trident Electron Energy Distribution", 220, 0, 2.2), + }; + private IHistogram1D positronPlot = aida.histogram1D("MTE Analysis/Trident Positron Energy Distribution", 220, 0, 2.2); + private IHistogram2D[] energy2DPlot = { + aida.histogram2D("MTE Analysis/Møller 2D Energy Distribution", 55, 0, 1.1, 55, 0, 1.1), + aida.histogram2D("MTE Analysis/Trident 2D Energy Distribution", 55, 0, 1.1, 55, 0, 1.1), + }; + private static final int MÃLLER = 0; + private static final int TRIDENT = 1; + private static final int ELASTIC = 2; + private boolean verbose = false; + + @Override + public void process(EventHeader event) { + if(event.hasCollection(ReconstructedParticle.class, particleCollectionName)) { + // Get the list of tracks. + List<ReconstructedParticle> trackList = event.get(ReconstructedParticle.class, particleCollectionName); + + if(verbose) { + System.out.println(trackList.size() + " tracks found."); + for(ReconstructedParticle track : trackList) { + System.out.printf("Track :: Q = %4.1f; E = %6.3f%n", + track.getCharge(), track.getEnergy()); + } + } + + // Check each of the event-type conditions. + boolean isMøller = false; + boolean isTrident = false; + boolean isElastic = false; + + // Produce all possible pairs of tracks. + List<ReconstructedParticle[]> pairList = getTrackPairs(trackList); + + // Check the Møller condition. A Møller event is expected + // to have two tracks, both negative, with a net energy + // within a certain band of the beam energy. + møllerTrackLoop: + for(ReconstructedParticle[] pair : pairList) { + // Both tracks are required to be negatively charged. + if(pair[0].getCharge() >= 0 || pair[1].getCharge() >= 0) { + continue møllerTrackLoop; + } + + // Both tracks must have clusters associated with them. + Cluster[] trackClusters = new Cluster[2]; + for(int i = 0; i < 2; i++) { + // Disallow tracks with no associated clusters. + if(pair[i].getClusters().size() == 0) { + continue møllerTrackLoop; + } + + // Store the first cluster associated with the track. + trackClusters[i] = pair[i].getClusters().get(0); + } + + // Require that the track clusters be within a certain + // time window of one another. + if(Math.abs(trackClusters[0].getCalorimeterHits().get(0).getTime() - trackClusters[1].getCalorimeterHits().get(0).getTime()) > 500) { + continue møllerTrackLoop; + } + + // No track may have an energy that exceeds 900 MeV. + if(pair[0].getMomentum().magnitude() >= 0.900 || pair[1].getMomentum().magnitude() >= 0.900) { + continue møllerTrackLoop; + } + + // Get the energy sum. + double sum = VecOp.add(pair[0].getMomentum(), pair[1].getMomentum()).magnitude(); + + // "Møller-like" track pairs must have energies within + // an allowed energy range. + if(sum < 0.800 || sum > 1.500) { + continue møllerTrackLoop; + } + + // Note that this is a Møller event. + isMøller = true; + + // Populate the Møller plots. + energyPlot[MÃLLER].fill(sum); + electronPlot[MÃLLER].fill(pair[0].getMomentum().magnitude()); + electronPlot[MÃLLER].fill(pair[1].getMomentum().magnitude()); + energy2DPlot[MÃLLER].fill(pair[0].getMomentum().magnitude(), pair[1].getMomentum().magnitude()); + } + + // Check the elastic condition. Elastic events should be + // negatively and have an energy approximately equal to + // the beam energy. + for(ReconstructedParticle track : trackList) { + if(track.getCharge() < 0 && track.getMomentum().magnitude() >= 0.900) { + isElastic = true; + energyPlot[ELASTIC].fill(track.getMomentum().magnitude()); + } + } + + // Check the trident condition. Tridents are events that + // contain both one positive and one negative track. + for(ReconstructedParticle[] pair : pairList) { + if((pair[0].getCharge() < 0 && pair[1].getCharge() > 0) || + pair[0].getCharge() > 0 && pair[1].getCharge() < 0) { + // Require that the energy of the electron is below + // 900 MeV. + if((pair[0].getCharge() < 0 && pair[0].getMomentum().magnitude() < 0.900) + || (pair[1].getCharge() < 0 && pair[1].getMomentum().magnitude() < 0.900)) { + isTrident = true; + if(pair[0].getCharge() > 0) { + positronPlot.fill(pair[1].getMomentum().magnitude()); + electronPlot[TRIDENT].fill(pair[0].getMomentum().magnitude()); + } else { + positronPlot.fill(pair[0].getMomentum().magnitude()); + electronPlot[TRIDENT].fill(pair[1].getMomentum().magnitude()); + } + energyPlot[TRIDENT].fill(VecOp.add(pair[0].getMomentum(), pair[1].getMomentum()).magnitude()); + energy2DPlot[TRIDENT].fill(pair[0].getMomentum().magnitude(), pair[1].getMomentum().magnitude()); + } + } + } + + if(verbose) { + System.out.printf("\tMøller :: %b%n", isMøller); + System.out.printf("\tTrident :: %b%n", isTrident); + System.out.printf("\tElastic :: %b%n", isElastic); + System.out.println(); + } + + // Get the number of charged tracks in the event. + int tracks = 0; + for(ReconstructedParticle track : trackList) { + if(track.getCharge() != 0) { tracks++; } + } + + // Add the result to the appropriate plots. + if(isMøller) { + chargedTracksPlot[MÃLLER].fill(tracks); + } else if(isTrident) { + chargedTracksPlot[TRIDENT].fill(tracks); + } else if(isElastic) { + chargedTracksPlot[ELASTIC].fill(tracks); + } + } + } + + private static final List<ReconstructedParticle[]> getTrackPairs(List<ReconstructedParticle> trackList) { + // Create an empty list for the pairs. + List<ReconstructedParticle[]> pairs = new ArrayList<ReconstructedParticle[]>(); + + // Add all possible pairs of tracks. + for(int i = 0; i < trackList.size(); i++) { + for(int j = i + 1; j < trackList.size(); j++) { + pairs.add(new ReconstructedParticle[] { trackList.get(i), trackList.get(j) }); + } + } + + // Return the list of tracks. + return pairs; + } + + private static final double getMagnitude(double[] vector) { + // Store the squares of each component of the vector. + double squareSum = 0; + + // Add the square of each vector component. + for(double d : vector) { + squareSum += d * d; + } + + // Return the square root of the sum. + return Math.sqrt(squareSum); + } +}