Author: [log in to unmask] Date: Wed Dec 9 18:08:46 2015 New Revision: 4041 Log: Merge nathan-dev back into trunk. Should be exactly 2 files changed. Svn merging takes longer than actually testing the code. Modified: java/trunk/ (props changed) java/trunk/recon/src/main/java/org/hps/recon/particle/ReconParticleDriver.java java/trunk/recon/src/main/java/org/hps/recon/utils/TrackClusterMatcher.java Modified: java/trunk/recon/src/main/java/org/hps/recon/particle/ReconParticleDriver.java ============================================================================= --- java/trunk/recon/src/main/java/org/hps/recon/particle/ReconParticleDriver.java (original) +++ java/trunk/recon/src/main/java/org/hps/recon/particle/ReconParticleDriver.java Wed Dec 9 18:08:46 2015 @@ -6,6 +6,8 @@ import hep.physics.vec.HepLorentzVector; import hep.physics.vec.VecOp; +import java.io.FileWriter; +import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -16,7 +18,6 @@ import org.hps.recon.tracking.CoordinateTransformations; import org.hps.recon.tracking.TrackUtils; import org.hps.recon.utils.TrackClusterMatcher; -import org.lcsim.event.CalorimeterHit; import org.lcsim.event.Cluster; import org.lcsim.event.EventHeader; import org.lcsim.event.ReconstructedParticle; @@ -25,8 +26,8 @@ import org.lcsim.event.base.BaseCluster; import org.lcsim.event.base.BaseReconstructedParticle; import org.lcsim.geometry.Detector; +import org.lcsim.geometry.subdetector.HPSEcal3; import org.lcsim.util.Driver; -import org.lcsim.geometry.subdetector.HPSEcal3; /** @@ -50,6 +51,9 @@ public static final int MOLLER_TOP = 0; public static final int MOLLER_BOT = 1; + // normalized cluster-track distance required for qualifying as a match: + private double MAXNSIGMAPOSITIONMATCH=30.0; + HPSEcal3 ecal; /** @@ -180,6 +184,15 @@ this.trackCollectionNames = trackCollectionNames; } + /** + * Set the requirement on cluster-track position matching in terms of N-sigma. + * + * @param nsigma + */ + public void setNSigmaPositionMatch(double nsigma) { + MAXNSIGMAPOSITIONMATCH=nsigma; + } + /** @@ -235,15 +248,16 @@ // Create a mapping of matched clusters to corresponding tracks. HashMap<Cluster, Track> clusterToTrack = new HashMap<Cluster,Track>(); - + // Loop through all of the track collections and try to match every // track to a cluster. Allow a cluster to be matched to multiple // tracks and use a probability (to be coded later) to determine what // the best match is. // TODO: At some point, pull this out to it's own method for (List<Track> tracks : trackCollections) { + for (Track track : tracks) { - + // Create a reconstructed particle to represent the track. ReconstructedParticle particle = new BaseReconstructedParticle(); @@ -257,6 +271,9 @@ // Derive the charge of the particle from the track. ((BaseReconstructedParticle) particle).setCharge(track.getCharge() * flipSign); + + // initialize PID quality to a junk value: + ((BaseReconstructedParticle)particle).setGoodnessOfPid(-9999); // Extrapolate the particle ID from the track. Positively // charged particles are assumed to be positrons and those @@ -267,36 +284,48 @@ ((BaseReconstructedParticle) particle).setParticleIdUsed(new SimpleParticleID(11, 0, 0, 0)); } + // normalized distance of the closest match: + double smallestNSigma=Double.MAX_VALUE; + + // try to find a matching cluster: Cluster matchedCluster = null; - - // Track the best matching cluster for the track. - // TODO: This should find the best match not just the first match. - clusterLoop: for (Cluster cluster : clusters) { - // Check if the cluster and track are a valid match. - if (matcher.isMatch(cluster, track)) { - - // Store the matched cluster to matched track. - clusterToTrack.put(cluster, track); - - // Store the matched cluster. - matchedCluster = cluster; - - // Since a match has been found, the loop can be - // terminated. - break clusterLoop; + + // normalized distance between this cluster and track: + final double thisNSigma=matcher.getNSigmaPosition(cluster, particle); + + // ignore if matching quality doesn't make the cut: + if (thisNSigma > MAXNSIGMAPOSITIONMATCH) continue; + + // ignore if we already found a cluster that's a better match: + if (thisNSigma > smallestNSigma) continue; + + // we found a new best cluster candidate for this track: + smallestNSigma = thisNSigma; + matchedCluster = cluster; + + // prefer using GBL tracks to correct (later) the clusters, for some consistency: + if (track.getType() >= 32 || !clusterToTrack.containsKey(matchedCluster)) { + clusterToTrack.put(matchedCluster,track); } } // If a cluster was found that matches the track... if (matchedCluster != null) { + + // add cluster to the particle: particle.addCluster(matchedCluster); - int pid = particle.getParticleIDUsed().getPDG(); + // use pid quality to store track-cluster matching quality: + ((BaseReconstructedParticle)particle).setGoodnessOfPid(smallestNSigma); + + // propogate pid to the cluster: + final int pid = particle.getParticleIDUsed().getPDG(); if (Math.abs(pid) == 11) { ((BaseCluster) matchedCluster).setParticleId(pid); } + // unmatched clusters will (later) be used to create photon particles: unmatchedClusters.remove(matchedCluster); } @@ -306,42 +335,40 @@ } // Iterate over the remaining unmatched clusters. - if (!unmatchedClusters.isEmpty()) { - for (Cluster unmatchedCluster : unmatchedClusters) { - - // Create a reconstructed particle to represent the unmatched cluster. - ReconstructedParticle particle = new BaseReconstructedParticle(); - - // The particle is assumed to be a photon, since it did not leave a track. - ((BaseReconstructedParticle) particle).setParticleIdUsed(new SimpleParticleID(22, 0, 0, 0)); - - int pid = particle.getParticleIDUsed().getPDG(); - if (Math.abs(pid) != 11) { - ((BaseCluster) unmatchedCluster).setParticleId(pid); - } - - // Add the cluster to the particle. - particle.addCluster(unmatchedCluster); - - // Set the reconstructed particle properties based on the cluster properties. - ((BaseReconstructedParticle) particle).setCharge(0); - - // Add the particle to the reconstructed particle list. - particles.add(particle); - } + for (Cluster unmatchedCluster : unmatchedClusters) { + + // Create a reconstructed particle to represent the unmatched cluster. + ReconstructedParticle particle = new BaseReconstructedParticle(); + + // The particle is assumed to be a photon, since it did not leave a track. + ((BaseReconstructedParticle) particle).setParticleIdUsed(new SimpleParticleID(22, 0, 0, 0)); + + int pid = particle.getParticleIDUsed().getPDG(); + if (Math.abs(pid) != 11) { + ((BaseCluster) unmatchedCluster).setParticleId(pid); + } + + // Add the cluster to the particle. + particle.addCluster(unmatchedCluster); + + // Set the reconstructed particle properties based on the cluster properties. + ((BaseReconstructedParticle) particle).setCharge(0); + + // Add the particle to the reconstructed particle list. + particles.add(particle); } // Apply the corrections to the Ecal clusters using track information, if available for (Cluster cluster : clusters) { if (cluster.getParticleId() != 0) { - if (clusterToTrack.containsKey(cluster)){ - Track matchedT = clusterToTrack.get(cluster); - double ypos = TrackUtils.getTrackStateAtECal(matchedT).getReferencePoint()[2]; - ClusterUtilities.applyCorrections(ecal, cluster, ypos); - } - else { - ClusterUtilities.applyCorrections(ecal, cluster); - } + if (clusterToTrack.containsKey(cluster)){ + Track matchedT = clusterToTrack.get(cluster); + double ypos = TrackUtils.getTrackStateAtECal(matchedT).getReferencePoint()[2]; + ClusterUtilities.applyCorrections(ecal, cluster, ypos); + } + else { + ClusterUtilities.applyCorrections(ecal, cluster); + } } } @@ -645,7 +672,7 @@ // Beam size variables. // The beamsize array is in the tracking frame /* TODO mg-May 14, 2014: the the beam size from the conditions db...also beam position! */ - protected double[] beamSize = {0.001, 0.130, 0.050}; //rough estimate from harp scans during engineering run production running + protected double[] beamSize = {0.001, 0.2, 0.02}; protected double bField; // flipSign is a kludge... Modified: java/trunk/recon/src/main/java/org/hps/recon/utils/TrackClusterMatcher.java ============================================================================= --- java/trunk/recon/src/main/java/org/hps/recon/utils/TrackClusterMatcher.java (original) +++ java/trunk/recon/src/main/java/org/hps/recon/utils/TrackClusterMatcher.java Wed Dec 9 18:08:46 2015 @@ -1,8 +1,4 @@ package org.hps.recon.utils; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; import hep.aida.IAnalysisFactory; import hep.aida.IHistogram1D; @@ -13,12 +9,17 @@ import hep.physics.vec.BasicHep3Vector; import hep.physics.vec.Hep3Vector; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.hps.recon.tracking.CoordinateTransformations; +import org.hps.recon.tracking.TrackUtils; import org.lcsim.event.Cluster; +import org.lcsim.event.ReconstructedParticle; import org.lcsim.event.Track; import org.lcsim.event.TrackState; import org.lcsim.geometry.FieldMap; -import org.hps.recon.tracking.CoordinateTransformations; -import org.hps.recon.tracking.TrackUtils; /** * Utility used to determine if a track and cluster are matched. @@ -63,6 +64,55 @@ private double topClusterTrackMatchDeltaYHigh = 28; // mm private double bottomClusterTrackMatchDeltaYLow = -28; // mm private double bottomClusterTrackMatchDeltaYHigh = 24; // mm + + /** + * Rafo's parameterization of cluster-seed x/y position residuals as function of energy. + * + * Derived using GBL/seed tracks, non-analytic extrapolation, uncorrected cluster positions, + * and EngRun2015-Nominal-v3-4-fieldmap detector. + * + * f = p0+e*(p1+e*(p2+e*(p3+e*(p4+e*p5)))) + */ + private static final double dxMeanTopPosiGBL[] = { 6.67414,-9.57296, 5.70647, 27.4523,-28.1103,-9.11424 }; + private static final double dxSigmTopPosiGBL[] = { 52.6437,-478.805, 1896.73,-3761.48, 3676.77,-1408.31 }; + private static final double dxMeanBotPosiGBL[] = { 4.13802, 15.8887,-74.2844,-9.78944, 308.541,-287.668 }; + private static final double dxSigmBotPosiGBL[] = { 37.6513,-294.851, 1002.15,-1639.08, 1228.02,-308.754 }; + + private static final double dxMeanTopElecGBL[] = {-1.6473, 5.58701, 25.3977,-17.1523,-121.025, 145.969 }; + private static final double dxSigmTopElecGBL[] = { 48.7018,-423.599, 1592.66,-2959.99, 2668.97,-919.876 }; + private static final double dxMeanBotElecGBL[] = {-6.63558, 83.7763,-460.451, 1275.63,-1702.83, 873.913 }; + private static final double dxSigmBotElecGBL[] = { 47.0029,-411.784, 1586.52,-3083.37, 2985.58,-1145.53 }; + + private static final double dyMeanTopPosiGBL[] = { 0.71245, 5.57585,-6.50267,-8.21688, 39.8607,-43.9661 }; + private static final double dySigmTopPosiGBL[] = { 33.0213,-275.174, 1168.77,-2642.34, 3045.52,-1406.21 }; + private static final double dyMeanBotPosiGBL[] = {-5.532, 74.9738,-383.972, 977.849,-1250.28, 637.75 }; + private static final double dySigmBotPosiGBL[] = { 19.019, -83.9253, 133.813, 119.883,-546.951, 405.207 }; + + private static final double dyMeanTopElecGBL[] = { 2.88498,-20.4101, 62.9689, 25.6386,-259.957, 207.145 }; + private static final double dySigmTopElecGBL[] = { 8.65583, 120.676,-1166.43, 3811.72,-5383.19, 2787.42 }; + private static final double dyMeanBotElecGBL[] = {-9.02276, 112.329,-489.761, 953.037,-829.96, 260.772 }; + private static final double dySigmBotElecGBL[] = { 23.4856,-108.19, 158.7, 189.261,-682.034, 459.15 }; + + private static final double dxMeanTopPosiSeed[] ={ 11.6245,-28.5061, 13.0332, 59.9465,-21.1014,-63.6126 }; + private static final double dxSigmTopPosiSeed[] ={ 61.5911,-540.596, 2077.22,-3973.22, 3704.45,-1332.07 }; + private static final double dxMeanBotPosiSeed[] ={ 4.53394, 11.3773,-63.7127,-2.81629, 273.868,-264.709 }; + private static final double dxSigmBotPosiSeed[] ={ 48.3163,-409.249, 1590.36,-3212.85, 3326.04,-1402.3 }; + + private static final double dxMeanTopElecSeed[] ={ 2.14163,-20.8713, 76.3054, 34.894,-340.272, 295.24 }; + private static final double dxSigmTopElecSeed[] ={ 48.585, -385.166, 1320.26,-2157.45, 1581.06,-366.012 }; + private static final double dxMeanBotElecSeed[] ={-3.44302, 12.4687, 4.09878,-30.0057,-13.3151, 40.2707 }; + private static final double dxSigmBotElecSeed[] ={ 48.4089,-385.494, 1341.37,-2271.52, 1814.02,-526.555 }; + + private static final double dyMeanTopPosiSeed[] ={-0.127741,10.4944, -18.242,-12.9155, 81.0116,-73.9773 }; + private static final double dySigmTopPosiSeed[] ={ 37.3097, -357.55, 1607.03,-3709.55, 4282.36,-1957.91 }; + private static final double dyMeanBotPosiSeed[] ={ 2.24392,-55.2003, 405.04,-1250.64, 1731.47,-887.262 }; + private static final double dySigmBotPosiSeed[] ={ 25.5776,-199.731, 754.59,-1408.72, 1240.36,-400.912 }; + + private static final double dyMeanTopElecSeed[] ={ 3.25429,-24.0858, 69.0145, 34.1213,-297.752, 239.939 }; + private static final double dySigmTopElecSeed[] ={ 19.9111,-53.2699,-261.915, 1593.2,-2774.01, 1605.54 }; + private static final double dyMeanBotElecSeed[] ={-7.72963, 98.1346, -427.91, 840.225,-751.188, 250.792 }; + private static final double dySigmBotElecSeed[] ={ 21.7909,-85.4757,-56.9423, 977.522,-1902.05, 1137.92 }; + /** * Z position to start extrapolation from @@ -194,10 +244,101 @@ tPos = new BasicHep3Vector(trackStateAtEcal.getReferencePoint()); tPos = CoordinateTransformations.transformVectorToDetector(tPos); } + + return Math.sqrt(Math.pow(cPos.x()-tPos.x(),2)+Math.pow(cPos.y()-tPos.y(),2)); + } + + /** + * Calculate #sigma between cluster-track x/y position at calorimeter. + * + * Based on Rafo's parameterizations. Requires non-analytic extrapolation + * and uncorrected cluster positions. + * + * @param cluster = position-uncorrected cluster + * @param track + * + * @return #sigma between cluster and track positions + */ + public double getNSigmaPosition(Cluster cluster,ReconstructedParticle particle) { + + if (particle.getTracks().size()<1) return Double.MAX_VALUE; + Track track=particle.getTracks().get(0); + + if (this.useAnalyticExtrapolator) + throw new RuntimeException("This is to be used with non-analytic extrapolator only."); + + // Get the cluster position: + Hep3Vector cPos = new BasicHep3Vector(cluster.getPosition()); + + // whether track is in top half of detector: + final boolean isTopTrack = track.getTrackStates().get(0).getTanLambda() > 0; + + // ignore if track and cluster in different halves: + if (isTopTrack != cPos.y()>0) return Double.MAX_VALUE; + + // Get the extrapolated track position at the calorimeter: + TrackState trackStateAtEcal = TrackUtils.getTrackStateAtECal(track); + Hep3Vector tPos = new BasicHep3Vector(trackStateAtEcal.getReferencePoint()); + tPos = CoordinateTransformations.transformVectorToDetector(tPos); + + // whether it's a GBL track: + final boolean isGBL = track.getType() >= 32; + + // choose which parameterization of mean and sigma to use: + double dxMean[],dyMean[],dxSigm[],dySigm[]; + if (particle.getCharge()>0) { + if (isTopTrack) { + dxMean = isGBL ? dxMeanTopPosiGBL : dxMeanTopPosiSeed; + dxSigm = isGBL ? dxSigmTopPosiGBL : dxSigmTopPosiSeed; + dyMean = isGBL ? dyMeanTopPosiGBL : dyMeanTopPosiSeed; + dySigm = isGBL ? dySigmTopPosiGBL : dySigmTopPosiSeed; + } + else { + dxMean = isGBL ? dxMeanBotPosiGBL : dxMeanBotPosiSeed; + dxSigm = isGBL ? dxSigmBotPosiGBL : dxSigmBotPosiSeed; + dyMean = isGBL ? dyMeanBotPosiGBL : dyMeanBotPosiSeed; + dySigm = isGBL ? dySigmBotPosiGBL : dySigmBotPosiSeed; + } + } + else if (particle.getCharge()<0) { + if (isTopTrack) { + dxMean = isGBL ? dxMeanTopElecGBL : dxMeanTopElecSeed; + dxSigm = isGBL ? dxSigmTopElecGBL : dxSigmTopElecSeed; + dyMean = isGBL ? dyMeanTopElecGBL : dyMeanTopElecSeed; + dySigm = isGBL ? dySigmTopElecGBL : dySigmTopElecSeed; + } + else { + dxMean = isGBL ? dxMeanBotElecGBL : dxMeanBotElecSeed; + dxSigm = isGBL ? dxSigmBotElecGBL : dxSigmBotElecSeed; + dyMean = isGBL ? dyMeanBotElecGBL : dyMeanBotElecSeed; + dySigm = isGBL ? dySigmBotElecGBL : dySigmBotElecSeed; + } + } + else return Double.MAX_VALUE; + + // get particle energy: + Hep3Vector p3 = new BasicHep3Vector(track.getTrackStates().get(0).getMomentum()); + p3 = CoordinateTransformations.transformVectorToDetector(p3); + double ee = p3.magnitude(); - return Math.sqrt(Math.pow(cPos.x()-tPos.x(),2)+Math.pow(cPos.y()-tPos.y(),2)); - } - + // Rafo's parameterization isn't measured above 650 MeV/c but expected to be constant: + if (ee > 0.65) ee=0.65; + + // calculate measured mean and sigma of deltaX and deltaY for this energy: + double aDxMean=0,aDxSigm=0,aDyMean=0,aDySigm=0; + for (int ii=dxMean.length-1; ii>=0; ii--) aDxMean = dxMean[ii] + ee*aDxMean; + for (int ii=dxSigm.length-1; ii>=0; ii--) aDxSigm = dxSigm[ii] + ee*aDxSigm; + for (int ii=dyMean.length-1; ii>=0; ii--) aDyMean = dyMean[ii] + ee*aDyMean; + for (int ii=dySigm.length-1; ii>=0; ii--) aDySigm = dySigm[ii] + ee*aDySigm; + + // calculate nSigma between track and cluster: + final double nSigmaX = (cPos.x() - tPos.x() - aDxMean) / aDxSigm; + final double nSigmaY = (cPos.y() - tPos.y() - aDyMean) / aDySigm; + return Math.sqrt(nSigmaX*nSigmaX + nSigmaY*nSigmaY); + //return Math.sqrt( 1 / ( 1/nSigmaX/nSigmaX + 1/nSigmaY/nSigmaY ) ); + } + + /** * Determine if a track is matched to a cluster. Currently, this is * determined by checking that the track and cluster are within the same @@ -388,4 +529,16 @@ e.printStackTrace(); } } + + /** + * Class to store track-cluster matching qualities. + */ + public class TrackClusterMatch { + private double nSigmaPositionMatch=Double.MAX_VALUE; + public TrackClusterMatch(ReconstructedParticle pp, Cluster cc) { + nSigmaPositionMatch = getNSigmaPosition(cc,pp); + } + public double getNSigmaPositionMatch() { return nSigmaPositionMatch; } + } + }