View Javadoc

1   package org.lcsim.geometry.compact.converter.lcdd;
2   
3   import static java.lang.Math.PI;
4   import static java.lang.Math.acos;
5   import static java.lang.Math.cos;
6   import static java.lang.Math.sin;
7   import static java.lang.Math.sqrt;
8   import static java.lang.Math.tan;
9   import static java.lang.Math.toDegrees;
10  
11  import java.util.Iterator;
12  
13  import org.jdom.Attribute;
14  import org.jdom.Element;
15  import org.jdom.JDOMException;
16  import org.lcsim.geometry.compact.converter.lcdd.util.Box;
17  import org.lcsim.geometry.compact.converter.lcdd.util.Define;
18  import org.lcsim.geometry.compact.converter.lcdd.util.LCDD;
19  import org.lcsim.geometry.compact.converter.lcdd.util.LCDDFactory;
20  import org.lcsim.geometry.compact.converter.lcdd.util.Material;
21  import org.lcsim.geometry.compact.converter.lcdd.util.PhysVol;
22  import org.lcsim.geometry.compact.converter.lcdd.util.PolyhedraRegular;
23  import org.lcsim.geometry.compact.converter.lcdd.util.Position;
24  import org.lcsim.geometry.compact.converter.lcdd.util.Rotation;
25  import org.lcsim.geometry.compact.converter.lcdd.util.SensitiveDetector;
26  import org.lcsim.geometry.compact.converter.lcdd.util.Trapezoid;
27  import org.lcsim.geometry.compact.converter.lcdd.util.Volume;
28  import org.lcsim.geometry.layer.LayerFromCompactCnv;
29  import org.lcsim.geometry.layer.LayerStack;
30  import org.lcsim.geometry.layer.Layering;
31  
32  /*
33   * Class to convert an EcalBarrel subdetector to the LCDD format.
34   * This subdetector constructs barrel staves in a pinwheel arrangement
35   * that is similar to the "ecal02" subdetector in the Mokka database.
36   * 
37   * @author Jeremy McCormick <jeremym@slac.stanford.edu>
38   * @version $Id: EcalBarrel.java,v 1.22 2009/11/02 20:39:42 jeremy Exp $
39   */
40  public class EcalBarrel extends LCDDSubdetector
41  {
42  	// Change to true if debugging.
43  	private boolean _debug = false;
44  	
45  	// 1 micron adjustment for geometric tolerances.
46  	// This is used to downsize the stave.  It is
47  	// also applied to the box dimensions of the
48  	// layers and slices.  Because these are half
49  	// measurements it results in a 2 micron tolerance
50  	// for these components.
51  	//private double tolerance=0.001;
52  	private double tolerance=0.0;	
53  
54  	EcalBarrel(Element node) throws JDOMException
55  	{
56  		super(node);
57  	}
58  
59  	/** Add the EcalBarrel geometry to an LCDD instance. */
60  	public void addToLCDD(LCDD lcdd, SensitiveDetector sens)
61  			throws JDOMException
62  	{	
63  		// Get the define block of LCDD.
64  		Define define = lcdd.getDefine();
65  		
66  		// The name of the detector.
67  		String name = node.getAttributeValue("name");
68  
69  		// The subdetector ID.
70  		int id = node.getAttribute("id").getIntValue();
71  		
72  		// Dimensions element.
73  		Element dimensions = node.getChild("dimensions");
74  		
75  		// Optional staves element.
76  		Element staves = node.getChild("staves");
77  
78  		// Check for required attributes.
79  		assert (dimensions != null);
80  		assert (dimensions.getAttribute("numsides") != null);
81  		assert (dimensions.getAttribute("rmin") != null);
82  		assert (dimensions.getAttribute("z") != null);
83  
84  		// Number of sides.
85  		int nsides = dimensions.getAttribute("numsides").getIntValue();
86  
87  		// Inner radius to front surface of barrel stave.
88  		double inner_radius = dimensions.getAttribute("rmin")
89  				.getDoubleValue();
90  		
91  		// The stave's Z dimension, which sets the Z of the subdetector.
92  		double module_y1 = dimensions.getAttribute("z").getDoubleValue();
93  		double module_y2 = module_y1;
94  				
95  		// Compute the delta phi per section.
96  		double dphi = PI * 2.0 / nsides;
97  		double hphi = dphi / 2;
98  
99  		// Compute the total thickness of the subdetector.
100 		double module_z = LayerFromCompactCnv
101 				.computeDetectorTotalThickness(node);
102 			
103 		// Compute the center Y offset of a single module.
104 		double module_y_offset = inner_radius + module_z / 2;
105 
106 		// Get the mother volume.
107 		Volume motherVolume = lcdd.pickMotherVolume(this);
108 		
109         double totalThickness = org.lcsim.geometry.layer.LayerFromCompactCnv.computeDetectorTotalThickness(node);
110         
111         // Create the polyhedra envelope for the subdetector.
112         PolyhedraRegular polyhedra = new PolyhedraRegular(
113                 name + "_polyhedra",
114                 nsides, inner_radius, inner_radius+totalThickness+tolerance*2.0, module_y1);
115         lcdd.getSolids().addSolid(polyhedra);
116         
117         // Create the volume for the envelope.
118         Volume envelopeVolume = new Volume(name + "_envelope");
119         envelopeVolume.setSolid(polyhedra);
120         Material air = lcdd.getMaterial("Air");
121         envelopeVolume.setMaterial(air);
122                 
123         // Set the rotation to make a side lay "flat".
124         double zrot = Math.PI / nsides;
125         Rotation rot = new Rotation(name + "_rotation");
126         rot.setZ(zrot);
127         define.addRotation(rot);
128         
129         // Create the physical volume of the subdetector.
130         PhysVol envelopePhysvol = new PhysVol(envelopeVolume);
131         envelopePhysvol.setRotation(rot);
132         envelopePhysvol.addPhysVolID("system",id);
133         envelopePhysvol.addPhysVolID("barrel",0);
134         motherVolume.addPhysVol(envelopePhysvol);
135 		
136 		// Compute the outer radius.
137 		double outer_radius = inner_radius + module_z;
138 
139 		// Compute trapezoid measurements.
140 		double bo = tan(hphi) * outer_radius;
141 		double bi = tan(hphi) * inner_radius;
142 
143 		// Compute the dx per layer, using side 
144 		// triangle calculations (from Norman Graf).
145 		double gamma = (PI * 2) / nsides;
146 		double dx = module_z / sin(gamma);
147 
148 		// The offset of a stave, derived from the dx term.
149 		double module_x_offset = dx / 2.0;
150 
151 		// Compute the top and bottom face measurements.
152 		double module_x2 = 2 * bo - dx;
153 		double module_x1 = 2 * bi + dx;
154 		
155 		// Create the trapezoid for the stave.
156 		Trapezoid module_trd = LCDDFactory.createTrapezoid(
157 				name + "_module_trd", 
158 				module_x1/2-tolerance, // Outer side, i.e. the "short" X side.
159 				module_x2/2-tolerance, // Inner side, i.e. the "long" X side.
160 				module_y1/2-tolerance, // Corresponds to subdetector (or module) Z.
161 				module_y2/2-tolerance, // "
162 				module_z/2-tolerance); // Thickness, in Y for top stave, when rotated.
163 		lcdd.add(module_trd);
164 
165 		// Create the logical volume for the stave.
166 		Volume module_volume = LCDDFactory.createVolume(name + "_module", lcdd
167 				.getMaterial("Air"), module_trd);
168 
169 		// DEBUG prints
170 		if (_debug)
171 		{
172 			System.out.println("name=" + name);
173 			System.out.println("nsides=" + nsides);
174 			System.out.println("inner_radius=" + inner_radius);
175 			System.out.println("module_y1=" + module_y1);
176 			System.out.println("module_y2=" + module_y2);
177 			System.out.println("module_z=" + module_z);
178 			System.out.println("module_y_offset=" + module_y_offset);
179 			System.out.println("module_x_offset=" + module_x_offset);
180 			System.out.println("gamma=" + gamma);
181 			System.out.println("dx=" + dx);
182 			System.out.println("bi=" + bi);
183 			System.out.println("bo=" + bo);
184 			System.out.println("");
185 		}
186 
187 		// Build the stave logical volume.
188 		try
189 		{
190 			buildBarrelStave(lcdd, sens, module_volume);
191 		} 
192 		catch (Exception e)
193 		{
194 			throw new RuntimeException("Failed to build layers into "
195 					+ module_volume.getVolumeName(), e);
196 		}
197 				
198 		// Set stave visualization.
199 		if (staves != null)
200 		{
201 			if (staves.getAttribute("vis") != null)
202 			{
203 				module_volume.setVisAttributes(lcdd.getVisAttributes(staves.getAttributeValue("vis")));
204 			}
205 		}
206 		
207 		// Add the stave volume to LCDD.				
208 		lcdd.add(module_volume);
209 
210 		// Phi start for a stave.
211 		double phi = ((PI) / nsides);
212 
213 		// Create nsides staves.
214 		for (int i = 0; i < nsides; i++)
215 		{			
216 			// Module number.
217 			int module_number = i;
218 
219 			// Rotation of the module.			
220 			Rotation rotation = LCDDFactory.createRotation(name + "_module"
221 					+ module_number + "_rotation", PI * 0.5, phi, 0);
222 			lcdd.add(rotation);
223 		
224 			// Compute the stave position; derived from calculations in Mokka 
225 			// Geometry/Tesla/Ecal02.cc
226 			double module_position_x = module_x_offset * cos(phi) - module_y_offset * sin(phi);
227 			double module_position_y = module_x_offset * sin(phi) + module_y_offset * cos(phi);
228 			double module_position_z = 0;
229 
230 			Position position = LCDDFactory.createPosition(
231 					name + "_module" + module_number + "_position", 
232 					module_position_x, module_position_y, module_position_z);
233 			lcdd.add(position);
234 
235 			// Place this module.
236 			PhysVol pv = LCDDFactory.createPhysVol(module_volume, position,
237 					rotation, null);
238 			pv.addPhysVolID("module", module_number);
239 
240 			// FIXME: put these ids on subdetector envelope when have it
241 			pv.addPhysVolID("system", node.getAttribute("id").getIntValue());
242 			pv.addPhysVolID("barrel", 0);
243 
244 			envelopeVolume.addPhysVol(pv);
245 
246 			// increment phi
247 			phi -= dphi;
248 		}
249 		        
250         // Set envelope volume attributes.
251         setAttributes(lcdd, node, envelopeVolume);
252 		
253 		lcdd.getStructure().addVolume(envelopeVolume);
254 	}
255 
256 	/**
257 	 * Build the barrel stave logical volume for this component.
258 	 * @param lcdd The LCDD file being created.
259 	 * @param subdetector The current EcalBarrel subdetector.
260 	 * @param sensitiveDetector The sensitive detector of the subdetector.
261 	 * @param container The trapezoid volume of the stave, to be filled with layers.
262 	 */
263 	private void buildBarrelStave(LCDD lcdd, /*LCDDSubdetector subdetector,*/
264 			SensitiveDetector sensitiveDetector, Volume container)
265 			throws Exception
266 	{
267 		Trapezoid trd = (Trapezoid) lcdd.getSolid(container.getSolidRef());
268 
269 		Element node = getElement();
270 
271 		if (trd == null)
272 		{
273 			throw new IllegalArgumentException("Volume " + container.getName()
274 					+ " is not a trapezoid.");
275 		}
276 
277 		double nsides = getElement().getChild("dimensions")
278 				.getAttribute("numsides").getDoubleValue();
279 
280 		Rotation irot = lcdd.getDefine().getRotation("identity_rot");
281 
282 		double z = trd.y1();
283 		double trd_z = trd.z();
284 
285 		// ------
286 		// Parameters for computing the layer X dimension, 
287 		// e.g. trapezoid's X1 value.
288 		// ------
289 		
290 		// Adjacent angle of triangle.
291 		double adj = (trd.x1() - trd.x2()) / 2;
292 		
293 		// Hypotenuse of triangle.
294 		double hyp = sqrt(trd_z * trd_z + adj * adj);
295 		
296 		// Lower-right angle of triangle.
297 		double beta = acos(adj / hyp);
298 		
299 		// Primary coefficient for figuring X.
300 		double tan_beta = tan(beta); 
301 		
302 		double subdetector_thickness = LayerFromCompactCnv
303 				.computeDetectorTotalThickness(node);
304 
305 		double layer_position_z = -(subdetector_thickness / 2);
306 
307 		String subdetector_name = getName();
308 
309 		// Delta phi.
310 		double dphi = PI * 2.0 / nsides;
311 
312 		// Half delta phi.
313 		double hphi = dphi / 2;
314 
315 		// Starting X dimension for the layer.
316 		double layer_dim_x = trd.x1();		
317 
318 		if (_debug)
319 		{
320 			System.out.println("slice start posZ=" + layer_position_z);
321 			System.out.println("dphi=" + toDegrees(dphi));
322 			System.out.println("hphi=" + toDegrees(hphi));
323 			System.out.println("starting slice X=" + layer_dim_x);
324 			System.out.println("adj=" + adj);
325 			System.out.println("beta=" + toDegrees(beta));
326 			System.out.println("");
327 		}
328 		
329 		Layering layering = Layering.makeLayering(node);
330 		
331 		LayerStack layers = layering.getLayerStack();
332 		
333 		// Loop over the sets of layer elements in the detector.
334 		int layer_number = 0;
335 		for (Iterator i = getElement().getChildren("layer")
336 				.iterator(); i.hasNext();)
337 		{
338 			Element layer_element = (Element) i.next();
339 			int repeat = (int)layer_element.getAttribute("repeat").getDoubleValue();
340 
341 			// Loop over number of repeats for this layer.
342 			for (int j=0; j<repeat; j++)
343 			{
344 				// Compute this layer's thickness.
345 				double layer_thickness = layers.getLayer(layer_number).getThickness();
346 				
347 				// Increment the Z position to place this layer.
348 				layer_position_z += layer_thickness / 2;
349 				
350 				// Name of the layer.
351 				String layer_name = subdetector_name + "_layer" + layer_number;
352 				
353 				// Position of the layer.
354 				Position layer_position = LCDDFactory.createPosition(
355 						layer_name + "_position", 0, 0, layer_position_z);
356 				lcdd.add(layer_position);
357 	
358 				// Compute the X dimension for this layer.
359 				double xcut = (layer_thickness / tan_beta);
360 				layer_dim_x -= xcut; 
361 								
362 				// Box of the layer.				
363 				Box layer_box = LCDDFactory.createBox(layer_name + "_box",
364 						layer_dim_x*2 - tolerance, 
365 						z*2 - tolerance, 
366 						layer_thickness - tolerance);
367 				lcdd.add(layer_box);
368 				
369 				// Volume of the layer.
370 				Volume layer_volume = LCDDFactory.createVolume(layer_name,
371 						lcdd.getMaterial("Air"), layer_box);
372 
373 				// Loop over the sublayers or slices for this layer.
374 				int slice_number = 0;
375 				double slice_position_z = -(layer_thickness / 2);
376 				for (Iterator k = layer_element.getChildren("slice").iterator(); k.hasNext();)
377 				{
378 					// XML element of slice.
379 					Element slice_element = (Element) k.next();
380 					
381 					// Name of the slice.
382 					String slice_name = layer_name + "_slice" + slice_number;
383 					
384 					// Sensitivity.
385 					Attribute s = slice_element.getAttribute("sensitive");
386 					boolean sensitive = s != null && s.getBooleanValue();
387 
388 					// Thickness of slice.
389 					double slice_thickness = slice_element.getAttribute("thickness").getDoubleValue();
390 
391 					// Increment Z position of slice.
392 					slice_position_z += slice_thickness / 2;
393 									
394 					// Position of slice.
395 					Position slice_position = LCDDFactory.createPosition(
396 							slice_name + "_position", 0, 0, slice_position_z);
397 					lcdd.add(slice_position);
398 					
399 					// Box of slice.
400 					Box slice_box = LCDDFactory.createBox(slice_name + "_box",
401 							layer_dim_x*2 - tolerance, 
402 							z*2 - tolerance, 
403 							slice_thickness - tolerance);
404 
405 					lcdd.add(slice_box);
406 
407 					// material of slice
408 					Material sliceMaterial = lcdd.getMaterial(slice_element
409 							.getAttributeValue("material"));
410 
411 					// volume of slice
412 					Volume slice_volume = LCDDFactory.createVolume(slice_name,
413 							sliceMaterial, slice_box);
414 					if (sensitive)
415 					{
416 						slice_volume.setSensitiveDetector(sensitiveDetector);
417 					}				
418 					
419 					setRegion(lcdd, slice_element, slice_volume);
420 					setLimitSet(lcdd, slice_element, slice_volume);
421 					setVisAttributes(lcdd, slice_element, slice_volume);
422 					
423 					// Add slice volume to LCDD.
424 					lcdd.add(slice_volume);
425 
426 					// Slice placement.
427 					PhysVol slice_physvol = LCDDFactory.createPhysVol(
428 							slice_volume, slice_position, irot);
429 					slice_physvol.addPhysVolID("layer", layer_number);
430 					slice_physvol.addPhysVolID("slice", slice_number);
431 					layer_volume.addPhysVol(slice_physvol);					
432 					
433 					// Increment Z position of slice.
434 					slice_position_z += slice_thickness / 2;
435 					
436 					// Increment slice number.
437 					++slice_number;
438 				}
439 				
440                 // Set region, limitset, and vis of layer.
441                 setRegion(lcdd, layer_element, layer_volume);
442                 setLimitSet(lcdd, layer_element, layer_volume);
443                 setVisAttributes(lcdd, layer_element, layer_volume);
444                 
445 				lcdd.add(layer_volume);
446 				
447 				// Place the layer.
448 				PhysVol layer_physvol = LCDDFactory.createPhysVol(
449 						layer_volume, layer_position, irot);
450 				layer_physvol.addPhysVolID("layer", layer_number);
451 				container.addPhysVol(layer_physvol);
452 
453 				// Increment to next layer Z position.
454 				layer_position_z += layer_thickness / 2;
455 				
456 				// Increment layer number.
457 				++layer_number;
458 			}
459 		}			
460 		
461 	}
462 
463 	public boolean isCalorimeter()
464 	{
465 		return true;
466 	}
467 
468 }
469 
470 // parameters from Mokka's ecal02 DB
471 //
472 // int nsides = 8;
473 // double inner_radius = 1700.0;
474 // double module_x_offset = 131.522;
475 // double module_y_offset = 1792.0;
476 // double module_x1=832.271;
477 // double module_x2=648.271;
478 // double module_y1=546.0;
479 // double module_y2=546.0;
480 // double module_z=92.0;