View Javadoc
1   package org.lcsim.geometry.subdetector;
2   
3   import hep.graphics.heprep.HepRep;
4   import hep.graphics.heprep.HepRepFactory;
5   
6   import java.util.ArrayList;
7   import java.util.Comparator;
8   import java.util.HashMap;
9   import java.util.HashSet;
10  import java.util.List;
11  import java.util.Set;
12  
13  import org.jdom.Element;
14  import org.jdom.JDOMException;
15  import org.lcsim.detector.converter.heprep.DetectorElementToHepRepConverter;
16  import org.lcsim.detector.identifier.IIdentifierHelper;
17  import org.lcsim.detector.identifier.Identifier;
18  import org.lcsim.geometry.IDDecoder;
19  import org.lcsim.geometry.util.IDEncoder;
20  
21  /**
22   * Reconstruction version of HPS ECal with crystal array.
23   * 
24   * @author Jeremy McCormick <jeremym@slac.stanford.edu>
25   * @author Timothy Nelson <tknelsonm@slac.stanford.edu>
26   * @version $Id: HPSEcal3.java,v 1.3 2012/04/30 18:04:38 jeremy Exp $
27   */
28  public class HPSEcal3 extends AbstractSubdetector {
29      private int nx;
30      private int ny;
31      //private double beamgap;
32      //private double dface;
33      private boolean oddX;
34      List<CrystalRange> removeCrystals = new ArrayList<CrystalRange>();
35  
36      public static class NeighborMap extends HashMap<Long, Set<Long>> {
37          IIdentifierHelper helper;
38  
39          public NeighborMap(IIdentifierHelper helper) {
40              this.helper = helper;
41          }
42  
43          public String toString() {
44              System.out.println("NeighborMap has " + this.size() + " entries.");
45              StringBuffer buff = new StringBuffer();
46              for (long id : this.keySet()) {
47                  buff.append(helper.unpack(new Identifier(id))).append("\n");
48                  Set<Long> nei = this.get(id);
49                  for (long nid : nei) {
50                      buff.append("  " + helper.unpack(new Identifier(nid))).append("\n");
51                  }
52              }
53              return buff.toString();
54          }
55      }
56  
57      private NeighborMap neighborMap = null;
58  
59      HPSEcal3(Element node) throws JDOMException {
60          super(node);
61  
62          Element layout = node.getChild("layout");
63  
64          nx = layout.getAttribute("nx").getIntValue();
65          ny = layout.getAttribute("ny").getIntValue();        
66          //beamgap = layout.getAttribute("beamgap").getDoubleValue();
67          //dface = layout.getAttribute("dface").getDoubleValue();
68  
69          if (nx % 2 != 0)
70              oddX = true;
71  
72          // Setup range of indices to be skipped.
73          for (Object obj : layout.getChildren("remove")) {
74              Element remove = (Element) obj;
75              try {
76                  removeCrystals.add(new CrystalRange(remove));
77              } catch (Exception x) {
78                  throw new RuntimeException(x);
79              }
80          }
81  
82          /*
83           * <remove ixmin="2" ixmax="10" iymin="-1" iymax="1"/>
84           */
85      }
86  
87      private static class CrystalRange {
88          int ixmin;
89          int ixmax;
90          int iymin;
91          int iymax;
92  
93          CrystalRange(Element elem) throws Exception {
94              ixmin = ixmax = iymin = iymax = 0;
95  
96              if (elem.getAttribute("ixmin") != null) {
97                  ixmin = elem.getAttribute("ixmin").getIntValue();
98              } else {
99                  throw new RuntimeException("Missing ixmin parameter.");
100             }
101 
102             if (elem.getAttribute("ixmax") != null) {
103                 ixmax = elem.getAttribute("ixmax").getIntValue();
104             } else {
105                 throw new RuntimeException("Missing ixmax parameter.");
106             }
107 
108             if (elem.getAttribute("iymin") != null) {
109                 iymin = elem.getAttribute("iymin").getIntValue();
110             } else {
111                 throw new RuntimeException("Missing ixmax parameter.");
112             }
113 
114             if (elem.getAttribute("iymax") != null) {
115                 iymax = elem.getAttribute("iymax").getIntValue();
116             } else {
117                 throw new RuntimeException("Missing iymax parameter.");
118             }
119         }
120     }
121 
122     private boolean isValidXY(int ix, int iy) {
123         if (!isValidX(ix))
124             return false;
125         if (!isValidY(iy))
126             return false;
127         return checkRange(ix, iy, this.removeCrystals);
128     }
129 
130     private boolean checkRange(int ix, int iy, List<CrystalRange> ranges) {
131         if (ranges.size() == 0)
132             return true;
133         for (CrystalRange range : ranges) {
134             if ((ix >= range.ixmin && ix <= range.ixmax) && ((iy >= range.iymin) && (iy <= range.iymax))) {
135                 return false;
136             }
137 
138         }
139         return true;
140     }
141 
142     //public double distanceToFace() {
143     //    return dface;
144     //}
145 
146     //public double beamGap() {
147     //    return beamgap;
148     //}
149 
150     /**
151      * The number of crystals in X in one section.
152      * 
153      * @return the number of crystals in X in one section
154      */
155     public double nx() {
156         return nx;
157     }
158 
159     /**
160      * The number of crystals in y in one section.
161      * 
162      * @return the number of crystals in Y in one section
163      */
164     public double ny() {
165         return ny;
166     }
167 
168     // Class for storing neighbor indices in XY and side.
169     static class XY implements Comparator<XY> {
170         int x;
171         int y;
172 
173         public XY(int x, int y) {
174             this.x = x;
175             this.y = y;
176         }
177 
178         public int x() {
179             return x;
180         }
181 
182         public int y() {
183             return y;
184         }
185 
186         public boolean equals(Object o) {
187             XY xy = (XY) o;
188             return xy.x() == x && xy.y() == y;
189         }
190 
191         public int compare(XY o1, XY o2) {
192             if (o1.equals(o2)) {
193                 return 0;
194             } else {
195                 return -1;
196             }
197         }
198     }
199 
200     /**
201      * Get the neighbors for a given cell ID. Each crystal not on an edge has 8 neighbors. Edge crystals have fewer. 
202      * @param id The cell ID.
203      * @return A <code>Set</code> containing the cell's neighbors.
204      */
205     Set<Long> getNeighbors(Long id) {
206         // Get the IDDecoder.
207         IDDecoder dec = getIDDecoder();
208 
209         // Set the ID.
210         dec.setID(id);
211 
212         // Get ID field values.
213         int x = dec.getValue("ix");
214         int y = dec.getValue("iy");
215 
216         // Get field indices.
217         int ix = dec.getFieldIndex("ix");
218         int iy = dec.getFieldIndex("iy");
219 
220         // Get X, Y, & side neighbor data for this crystal.
221         Set<XY> neighbors = getNeighbors(x, y);
222 
223         // Get buffer with values from current ID.
224         int[] buffer = new int[dec.getFieldCount()];
225         dec.getValues(buffer);
226 
227         // Create an encoder to make neighbor IDs.
228         IDEncoder enc = new IDEncoder(dec.getIDDescription());
229 
230         // Set to hold neighbor IDs.
231         Set<Long> ids = new HashSet<Long>();
232 
233         // Loop over neighbor objects to make IDs.
234         for (XY xyside : neighbors) {
235             buffer[ix] = xyside.x;
236             buffer[iy] = xyside.y;
237             long nId = enc.setValues(buffer);
238             ids.add(nId);
239         }
240 
241         return ids;
242     }
243 
244     Set<XY> getNeighbors(int ix, int iy) {
245         Set<Integer> xneighbors = getXNeighbors(ix);
246         Set<Integer> yneighbors = getYNeighbors(iy);
247 
248         Set<XY> neighbors = new HashSet<XY>();
249 
250         for (Integer jx : xneighbors) {
251             for (Integer jy : yneighbors) {
252                 // Filter out self.
253                 if (jx == ix && jy == iy) {
254                     continue;
255                 }
256 
257                 // Check for valid neighbor.
258                 // FIXME: Duplication of isValidX + isValidY.
259                 if (!isValidXY(jx, jy))
260                     continue;
261 
262                 neighbors.add(new XY(jx, jy));
263             }
264         }
265 
266         return neighbors;
267     }
268 
269     Set<Integer> getXNeighbors(int ix) {
270         Set<Integer> neighbors = new HashSet<Integer>();
271 
272         // Add self.
273         neighbors.add(ix);
274 
275         // Left neighbor.
276         if (isValidX(ix - 1)) {
277             neighbors.add(ix - 1);
278         } else if (isValidX(ix - 2)) {
279             neighbors.add(ix - 2);
280         }
281 
282         // Right neighbor.
283         if (isValidX(ix + 1)) {
284             neighbors.add(ix + 1);
285         } else if (isValidX(ix + 2)) {
286             neighbors.add(ix + 2);
287         }
288 
289         return neighbors;
290     }
291 
292     Set<Integer> getYNeighbors(int iy) {
293         Set<Integer> neighbors = new HashSet<Integer>();
294 
295         // Add self.
296         neighbors.add(iy);
297 
298         // Lower neighbor.
299         if (isValidY(iy - 1)) {
300             neighbors.add(iy - 1);
301         }
302 
303         // Upper neighbor.
304         if (isValidY(iy + 1)) {
305             neighbors.add(iy + 1);
306         }
307 
308         return neighbors;
309     }
310 
311     boolean isValidY(int iy) {
312         // Zero is not valid because ID scheme goes from 1.
313         return iy >= -ny && iy <= ny && iy != 0;
314     }
315 
316     boolean isValidX(int ix) {
317         // Even case.
318         if (!oddX) {
319             return ix >= -nx / 2 && ix <= nx / 2 && ix != 0;
320         }
321         // Odd case.
322         else {
323             return ix >= (-nx - 1) / 2 && ix <= (nx + 1) / 2;
324         }
325     }
326 
327     /**
328      * Create a map of crystal IDs to the <code>Set</code> of neighbor crystal IDs.
329      * 
330      * @return A map of neighbors for each crystal ID.
331      */
332     public NeighborMap getNeighborMap() {
333         if (neighborMap != null) {
334             return neighborMap;
335         }
336 
337         // Setup the private instance of the map.
338         neighborMap = new NeighborMap(this.getDetectorElement().getIdentifierHelper());
339 
340         IDDecoder dec = getIDDecoder();
341         IDEncoder enc = new IDEncoder(dec.getIDDescription());
342 
343         int nfields = dec.getFieldCount();
344         int[] vals = new int[nfields];
345 
346         vals[dec.getFieldIndex("system")] = getSystemID();
347 
348         int idxx = dec.getFieldIndex("ix");
349         int idxy = dec.getFieldIndex("iy");
350 
351         /*
352         int hnx = nx;
353 
354         // Calculate number of X for loop. (from LCDD conv)
355         if (oddX) {
356             hnx -= 1;
357             hnx /= 2;
358         } else {
359             hnx /= 2;
360         }
361         */
362 
363         // Loop over y.
364         for (int iy = -ny; iy <= ny; iy++) {
365             int loopx = (int) Math.floor(nx / 2);
366             // Loop over x.
367             for (int ix = -loopx; ix <= loopx; ix++) {
368                 if (!isValidXY(ix, iy))
369                     continue;
370 
371                 vals[idxx] = ix;
372                 vals[idxy] = iy;
373 
374                 Long id = enc.setValues(vals);
375                 Set<Long> neighbors = getNeighbors(id);
376 
377                 neighborMap.put(id, neighbors);
378             }
379         }
380 
381         return neighborMap;
382     }
383 
384     public void appendHepRep(HepRepFactory factory, HepRep heprep) {
385         DetectorElementToHepRepConverter.convert(getDetectorElement(), factory, heprep, -1, false, getVisAttributes().getColor());
386     }
387 }