View Javadoc

1   /*
2    * SiPixels.java
3    *
4    * Created on May 10, 2008, 4:52 PM
5    *
6    * To change this template, choose Tools | Template Manager
7    * and open the template in the editor.
8    */
9   package org.lcsim.detector.tracker.silicon;
10  
11  import hep.physics.vec.BasicHep3Vector;
12  import hep.physics.vec.Hep3Vector;
13  import hep.physics.vec.VecOp;
14  import java.util.HashSet;
15  import java.util.Set;
16  import java.util.SortedMap;
17  import java.util.TreeMap;
18  import org.lcsim.detector.IDetectorElement;
19  import org.lcsim.detector.ITransform3D;
20  import org.lcsim.detector.Transform3D;
21  import org.lcsim.detector.solids.GeomOp2D;
22  import org.lcsim.detector.solids.GeomOp3D;
23  import org.lcsim.detector.solids.Point3D;
24  import org.lcsim.detector.solids.Polygon3D;
25  import org.lcsim.util.probability.BivariateDistribution;
26  
27  /**
28   *
29   * @author tknelson
30   */
31  public class SiPixels implements SiSensorElectrodes {
32  
33      private ChargeCarrier _carrier; // charge carrier collected
34      private int _nrows; // number of rows - row index measures x
35      private int _ncols; // number of columns - column index measures y
36      private double _row_pitch; // row pitch
37      private double _col_pitch; // column pitch
38      private double _window_size = 3.0; // # sigma for computing electrode data
39      private double _capacitance = 0.1;  // capacitance of a pixel in pF
40  
41      // cached for convenience
42      private double _row_offset; // row offset
43      private double _col_offset; // column offset
44      private IDetectorElement _detector; // associated detector element
45      private ITransform3D _parent_to_local; // parent to local transform
46      private ITransform3D _local_to_global; // transformation to global coordinates
47      private ITransform3D _global_to_local; // transformation from global coordinates
48      private Polygon3D _geometry; // region in which strips are defined
49  
50      /** Creates a new instance of SiPixels */
51      public SiPixels(ChargeCarrier carrier, double row_pitch, double col_pitch,
52              IDetectorElement detector, ITransform3D parent_to_local) {
53  
54  //        System.out.println("Plane of polygon in sensor coordinates has... ");
55  //        System.out.println("                        normal: "+((SiSensor)detector).getBiasSurface(carrier).getNormal());
56  //        System.out.println("                        distance: "+((SiSensor)detector).getBiasSurface(carrier).getDistance());
57  
58          setCarrier(carrier);
59          setRowPitch(row_pitch);
60          setColumnPitch(col_pitch);
61          setGeometry(((SiSensor) detector).getBiasSurface(carrier).transformed(parent_to_local));
62          setPixelNumbering();
63          setDetectorElement(detector);
64          setParentToLocal(parent_to_local);
65          setGlobalToLocal(Transform3D.multiply(parent_to_local, detector.getGeometry().getGlobalToLocal()));
66          setLocalToGlobal(getGlobalToLocal().inverse());
67      }
68  
69      // Cell shape, assumed to be strips or rectancular pixels
70      public int getNAxes() {
71          return 2;
72      }
73  
74      // Get Detector element for associated sensor
75      public IDetectorElement getDetectorElement() {
76          return _detector;
77      }
78  
79      // Transformation from sensor coordinates to electrode coordinates
80      public ITransform3D getParentToLocal() {
81          return _parent_to_local;
82      }
83  
84      // Transformation from electrode coordinates to global coordinates
85      public ITransform3D getLocalToGlobal() {
86          return _local_to_global;
87      }
88  
89      // Transformation from gloabl coordinates to electrode coordinates
90      public ITransform3D getGlobalToLocal() {
91          return _global_to_local;
92      }
93  
94      // Polygon on which electrodes are defined
95      public Polygon3D getGeometry() {
96          return _geometry;
97      }
98  
99      // Direction of each measured coordinate
100     public Hep3Vector getMeasuredCoordinate(int axis) {
101         if (axis == 0) return new BasicHep3Vector(1.0, 0.0, 0.0);
102         else if (axis == 1) return new BasicHep3Vector(0.0, 1.0, 0.0);
103         else return null;
104     }
105 
106     // Direction of each non-measured coordinate (i.e. strip axis for strips)
107     public Hep3Vector getUnmeasuredCoordinate(int axis) {
108         if (axis == 0) return new BasicHep3Vector(0.0, 1.0, 0.0);
109         if (axis == 1) return new BasicHep3Vector(1.0, 0.0, 0.0);
110         else return null;
111     }
112 
113     // Neigbor ncells away along each axis
114     public int getNeighborCell(int cell_id, int ncells_row, int ncells_col) {
115 
116         //  Get the column and row numbers of the neighbor cell
117         int nbrcol = getColumnNumber(cell_id) + ncells_col;
118         int nbrrow = getRowNumber(cell_id) + ncells_row;
119 
120         //  Get teh neighbor cell ID and check that it's valid
121         int nbrcell = getCellID(nbrrow, nbrcol);
122         if (nbrcell < 0) return nbrcell;
123 
124         //  Check that the cell is valid (needed for non-rectangular geometries)
125         if (!isValidCell(cell_id)) return -1;
126 
127         //  Valid neighbor - return the cell ID
128         return nbrcell;
129     }
130 
131     // Get all nearest neighbor cells
132     public Set<Integer> getNearestNeighborCells(int cell_id) {
133         Set<Integer> neighbors = new HashSet<Integer>();
134 
135         //  Loop over cells in 3x3 array around cell_id
136         for (int irow = -1; irow <= 1; irow++) {
137             for (int icol = - 1; icol <= 1; icol++) {
138                 
139                 //  Exclude the starting cell
140                 if (irow == 0 && icol == 0) continue;
141 
142                 //  Get the neighbor cell and add it to the neighbor set if valid
143                 int neighbor = getNeighborCell(cell_id, irow, icol);
144                 if (neighbor >= 0) neighbors.add(neighbor);
145             }
146         }
147         return neighbors;
148     }
149 
150     // Cell number is valid
151     public boolean isValidCell(int cell_id) {
152         return GeomOp3D.intersects(new Point3D(getCellPosition(cell_id)), _geometry);  // FIXME: should cell position be a Point3D??
153     }
154 
155     // Number of cells (strips or pixels)
156     public int getNCells() {
157         return _nrows * _ncols;
158     }
159 
160     // Number of cells along each axis
161     public int getNCells(int axis) {
162         if (axis == 0) return _nrows;
163         else if (axis == 1) return _ncols;
164         else return 0;
165     }
166 
167     // Size of a cell (strip or pixel pitch)
168     public double getPitch(int axis) {
169         if (axis == 0) return _row_pitch;
170         else if (axis == 1) return _col_pitch;
171         else return 0;
172     }
173 
174     // ID of cell at a given position (cell number)
175     public int getCellID(Hep3Vector position) {
176         return getCellID(getRowNumber(position), getColumnNumber(position));
177     }
178 
179     // Row number of cell at given position
180     public int getRowNumber(Hep3Vector position) {
181         int row = (int) Math.round((position.x() + _row_offset) / _row_pitch);
182         if (row < 0) row = 0;
183         if (row >= _nrows) row = _nrows - 1;
184         return row;
185     }
186 
187     // Column number of cell at given position
188     public int getColumnNumber(Hep3Vector position) {
189         int col = (int) Math.round((position.y() + _col_offset) / _col_pitch);
190         if (col < 0) col = 0;
191         if (col >= _ncols) col = _ncols - 1;
192         return col;
193     }
194 
195     // ID of cell from row and column number
196     public int getCellID(int row_number, int column_number) {
197         if (row_number < 0 || row_number >=_nrows) return -1;
198         if (column_number < 0 || column_number >= _ncols) return -1;
199         return row_number * _ncols + column_number;
200     }
201 
202     // Row number of cell from ID
203     public int getRowNumber(int cell_id) {
204         return cell_id / _ncols;
205     }
206 
207     // Column number of cell from ID
208     public int getColumnNumber(int cell_id) {
209         return cell_id - getRowNumber(cell_id) * _ncols;
210     }
211 
212     // Location of given position within a cell
213     public Hep3Vector getPositionInCell(Hep3Vector position) {
214         return VecOp.sub(position, getCellPosition(getCellID(position)));
215     }
216 
217     // Position of a particular cell (by cell number)
218     public Hep3Vector getCellPosition(int cell_id) {
219         return new BasicHep3Vector(getRowNumber(cell_id) * _row_pitch - _row_offset, getColumnNumber(cell_id) * _col_pitch - _col_offset, 0.0);
220     }
221 
222     // Charge carrier
223     public ChargeCarrier getChargeCarrier() {
224         return _carrier;
225     }
226 
227     /**
228      * Returns the capacitance of a pixel in units of pF.  For SiPixels
229      * the capacitance of all pixels are taken to be the same.
230      *
231      * @param cell_id
232      * @return
233      */
234     public double getCapacitance(int cell_id) {
235         return getCapacitance();
236     }
237 
238     /**
239      * Nominal pixel capacitance in units of pF.
240      *
241      * @return
242      */
243     public double getCapacitance() {
244         return _capacitance;
245     }
246 
247     /**
248      * Set the pixel capacitance.  Currently, all pixels are assumed to have
249      * identical capacitance.  Note that the pixel capacitance is used in the
250      * readout noise calculation.  Units are pF.
251      *
252      * @param capacitance
253      */
254     public void setCapacitance(double capacitance) {
255         _capacitance = capacitance;
256     }
257 
258     /**
259      * Sets the size of the window used to distribute charge over.  The window
260      * size is specified in units the Gaussian RMS along the measurement axes.
261      * For example, setWindowSize(3.0) will result in the computeElectrodeData
262      * method calculating the charge deposition for all pixel cells that are
263      * fully or partially contained within a +-3 sigma window in the x and y
264      * directions about the mean of the charge distribution.
265      *
266      * @param window_size window size in units of sigma
267      */
268     public void setWindowSize(double window_size) {
269         _window_size = Math.abs(window_size);
270     }
271 
272     /**
273      * Integrate a 2D Gaussian charge distribution over the electrodes for this
274      * pixel sensor.  The charge distribution argument must be an instance of
275      * the GaussianDistribution2D, class, which provides the access to the
276      * parameters of the bivariate charge distribution.  The method uses the
277      * BivariateDistribution class to perform the integration.  The size of
278      * the pixel window can be set using the setWindowSize method.
279      *
280      * @param distribution charge distribution
281      * @return Map containing pixel charges keyed by pixel number
282      */
283     public SortedMap<Integer, Integer> computeElectrodeData(ChargeDistribution distribution) {
284 
285         //  Check to make sure we have a 2D charge distribution
286         if (!(distribution instanceof GaussianDistribution2D))
287             throw new RuntimeException("Electrode charge distribution not recognized");
288         GaussianDistribution2D gdistribution = (GaussianDistribution2D) distribution;
289 
290         //  Create a map to store the electrode data
291         SortedMap<Integer, Integer> electrode_data = new TreeMap<Integer, Integer>();
292 
293         //  Instantiate the bivariate probability distribution
294         BivariateDistribution bivariate = new BivariateDistribution();
295 
296         //  Find the center of the charge distribution
297         Hep3Vector gmean = gdistribution.getMean();
298         double x0 = gmean.x();
299         double y0 = gmean.y();
300 
301         //  Get the measurement axes - axis 0 is the x axis and axis 1 is the y axis
302         Hep3Vector xaxis = getMeasuredCoordinate(0);
303         Hep3Vector yaxis = getMeasuredCoordinate(1);
304 
305         //  Get the x, y widths and correlation coeficient for the charge distribution
306         double xsig = gdistribution.sigma1D(xaxis);
307         double ysig = gdistribution.sigma1D(yaxis);
308         double rho = gdistribution.covxy(xaxis, yaxis) / (xsig * ysig);
309 
310         //  Get the x and y pitches
311         double xpitch = getPitch(0);
312         double ypitch = getPitch(1);
313 
314         //  Find the window of cells that contain measurable charge by finding
315         //  the corner cells for a window around the base pixel
316         double xmin0 = x0 - _window_size * xsig;
317         double ymin0 = y0 - _window_size * ysig;
318         int cell1 = getCellID(new BasicHep3Vector(xmin0, ymin0, 0.));
319         double xmax0 = x0 + _window_size * xsig;
320         double ymax0 = y0 + _window_size * ysig;
321         int cell2 = getCellID(new BasicHep3Vector(xmax0, ymax0, 0.));
322 
323         //  Get the row and column indices for the corner cells
324         int ixmin = getRowNumber(cell1);
325         int iymin = getColumnNumber(cell1);
326         int ixmax = getRowNumber(cell2);
327         int iymax = getColumnNumber(cell2);
328 
329         //  Establish the x, y binning
330         Hep3Vector corner1 = getCellPosition(cell1);
331         double xmin = corner1.x() - 0.5 * xpitch;
332         double ymin = corner1.y() - 0.5 * ypitch;
333         int nxbins = ixmax - ixmin + 1;
334         int nybins = iymax - iymin + 1;
335         if (nxbins < 1) {
336             System.out.println("x binning error - ixmax: "+ixmax+" ixmin: "+ixmin+" xmin: "+xmin+" xmin0: "+xmin0+" xmax0: "+xmax0+" xsig: "+xsig);
337             nxbins = 1;
338         }
339         if (nybins < 1) {
340             System.out.println("y binning error - iymax: "+iymax+" iymin: "+iymin+" ymin: "+ymin+" ymin0: "+ymin0+" ymax0: "+ymax0+" ysig: "+ysig);
341             nybins = 1;
342         }
343 
344         bivariate.xBins(nxbins, xmin, xpitch);
345         bivariate.yBins(nybins, ymin, ypitch);
346 
347         //  Calculate the probability distribution for these bins
348         double[][] prob = bivariate.Calculate(x0, y0, xsig, ysig, rho);
349 
350         //  Get the total charge in the distribution
351         double normalization = gdistribution.getNormalization();
352 
353         //  Loop over the probability distribution bins
354         for (int ix = 0; ix < nxbins; ix++) {
355             for (int iy = 0; iy < nybins; iy++) {
356 
357                 //  Find the pixel corresponding to this bin
358                 int ipixel = getCellID(ixmin + ix, iymin + iy);
359 
360                 //  Make sure we have a valid pixel
361                 if (isValidCell(ipixel)) {
362 
363                     //  Calculate the charge in this pixel
364                     int pixel_charge = (int) Math.round(normalization * prob[ix][iy]);
365 
366                     //  Store the pixel charge in the electrode data map
367                     if (pixel_charge != 0) {
368                         electrode_data.put(ipixel, pixel_charge);
369                     }
370                 }
371             }
372         }
373         return electrode_data;
374     }
375 
376     // Private setters
377     //==================
378     public void setCarrier(ChargeCarrier carrier) {
379         _carrier = carrier;
380     }
381 
382     public void setGeometry(Polygon3D geometry) {
383 //        System.out.println("Plane of polygon has... ");
384 //        System.out.println("                        normal: "+geometry.getNormal());
385 //        System.out.println("                        distance: "+geometry.getDistance());
386 //
387 //        System.out.println("Working plane has... ");
388 //        System.out.println("                        normal: "+GeomOp2D.PLANE.getNormal());
389 //        System.out.println("                        distance: "+GeomOp2D.PLANE.getDistance());
390 
391         if (GeomOp3D.equals(geometry.getPlane(), GeomOp2D.PLANE)) {
392             _geometry = geometry;
393         } else {
394             throw new RuntimeException("Electrode geometry must be defined in x-y plane!!");
395         }
396     }
397 
398     private void setPixelNumbering() {
399         double xmin = Double.MAX_VALUE;
400         double xmax = Double.MIN_VALUE;
401         double ymin = Double.MAX_VALUE;
402         double ymax = Double.MIN_VALUE;
403         for (Point3D vertex : _geometry.getVertices()) {
404             xmin = Math.min(xmin, vertex.x());
405             xmax = Math.max(xmax, vertex.x());
406             ymin = Math.min(ymin, vertex.y());
407             ymax = Math.max(ymax, vertex.y());
408         }
409 
410         setNRows((int) Math.ceil((xmax - xmin) / getPitch(0)));
411         setNColumns((int) Math.ceil((ymax - ymin) / getPitch(1)));
412 
413     }
414 
415     private void setNRows(int nrows) {
416         _nrows = nrows;
417         setRowOffset();
418     }
419 
420     private void setNColumns(int ncolumns) {
421         _ncols = ncolumns;
422         setColumnOffset();
423     }
424 
425     private void setRowOffset() {
426         double xmin = Double.MAX_VALUE;
427         double xmax = Double.MIN_VALUE;
428         for (Point3D vertex : _geometry.getVertices()) {
429             xmin = Math.min(xmin, vertex.x());
430             xmax = Math.max(xmax, vertex.x());
431         }
432 
433         double row_center = (xmin + xmax) / 2;
434 
435         _row_offset = ((_nrows - 1) * _row_pitch) / 2 - row_center;
436 
437     }
438 
439     private void setColumnOffset() {
440         double ymin = Double.MAX_VALUE;
441         double ymax = Double.MIN_VALUE;
442         for (Point3D vertex : _geometry.getVertices()) {
443             ymin = Math.min(ymin, vertex.y());
444             ymax = Math.max(ymax, vertex.y());
445         }
446 
447         double column_center = (ymin + ymax) / 2;
448 
449         _col_offset = ((_ncols - 1) * _col_pitch) / 2 - column_center;
450 
451     }
452 
453     private void setRowPitch(double row_pitch) {
454         _row_pitch = row_pitch;
455     }
456 
457     private void setColumnPitch(double col_pitch) {
458         _col_pitch = col_pitch;
459     }
460 
461     private void setDetectorElement(IDetectorElement detector) {
462         _detector = detector;
463     }
464 
465     private void setParentToLocal(ITransform3D parent_to_local) {
466         _parent_to_local = parent_to_local;
467     }
468 
469     private void setLocalToGlobal(ITransform3D local_to_global) {
470         _local_to_global = local_to_global;
471     }
472 
473     private void setGlobalToLocal(ITransform3D global_to_local) {
474         _global_to_local = global_to_local;
475     }
476 }