View Javadoc

1   /*
2    * To change this template, choose Tools | Templates
3    * and open the template in the editor.
4    */
5   
6   package org.lcsim.recon.tracking.seedtracker.strategybuilder;
7   
8   import java.io.File;
9   import java.util.ArrayList;
10  import java.util.Collections;
11  import java.util.Comparator;
12  import java.util.Date;
13  import java.util.HashMap;
14  import java.util.HashSet;
15  import java.util.Iterator;
16  import java.util.LinkedList;
17  import java.util.List;
18  import java.util.Map;
19  import java.util.Random;
20  import java.util.Set;
21  import org.lcsim.recon.tracking.seedtracker.SeedLayer;
22  import org.lcsim.recon.tracking.seedtracker.SeedLayer.SeedType;
23  import org.lcsim.recon.tracking.seedtracker.SeedStrategy;
24  import org.lcsim.recon.tracking.seedtracker.StrategyXMLMetadata;
25  import org.lcsim.recon.tracking.seedtracker.StrategyXMLUtils;
26  import org.lcsim.detector.DetectorElementStore;
27  import org.lcsim.detector.IDetectorElement;
28  import org.lcsim.detector.IDetectorElementContainer;
29  import org.lcsim.event.EventHeader;
30  import org.lcsim.event.MCParticle;
31  import org.lcsim.event.SimTrackerHit;
32  import org.lcsim.fit.helicaltrack.HitIdentifier;
33  import org.lcsim.geometry.Detector;
34  import org.lcsim.geometry.subdetector.BarrelEndcapFlag;
35  
36  /**
37   * StrategyBuilder automatically generates strategies for a detector by
38   * seeing what layers MCParticles tend to go through. 
39   * 
40   * See interface for public method doc. 
41   * 
42   * @author cozzy
43   */
44  public class StrategyBuilder extends AbstractStrategyBuilder implements IStrategyBuilder {
45      
46      public static final String defaultOutputFile = System.getProperties().getProperty("java.io.tmpdir")+
47              System.getProperties().getProperty("file.separator")+
48              "BuiltStrategies.xml"; 
49      
50      public static final int RandomSeed = 1234; 
51      
52      //these are defined separately because RunStrategyBuilder uses some of these too... 
53      public static final boolean defaultVerbose = false; 
54      public static final int defaultMinLayers = 7; 
55      public static final int defaultConfirmLayers =1; 
56      public static final int defaultSeedLayers =3; 
57      public static final int defaultMinUnweightedScore = 1; 
58      public static final boolean defaultSymmetrize = true;  
59     
60      private boolean verbose = defaultVerbose; 
61      private boolean symmetrize = defaultSymmetrize; 
62      private int min_layers = defaultMinLayers;  
63      private int confirm_layers = defaultConfirmLayers;  
64      private int seed_layers = defaultSeedLayers; 
65      private String outputFile = defaultOutputFile; 
66      private int minUnweightedScore = defaultMinUnweightedScore;  
67      private boolean oldConfirm = false; 
68      private List<SeedStrategy> startingStrategies = new ArrayList<SeedStrategy>(); 
69      private Set<Set<DumbLayer>> startingSet = new HashSet<Set<DumbLayer>>(); //this will be generated from startingStrategies
70      private SeedStrategy prototype = new SeedStrategy("null", new ArrayList<SeedLayer>()); 
71      private LayerWeight weighter = null;
72      private HitIdentifier ID = new HitIdentifier();
73      //this stores the list of all sets of layers
74      private List<Set<DumbLayer>> setlist = new ArrayList<Set<DumbLayer>>();
75      private String detectorName; 
76      private IParticleFilter filter; 
77      private List<List<DumbLayer>> adjacentlist = new ArrayList<List<DumbLayer>>(); 
78      //4:26
79      private Random random = new Random(RandomSeed); 
80      private Map<Set<DumbLayer>,SubsetScore> scoremap; 
81  
82      @Override 
83      protected void startOfData(){
84        //Use default filter if none is specified
85          if (filter==null) {
86              filter = new StrategyBasedFilter(prototype); 
87          }       
88      }
89      
90      //In the process step, we build two lists of collections:
91      //      The set list is a list of the sets of layers hit by MCParticles hitting over min layers layers
92      //      The adjacence list is a list of a list of hits that are determined to be adjacent based on MCParticle trajectory
93      @Override
94      protected void process(EventHeader event){
95  
96          super.process(event);
97          
98          if (verbose) {
99              if (event.getEventNumber() % 100 == 0)
100             System.out.println("Processed "+event.getEventNumber()+" events.");
101         }
102         
103         filter.setEvent(event);
104         
105         //Build MCMap from SimTrackerHits, including inefficiency modeling
106         Map<MCParticle, List<SimTrackerHit>> mcmap = buildMCMap(event);
107         
108         //filter MCs 
109         Iterator<MCParticle> mciter = mcmap.keySet().iterator(); 
110         while (mciter.hasNext()){
111             MCParticle next = mciter.next(); 
112             if (!filter.passes(next)){
113                 mciter.remove(); 
114             }
115         }
116         
117         //Build and add layer sets, as well as adjacent lists
118         for(List<SimTrackerHit> l : mcmap.values()) {
119             Set<DumbLayer> set = new HashSet<DumbLayer>(); 
120             
121             //sort by time, which allows creation of adjacence lists. 
122             Collections.sort(l, new Comparator() {
123 
124                 public int compare(Object o1, Object o2) {
125                     SimTrackerHit h1 = (SimTrackerHit) o1; 
126                     SimTrackerHit h2 = (SimTrackerHit) o2; 
127                     return Double.compare(h1.getTime(), h2.getTime()); 
128                 }
129             });
130             
131             //this will store all the working adjacent lists
132             LinkedList<List<DumbLayer>> tempAdjacentLayersList = new LinkedList<List<DumbLayer>>(); 
133             LinkedList<List<DumbLayer>> pendingAdjacentLayersList = new LinkedList<List<DumbLayer>>(); 
134             for (SimTrackerHit h : l) {
135                 IDetectorElementContainer cont = DetectorElementStore.getInstance().find(h.getIdentifier());
136                 if(cont.isEmpty()) continue; 
137                 IDetectorElement de = cont.get(0); 
138                 String detname = ID.getName(de);
139                 int lyr = ID.getLayer(de);
140                 BarrelEndcapFlag be = ID.getBarrelEndcapFlag(de); 
141                 
142                 //kludgy divide by two thing
143                 if (weighter.isDivideByTwoInTrackerForward() && be.isEndcap() && 
144                         (detname.indexOf("TrackerForward") > -1 || detname.indexOf("TkrForward") > -1) ) {
145                         lyr/=2;  // sid01/sid02 doubles up on layer numbering in the endcap. 
146                 } else if (weighter.isDivideByTwoInTrackerEndcap() && be.isEndcap() && 
147                         (detname.indexOf("TrackerEndcap") > -1 || detname.indexOf("TkrEndcap") > -1) ) {
148                         lyr/=2;  // sid01 doubles up on layer numbering in the forward. 
149                 }
150                 
151                 //if symmetrizing, we want to treat North and South layers equivalently. 
152                 if (symmetrize && be.isEndcap()) be = BarrelEndcapFlag.ENDCAP; 
153                 DumbLayer dl = new DumbLayer(detname, lyr, be); 
154                 set.add(dl);
155                 
156                 //create a new adjacent list that starts with this layer if none already exists
157                 //(This is necessary because of the doubling of SimTrackerHits in the tracker endcap)
158                 if (tempAdjacentLayersList.isEmpty() || !tempAdjacentLayersList.getLast().contains(dl)) {
159                     List<DumbLayer> adjacentLayers = new ArrayList<DumbLayer>(); 
160                     tempAdjacentLayersList.addLast(adjacentLayers);
161                 }
162                 
163                 //see which adjacent lists already have enough layers, and add those to the list
164                 Iterator<List<DumbLayer>> it = tempAdjacentLayersList.iterator(); 
165                 while (it.hasNext()) {
166                     List<DumbLayer> s = it.next(); 
167                     if(!s.contains(dl)) s.add(dl); //otherwise we get doubled layers in the forward region of the tracker 
168                     if (s.size() == confirm_layers + seed_layers) {
169                         pendingAdjacentLayersList.add(s); 
170                         it.remove(); 
171                     }
172                 }
173             }
174                         
175             //Ensure layer set has minimum number of layers
176             if (set.size() >= min_layers) {
177                 setlist.add(set); 
178                 adjacentlist.addAll(pendingAdjacentLayersList);
179             }
180         }
181     }
182     
183     @Override
184     protected void suspend(){
185         
186         if (verbose) System.out.println("Finished processing. Beginning analysis."); 
187         Set<DumbLayer> allLayers = new HashSet<DumbLayer>(); 
188         
189         //Get all layers that are used at some point
190         for (Set<DumbLayer> set : setlist) 
191             allLayers.addAll(set); 
192         if (verbose) System.out.println(allLayers.size()+" total layers.");
193         
194        //create startingSet... this will be used so that new strategies aren't extraneously generated
195        for (SeedStrategy strategy : startingStrategies){
196             Set<DumbLayer> subset = StrategyBuilderUtils.getRelevantSet(strategy, true);
197             startingSet.add(subset); 
198         }
199         if (verbose) System.out.println(startingStrategies.size()+" starting strategies defined.");
200        
201         //Generate the scorer and assign its weighter
202         SubsetScorer scorer = new SubsetScorer(setlist,adjacentlist); 
203         scorer.setLayerWeight(weighter); 
204         scoremap = new HashMap<Set<DumbLayer>,SubsetScore>(); 
205         
206         
207         for (Set<DumbLayer> starter : startingSet) {
208             scoremap.put(starter, scorer.getScoreObject(starter)); 
209             scorer.markUsed(starter);
210         }
211         
212         //Generate all possible subsets of the right size of allLayers
213         List<Set<DumbLayer>> all_subsets = StrategyBuilderUtils.generateAllPossibleDumbLayerSubsetsList(allLayers, confirm_layers+seed_layers); 
214         if (verbose) System.out.println(all_subsets.size() + " possible subsets of size "+(confirm_layers+seed_layers)); 
215         
216         //convert setlist to set to eliminate duplicates... 
217         Set<Set<DumbLayer>> setset = new HashSet<Set<DumbLayer>>(setlist.size()); 
218         setset.addAll(setlist); 
219         
220         //usedSets keeps track of what has been used already
221         Set<Set<DumbLayer>> usedSets = new HashSet<Set<DumbLayer>>(setset.size()); 
222         
223         //final_sets will store the generated seed + confirm layers
224         Set<Set<DumbLayer>> final_sets = new HashSet<Set<DumbLayer>>(); 
225         if (verbose) System.out.println("Layer set has "+setset.size()+" entries.");
226         //map from a final_set to all other associated layers to generate extension layers
227         Map<Set<DumbLayer>, Set<DumbLayer>> extendmap = new HashMap<Set<DumbLayer>,Set<DumbLayer>>();
228         
229         
230         SubsetScore score = new SubsetScore(0,0,0); 
231         //Figure out a "good" set of four-layer combinations by brute force...
232         //We have a scoring algorithm and we find the maximal scoring one. 
233         while (true){
234             if (verbose) System.out.println(setset.size() - usedSets.size() + " entries left to be covered."); 
235             if (usedSets.size() == setset.size()) break; //if we've used all sets, then we're done! 
236             
237             Set<DumbLayer> max = all_subsets.get(0); 
238             SubsetScore maxScore = new SubsetScore(0,0,0); 
239             
240             //get the highest scoring strategy...
241             for (Set<DumbLayer> trial : all_subsets){
242                 score = scorer.getScoreObject(trial); 
243                 if (score.score() > maxScore.score()) {
244                     maxScore = score; 
245                     max = trial; 
246                 }
247             }
248             
249             //ignore anything that has too few occurrences... 
250             if (maxScore.numTracks() <= minUnweightedScore) break; 
251             
252             scorer.markUsed(max);
253             final_sets.add(max); 
254             extendmap.put(max, new HashSet<DumbLayer>()); 
255             scoremap.put(max,maxScore); 
256             for (Set<DumbLayer> this_set : setset) {
257                 if (this_set.containsAll(max)) { //If this set contains all the layers in max, it should be findable! 
258                     
259                     //add extension layers to extendmap
260                     Set<DumbLayer> nw = new HashSet<DumbLayer>(); 
261                     for (DumbLayer dumb : this_set){                             
262                         if(!max.contains(dumb)) nw.add(dumb);
263                     }
264                     Set<DumbLayer> old = extendmap.get(max); 
265                     old.addAll(nw); 
266                     extendmap.put(max, old); 
267                     
268                     //remove this set from consideration
269                     usedSets.add(this_set); 
270                 }
271             }
272             
273             
274         }
275         if(verbose) System.out.println("Done finding strategies"); 
276         if(verbose) System.out.println(final_sets.toString());  
277        
278         //Generate the StrategyList 
279         int counter = 0; 
280                 
281         StrategyXMLMetadata meta = new StrategyXMLMetadata(); 
282         
283         List<SeedStrategy> strat_list = new ArrayList<SeedStrategy>(); 
284         strat_list.addAll(startingStrategies); 
285         
286         //Write comments for starting strategies
287         for (SeedStrategy starter : startingStrategies) {
288             int unw_score = scoremap.get(StrategyBuilderUtils.getRelevantSet(starter,true)).numTracks(); 
289             meta.strategyComments.put(starter, "Num findable tracks (total, not additional): "+unw_score);
290         }
291         
292         //create Strategies from final_sets... this part is klunky right now. 
293         for (Set<DumbLayer> s : final_sets) {
294 
295             List<DumbLayer> dlyrlst = new ArrayList<DumbLayer>(); 
296             dlyrlst.addAll(s); 
297             
298             /**
299              * Here we figure out which layers to use for seeding and which to use
300              * for confirming. Since it is highly advantageous for the seed layers 
301              * to be adjacent, we will try to enforce that if possible. 
302              */
303             
304             //get adjacence info if set is adjacent. 
305             List<DumbLayer> adjacenceInfo = null; 
306             for (List<DumbLayer> l : adjacentlist) {
307                 if (s.containsAll(l) && l.containsAll(s)) {
308                     adjacenceInfo = l; 
309                     break; 
310                 }
311             }
312             
313             //if not all layers are adjacent, then perhaps a subset of size seed_layers is... 
314             if (adjacenceInfo == null) {
315                 for (Set<DumbLayer> ss : StrategyBuilderUtils.generateAllPossibleDumbLayerSubsetsList(s, seed_layers)){
316                     for (List<DumbLayer> l : adjacentlist) {
317                         if (ss.containsAll(l) && l.containsAll(ss)) {
318                             adjacenceInfo = l; 
319                             break; 
320                         }
321                     }
322                 }
323             }
324                         
325             /**
326              * The following operations manipulate dlyrlst such that the first num_confirm items will be
327              * used for confirmation and the rest will be used for seeding.  
328              * 
329              * This is kind of kludgy and it might be less confusing if it were changed. 
330              */
331             
332             //if these layers aren't adjacent, just use the weights to figure out which layers to confirm with
333             if (adjacenceInfo == null || oldConfirm) {
334                 //sort the list from smallest to largest weight. Use smallest weight(s) for confirmation layer(s). 
335                 Collections.sort(dlyrlst, weighter.getComparator()); 
336             } 
337             
338             //If all layers are adjacent, we use either the first or last layers to confirm, depending on the layer weights
339             else if (adjacenceInfo.size() == dlyrlst.size()) { 
340                 dlyrlst = adjacenceInfo; 
341                 if (weighter.getWeight(dlyrlst.get(0)) > weighter.getWeight(dlyrlst.get(dlyrlst.size()-1)))
342                     Collections.reverse(dlyrlst);
343                     
344             //Otherwise, use the adjacent layers as seeds and not the others... 
345             } else {
346                 dlyrlst.removeAll(adjacenceInfo);
347                 dlyrlst.addAll(adjacenceInfo); 
348             }
349                      
350             int confirmed = 0; //add the first num_confirm as confirmed... the rest as seed. 
351 
352             List<SeedLayer> lyrlst = new ArrayList<SeedLayer>(); 
353 
354             //get extension layers...sort from smallest weight to largest weight
355             //                      because the list will be reversed. 
356             List<SeedLayer> extendlyr = new ArrayList<SeedLayer>(); 
357             List<DumbLayer> dumbextendlyr = new ArrayList<DumbLayer>(); 
358             dumbextendlyr.addAll(extendmap.get(s)); 
359             Collections.sort(dumbextendlyr, weighter.getComparator()); 
360             
361             for (DumbLayer lyr : dumbextendlyr) {
362                 extendlyr.add(new SeedLayer(lyr.detectorName, lyr.layer, lyr.be, SeedType.Extend)); 
363             }
364             
365             lyrlst.addAll(extendlyr);             
366             //get seed/confirmation layers
367             for (DumbLayer lyr : dlyrlst){
368                 SeedType type;
369                 
370                 if (confirmed < confirm_layers) {
371                     type = SeedType.Confirm;
372                     confirmed++; 
373                 }
374                 else type = SeedType.Seed; 
375                 lyrlst.add(new SeedLayer(lyr.detectorName, lyr.layer, lyr.be, type)); 
376             }
377             Collections.reverse(lyrlst); // reverse so seed layers on top 
378             
379             String name = "AUTOGEN" + counter++ +"_"+lyrlst.hashCode(); 
380             
381             //copy over cutoff info from prototype
382             SeedStrategy stgy = new SeedStrategy(name,lyrlst);
383             stgy.copyCutoffsFromStrategy(prototype); 
384             strat_list.add(stgy); 
385             
386             //Write in scoring information about each strategy  
387             score = scoremap.get(s);
388             String comment = "AUTOGEN STATISTICS: \n\t\t\tScore: "+score.score() + "\n";
389             comment += "\t\t\tUnweighted Score (num new tracks): "+score.numTracks() + "\n"; 
390             comment += "\t\t\tAdjacency: "+score.adjacency() +"\n\t\t"; 
391             meta.strategyComments.put(stgy, comment); 
392         }
393         
394 
395         String comment = "Strategy list Autogenerated by Strategy Builder on "+new Date()+".";
396         meta.targetDetector =  detectorName; 
397         meta.comment = comment;
398         
399         //If symmetrizing, make Endcap Layers for both north and south
400         if (symmetrize) {
401             if (verbose) System.out.println("Symmetrizing..."); 
402             StrategyBuilderUtils.symmetrizeStrategies(strat_list, meta);
403         }
404         
405         //sort Strategy list
406         if (verbose) System.out.println("Sorting output"); 
407         Collections.sort(strat_list, new Comparator() {
408 
409             public int compare(Object o1, Object o2) {
410                 SeedStrategy one = (SeedStrategy) o1; 
411                 SeedStrategy two = (SeedStrategy) o2;
412                 return Double.compare(scoremap.get(StrategyBuilderUtils.getRelevantSet(two,true)).numTracks(), 
413                         scoremap.get(StrategyBuilderUtils.getRelevantSet(one,true)).numTracks());
414             }
415         }); 
416         
417         StrategyXMLUtils.writeStrategyListToFile(strat_list, new File(outputFile), meta);
418         if (verbose) System.out.println(strat_list.size()+" strategies generated."); 
419         if (verbose) System.out.println("Strategies XML file written at "+outputFile); 
420     }
421     
422     @Override
423     protected void detectorChanged(Detector detector){
424         detectorName = detector.getDetectorName(); 
425         
426         //use default weighter if not specified... default depends on detector name
427         if (weighter==null) {
428            setLayerWeight(new DefaultLayerWeight(detectorName).getWeight());
429         }
430         
431         //check that detectors match layer weights (unless the TargetDetector is unspecified)
432         if(!weighter.getTargetDetector().equals("None Specified") && !weighter.getTargetDetector().equals(detectorName)) {
433             throw new DetectorMismatchException(detectorName, weighter.getTargetDetector()); 
434         }
435     }
436     
437     
438     // ===============setters===============// documented in interface
439     public void setOutput(String filename){
440         outputFile = filename;
441     }
442     
443     public void setLayerWeight(LayerWeight lw){
444         weighter = lw; 
445     }
446     
447     public void setMinLayers(int min){
448         min_layers = min; 
449     }
450     
451     public void setNumConfirmLayers(int clayers){
452         confirm_layers = clayers; 
453     }
454 
455     public void setNumSeedLayers(int slayers){
456         seed_layers = slayers; 
457     }
458     
459     public void setStrategyPrototype(SeedStrategy proto){
460         prototype = proto; 
461     }
462     
463     public void setStartingStrategyList(List<SeedStrategy> slist){
464         startingStrategies = slist; 
465     }
466     
467     public void setVerbose(boolean v){
468         verbose = true; 
469     }
470     
471     public void setMinimumUnweightedScore(int sc){
472         minUnweightedScore = sc; 
473     }
474             
475     public void setParticleFilter(IParticleFilter pfilter){
476         filter = pfilter; 
477     }
478     
479     public void setSymmetrize(boolean set){
480         symmetrize = set; 
481     }
482 
483     //========privates ============//
484     private Map<MCParticle, List<SimTrackerHit>> buildMCMap(final EventHeader event) {
485 
486         //Build MC Map from SimTrackerHits
487         Map<MCParticle, List<SimTrackerHit>> mcmap = new HashMap<MCParticle, List<SimTrackerHit>>();
488         List<SimTrackerHit> allhits = new ArrayList<SimTrackerHit>();
489 
490         
491         //This will return the list of lists in a random order... we want the order 
492         // to be consistent so that the same hits are ignored due to inefficiency each time
493         List<List<SimTrackerHit>> simhits = event.get(SimTrackerHit.class); 
494         
495         
496         //Each collection should have a unique name, so sorting by name works here
497         Collections.sort(simhits, new Comparator() {
498 
499             public int compare(Object o1, Object o2) {
500                 List<SimTrackerHit> l1 = (List<SimTrackerHit>)o1; 
501                 List<SimTrackerHit> l2 = (List<SimTrackerHit>)o2; 
502                 
503                 return String.CASE_INSENSITIVE_ORDER.compare(event.getMetaData(l1).getName(), event.getMetaData(l2).getName()); 
504             }
505         }); 
506         
507         for (List<SimTrackerHit> l : simhits) {
508 
509             Collections.sort(l, new Comparator() {
510 
511                 //This might be a little overkill... but it should have a very
512                 //high probability of distinguishing between SimTrackerHits... 
513                 public int compare(Object o1, Object o2) {
514                     SimTrackerHit h1 = (SimTrackerHit) o1;
515                     SimTrackerHit h2 = (SimTrackerHit) o2; 
516                     
517                     if (h1.getTime()!=h2.getTime()) {
518                         return Double.compare(h1.getTime(), h2.getTime()); 
519                     } 
520                     
521                     if (h1.getCellID()!=h2.getCellID()) {
522                         return Double.compare(h1.getCellID(), h2.getCellID()); 
523                     }
524                     
525                     if (h1.getdEdx()!=h2.getdEdx()) {
526                         return Double.compare(h1.getdEdx(), h2.getdEdx()); 
527                     }
528                     
529                     return Double.compare(h1.getPathLength(), h2.getPathLength()); 
530                 }
531             });
532             
533             /**
534              * We simulate inefficiency in SimTrackerHit => TrackerHit conversion,
535              * otherwise the strategies will miss certain classes of hits
536              */
537 
538             EventHeader.LCMetaData meta = event.getMetaData(l);
539             String readout = meta.getName();
540             double efficiency = weighter.getReadoutEfficiency(readout);
541             for (SimTrackerHit h : l) {
542                 if (random.nextDouble() < efficiency) {
543                     allhits.add(h);
544                 }
545             }
546         }
547 
548         for (SimTrackerHit h : allhits) {
549             MCParticle p = h.getMCParticle();
550             List<SimTrackerHit> these_hits;
551             if (mcmap.containsKey(p)) {
552                 these_hits = mcmap.get(p);
553             } else {
554                 these_hits = new ArrayList<SimTrackerHit>();
555             }
556 
557             these_hits.add(h);
558             mcmap.put(p, these_hits);
559         }
560 
561         return mcmap;
562     }
563 
564 }