View Javadoc

1   /*
2    * NonprojectiveCylinder.java
3    *
4    * Created on March 30, 2005, 3:09 PM
5    */
6   package org.lcsim.geometry.segmentation;
7   
8   import static java.lang.Math.atan;
9   import static java.lang.Math.cos;
10  import static java.lang.Math.sin;
11  import static java.lang.Math.sqrt;
12  import static java.lang.Math.floor;
13  import static java.lang.Math.PI;
14  import org.jdom.DataConversionException;
15  import org.jdom.Element;
16  import org.lcsim.geometry.layer.Layer;
17  import org.lcsim.geometry.layer.LayerStack;
18  import org.lcsim.geometry.util.BaseIDDecoder;
19  import org.lcsim.geometry.util.IDDescriptor;
20  import org.lcsim.geometry.util.IDEncoder;
21  
22  /**
23   * @author jeremym
24   *
25   * Nonprojective segmentation of a cylinder with delta z and phi as parameters.
26   *
27   */
28  public class NonprojectiveCylinder extends BarrelCylinderSegmentationBase
29  {
30      private double gridSizePhi;
31      private double gridSizeZ;
32  
33      private int zIndex;
34      private int phiIndex;
35      private int systemIndex;
36      private int barrelIndex;
37      
38      private static final String fieldNames[] = {"gridSizePhi", "gridSizeZ"};
39  
40      /** Creates a new instance of NonprojectiveCylinder */
41      public NonprojectiveCylinder(Element node) throws DataConversionException
42      {
43          super(node);
44  
45          gridSizePhi = node.getAttribute("gridSizePhi").getDoubleValue();
46          gridSizeZ = node.getAttribute("gridSizeZ").getDoubleValue();
47          
48          this.cellSizes.add(0, gridSizePhi);
49          this.cellSizes.add(1, gridSizeZ);
50      }
51      
52      public String[] getSegmentationFieldNames() {
53          return fieldNames;
54      }
55      
56      public double getCellSizeU()
57      {
58          return gridSizeZ;
59      }
60      
61      public double getCellSizeV()
62      {
63          return gridSizeZ;
64      }
65  
66      void setGridSizePhi(double gsp)
67      {
68          gridSizePhi = gsp;
69      }
70  
71      void setGridSizeZ(double gsz)
72      {
73          gridSizeZ = gsz;
74      }
75  
76      public double getGridSizePhi()
77      {
78          return gridSizePhi;
79      }
80  
81      public double getGridSizeZ()
82      {
83          return gridSizeZ;
84      }
85  
86      public double getPhi()
87      {
88          return (((double) getValue(phiIndex)) + 0.5) * computeDeltaPhiForLayer();
89      }
90  
91      public double getTheta()
92      {
93          double x = this.getX();
94          double y = this.getY();
95          double theta = atan(sqrt(x * x + y * y) / getZ());
96  
97          /** Normalize to positive theta. */
98          if (theta < 0)
99          {
100             theta += PI;
101         }
102 
103         return theta;
104     }
105 
106     public double getX()
107     {
108         return getDistanceToSensitive( getLayer() ) * cos( getPhi() );
109     }
110 
111     public double getY()
112     {
113         return getDistanceToSensitive(getLayer()) * sin(getPhi());
114     }
115 
116     public double getZ()
117     {
118         return ((double) getValue(zIndex) + 0.5) * gridSizeZ;
119     }
120 
121     public double computeDeltaPhiForLayer(int layer)
122     {
123         double circ = getDistanceToSensitive(layer) * (2 * PI);
124         int nphi = (int) Math.floor(circ / gridSizePhi);
125         double deltaPhi = (2 * PI) / nphi;
126         return deltaPhi;
127     }
128 
129     public double computeDeltaPhiForLayer()
130     {
131         return computeDeltaPhiForLayer(getLayer());
132     }
133 
134     public void setIDDescription(IDDescriptor id)
135     {
136         super.setIDDescription(id);
137         phiIndex = id.indexOf("phi");
138         zIndex = id.indexOf("z");
139         systemIndex = id.indexOf("system");
140         barrelIndex = id.indexOf("barrel");
141     }
142 
143     public boolean supportsNeighbours()
144     {
145         return true;
146     }
147 
148     /**
149      * Find neighbouring cells to the current cell. Cell neighbors are found
150      * based on the direction (theta,phi) of the reference cell w.r.t the
151      * origin.
152      *
153      * @return array of cellIDs for the neighbouring cells
154      */
155     public long[] getNeighbourIDs(int layerRange, int zRange, int phiRange)
156     {
157         IDEncoder gnEncoder = new IDEncoder(descriptor);
158         BaseIDDecoder gnDecoder = new BaseIDDecoder(descriptor);
159         gnEncoder.setValues(values);
160         long origID = gnEncoder.getID();
161         gnDecoder.setID(gnEncoder.getID());
162 
163         int nMax = (2*layerRange + 1)*(2*zRange + 1)*(2*phiRange + 1) - 1;
164         long[] result = new long[nMax];
165 
166         // theta and phi are used to find central neighbors in other layers
167         double theta = getTheta();
168         double phi = getPhi();
169 
170         // Be careful with # of Z bins.
171 	// The rules are:
172 	//   * A cell is valid if its center has (z >= getZMin() && z <= getZMax())
173 	//   * The center of a bin is always at z = (integer + 0.5) * gridSizeZ
174 	//      => Bins are arranged about z=0, with a bin edge at z=0
175 	//      => If getZMin()==-getZMax() then there is always an even number of bins
176 	//
177 	// If things are nicely symmetric, i.e. getZMin()==-getZMax(), then this turns
178 	// into a very easy problem: if we have n bins ON EACH SIDE (for integer n) then
179 	//       n - 0.5 <= zMax/gridSizeZ < n + 0.5
180 	// and so we can work backwards (and add in the other half) to obtain the total
181 	// number of bins 2n as 2*Math.round((zMax-zMin)/(2.0*gridSizeZ)).
182 	//
183 	// If things are not symmetric but span the z=0 point then we're OK as long as
184 	// we're careful -- the easiest way to look at it is to do each half of the
185 	// detector separately:
186 	//    Number of bins in z>0:  Math.round((zMax - 0.0)/gridSizeZ)
187 	//    Number of bins in z<0:  Math.round((0.0 - zMin)/gridSizeZ)
188 	//
189 	// If things are asymmetric and do not include z=0 then things are more tricky again.
190 	// We'd have to work like this:
191 	//    a) find the first bin-center z1 after GetZMin()
192 	//    b) find the last bin-center z2 before GetZMax()
193 	//    c) count number of bins as Math.round((z2-z1)/gridSizeZ)
194 	// where 
195 	//   z1 = gridSizeZ * (Math.ceil ((zMin/gridSizeZ)+0.5) - 0.5)
196 	//   z2 = gridSizeZ * (Math.floor((zMax/gridSizeZ)-0.5) + 0.5)
197 	//
198 	// So the most general form for the number of bins should be
199 	//   Math.round( (gridSizeZ*(Math.floor((zMax/gridSizeZ)-0.5)+0.5) - gridSizeZ*(Math.floor((zMax/gridSizeZ)-0.5)+0.5) ) / gridSizeZ )
200 	//
201 	// ... but we will assume that the detector is symmetric here -- that is true
202 	// for any barrel calorimeter generated from a compact.xml geometry file.
203 	int zBins = 2 * (int) Math.round( (getZMax()-getZMin())/(2.0*getGridSizeZ()) );
204 
205         int size = 0;
206         for (int i = -layerRange; i <= layerRange; ++i)
207         {
208             int ilay = values[layerIndex] + i;
209 
210             if (ilay < 0 || ilay >= getNumberOfLayers())
211                 continue;
212             gnEncoder.setValue(layerIndex, ilay);
213 
214             double dphi = this.computeDeltaPhiForLayer(ilay);
215             int phiBins = (int) Math.round(2 * Math.PI / dphi); // Use round() not floor() since a floor() was already applied in the definition of dphi
216 
217 	    if (i != 0) {
218                 double cylR = getRadiusSensitiveMid(ilay);
219                 double x = cylR * Math.cos(phi);
220                 double y = cylR * Math.sin(phi);
221                 double z = cylR / Math.tan(theta);
222 
223                 long id = this.findCellContainingXYZ(x,y,z);
224                 if(id==0) continue;
225                 // save indices in a new array, as values[] keeps the original ref
226                 gnDecoder.setID(id);
227             } else {
228 		// This is the original layer => center cell is just the original cell
229                 gnDecoder.setID(origID);
230             }
231 
232             for (int j = -zRange; j <= zRange; ++j)
233             {
234                 int iz = gnDecoder.getValue(zIndex) + j;
235 
236                 if (iz < -zBins / 2 || iz >= zBins / 2) {
237 		    // We make the implicit assumption that detector is symmetric, and so number 
238 		    // of bins is even and looks like [-n, +n-1]. For example, for the
239 		    // very simple case of two bins, the range of bin indices is {-1, 0}.
240 		    // In this instance we're outside the valid range, so skip this bin.
241                     continue;
242 		}
243                 gnEncoder.setValue(zIndex, iz);
244 
245                 for (int k = -phiRange; k <= phiRange; ++k)
246                 {
247                     // skip reference cell (not a neighbor of its own)
248                     if (i==0 && j==0 && k==0) continue;
249 
250                     int iphi = gnDecoder.getValue(phiIndex) + k;
251                     // phi is cyclic
252                     if (iphi < 0) iphi += phiBins;
253                     if (iphi >= phiBins) iphi -= phiBins;
254 
255                     if (iphi < 0 || iphi >= phiBins) continue;
256                     gnEncoder.setValue(phiIndex, iphi);
257                     result[size++] = gnEncoder.getID();
258                 }
259             }
260         }
261 
262         // resize resulting array if necessary
263         if (size < result.length)
264         {
265             long[] temp = new long[size];
266             System.arraycopy(result, 0, temp, 0, size);
267             result = temp;
268         }
269 
270         return result;
271     }
272 
273     /**
274      * Return the cell which contains a given point (x,y,z), or zero.
275      *
276      * @param x Cartesian X coordinate.
277      * @param y Cartesian Y coordinate.
278      * @param z Cartesian Z coordinate.
279      *         
280      * @return ID of cell containing the point (maybe either in absorber or live
281      *         material)
282      */
283     public long findCellContainingXYZ(double x, double y, double z)
284     {
285 
286         // validate point
287         if (z < getZMin()) return 0;
288         if (z > getZMax()) return 0;
289         double rho = Math.sqrt(x * x + y * y);
290         if (rho < getRMin()) return 0;
291         if (rho > getRMax()) return 0;
292 
293         // ok, point is valid, so a valid ID should be returned
294         int ilay = getLayerBin(rho);
295         int iz = getZBin(z);
296 
297         double phi = Math.atan2(y, x);
298         if (phi < 0) phi += 2 * Math.PI;
299         int iphi = getPhiBin(ilay, phi);
300 
301         IDEncoder enc = new IDEncoder(descriptor);
302         enc.setValues(values);
303         enc.setValue(layerIndex, ilay);
304         enc.setValue(zIndex, iz);
305         enc.setValue(phiIndex, iphi);
306 	enc.setValue(barrelIndex,0);
307 	enc.setValue(systemIndex, detector.getSystemID());
308         long resultID = enc.getID();
309 
310         return resultID;
311     }
312 
313     public int getPhiBin(int ilay, double phi)
314     {
315         // phic is phi at center of cells with iphi=0
316         double deltaPhi = this.computeDeltaPhiForLayer(ilay);
317         double phic = deltaPhi / 2;
318         int iphi = (int) Math.floor((phi - phic) / deltaPhi + 0.5);
319         return iphi;
320     }
321 
322     public int getZBin(double z)
323     {
324         // zc is z at center of cells with iz=0
325         int numz = (int) Math.floor((getZMax() - getZMin()) / gridSizeZ);
326         // double zc = 0; // for odd numz
327         // if( numz%2 == 0 ) zc = gridSizeZ / 2;
328         double zc = gridSizeZ / 2;
329         int iz = (int) Math.floor((z - zc) / gridSizeZ + 0.5);
330         return iz;
331     }
332 
333     /**
334      * Returns cylindrical radius to center of sensitive slice of any layer
335      *
336      * @param layer
337      *            layer index
338      */
339     private double getRadiusSensitiveMid(int ilay)
340     {
341         LayerStack stack = getLayering().getLayers();
342         Layer layer = stack.getLayer(ilay);
343 
344         double preLayers = 0;
345         if (ilay > 0)
346             preLayers = stack.getSectionThickness(0, ilay - 1);
347 
348         return this.getRMin() + preLayers + layer.getThicknessToSensitiveMid();
349     }
350 }