View Javadoc

1   package org.lcsim.recon.cat;
2   
3   import java.util.*;
4   import org.lcsim.recon.cat.util.Const;
5   import org.lcsim.event.*;
6   import org.lcsim.util.Driver;
7   import org.lcsim.geometry.IDDecoder;
8   import org.lcsim.util.aida.AIDA;
9   
10  /**
11   * Calorimeter-assisted tracking algorithm for the SiD.
12   * This processor finds track with outside-to-inside pattern recognition.
13   * Starting from Ecal clusters and their pointing direction, hits are
14   * associated to a track.
15   * <p>
16   * See ExampleDriver.java on how to use it<br>
17   * <p>
18   * This org.lcsim version is based on a previous hep.lcd version.
19   * known dependencies on org.lcsim: <br>
20   * a few parameters are tuned specifically to SiD in org.lcsim, lines with
21   * such dependencies are labeled with "FixMe".
22   * <p>
23   *
24   * @see MipStub
25   * @see GarfieldTrack
26   * @see GarfieldHit
27   *
28   * @author  E. von Toerne
29   * @author  D. Onoprienko
30   * @version $Id: GarfieldTrackFinder.java,v 1.2 2007/12/08 02:39:11 jeremy Exp $
31   */
32  final public class GarfieldTrackFinder extends Driver{
33    
34    // -- Named constants :  -----------------------------------------------------
35    
36    static final int APPEND = 0;
37    static final int OVERWRITE = 1;
38    static final int NONE = 2;
39    
40    static final int YES = 1;
41    static final int NO = 0;
42    
43    // -- Private Data :  --------------------------------------------------------
44    
45    private List<GarfieldHit> listOfHits; // contains list of GarfieldHits of the event
46    private int listOfHitsLowestLayer, listOfHitsHighestLayer;
47    private List<GarfieldHit> selectedHits; //contains selected hits for attachment to track
48    
49    private GarfieldHitConverter hitConverter;
50    
51    // -- Cut values and other finder parameters :  ------------------------------
52    
53    static double k_cm = 10.;
54    private int garfieldnTRKHitMin = 3; // minimum number of hits on a track
55    private boolean refitDuringPatternReco = true;
56    private double garfieldChi2ContribCut = 50.; // max. Chi^2 contrib. of a hit
57    private double garfieldChi2Cut = 100.; // max. Chi^2/Hit value
58    private double garfieldChi2Cut3Ndf=50.;
59    private double garfieldKappaCut3Ndf=1./(50. * k_cm);
60    private double scaleSearchRegion = 3.; // scale search region by this factor if first attempt fails
61    private double maxDistanceFirstHitToBase = 40.*k_cm;
62    private int nNewHitsMax=5; // maximum number of new hits
63    private int nFitIterations=5; // precision of Chi2-fit
64    
65    // Finder modes. See setMode() for details.
66    
67    private int modeModifyTracklist = APPEND;
68    private boolean modeUseVXD = true;
69    private boolean modeBestTrackOnly = true;
70    private boolean modeFromOutsideIn = true;
71    private boolean modeUseAllMipStubs = true;
72    private int pickEventNo=-1;
73    private int pickMipStubNo=-1;
74    private double pickMipStubAngle=-100000.;
75    
76    /**
77     * Level of verbosity: 0 no output, 1 some, 3 lot, 5 lots and lots
78     */
79    private int debugLevel = 0;
80    
81    
82    // -- Some variables to store intermediate results :  ------------------------
83    
84    private int issuedTrackIDValue; // last issued trackID
85    private double minDistanceInHits;
86    private GarfieldHit bestHit;
87    private int bestTrackID;
88    private double bestGrade;
89    static int maxTrkNmbr=10000;
90    static double bfield = 5.;
91    //private ReconstructedTrack[] defTrks = new ReconstructedTrack[maxTrkNmbr];
92    
93    // -- Constructors :  --------------------------------------------------------
94    
95    /**
96     * Construct <code>GarfieldTrackFinder</code> with default parameters
97     * and no debugging output requested.
98     */
99    public GarfieldTrackFinder() {this(0);}
100   
101   /**
102    * Construct <code>GarfieldTrackFinder</code> with default parameters.
103    *
104    * @param debugLevel   The higher the <code>debugLeve</code>, the more
105    *                     debugging output and test histograms are produced.
106    */
107   public GarfieldTrackFinder(int debugLevel) {
108     selectedHits = new ArrayList<GarfieldHit>();
109     this.debugLevel = debugLevel;
110     minDistanceInHits=0.;
111     issuedTrackIDValue=0;
112   }
113   
114   // -- Setters :  -------------------------------------------------------------
115   
116   /**
117    * Set desired amount of debugging output and test histograms,
118    * =0 no output, > 0 debug output
119    */
120   public void setDebugLevel(int i){ debugLevel = i;}
121   
122   /**
123    * Set track finder's mode of operation.<p>
124    *
125    * @param  category  Mode category (what to set)
126    * @param      mode  Requested mode
127    *
128    * List of categories and correspondin mode choices
129    * (default mode is listed first) :
130    * <P>
131    * "Modify Event TrackList"
132    * <UL>
133    * <LI>APPEND :
134    *      Append found tracks to the event's tracklist.
135    * <LI>OVERWRITE :
136    *      Replace the event's tracklist with the list of found tracks.
137    * <LI>NONE :
138    *      Do not modify event's tracklist.
139    * <P>
140    * "Use VXD Hits"
141    * <UL>
142    * <LI>YES :
143    *      Extrapolate Garfield Tracks into VXD if qualifying hits are found there.
144    * <LI>NO :
145    *      Do not use VXD hits.
146    * <P>
147    * "Best Track Only"
148    * <UL>
149    * <LI>YES :
150    *      Accept only one track per MipStub.
151    * <LI>NO :
152    *      Accept all tracks that pass the cuts.
153    * <P>
154    * "From Outside In"
155    * <UL>
156    * <LI>YES :
157    *      Add hits to the track starting from outer layers and moving in.
158    * <LI>NO :
159    *      Add hits to the track starting with inner layers and moving out.
160    * </UL>
161    */
162   public void setMode(String category, int mode) {
163     
164     if (category.equalsIgnoreCase("Modify Event TrackList")) {
165       switch (mode) {
166         case APPEND :
167           modeModifyTracklist = APPEND; break;
168         case OVERWRITE :
169           modeModifyTracklist = OVERWRITE; break;
170         case NONE :
171           modeModifyTracklist = NONE; break;
172         default :
173           throw new IllegalArgumentException("Garfield track finder : Unknown mode");
174       }
175       
176     } else if (category.equalsIgnoreCase("Use VXD Hits")) {
177       modeUseVXD = (mode == YES);
178       
179     } else if (category.equalsIgnoreCase("Best Track Only")) {
180       modeBestTrackOnly = (mode == YES);
181       
182     } else if (category.equalsIgnoreCase("From Outside In")) {
183       modeFromOutsideIn = (mode == YES);
184       
185     } else if (category.equalsIgnoreCase("Refit During PatternReco")) {
186       refitDuringPatternReco = (mode == YES);
187       
188     } else if (category.equalsIgnoreCase("Use All MipStubs")) {
189       modeUseAllMipStubs = (mode == YES);
190       
191     } else {
192       throw new IllegalArgumentException("Garfield track finder : Unknown mode category");
193     }
194   }
195   
196   
197   public void setNNewHitsMax(int i){ nNewHitsMax= i;};
198   public void setNTrkHitMin(int i){ garfieldnTRKHitMin = i;};
199   public void setNFitIterations(int i){ nFitIterations=i;}
200   public void setChi2ContribCut(double d){  garfieldChi2ContribCut = d;}
201   public void setChi2Cut(double d){  garfieldChi2Cut = d;}
202   public void setScaleSearchRegion(double d){ scaleSearchRegion=d;}
203   /** allows processing of a single event. */
204   public void setPickEventNo(int i){ pickEventNo=i;}
205   /** allows processing of a single MipStub. */
206   public void setPickMipStubNo(int i){ pickMipStubNo=i;}
207   /** allows processing of a phi-region. */
208   public void setPickMipStubAngle(double a){ pickMipStubAngle=a;}
209   
210   
211   // -- Implementing AbstractProcessor :  --------------------------------------
212   
213   /**
214    * Process event (called by the framework).
215    **/
216   public void process(EventHeader event) {
217     
218     // Array to hold found tracks :
219     
220     issuedTrackIDValue=0;
221     ArrayList<GarfieldTrack> listGarfieldTracks = new ArrayList<GarfieldTrack>();
222     
223     // Debugging: process only specific event if requested
224     
225     int eventNumber = event.getEventNumber();
226     if (debugLevel>=1) System.out.println("GarfieldTrackFinder ev="+eventNumber);
227     if (pickEventNo>=0 && pickEventNo!=eventNumber) {
228       event.put("GarfieldTracks", listGarfieldTracks);
229       return;
230     }
231     
232     // Prepare tracker hit and MipStub lists
233     
234     List<MipStub> listMipStub;
235     if (modeUseAllMipStubs){
236       listMipStub = event.get(MipStub.class,"GarfieldMipStubs");
237     } else {
238       listMipStub = event.get(MipStub.class,"UnassociatedGarfieldMipStubs");
239     }
240     
241     listOfHits = event.get(GarfieldHit.class, "GarfieldHits");
242     if (!listOfHits.isEmpty()) {
243       listOfHitsLowestLayer = listOfHits.get(listOfHits.size()-1).getLayer();
244       listOfHitsHighestLayer = listOfHits.get(0).getLayer();
245       if (debugLevel>=2){
246         System.out.println("GarfieldTrackFinder hit lowest/highest layer " 
247                 + listOfHitsLowestLayer + " " + listOfHitsHighestLayer);
248         System.out.println("GarfieldTrackFinder barrelLayerVXD "+Const.det().VXD_BARREL.nLayers() + 
249                            " tracker="+ Const.det().TRACKER_BARREL.nLayers() + 
250                            " tracker endcap="+Const.det().TRACKER_ENDCAP.nLayers());
251         
252         for (GarfieldHit hh : listOfHits) { hh.debug();}
253         
254       }
255       
256     }
257     
258     // Loop over MIP stubs
259     
260     int iMipStub = 0;
261     
262     for (MipStub mStub : listMipStub) {
263       
264       iMipStub++;
265       bestGrade = -1.E12;
266       bestTrackID= -1;
267       
268       if ((pickMipStubNo>=0 && iMipStub!=pickMipStubNo) ||
269               (pickMipStubAngle>-100. && Math.abs(pickMipStubAngle - mStub.getPhi())>10./180.*Math.PI)){
270         continue;
271       }
272       
273       List<GarfieldTrack> listTemporaryTracks = new ArrayList<GarfieldTrack>();
274       GarfieldTrack newTrk = new GarfieldTrack(mStub, debugLevel);
275       newTrk.setID(issueTrackID());
276       listTemporaryTracks.add(newTrk);
277       
278       // start adding hits to the track and increasing
279       // listTemporaryTracks while looping over it
280       
281       int ii = 0;
282       boolean done = false;
283       while(!done) { // loop over list of temporary tracks
284         GarfieldTrack g = (GarfieldTrack) listTemporaryTracks.get(ii);
285         if (debugLevel>=2){
286           System.out.println("GarfieldTrackFinder, next track = "+g.getID());
287           g.debug();
288         }
289         
290         // compiling list selectedHits of eligible hits from the next layer
291         
292         getNextHits(g,1.);
293         if (selectedHits.size()==0 && g.getEquivnHits()>0){
294           getNextHits(g,scaleSearchRegion);
295         }
296         if (selectedHits.size()==0 && g.getEquivnHits()>1){
297           g.fastChi2Fit(this.nFitIterations); // try if refitting helps us to find hits
298           getNextHits(g,this.scaleSearchRegion);
299         }
300         
301         // creating new track object for each of the eligible hits and adding it to
302         // the listTemporaryTracks
303         
304         int nNewHits = selectedHits.size();
305         if (nNewHits==0) {
306           // no hits found, done !
307           if (debugLevel>=2) System.out.println("GarfieldTrackFinder done with picking up hits track="+g.getID()+", nhit="+g.getnHits()+" helix base="+g.hel.base(0)+" "+g.hel.base(1)+"minD="+this.minDistanceInHits);
308         } else if (nNewHits > nNewHitsMax){
309           if (debugLevel >=2) System.out.println("GarfieldTrackFinder process: too many new hits nHNew="+nNewHits+" adding best Hit only");
310           GarfieldTrack gNew = new GarfieldTrack(g);
311           gNew.setID(issueTrackID());
312           if (addHit(gNew,bestHit)){listTemporaryTracks.add(gNew);}
313         } else {
314           for (int j = 0 ; j<nNewHits ; j++){
315             GarfieldHit h = (GarfieldHit) selectedHits.get(j);
316             GarfieldTrack gNew = new GarfieldTrack(g);
317             gNew.setID(issueTrackID());
318             if (addHit(gNew,h)) listTemporaryTracks.add(gNew);
319             if (listTemporaryTracks.size() > maxTrkNmbr){
320               panicPurge(listTemporaryTracks,maxTrkNmbr); // panic, too many tracks
321             }
322           }
323         }
324         
325         if (debugLevel>=3) System.out.println("GarfieldTrackFinder done with temporary track "+g.getID());
326         ii++; // increase counter of tracks
327         done = ii == listTemporaryTracks.size();
328       } // loop over list of temporary tracks
329       
330       if (debugLevel>=2) System.out.println("GarfieldTrackFinder finished creating track seed using MipStub. nTracks="+listTemporaryTracks.size());
331       int mipStubStartInTrackList = listGarfieldTracks.size();
332       
333       // Go through the list of temporary tracks, apply cuts, add good tracks
334       // to the listGarfieldTracks
335       
336       ListIterator k = listTemporaryTracks.listIterator();
337       while (k.hasNext()) {
338         GarfieldTrack gg = (GarfieldTrack) k.next();
339         if (gg.isRejected()) continue;
340         if (gg.getEquivnHits()<garfieldnTRKHitMin) continue;
341         //gg.purgeHits(garfieldChi2ContribCut, garfieldnTRKHitMin, nFitIterations);
342         if (gg.getnHits()<garfieldnTRKHitMin) continue;
343         if (gg.getNdf()<=3){ // special cuts for small number of degrees of freedom
344           double kabs = Math.abs(gg.hel.kappa());
345           if (gg.getChi2()/gg.getNdf()>this.garfieldChi2Cut3Ndf) continue; // harder cut on tracks with rely too much on MipStub
346           if (kabs>this.garfieldKappaCut3Ndf) continue; // harder cut on tracks with rely too much on MipStub
347         }
348         if (gg.getNumberOfStepovers()>1) continue;
349         gg.fastChi2Fit(this.nFitIterations*4);
350         if (gg.getChi2()/gg.getNdf() < garfieldChi2Cut){
351           gg.setTrackParameters();
352           gg.setRadii();
353           gg.setGrade(garfieldChi2ContribCut);
354           gg.setDone();
355           if (gg.getGrade()>bestGrade){
356             bestTrackID =gg.getID();
357             bestGrade =gg.getGrade();
358           }
359           listGarfieldTracks.add(gg);
360           if (debugLevel >=2) System.out.println("*accepting track "+gg.getID()+", nH="+gg.getnHits()+" chi2="+gg.getChi2()+" ndf="+gg.getNdf()+" cutvalue="+this.garfieldChi2Cut);
361         } else {
362           if (debugLevel >=2) System.out.println("rejecting track "+gg.getID()+", nH="+gg.getnHits()+" chi2="+gg.getChi2()+" ndf="+gg.getNdf()+" cutvalue="+this.garfieldChi2Cut);
363         }
364         if (debugLevel >=3) gg.debug();
365         if (debugLevel >=3) System.out.println("******************");
366       }
367       
368       if (debugLevel>=3) System.out.println("clearing  listTemporaryTracks");
369       
370       // If modeBestTrackOnly is set, go back and purge all tracks to the
371       // current MipStub which are not optimal. Added Aug 5th 2004 EvT.
372       
373       if (modeBestTrackOnly){
374         for (int k1=mipStubStartInTrackList; k1<listGarfieldTracks.size(); k1++) {
375           GarfieldTrack gg1 = (GarfieldTrack) listGarfieldTracks.get(k1);
376           if (gg1.getID() != bestTrackID) gg1.purgeTrack();
377         }
378       }
379       
380       listTemporaryTracks.clear();
381     } // End of loop over MIP stubs
382     
383     //  Purge duplicate tracks :
384     
385     if (debugLevel > 0) System.out.println("GarfieldTrackFinder purging tracks now");
386     for (int k1=0;k1<listGarfieldTracks.size();k1++){
387       GarfieldTrack gg1 = (GarfieldTrack) listGarfieldTracks.get(k1);
388       if (gg1.isPurged()) continue;
389       int nh1 = gg1.getnHits();
390       for (int k2=k1+1;k2<listGarfieldTracks.size();k2++){
391         GarfieldTrack gg2 = (GarfieldTrack) listGarfieldTracks.get(k2);
392         if (gg2.isPurged()) continue;
393         int nh2 = gg2.getnHits();
394         double overLap=overlapRatio(gg1,gg2);
395         if (overLap>0.35){ // set to 0.3 if problems with fake 3hit tracks occur
396           // purge one track
397           if (gg1.getGrade() < gg2.getGrade()) gg1.purgeTrack();
398           else gg2.purgeTrack();
399           if (debugLevel > 1) System.out.println("purged one of pair "+gg1.getID()+" "+gg2.getID());
400         }
401       }
402     }
403     
404     // Fit and add unpurged tracks to the final list :
405     
406     int nPurged = 0;
407     ArrayList<GarfieldTrack> listGarfieldTracksGood = new ArrayList();
408     int NTrks=0;
409     for (int k=0;k<listGarfieldTracks.size();k++){
410       GarfieldTrack gg = (GarfieldTrack) listGarfieldTracks.get(k);
411       if (! gg.isPurged()){
412         gg.fullChi2Fit(0.05, this.nFitIterations);
413         gg.setTrackParameters();
414         gg.setRadii();
415         gg.setGrade(garfieldChi2ContribCut);
416         gg.setDone();
417         listGarfieldTracksGood.add(gg);
418         /*
419         ReconstructedTrack track = new ReconstructedTrack(bfield,
420         gg.getPara("d0"),
421         gg.getPara("phi0"),
422         gg.getPara("kappa"),
423         gg.getPara("z0"),
424         gg.getPara("lambda"));
425         track.mcp = gg.getMCParticle();
426          
427         defTrks[NTrks] = track;
428          */
429         NTrks++;
430       } else {
431         nPurged++;
432       }
433     }
434     
435     if (debugLevel>=1){
436       System.out.println("List of good GarfieldTracks");
437       for (GarfieldTrack gd : listGarfieldTracksGood){
438         gd.debug();
439       }
440     }
441     if (debugLevel>=1) System.out.println("GarfieldTrackFinder purged "+nPurged+" tracks");
442     if (debugLevel>=3) System.out.println("clearing listGarfieldTracks");
443     
444     listGarfieldTracks.clear();
445     
446     // Put list of tracks into the event :
447     
448     listGarfieldTracksGood.trimToSize();
449     event.put("GarfieldTracks",listGarfieldTracksGood);
450     System.out.println("GarfieldTrackfinder found Ntracks="+listGarfieldTracksGood.size());
451     /*
452     if (modeModifyTracklist == APPEND) {
453       TrackList tlist = event.getTrackList();
454       Enumeration e = tlist.getTracks();
455       int lngth = NTrks + tlist.getNTracks();
456       final Vector v = new Vector(lngth);
457       while (e.hasMoreElements()){
458         ReconstructedTrack t = (ReconstructedTrack) e.nextElement();
459         v.add(t);
460       }
461       for(int i=0; i<NTrks; i++) v.add(defTrks[i]);
462       event.put(event.TrackList,new ReconTrackList(v));
463     } else if (modeModifyTracklist == OVERWRITE){
464       final Vector v = new Vector(NTrks);
465       for(int i=0; i<NTrks; i++) v.add(defTrks[i]);
466       event.put(event.TrackList,new ReconTrackList(v));
467     }
468      */
469   }
470   
471   // -- Helper methods implementing parts of tracking algorithm :  -------------
472   
473   /**
474    * Compile a list of eligible hits in the next layer and put it into
475    * <code>selectedHits</code> for pattern recognition code to use.
476    */
477   private void getNextHits(GarfieldTrack g, double scale) {
478     
479     selectedHits.clear();
480     
481     int lastLayer = g.lastLayer;
482     if (modeFromOutsideIn && (lastLayer == listOfHitsLowestLayer)) return;
483     
484     int newLayer= -1;
485     int diffLayer = 0;
486     minDistanceInHits = 1000000.;
487     double d;
488     boolean hitsAreOnTrack = false;
489     double phi1=0.;
490     if (g.getnHits()>0){
491       hitsAreOnTrack=true;
492       phi1= ((GarfieldHit) g.hits.get(g.getnHits()-1)).getPhi();
493     }
494     double dMax = g.distanceCutValue()*scale;
495     
496     // Loop over hits
497     
498     for (GarfieldHit h : listOfHits) {
499       
500       int hLayer = h.getLayer();
501       boolean good = false;
502       
503       if (modeFromOutsideIn){ // outSideIn Tracking
504         diffLayer = lastLayer - hLayer;
505         if (newLayer == -1 && hLayer < lastLayer) good=true;
506         if (newLayer != -1 && hLayer == newLayer) good=true;
507       }
508       if (!modeFromOutsideIn){ // InSideOut Tracking
509         diffLayer = hLayer - lastLayer;
510         if (newLayer == -1 && hLayer > lastLayer) good=true;
511         if (newLayer != -1 && hLayer == newLayer) good=true;
512       }
513       
514       if ((newLayer != -1) && !(good)) break; // D.O.
515       if (diffLayer > 6) break; // added July 25 to speed up code
516       if (diffLayer > 1 && !hitsAreOnTrack ) break; // added July 25 to speed up code
517       
518       if (! good) continue;
519       
520       if (hitsAreOnTrack) {
521         double phi2 = h.getPhi();
522         double deltaPhi = Math.abs(phi1-phi2);
523         deltaPhi = Math.min(deltaPhi,Math.abs(deltaPhi-2.*Math.PI));
524         if (deltaPhi>Math.PI/2.) good=false;
525       }
526       if (! good) continue;
527       
528       d=h.distanceToHit(g.hel,g.hasZMeasurement());
529       if (d<this.minDistanceInHits){
530         this.minDistanceInHits=d;
531         this.bestHit = h;
532       }
533       if (d<dMax){
534         this.selectedHits.add(h);
535         newLayer = hLayer;
536       }
537     }
538     if (debugLevel>=2){
539       if (selectedHits.size()>0) {
540         System.out.println("GarfieldTrackFinder getNextHits T="
541                 +g.getID()+" found Nhits="+ selectedHits.size()+" in Layer "+newLayer+
542                 " minD="+minDistanceInHits);
543       } else {
544         System.out.println("GarfieldTrackFinder getNextHits T="+g.getID()+
545                 ", did not find hits. The lastLayer="+lastLayer+" minD="+minDistanceInHits);
546       }
547     }
548   }
549   
550   /**
551    * Add a hit to an existing track including decisions on how to update helix paramters.
552    * Returns <code>true</code> if the hit is successfully added,
553    * <code>false</code> if the hit has too large chi^2.
554    */
555   private boolean addHit(GarfieldTrack g, GarfieldHit h) {
556     
557     double distanceToBase = g.hel.distanceBaseToPoint(h);
558     if ((g.getnHits() == 0 &&  distanceToBase > maxDistanceFirstHitToBase) ||
559             (g.getnHits() > 0 &&  distanceToBase > 80.*k_cm)) {
560       if (debugLevel>=2) System.out.println("routine addHit reject new hit T="+
561               g.getID()+" h="+h.getID()+" dist="+distanceToBase);
562       return false;
563     }
564     
565     g.addHit(h);
566     if (debugLevel>=2) System.out.println("Track "+g.getID()+" adding hit=" +
567             h.getID()+ ",  Layer="+h.getLayer()+" new nHit="+g.getnHits());
568     if ( g.getNumberOfStepovers()>1) return false;
569     g.calculateHelixFromHits();
570     
571     if (this.refitDuringPatternReco && g.getEquivnHits() >= 3) {
572       g.fastChi2Fit(nFitIterations);
573       if (g.chi2Contrib(g.nHits-1)>200.0*this.garfieldChi2ContribCut ){
574         g.dropHit(g.nHits-1); // drop last hit
575         g.calculateHelixFromHits();
576         if (debugLevel>=2) {
577           System.out.println("rejecting Hit T="+g.getID()+" H="+h.getID()+" chi2ndf="+g.getChi2()/g.getNdf()+" stpOver="+g.getNumberOfStepovers());
578           if (debugLevel>=4) g.debug();
579         }
580         return false;
581       } else {
582         g.setStatus("WITH_HITS_FITTED");
583         return true;
584       }
585     } else{
586       g.setStatus("WITH_HITS");
587       return true;
588     }
589   }
590   
591   
592   // -- Simple helper functions :  ---------------------------------------------
593   
594   private int issueTrackID() {return issuedTrackIDValue++;}
595   
596   private boolean isEven(int i){
597     if (2*((int) (i/2))==i) return true;
598     return false;
599   }
600   
601   private double overlapRatio(GarfieldTrack g1, GarfieldTrack g2) {
602     int nOverlap = 0;
603     for (int j=0; j<g1.getnHits(); j++){
604       for (int k=0; k<g2.getnHits(); k++){
605         if (g1.getHitID(j)==g2.getHitID(k)) nOverlap++;
606       }
607     }
608     int nMin = Math.min(g1.getnHits(),g2.getnHits());
609     if (nMin<1) return -1.;
610     double r = ((double) nOverlap)/((double) nMin);
611     return r;
612   }
613   
614   private void panicPurge(List lst, int ntrMax) {
615     int ntr = lst.size();
616     System.out.println("GarfieldTrackFinder PanicPurge size="+ntr+" max allowed "+ntrMax);
617     System.out.println("GarfieldTrackFinder PanicPurge brutal removal of last couple of tracks");
618     for (int j = ntr-1; j<ntrMax ; j--){
619       lst.remove(j);
620     }
621   }
622   
623   
624   // --  Plotting, Debugging, Testing, and Examples :  -------------------------
625   
626   public void testEvent(EventHeader event){
627     int[] hitList = new int[]{0,1,2,3,4,5,6};
628     int nHitInList =7;
629     int mstubID = 0;
630     List listMipStub = (List) event.get("GarfieldMipStubs");
631     MipStub mStub = (MipStub) listMipStub.get(mstubID);
632     GarfieldTrack newTrk = new GarfieldTrack(mStub, 0);
633     newTrk.setID(this.issueTrackID());
634     for (int i=0; i<nHitInList;i++){
635       addHit(newTrk,(GarfieldHit) (this.listOfHits).get(hitList[i]));
636       newTrk.debug();
637     }
638     newTrk.debug();
639     newTrk.calculateChi2();
640     newTrk.fullChi2Fit(0.1,this.nFitIterations);
641     newTrk.debug();
642     newTrk.purgeHits(this.garfieldChi2ContribCut,this.garfieldnTRKHitMin,this.nFitIterations);
643     newTrk.debug();
644     newTrk.setTrackParameters();
645     newTrk.setRadii();
646     newTrk.setDone();
647     newTrk.debug();
648   }
649   
650   /**
651    * example for stand-alone tracking for Wolfgang M.
652    */
653   public void testGenericTrackFit(EventHeader event) {
654     GarfieldTrack newTrk = new GarfieldTrack();
655     newTrk.setDebugLevel(3);
656     newTrk.setID(issueTrackID());
657     double k_mm = 0.1;
658     double err = 2.0 * k_mm; // global error on example hit is 2 millimeter
659     int layer = 0; // just a dummy
660     int id = 0;
661     newTrk.addHit( new GarfieldHit(new double[]{0.0, 0.0, 0.0}, err, layer, id++));
662     newTrk.addHit( new GarfieldHit(new double[]{0.2, 0.2, 1.0}, err, layer, id++));
663     newTrk.addHit( new GarfieldHit(new double[]{0.2, 0.4, 2.0}, err, layer, id++));
664     newTrk.addHit( new GarfieldHit(new double[]{0.4, 0.4, 3.0}, err, layer, id++));
665     newTrk.addHit( new GarfieldHit(new double[]{0.6, 0.6, 4.0}, err, layer, id++));
666     newTrk.addHit( new GarfieldHit(new double[]{0.6, 0.8, 4.0}, err, layer, id++));
667     newTrk.addHit( new GarfieldHit(new double[]{0.8, 0.8, 5.0}, err, layer, id++));
668     newTrk.addHit( new GarfieldHit(new double[]{1.0, 1.0, 6.0}, err, layer, id++));
669     newTrk.addHit( new GarfieldHit(new double[]{1.0, 1.2, 7.0}, err, layer, id++));
670     newTrk.debug();
671     double p0[]=newTrk.getHitPoint(0);
672     double p1[]=newTrk.getHitPoint(newTrk.getnHits()-1);
673     newTrk.setHelix(p0,p1);
674     newTrk.fullChi2Fit(0.1,20);  // first argument is first stepsize
675     newTrk.debug();
676     newTrk.setTrackParameters();
677     newTrk.setRadii();
678     newTrk.setDone();
679     newTrk.debug();
680     
681     // plotting helix
682     double step = 0.1;
683     for (int i=-2 ; i<20;i++){
684       newTrk.hel.setPointOnHelix(step * (double) i);
685     }
686     // plotting hits
687     for (int k=0 ; k<newTrk.getnHits();k++){
688       double[] ph = newTrk.getHitPoint(k);
689     }
690   }
691   
692   /**
693    * another example on how to fit a track
694    */
695   public void testEv5(EventHeader event) {
696     List listMipStub = (List) event.get("GarfieldMipStubs");
697     MipStub mStub = (MipStub) listMipStub.get(0);
698     GarfieldTrack newTrk = new GarfieldTrack(mStub, 0);
699     newTrk.setID(this.issueTrackID());
700     GarfieldHit ha = (GarfieldHit) (this.listOfHits).get(0);
701     GarfieldHit hb = (GarfieldHit) (this.listOfHits).get(4);
702     GarfieldHit hc = (GarfieldHit) (this.listOfHits).get(9);
703     GarfieldHit hd = (GarfieldHit) (this.listOfHits).get(11);
704     GarfieldHit he = (GarfieldHit) (this.listOfHits).get(14);
705     addHit(newTrk,ha);
706     addHit(newTrk,hb);
707     addHit(newTrk,hc);
708     addHit(newTrk,hd);
709     addHit(newTrk,he);
710     newTrk.debug();
711     newTrk.calculateChi2();
712     newTrk.setHelixBaseToPCA();
713     newTrk.calculateChi2();
714     newTrk.debug();
715     newTrk.fastChi2Fit(5);
716     newTrk.debug();
717     newTrk.fastChi2Fit(5);
718     newTrk.debug();
719     newTrk.purgeHits(this.garfieldChi2ContribCut,this.garfieldnTRKHitMin,this.nFitIterations/2);
720   }
721   
722 }