View Javadoc
1   package org.hps.recon.utils;
2   
3   import hep.aida.IAnalysisFactory;
4   import hep.aida.IHistogram1D;
5   import hep.aida.IHistogram2D;
6   import hep.aida.IHistogramFactory;
7   import hep.aida.ITree;
8   import hep.aida.ref.rootwriter.RootFileStore;
9   import hep.physics.vec.BasicHep3Vector;
10  import hep.physics.vec.Hep3Vector;
11  
12  import java.io.IOException;
13  import java.util.HashMap;
14  import java.util.Map;
15  
16  import org.hps.recon.tracking.CoordinateTransformations;
17  import org.hps.recon.tracking.TrackUtils;
18  import org.lcsim.event.Cluster;
19  import org.lcsim.event.ReconstructedParticle;
20  import org.lcsim.event.Track;
21  import org.lcsim.event.TrackState;
22  import org.lcsim.event.TrackerHit;
23  import org.lcsim.geometry.FieldMap;
24  
25  /**
26   * Utility used to determine if a track and cluster are matched.
27   *
28   * @author <a href="mailto:moreno1@ucsc.edu">Omar Moreno</a>
29   */
30  public class TrackClusterMatcher {
31  
32      Double beamEnergy;
33  
34      /**
35       * The B field map
36       */
37      FieldMap bFieldMap = null;
38  
39      // Plotting
40      private ITree tree;
41      private IHistogramFactory histogramFactory;
42      private Map<String, IHistogram1D> plots1D;
43      private Map<String, IHistogram2D> plots2D;
44  
45      /**
46       * Flag used to determine if plots are enabled/disabled
47       */
48      boolean enablePlots = false;
49  
50      /**
51       * Flag used to determine whether the analytic or field map extrapolator
52       * should be used.
53       */
54      boolean useAnalyticExtrapolator = false;
55  
56      /**
57       * These cuts are set at +/- 4 sigma extracted from Gaussian fits to the
58       * track-cluster residual distributions. The data used to determine these
59       * limits is a pass 2 test file (t2.6) using run 5772.
60       */
61      private double topClusterTrackMatchDeltaXLow = -14.5; // mm 
62      private double topClusterTrackMatchDeltaXHigh = 23.5; // mm 
63      private double bottomClusterTrackMatchDeltaXLow = -19.5; // mm 
64      private double bottomClusterTrackMatchDeltaXHigh = 16.5; // mm 
65  
66      private double topClusterTrackMatchDeltaYLow = -21.5; // mm 
67      private double topClusterTrackMatchDeltaYHigh = 28; // mm 
68      private double bottomClusterTrackMatchDeltaYLow = -28; // mm 
69      private double bottomClusterTrackMatchDeltaYHigh = 24; // mm 
70  
71      /**
72       * Rafo's parameterization of cluster-seed x/y position residuals as function of energy.
73       * 
74       * Derived using GBL/seed tracks, non-analytic extrapolation, uncorrected cluster positions,
75       * and EngRun2015-Nominal-v4-4-fieldmap detector.
76       * 
77       *  f = p0+e*(p1+e*(p2+e*(p3+e*(p4+e*p5))))
78       */
79      double dyMeanBotElecGBL_noL6_2015[] = {-7.17364, 51.9077, -149.155, 147.207, };
80      double dySigmBotElecGBL_noL6_2015[] = {13.7868, 9.41886, -156, 209.963, };
81      double dyMeanBotPosiGBL_noL6_2015[] = {11.2292, -165.876, 591.592, -601.435, };
82      double dySigmBotPosiGBL_noL6_2015[] = {5.1586, 79.949, -321.238, 313.925, };
83      double dyMeanTopElecGBL_noL6_2015[] = {6.33503, -64.1156, 204.924, -204.87, };
84      double dySigmTopElecGBL_noL6_2015[] = {29.3164, -130.8, 254.59, -178.537, };
85      double dyMeanTopPosiGBL_noL6_2015[] = {0.394555, 43.2088, -222.677, 252.737, };
86      double dySigmTopPosiGBL_noL6_2015[] = {20.4963, -47.4179, 3.31726, 54.2533, };
87      double dxMeanBotElecGBL_noL6_2015[] = {84.2526, -618.986, 1549.52, -1273.73, };
88      double dxSigmBotElecGBL_noL6_2015[] = {43.669, -207.526, 423.221, -317.005, };
89      double dxMeanBotPosiGBL_noL6_2015[] = {-81.5944, 593.329, -1440.75, 1144.2, };
90      double dxSigmBotPosiGBL_noL6_2015[] = {16.9999, 22.0071, -201.033, 214.312, };
91      double dxMeanTopElecGBL_noL6_2015[] = {70.2111, -490.225, 1170.85, -906.215, };
92      double dxSigmTopElecGBL_noL6_2015[] = {23.5848, -22.8375, -100.662, 162.045, };
93      double dxMeanTopPosiGBL_noL6_2015[] = {-106.445, 825.712, -2119.92, 1795.38, };
94      double dxSigmTopPosiGBL_noL6_2015[] = {31.8189, -120.106, 238.918, -212.629, };
95      double dyMeanBotElecGBL_hasL6_2015[] = {4.55098, -47.132, 164.208, -251.225, 138.445, };
96      double dySigmBotElecGBL_hasL6_2015[] = {-1.07979, 69.2823, -280.618, 427.443, -223.653, };
97      double dyMeanBotPosiGBL_hasL6_2015[] = {-6.61989, 45.9481, -134.208, 173.095, -85.3071, };
98      double dySigmBotPosiGBL_hasL6_2015[] = {14.5271, -66.9343, 155.271, -169.818, 71.0514, };
99      double dyMeanTopElecGBL_hasL6_2015[] = {-16.4252, 137.898, -421.075, 567.766, -281.956, };
100     double dySigmTopElecGBL_hasL6_2015[] = {12.6111, -58.7843, 142.594, -162.391, 71.1072, };
101     double dyMeanTopPosiGBL_hasL6_2015[] = {-2.72525, 23.3503, -71.8925, 105.965, -57.5752, };
102     double dySigmTopPosiGBL_hasL6_2015[] = {14.5742, -78.1711, 219.883, -297.48, 154.408, };
103     double dxMeanBotElecGBL_hasL6_2015[] = {-6.23259, 86.3158, -284.914, 379.255, -183.232, };
104     double dxSigmBotElecGBL_hasL6_2015[] = {23.6521, -135.592, 366.388, -461.049, 220.64, };
105     double dxMeanBotPosiGBL_hasL6_2015[] = {-11.6694, 78.7264, -226.947, 282.234, -122.858, };
106     double dxSigmBotPosiGBL_hasL6_2015[] = {19.2137, -86.8682, 183.822, -178.96, 65.566, };
107     double dxMeanTopElecGBL_hasL6_2015[] = {9.33562, -47.4252, 137.437, -169.957, 74.312, };
108     double dxSigmTopElecGBL_hasL6_2015[] = {16.6497, -76.1094, 179.447, -204.136, 89.8847, };
109     double dxMeanTopPosiGBL_hasL6_2015[] = {-10.195, 57.1086, -123.843, 128.667, -45.7468, };
110     double dxSigmTopPosiGBL_hasL6_2015[] = {23.3435, -118.887, 277.13, -296.727, 119.435, };
111     
112     /*edges of the fits*/
113     double pHigh_hasL6_2015 = .8;
114     double pHigh_noL6_2015 = .5;
115     double pLow_hasL6_2015 = .18;
116     double pLow_noL6_2015 = .18;
117     
118     // parameters for 2.3 GeV running.
119     double dyMeanBotElecGBL_noL6_2016[] = {-6.48114, 22.2464, -24.0258, 7.82766, };
120     double dySigmBotElecGBL_noL6_2016[] = {17.9252, -41.3164, 41.3304, -14.7517, };
121     double dyMeanBotPosiGBL_noL6_2016[] = {-11.7136, 34.6864, -32.7093, 10.1458, };
122     double dySigmBotPosiGBL_noL6_2016[] = {26.568, -77.1315, 91.2976, -37.8038, };
123     double dyMeanTopElecGBL_noL6_2016[] = {-7.89099, 32.4447, -44.7542, 20.3456, };
124     double dySigmTopElecGBL_noL6_2016[] = {17.7918, -36.6478, 29.4694, -8.06169, };
125     double dyMeanTopPosiGBL_noL6_2016[] = {-2.58173, 11.3186, -17.3132, 7.42765, };
126     double dySigmTopPosiGBL_noL6_2016[] = {-2.18936, 43.6352, -75.0924, 35.9227, };
127     double dxMeanBotElecGBL_noL6_2016[] = {42.964, -128.548, 140.918, -51.64, };
128     double dxSigmBotElecGBL_noL6_2016[] = {36.8183, -99.2736, 106.351, -39.844, };
129     double dxMeanBotPosiGBL_noL6_2016[] = {-46.6173, 157.474, -187.009, 73.96, };
130     double dxSigmBotPosiGBL_noL6_2016[] = {46.0317, -143.346, 172.54, -70.9532, };
131     double dxMeanTopElecGBL_noL6_2016[] = {30.9052, -90.09, 104.774, -41.15, };
132     double dxSigmTopElecGBL_noL6_2016[] = {8.16952, 29.1376, -77.9399, 44.8599, };
133     double dxMeanTopPosiGBL_noL6_2016[] = {-59.7332, 195.964, -218.42, 82.1346, };
134     double dxSigmTopPosiGBL_noL6_2016[] = {64.1991, -208.484, 247.029, -98.3336, };
135     double dyMeanBotElecGBL_hasL6_2016[] = {-6.28213, 33.3392, -61.5168, 45.574, -11.71, };
136     double dySigmBotElecGBL_hasL6_2016[] = {9.92401, -25.3032, 31.6691, -18.2118, 3.92029, };
137     double dyMeanBotPosiGBL_hasL6_2016[] = {5.01792, -23.2565, 35.5235, -23.1599, 5.40634, };
138     double dySigmBotPosiGBL_hasL6_2016[] = {0.815639, 17.5981, -37.0339, 27.7743, -7.11745, };
139     double dyMeanTopElecGBL_hasL6_2016[] = {3.23873, -23.9294, 51.0193, -40.8843, 11.0352, };
140     double dySigmTopElecGBL_hasL6_2016[] = {9.40385, -21.4277, 24.1184, -12.3961, 2.31535, };
141     double dyMeanTopPosiGBL_hasL6_2016[] = {-7.05313, 26.9298, -38.4908, 24.3693, -5.65434, };
142     double dySigmTopPosiGBL_hasL6_2016[] = {4.16791, 1.32443, -10.1539, 9.019, -2.38522, };
143     double dxMeanBotElecGBL_hasL6_2016[] = {-4.48925, 42.3, -72.9497, 50.2509, -12.4418, };
144     double dxSigmBotElecGBL_hasL6_2016[] = {11.4499, -27.7882, 36.0098, -22.508, 5.50197, };
145     double dxMeanBotPosiGBL_hasL6_2016[] = {-6.64995, 14.7855, -18.5416, 10.4191, -2.00325, };
146     double dxSigmBotPosiGBL_hasL6_2016[] = {7.49727, -6.337, -2.95785, 6.44046, -2.16203, };
147     double dxMeanTopElecGBL_hasL6_2016[] = {-4.53952, 45.9687, -80.7531, 58.3937, -15.0482, };
148     double dxSigmTopElecGBL_hasL6_2016[] = {11.0752, -25.3525, 30.0412, -16.6484, 3.52064, };
149     double dxMeanTopPosiGBL_hasL6_2016[] = {-13.2928, 37.9236, -45.849, 26.8323, -5.80805, };
150     double dxSigmTopPosiGBL_hasL6_2016[] = {9.36327, -16.2788, 14.5396, -5.60705, 0.705979, };
151     
152     /*edges of the fits*/
153     double pHigh_hasL6_2016 = 1.6;
154     double pHigh_noL6_2016 = 1.0;
155     double pLow_hasL6_2016 = .44;
156     double pLow_noL6_2016 = .44;
157     
158     /**
159      * Z position to start extrapolation from
160      */
161     double extStartPos = 700; // mm
162 
163     /**
164      * The extrapolation step size
165      */
166     double stepSize = 5.; // mm
167 
168     private boolean snapToEdge = true;
169     
170     public void setSnapToEdge(boolean val){
171         this.snapToEdge = val;
172     }
173     
174 
175     /**
176      * Constant denoting the index of the {@link TrackState} at the Ecal
177      */
178     private static final int ECAL_TRACK_STATE_INDEX = 1;
179 
180     /**
181      * Constructor
182      */
183     public TrackClusterMatcher() {
184     }
185 
186     /**
187      * Enable/disable booking, filling of Ecal cluster and extrapolated track 
188      * position plots.
189      * 
190      * @param enablePlots true to enable, false to disable
191      */
192     public void enablePlots(boolean enablePlots) {
193         this.enablePlots = enablePlots;
194         if (enablePlots == true) {
195             this.bookHistograms();
196         }
197     }
198 
199     /**
200      * Set the 3D field map to be used by the extrapolator.
201      *
202      * @param bFieldMap The {@link FieldMap} object containing a mapping to the
203      * 3D field map.
204      */
205     public void setBFieldMap(FieldMap bFieldMap) {
206         this.bFieldMap = bFieldMap;
207     }
208 
209     /**
210      * Use the analytic track extrapolator i.e. the no fringe extrapolator. The
211      * field map extrapolator is used by default.
212      *
213      * @param useAnalyticExtrapolator Set to true to use the analytic
214      * extrapolator, false otherwise.
215      */
216     public void setUseAnalyticExtrapolator(boolean useAnalyticExtrapolator) {
217         this.useAnalyticExtrapolator = useAnalyticExtrapolator;
218     }
219 
220     /**
221      * Set the window in which the x residual of the extrapolated bottom track
222      * position at the Ecal and the Ecal cluster position must be within to be
223      * considered a 'good match'
224      *
225      * @param xLow
226      * @param xHigh
227      */
228     public void setBottomClusterTrackDxCuts(double xLow, double xHigh) {
229         this.topClusterTrackMatchDeltaXLow = xLow;
230         this.topClusterTrackMatchDeltaXHigh = xHigh;
231     }
232 
233     /**
234      * Set the window in which the y residual of the extrapolated bottom track
235      * position at the Ecal and the Ecal cluster position must be within to be
236      * considered a 'good match'
237      *
238      * @param yLow
239      * @param yHigh
240      */
241     public void setBottomClusterTrackDyCuts(double yLow, double yHigh) {
242         this.topClusterTrackMatchDeltaYLow = yLow;
243         this.topClusterTrackMatchDeltaYHigh = yHigh;
244     }
245 
246     /**
247      * Set the window in which the x residual of the extrapolated top track
248      * position at the Ecal and the Ecal cluster position must be within to be
249      * considered a 'good match'
250      *
251      * @param xLow
252      * @param xHigh
253      */
254     public void setTopClusterTrackDxCuts(double xLow, double xHigh) {
255         this.topClusterTrackMatchDeltaXLow = xLow;
256         this.topClusterTrackMatchDeltaXHigh = xHigh;
257     }
258 
259     /**
260      * Set the window in which the y residual of the extrapolated top track
261      * position at the Ecal and the Ecal cluster position must be within to be
262      * considered a 'good match'
263      *
264      * @param yLow
265      * @param yHigh
266      */
267     public void setTopClusterTrackDyCuts(double yLow, double yHigh) {
268         this.topClusterTrackMatchDeltaYLow = yLow;
269         this.topClusterTrackMatchDeltaYHigh = yHigh;
270     }
271 
272     /**
273      * Get distance between track and cluster.
274      * 
275      * @param cluster
276      * @param track
277      * @return distance between cluster and track
278      */
279     public double getDistance(Cluster cluster,Track track) {
280         
281         // Get the cluster position
282         Hep3Vector cPos = new BasicHep3Vector(cluster.getPosition());
283         
284         // Extrapolate the track to the Ecal cluster position
285         Hep3Vector tPos = null;
286         if (this.useAnalyticExtrapolator) {
287             tPos = TrackUtils.extrapolateTrack(track, cPos.z());
288         } else {
289             TrackState trackStateAtEcal = TrackUtils.getTrackStateAtECal(track);
290             tPos = new BasicHep3Vector(trackStateAtEcal.getReferencePoint());
291             tPos = CoordinateTransformations.transformVectorToDetector(tPos);
292         }
293        
294         return Math.sqrt(Math.pow(cPos.x()-tPos.x(),2)+Math.pow(cPos.y()-tPos.y(),2));
295     }
296     
297     /**
298      * Calculate #sigma between cluster-track x/y position at calorimeter.
299      *
300      * Based on Rafo's parameterizations.  Requires non-analytic extrapolation
301      * and uncorrected cluster positions.
302      * 
303      * @param cluster = position-uncorrected cluster
304      * @param particle recon particle with tracks
305      *
306      * @return #sigma between cluster and track positions
307      */
308     public double getNSigmaPosition(Cluster cluster, ReconstructedParticle particle) {
309         if (particle.getTracks().size()<1) return Double.MAX_VALUE;
310         Track track=particle.getTracks().get(0);
311         return getNSigmaPosition(cluster, track, new BasicHep3Vector(track.getTrackStates().get(0).getMomentum()).magnitude());
312     }
313     public double getNSigmaPosition(Cluster cluster, Track track, double p){
314         
315         
316         if (this.useAnalyticExtrapolator)
317             throw new RuntimeException("This is to be used with non-analytic extrapolator only.");
318 
319         // Get the cluster position:
320         Hep3Vector cPos = new BasicHep3Vector(cluster.getPosition());
321 
322         // whether track is in top half of detector:
323         final boolean isTopTrack = track.getTrackStates().get(0).getTanLambda() > 0;
324 
325         // ignore if track and cluster in different halves:
326         if (isTopTrack != cPos.y()>0) return Double.MAX_VALUE;
327 
328         // Get the extrapolated track position at the calorimeter:
329         TrackState trackStateAtEcal = TrackUtils.getTrackStateAtECal(track);
330         if(trackStateAtEcal == null){
331             // Track never made it to the ECAL, so it curled before doing this and probably extrapolateTrackUsingFieldMap aborted.
332             return Double.MAX_VALUE;
333         }
334         Hep3Vector tPos = new BasicHep3Vector(trackStateAtEcal.getReferencePoint());
335         tPos = CoordinateTransformations.transformVectorToDetector(tPos);
336 
337         // whether it's a GBL track:
338         final boolean isGBL = track.getType() >= 32;
339        
340         boolean hasL6 = false;
341         for(TrackerHit hit : track.getTrackerHits()){
342             if(TrackUtils.getLayer(hit) == 11)
343                 hasL6 = true;
344         }
345         
346         // choose which parameterization of mean and sigma to use:
347         double dxMean[],dyMean[],dxSigm[],dySigm[];
348         int charge = TrackUtils.getCharge(track);
349         
350         boolean use1pt05parameters = false, use2pt3parameters;
351         
352         //Why is the following done like that? Causes problems when trying to just run the HpsReconParticleDriver - Matt Solt
353         if(beamEnergy < 2){
354             use1pt05parameters = true;
355             use2pt3parameters = false;
356         } else { // if we do 4.4 or 6.6 GeV or other beam energies, use the 2.3 GeV values,
357             // until we have new parameters for that beam energy.  
358             use1pt05parameters = false;
359             use2pt3parameters = true;
360         }
361             
362         
363         if(use1pt05parameters){
364             if (charge>0) {
365                 if (isTopTrack) {
366                     dxMean = !hasL6 ? dxMeanTopPosiGBL_noL6_2015 : dxMeanTopPosiGBL_hasL6_2015;
367                     dxSigm = !hasL6 ? dxSigmTopPosiGBL_noL6_2015 : dxSigmTopPosiGBL_hasL6_2015;
368                     dyMean = !hasL6 ? dyMeanTopPosiGBL_noL6_2015 : dyMeanTopPosiGBL_hasL6_2015;
369                     dySigm = !hasL6 ? dySigmTopPosiGBL_noL6_2015 : dySigmTopPosiGBL_hasL6_2015;
370                 }
371                 else {
372                     dxMean = !hasL6 ? dxMeanBotPosiGBL_noL6_2015 : dxMeanBotPosiGBL_hasL6_2015;
373                     dxSigm = !hasL6 ? dxSigmBotPosiGBL_noL6_2015 : dxSigmBotPosiGBL_hasL6_2015;
374                     dyMean = !hasL6 ? dyMeanBotPosiGBL_noL6_2015 : dyMeanBotPosiGBL_hasL6_2015;
375                     dySigm = !hasL6 ? dySigmBotPosiGBL_noL6_2015 : dySigmBotPosiGBL_hasL6_2015;
376                 }
377             }
378             else if (charge<0) {
379                 if (isTopTrack) {
380                     dxMean = !hasL6 ? dxMeanTopElecGBL_noL6_2015 : dxMeanTopElecGBL_hasL6_2015;
381                     dxSigm = !hasL6 ? dxSigmTopElecGBL_noL6_2015 : dxSigmTopElecGBL_hasL6_2015;
382                     dyMean = !hasL6 ? dyMeanTopElecGBL_noL6_2015 : dyMeanTopElecGBL_hasL6_2015;
383                     dySigm = !hasL6 ? dySigmTopElecGBL_noL6_2015 : dySigmTopElecGBL_hasL6_2015;
384                 }
385                 else {
386                     dxMean = !hasL6 ? dxMeanBotElecGBL_noL6_2015 : dxMeanBotElecGBL_hasL6_2015;
387                     dxSigm = !hasL6 ? dxSigmBotElecGBL_noL6_2015 : dxSigmBotElecGBL_hasL6_2015;
388                     dyMean = !hasL6 ? dyMeanBotElecGBL_noL6_2015 : dyMeanBotElecGBL_hasL6_2015;
389                     dySigm = !hasL6 ? dySigmBotElecGBL_noL6_2015 : dySigmBotElecGBL_hasL6_2015;
390                 }
391             }
392             else return Double.MAX_VALUE;
393         }
394         else if (use2pt3parameters){
395             
396             if (charge>0) {
397                 if (isTopTrack) {
398                     dxMean = !hasL6 ? dxMeanTopPosiGBL_noL6_2016 : dxMeanTopPosiGBL_hasL6_2016;
399                     dxSigm = !hasL6 ? dxSigmTopPosiGBL_noL6_2016 : dxSigmTopPosiGBL_hasL6_2016;
400                     dyMean = !hasL6 ? dyMeanTopPosiGBL_noL6_2016 : dyMeanTopPosiGBL_hasL6_2016;
401                     dySigm = !hasL6 ? dySigmTopPosiGBL_noL6_2016 : dySigmTopPosiGBL_hasL6_2016;
402                 }
403                 else {
404                     dxMean = !hasL6 ? dxMeanBotPosiGBL_noL6_2016 : dxMeanBotPosiGBL_hasL6_2016;
405                     dxSigm = !hasL6 ? dxSigmBotPosiGBL_noL6_2016 : dxSigmBotPosiGBL_hasL6_2016;
406                     dyMean = !hasL6 ? dyMeanBotPosiGBL_noL6_2016 : dyMeanBotPosiGBL_hasL6_2016;
407                     dySigm = !hasL6 ? dySigmBotPosiGBL_noL6_2016 : dySigmBotPosiGBL_hasL6_2016;
408                 }
409             }
410             else if (charge<0) {
411                 if (isTopTrack) {
412                     dxMean = !hasL6 ? dxMeanTopElecGBL_noL6_2016 : dxMeanTopElecGBL_hasL6_2016;
413                     dxSigm = !hasL6 ? dxSigmTopElecGBL_noL6_2016 : dxSigmTopElecGBL_hasL6_2016;
414                     dyMean = !hasL6 ? dyMeanTopElecGBL_noL6_2016 : dyMeanTopElecGBL_hasL6_2016;
415                     dySigm = !hasL6 ? dySigmTopElecGBL_noL6_2016 : dySigmTopElecGBL_hasL6_2016;
416                 }
417                 else {
418                     dxMean = !hasL6 ? dxMeanBotElecGBL_noL6_2016 : dxMeanBotElecGBL_hasL6_2016;
419                     dxSigm = !hasL6 ? dxSigmBotElecGBL_noL6_2016 : dxSigmBotElecGBL_hasL6_2016;
420                     dyMean = !hasL6 ? dyMeanBotElecGBL_noL6_2016 : dyMeanBotElecGBL_hasL6_2016;
421                     dySigm = !hasL6 ? dySigmBotElecGBL_noL6_2016 : dySigmBotElecGBL_hasL6_2016;
422                 }
423             }
424             else return Double.MAX_VALUE;
425         }
426         else 
427             return Double.MAX_VALUE; //this line is never executed.   
428         
429 
430         // Beyond the edges of the fits in momentum, assume that the parameters are constant:
431         if(use1pt05parameters){
432             if (p > pHigh_hasL6_2015 && hasL6) 
433                 p = pHigh_hasL6_2015;
434             else if (p > pHigh_noL6_2015 && !hasL6) 
435                 p= pHigh_noL6_2015;
436             else if (p < pLow_hasL6_2015 && hasL6)
437                 p = pLow_hasL6_2015;
438             else if (p < pLow_noL6_2015 && !hasL6)
439                 p = pLow_noL6_2015;
440         }
441         if(use2pt3parameters){
442             if (p > pHigh_hasL6_2016 && hasL6) 
443                 p = pHigh_hasL6_2016;
444             else if (p > pHigh_noL6_2016 && !hasL6) 
445                 p= pHigh_noL6_2016;
446             else if (p < pLow_hasL6_2016 && hasL6)
447                 p = pLow_hasL6_2016;
448             else if (p < pLow_noL6_2016 && !hasL6)
449                 p = pLow_noL6_2016;
450         }
451         // calculate measured mean and sigma of deltaX and deltaY for this energy:
452         double aDxMean=0,aDxSigm=0,aDyMean=0,aDySigm=0;
453         for (int ii=dxMean.length-1; ii>=0; ii--) aDxMean = dxMean[ii] + p*aDxMean;
454         for (int ii=dxSigm.length-1; ii>=0; ii--) aDxSigm = dxSigm[ii] + p*aDxSigm;
455         for (int ii=dyMean.length-1; ii>=0; ii--) aDyMean = dyMean[ii] + p*aDyMean;
456         for (int ii=dySigm.length-1; ii>=0; ii--) aDySigm = dySigm[ii] + p*aDySigm;
457 
458       //if the track's extrapolated position is within 1/2 a crystal width of the edge of 
459         // the ecal edge, then move it to be 1/2 a crystal from the edge in y.  
460        
461         Hep3Vector originalTPos = tPos;
462         
463         if(snapToEdge )
464             tPos= snapper.snapToEdge(tPos,cluster);
465         
466         
467         // calculate nSigma between track and cluster:
468         final double nSigmaX = (cPos.x() - tPos.x() - aDxMean) / aDxSigm;
469         final double nSigmaY = (cPos.y() - tPos.y() - aDyMean) / aDySigm;
470         
471         double nSigma = Math.sqrt(nSigmaX*nSigmaX + nSigmaY*nSigmaY);
472         
473         if(debug && Math.abs(cPos.x()-tPos.x())<50 &&  Math.abs(cPos.y()-tPos.y())<50){
474             System.out.println("TC MATCH RESULTS:");
475             System.out.println("isTop:  " + isTopTrack);
476             System.out.println("charge: " + charge);
477             System.out.println("hasL6:  " + hasL6);
478             System.out.println("p: " + p);
479             System.out.println("cx: " + cPos.x());
480             System.out.println("cy: " + cPos.y());
481             System.out.println("tx: " + originalTPos.x());
482             System.out.println("ty: " + originalTPos.y());
483             
484             System.out.println("nSigmaX: " + nSigmaX);
485             System.out.println("nSigmaY: " + nSigmaY);
486             System.out.println("nSigma: " + nSigma);
487         }
488         
489         return nSigma;
490         //return Math.sqrt( 1 / ( 1/nSigmaX/nSigmaX + 1/nSigmaY/nSigmaY ) );
491     }
492 
493     boolean debug = false;
494     
495     SnapToEdge snapper = new SnapToEdge();
496 
497     /**
498      * Determine if a track is matched to a cluster. Currently, this is
499      * determined by checking that the track and cluster are within the same
500      * detector volume of each other and that the extrapolated track position is
501      * within some defined distance of the cluster.
502      *
503      * @param cluster : The Ecal cluster to check
504      * @param track : The SVT track to check
505      * @return true if all cuts are pased, false otherwise.
506      */
507     public boolean isMatch(Cluster cluster, Track track) {
508 
509         // Check that the track and cluster are in the same detector volume.
510         // If not, there is no way they can be a match.
511         if ((track.getTrackStates().get(0).getTanLambda() > 0 && cluster.getPosition()[1] < 0)
512                 || (track.getTrackStates().get(0).getTanLambda() < 0 && cluster.getPosition()[1] > 0)) {
513             return false;
514         }
515 
516         // Get the cluster position
517         Hep3Vector clusterPosition = new BasicHep3Vector(cluster.getPosition());
518         //System.out.println("Cluster Position: " + clusterPosition.toString());
519 
520         // Extrapolate the track to the Ecal cluster position
521         Hep3Vector trackPosAtEcal = null;
522         if (this.useAnalyticExtrapolator) {
523             //System.out.println("Using analytic field extrapolator."); 
524             trackPosAtEcal = TrackUtils.extrapolateTrack(track, clusterPosition.z());
525         } else {
526             //System.out.println("Using field map extrapolator"); 
527             TrackState trackStateAtEcal = TrackUtils.getTrackStateAtECal(track);
528             trackPosAtEcal = new BasicHep3Vector(trackStateAtEcal.getReferencePoint());
529             trackPosAtEcal = CoordinateTransformations.transformVectorToDetector(trackPosAtEcal);
530         }
531         //System.out.println("Track position at Ecal: " + trackPosAtEcal.toString());
532 
533         // Calculate the difference between the cluster position at the Ecal and
534         // the track in both the x and y directions
535         double deltaX = clusterPosition.x() - trackPosAtEcal.x();
536         double deltaY = clusterPosition.y() - trackPosAtEcal.y();
537 
538         //System.out.println("delta X: " + deltaX);
539         //System.out.println("delta Y: " + deltaY);
540         if (enablePlots) {
541             if (track.getTrackStates().get(0).getTanLambda() > 0) {
542 
543                 plots1D.get("Ecal cluster x - track x @ Ecal - top - all").fill(deltaX);
544                 plots2D.get("Ecal cluster x v track x @ Ecal - top - all").fill(clusterPosition.x(),
545                         trackPosAtEcal.x());
546                 plots1D.get("Ecal cluster y - track y @ Ecal - top - all").fill(deltaY);
547                 plots2D.get("Ecal cluster y v track y @ Ecal - top - all").fill(clusterPosition.y(),
548                         trackPosAtEcal.y());
549 
550             } else if (track.getTrackStates().get(0).getTanLambda() < 0) {
551 
552                 plots1D.get("Ecal cluster x - track x @ Ecal - bottom - all").fill(deltaX);
553                 plots2D.get("Ecal cluster x v track x @ Ecal - bottom - all").fill(clusterPosition.x(),
554                         trackPosAtEcal.x());
555                 plots1D.get("Ecal cluster y - track y @ Ecal - bottom - all").fill(deltaY);
556                 plots2D.get("Ecal cluster y v track y @ Ecal - bottom - all").fill(clusterPosition.y(),
557                         trackPosAtEcal.y());
558             }
559         }
560 
561         // Check that dx and dy between the extrapolated track and cluster 
562         // positions is reasonable.  Different requirements are imposed on 
563         // top and bottom tracks in order to account for offsets.
564         if ((track.getTrackStates().get(0).getTanLambda() > 0 && (deltaX > topClusterTrackMatchDeltaXHigh
565                 || deltaX < topClusterTrackMatchDeltaXLow))
566                 || (track.getTrackStates().get(0).getTanLambda() < 0 && (deltaX > bottomClusterTrackMatchDeltaXHigh
567                 || deltaX < bottomClusterTrackMatchDeltaXLow))) {
568             return false;
569         }
570 
571         if ((track.getTrackStates().get(0).getTanLambda() > 0 && (deltaY > topClusterTrackMatchDeltaYHigh
572                 || deltaY < topClusterTrackMatchDeltaYLow))
573                 || (track.getTrackStates().get(0).getTanLambda() < 0 && (deltaY > bottomClusterTrackMatchDeltaYHigh
574                 || deltaY < bottomClusterTrackMatchDeltaYLow))) {
575             return false;
576         }
577 
578         if (enablePlots) {
579             if (track.getTrackStates().get(0).getTanLambda() > 0) {
580 
581                 plots1D.get("Ecal cluster x - track x @ Ecal - top - matched").fill(deltaX);
582                 plots2D.get("Ecal cluster x v track x @ Ecal - top - matched").fill(clusterPosition.x(),
583                         trackPosAtEcal.x());
584                 plots1D.get("Ecal cluster y - track y @ Ecal - top - matched").fill(deltaY);
585                 plots2D.get("Ecal cluster y v track y @ Ecal - top - matched").fill(clusterPosition.y(),
586                         trackPosAtEcal.y());
587 
588             } else if (track.getTrackStates().get(0).getTanLambda() < 0) {
589 
590                 plots1D.get("Ecal cluster x - track x @ Ecal - bottom - matched").fill(deltaX);
591                 plots2D.get("Ecal cluster x v track x @ Ecal - bottom - matched").fill(clusterPosition.x(),
592                         trackPosAtEcal.x());
593                 plots1D.get("Ecal cluster y - track y @ Ecal - bottom - matched").fill(deltaY);
594                 plots2D.get("Ecal cluster y v track y @ Ecal - bottom - matched").fill(clusterPosition.y(),
595                         trackPosAtEcal.y());
596             }
597         }
598 
599         // If all cuts are pased, return true.
600         return true;
601     }
602 
603     /**
604      * Book histograms of Ecal cluster x/y vs extrapolated track x/y
605      */
606     private void bookHistograms() {
607 
608         plots1D = new HashMap<String, IHistogram1D>();
609         plots2D = new HashMap<String, IHistogram2D>();
610 
611         tree = IAnalysisFactory.create().createTreeFactory().create();
612         histogramFactory = IAnalysisFactory.create().createHistogramFactory(tree);
613 
614         //--- All tracks and clusters ---//
615         //-------------------------------//
616         //--- Top ---//
617         plots1D.put("Ecal cluster x - track x @ Ecal - top - all",
618                 histogramFactory.createHistogram1D("Ecal cluster x - track x @ Ecal - top - all", 200, -200, 200));
619 
620         plots2D.put("Ecal cluster x v track x @ Ecal - top - all",
621                 histogramFactory.createHistogram2D("Ecal cluster x v track x @ Ecal - top - all", 200, -200, 200, 200, -200, 200));
622 
623         plots1D.put("Ecal cluster y - track y @ Ecal - top - all",
624                 histogramFactory.createHistogram1D("Ecal cluster y - track y @ Ecal - top - all", 100, -100, 100));
625 
626         plots2D.put("Ecal cluster y v track y @ Ecal - top - all",
627                 histogramFactory.createHistogram2D("Ecal cluster y v track  @ Ecal - top - all", 100, -100, 100, 100, -100, 100));
628 
629         //--- Bottom ---//
630         plots1D.put("Ecal cluster x - track x @ Ecal - bottom - all",
631                 histogramFactory.createHistogram1D("Ecal cluster x - track x @ Ecal - bottom - all", 200, -200, 200));
632 
633         plots2D.put("Ecal cluster x v track x @ Ecal - bottom - all",
634                 histogramFactory.createHistogram2D("Ecal cluster x v track x @ Ecal - bottom - all", 200, -200, 200, 200, -200, 200));
635 
636         plots1D.put("Ecal cluster y - track y @ Ecal - bottom - all",
637                 histogramFactory.createHistogram1D("Ecal cluster y - track y @ Ecal - bottom - all", 100, -100, 100));
638 
639         plots2D.put("Ecal cluster y v track y @ Ecal - bottom - all",
640                 histogramFactory.createHistogram2D("Ecal cluster y v track  @ Ecal - bottom - all", 100, -100, 100, 100, -100, 100));
641 
642         //--- Matched tracks ---//
643         //----------------------//
644         //--- Top ---//
645         plots1D.put("Ecal cluster x - track x @ Ecal - top - matched",
646                 histogramFactory.createHistogram1D("Ecal cluster x - track x @ Ecal - top - matched", 200, -200, 200));
647 
648         plots2D.put("Ecal cluster x v track x @ Ecal - top - matched",
649                 histogramFactory.createHistogram2D("Ecal cluster x v track x @ Ecal - top - matched", 200, -200, 200, 200, -200, 200));
650 
651         plots1D.put("Ecal cluster y - track y @ Ecal - top - matched",
652                 histogramFactory.createHistogram1D("Ecal cluster y - track y @ Ecal - top - matched", 100, -100, 100));
653 
654         plots2D.put("Ecal cluster y v track y @ Ecal - top - matched",
655                 histogramFactory.createHistogram2D("Ecal cluster y v track  @ Ecal - top - matched", 100, -100, 100, 100, -100, 100));
656 
657         //--- Bottom ---//
658         plots1D.put("Ecal cluster x - track x @ Ecal - bottom - matched",
659                 histogramFactory.createHistogram1D("Ecal cluster x - track x @ Ecal - bottom - matched", 200, -200, 200));
660 
661         plots2D.put("Ecal cluster x v track x @ Ecal - bottom - matched",
662                 histogramFactory.createHistogram2D("Ecal cluster x v track x @ Ecal - bottom - matched", 200, -200, 200, 200, -200, 200));
663 
664         plots1D.put("Ecal cluster y - track y @ Ecal - bottom - matched",
665                 histogramFactory.createHistogram1D("Ecal cluster y - track y @ Ecal - bottom - matched", 100, -100, 100));
666 
667         plots2D.put("Ecal cluster y v track y @ Ecal - bottom - matched",
668                 histogramFactory.createHistogram2D("Ecal cluster y v track  @ Ecal - bottom - matched", 100, -100, 100, 100, -100, 100));
669 
670     }
671 
672     /**
673      * Save the histograms to a ROO file
674      */
675     public void saveHistograms() {
676 
677         String rootFile = "track_cluster_matching_plots.root";
678         RootFileStore store = new RootFileStore(rootFile);
679         try {
680             store.open();
681             store.add(tree);
682             store.close();
683         } catch (IOException e) {
684             e.printStackTrace();
685         }
686     }
687 
688     /**
689      * Class to store track-cluster matching qualities.
690      */
691     public class TrackClusterMatch {
692         private double nSigmaPositionMatch=Double.MAX_VALUE;
693         public TrackClusterMatch(ReconstructedParticle pp, Cluster cc) {
694             nSigmaPositionMatch = getNSigmaPosition(cc,pp);
695         }
696         public double getNSigmaPositionMatch() { return nSigmaPositionMatch; }
697     }
698 
699     public void setBeamEnergy(double beamEnergy) {
700         this.beamEnergy = beamEnergy;
701     }
702     
703 }