View Javadoc

1   package org.lcsim.geometry.compact.converter.pandora;
2   
3   import static org.lcsim.geometry.Calorimeter.CalorimeterType.EM_BARREL;
4   import static org.lcsim.geometry.Calorimeter.CalorimeterType.EM_ENDCAP;
5   import static org.lcsim.geometry.Calorimeter.CalorimeterType.HAD_BARREL;
6   import static org.lcsim.geometry.Calorimeter.CalorimeterType.HAD_ENDCAP;
7   import static org.lcsim.geometry.Calorimeter.CalorimeterType.MUON_BARREL;
8   import static org.lcsim.geometry.Calorimeter.CalorimeterType.MUON_ENDCAP;
9   import hep.physics.particle.properties.ParticlePropertyManager;
10  import hep.physics.particle.properties.ParticleType;
11  import hep.physics.vec.BasicHep3Vector;
12  import hep.physics.vec.Hep3Vector;
13  
14  import java.io.InputStream;
15  import java.io.OutputStream;
16  import java.text.DecimalFormat;
17  import java.util.ArrayList;
18  import java.util.List;
19  
20  import javax.swing.filechooser.FileFilter;
21  
22  import org.jdom.Document;
23  import org.jdom.Element;
24  import org.jdom.output.Format;
25  import org.jdom.output.XMLOutputter;
26  import org.lcsim.conditions.ConditionsManager;
27  import org.lcsim.conditions.ConditionsManager.ConditionsNotFoundException;
28  import org.lcsim.conditions.ConditionsSet;
29  import org.lcsim.detector.material.BetheBlochCalculator;
30  import org.lcsim.detector.material.IMaterial;
31  import org.lcsim.detector.material.MaterialStore;
32  import org.lcsim.detector.solids.Tube;
33  import org.lcsim.geometry.Calorimeter;
34  import org.lcsim.geometry.Detector;
35  import org.lcsim.geometry.GeometryReader;
36  import org.lcsim.geometry.compact.Subdetector;
37  import org.lcsim.geometry.compact.converter.Converter;
38  import org.lcsim.geometry.field.Solenoid;
39  import org.lcsim.geometry.layer.Layer;
40  import org.lcsim.geometry.layer.LayerSlice;
41  import org.lcsim.geometry.layer.LayerStack;
42  import org.lcsim.geometry.segmentation.AbstractCartesianGrid;
43  import org.lcsim.geometry.subdetector.AbstractPolyhedraCalorimeter;
44  import org.lcsim.geometry.subdetector.MultiLayerTracker;
45  import org.lcsim.geometry.util.BaseIDDecoder;
46  import org.lcsim.geometry.util.IDDescriptor;
47  import org.lcsim.geometry.util.SamplingFractionManager;
48  
49  /**
50   * This class converts from a compact detector description into slicPandora's
51   * geometry input format.
52   * 
53   * @author Jeremy McCormick <jeremym@slac.stanford.edu>
54   * @version $Id: Main.java,v 1.34 2012/02/08 19:59:04 jeremy Exp $
55   */
56  public class Main implements Converter
57  {
58      private final static boolean DEBUG = false;
59      
60      // ConditionsManager instance.
61      private ConditionsManager conditionsManager = ConditionsManager.defaultInstance();
62      
63      // Numerical formatting.
64      static final DecimalFormat xlen = new DecimalFormat("#.########");
65      static final DecimalFormat xfrac = new DecimalFormat("#.########");
66      static final DecimalFormat xthick = new DecimalFormat("#.######");
67  
68      public void convert(String inputFileName, InputStream in, OutputStream out) throws Exception
69      {
70          GeometryReader reader = new GeometryReader();
71          Detector det = reader.read(in);
72          String detectorName = det.getDetectorName();
73          try
74          {
75              conditionsManager.setDetector(detectorName, 0);
76          }
77          catch (ConditionsNotFoundException x)
78          {
79              throw new RuntimeException("Failed to setup conditions system for detector: " + detectorName, x);
80          }
81          Document doc = convertDetectorToPandora(det);
82          XMLOutputter outputter = new XMLOutputter();
83          if (out != null)
84          {
85              outputter.setFormat(Format.getPrettyFormat());
86              outputter.output(doc, out);
87              out.close();
88          }
89      }
90  
91      public Document convertDetectorToPandora(Detector detector)
92      {
93          // Setup XML output document.
94          Document outputDoc = new Document();
95          Element root = new Element("pandoraSetup");
96          outputDoc.setRootElement(root);
97          Element calorimeters = new Element("calorimeters");
98          root.addContent(calorimeters);
99          
100         // Add basic detector data element.
101         Element detectorTag = new Element("detector");
102         detectorTag.setAttribute("name", detector.getDetectorName());
103         root.addContent(detectorTag);
104 
105         // Setup CalorimeterCalibration conditions.
106         ConditionsSet calorimeterCalibration = null;
107         try
108         {
109             calorimeterCalibration = conditionsManager.getConditions("CalorimeterCalibration");
110         }
111         catch (Exception x)
112         {
113         }
114         boolean haveCalCalib = (calorimeterCalibration == null) ? false : true;
115 
116         // Process the subdetectors.
117         for (Subdetector subdetector : detector.getSubdetectors().values())
118         {
119             //System.out.println(subdetector.getName());
120             // Only handle calorimeters that are planar.
121             if (subdetector instanceof AbstractPolyhedraCalorimeter)
122             {
123                 Element calorimeter = new Element("calorimeter");
124                 AbstractPolyhedraCalorimeter polycal = (AbstractPolyhedraCalorimeter) subdetector;
125 
126                 // Look for specific calorimeter types in the compact
127                 // description.
128                 Calorimeter.CalorimeterType calType = polycal.getCalorimeterType();
129                 if (calType.equals(HAD_BARREL) || calType.equals(HAD_ENDCAP) || calType.equals(EM_ENDCAP) || calType.equals(EM_BARREL) || calType.equals(MUON_BARREL) || calType.equals(MUON_ENDCAP))
130                 {
131                     // Set basic parameters in pandora calorimeter.
132                     calorimeter.setAttribute("type", Calorimeter.CalorimeterType.toString(calType));
133                     calorimeter.setAttribute("innerR", Double.toString(polycal.getInnerRadius()));
134                     calorimeter.setAttribute("innerZ", Double.toString(polycal.getInnerZ()));
135                     calorimeter.setAttribute("innerPhi", Double.toString(polycal.getSectionPhi()));
136                     calorimeter.setAttribute("innerSymmetryOrder", Double.toString(polycal.getNumberOfSides()));
137                     calorimeter.setAttribute("outerR", Double.toString(polycal.getOuterRadius()));
138                     calorimeter.setAttribute("outerZ", Double.toString(polycal.getOuterZ()));
139                     calorimeter.setAttribute("outerPhi", Double.toString(polycal.getSectionPhi()));
140                     calorimeter.setAttribute("outerSymmetryOrder", Double.toString(polycal.getNumberOfSides()));
141                     calorimeter.setAttribute("collection", subdetector.getReadout().getName());
142 
143                     // Get the cell sizes from the segmentation.
144                     List<Double> cellSizes = getCellSizes(subdetector);
145                     
146                     // For endcaps, X is U, and Y is V.
147                     if (subdetector.isEndcap())
148                     {
149                         calorimeter.setAttribute("cellSizeU", Double.toString(cellSizes.get(0)));
150                         calorimeter.setAttribute("cellSizeV", Double.toString(cellSizes.get(1)));
151                     }
152                     // The UV mapping is flipped around for barrel.  X is V, and Y is U.
153                     else if (subdetector.isBarrel())
154                     {
155                         calorimeter.setAttribute("cellSizeU", Double.toString(cellSizes.get(1)));
156                         calorimeter.setAttribute("cellSizeV", Double.toString(cellSizes.get(0)));
157                     }
158 
159                     // Create identifier description and add to subdet.
160                     calorimeter.addContent(makeIdentifierDescription(polycal));
161 
162                     // Add the calorimeter.
163                     calorimeters.addContent(calorimeter);
164 
165                     LayerStack layers = polycal.getLayering().getLayerStack();
166 
167                     Element layersElem = new Element("layers");
168                     layersElem.setAttribute("nlayers", Integer.toString(layers.getNumberOfLayers()));
169 
170                     calorimeter.addContent(layersElem);
171 
172                     double layerD = 0.;
173 
174                     if (polycal.isBarrel())
175                     {
176                         layerD = polycal.getInnerRadius();
177                     }
178                     else if (polycal.isEndcap())
179                     {
180                         layerD = polycal.getInnerZ();
181                     }
182 
183                     CalorimeterConditions subdetectorCalorimeterConditions = null;
184 
185                     if (haveCalCalib)
186                     {
187                         subdetectorCalorimeterConditions = new CalorimeterConditions((Calorimeter) subdetector, calorimeterCalibration);
188                     }
189 
190                     // Set MIP energy from calibration.
191                     if (haveCalCalib)
192                     {
193                         calorimeter.setAttribute("mipEnergy", xfrac.format(subdetectorCalorimeterConditions.getMipEnergy()));
194                         calorimeter.setAttribute("mipSigma", xfrac.format(subdetectorCalorimeterConditions.getMipSigma()));
195                         calorimeter.setAttribute("mipCut", xfrac.format(subdetectorCalorimeterConditions.getMipCut()));
196                         calorimeter.setAttribute("timeCut", xfrac.format(subdetectorCalorimeterConditions.getTimeCut()));
197                     } 
198                     // Set MIP energy from Bethe-Bloche calculation.
199                     // TODO Check accuracy of this algorithm.
200                     else
201                     {
202                         List<LayerSlice> sensors = subdetector.getLayering().getLayerStack().getLayer(0).getSensors();
203                         LayerSlice sensor = sensors.get(0);
204                         IMaterial sensorMaterial = MaterialStore.getInstance().get(sensor.getMaterial().getName());
205 
206                         ParticleType particleType = ParticlePropertyManager.getParticlePropertyProvider().get(13);
207 
208                         Hep3Vector p = new BasicHep3Vector(-6.8641, -7.2721, 1.2168e-7);
209 
210                         double emip = BetheBlochCalculator.computeBetheBloch(sensorMaterial, p, particleType.getMass(), particleType.getCharge(), sensor.getThickness());
211 
212                         // Set MIP Energy from Bethe Bloche.
213                         calorimeter.setAttribute("mipEnergy", xfrac.format(emip));
214 
215                         // Set defaults for CalCalib parameters.
216                         calorimeter.setAttribute("mipSigma", "0");
217                         calorimeter.setAttribute("mipCut", "0");
218                         calorimeter.setAttribute("timeCut", xfrac.format(Double.MAX_VALUE));
219                     }
220                     
221                     double totalX0 = 0;
222 
223                     for (int i = 0; i < layers.getNumberOfLayers(); i++)
224                     {
225                         //System.out.println("  layer " + i);
226                         Layer layer = layers.getLayer(i);
227 
228                         Element layerElem = new Element("layer");
229                         layersElem.addContent(layerElem);
230 
231                         // Set radiation and interaction lengths.
232                         double intLen = 0;
233                         double radLen = 0;
234                         for (int j = 0; j < layer.getNumberOfSlices(); j++)
235                         {
236                             LayerSlice slice = layer.getSlice(j);
237                             //System.out.println("    slice " + j + " " + slice.getMaterial().getName());                            
238                             double x0 = slice.getMaterial().getRadiationLength();
239                             //System.out.println("      x0_mat_D="+x0);
240                             //System.out.println("      x0_mat="+slice.getMaterial().getRadiationLength());
241                             radLen += slice.getThickness() / (x0*10);
242                             //System.out.println("      radLen="+radLen);
243                             
244                             double lambda = slice.getMaterial().getNuclearInteractionLength();
245                             intLen += slice.getThickness() / (lambda*10);
246                         }
247                         //System.out.println("    x0_lyr_tot=" + radLen);
248                         
249                         
250                         totalX0 += radLen;
251                         
252                         //System.out.println("    layer " + i + " " + radLen);
253                         
254                         layerElem.setAttribute("radLen", xlen.format(radLen));
255                         layerElem.setAttribute("intLen", xlen.format(intLen));
256 
257                         // Set distance to IP.
258                         double layerD2 = layerD + layer.getThicknessToSensitiveMid();
259                         layerElem.setAttribute("distanceToIp", xthick.format(layerD2));
260 
261                         // Set cell thickness.
262                         layerElem.setAttribute("cellThickness", xthick.format(layer.getThickness()));
263 
264                         // Set EM and HAD sampling fractions from
265                         // CalorimeterCalibration conditions, if present.
266                         if (haveCalCalib)
267                         {
268                             SamplingLayerRange layerRange = subdetectorCalorimeterConditions.getSamplingLayerRange(i);
269                             if (calType == EM_BARREL || calType == EM_ENDCAP)
270                             {
271                                 layerElem.setAttribute("samplingFraction", xfrac.format(layerRange.getEMSampling()));
272                             }
273                             if (calType == HAD_BARREL || calType == HAD_ENDCAP)
274                             {
275                                 layerElem.setAttribute("samplingFraction", xfrac.format(layerRange.getHADSampling()));
276                             }
277                             if (calType == MUON_BARREL || calType == MUON_ENDCAP)
278                             {
279                                 layerElem.setAttribute("samplingFraction", xfrac.format(layerRange.getHADSampling()));
280                             }
281                             layerElem.setAttribute("emSamplingFraction", xfrac.format(layerRange.getEMSampling()));
282                             layerElem.setAttribute("hadSamplingFraction", xfrac.format(layerRange.getHADSampling()));
283                         }
284                         // Set from base SamplingFraction conditions. May throw
285                         // an exception if neither CalorimeterCalibration
286                         // or SamplingFractions conditions exists.
287                         else
288                         {
289                             double samplingFraction = SamplingFractionManager.defaultInstance().getSamplingFraction(subdetector, i);
290                             layerElem.setAttribute("emSamplingFraction", xfrac.format(samplingFraction));
291                             layerElem.setAttribute("hadSamplingFraction", xfrac.format(samplingFraction));
292                         }
293 
294                         // Increment layer distance by thickness of layer.
295                         layerD += layer.getThickness();
296                     }
297                     
298                     //System.out.println("    X0 Sum = " + totalX0);
299                 }
300 
301                 // Set digital flag.
302                 try
303                 {
304                     // Set digital attribute from conditions, if present.
305                     ConditionsSet conditions = conditionsManager.getConditions("SamplingFractions/" + subdetector.getName());
306                     boolean isDigital = conditions.getBoolean("digital");
307                     calorimeter.setAttribute("digital", String.valueOf(isDigital));
308                 }
309                 catch (Exception x)
310                 {
311                     calorimeter.setAttribute("digital", "false");
312                 }                
313             }                        
314         }
315                      
316         // TODO clean up the hard coded assumptions on coil geometry
317         double coilRadLen = 0;
318         double coilIntLen = 0;
319         int coilLayers = 0;
320         double coilInnerR = 0;
321         double coilOuterR = 0;
322         double bfield = 0;
323         double coilMaxZ = 0;
324         try 
325         {
326         	MultiLayerTracker c = (MultiLayerTracker) detector.getSubdetector("SolenoidCoilBarrel");
327         	if (c != null) 
328         	{
329         		coilLayers = c.getNumberOfLayers();
330         		coilInnerR = c.getInnerR()[0];
331         		coilOuterR = c.getInnerR()[coilLayers-1] + c.getLayerThickness(coilLayers-1);
332         		for (int layern = 0; layern != c.getNumberOfLayers(); layern++) 
333         		{
334         		    for (LayerSlice slice : c.getLayer(layern).getSlices())
335         		    {
336         		        double x0 = slice.getMaterial().getRadiationLength();
337         		        double sliceRadLen = slice.getThickness() / (x0*10);                   
338         		        double lambda = slice.getMaterial().getNuclearInteractionLength();
339         		        double sliceIntLen = slice.getThickness() / (lambda*10);
340         		        
341         		        coilRadLen += sliceRadLen;
342         		        coilIntLen += sliceIntLen; 
343         		    }
344         		}
345         		//calculate average interaction/radiation length in coil material
346         		coilRadLen = coilRadLen/(coilOuterR-coilInnerR);
347         		coilIntLen = coilIntLen/(coilOuterR-coilInnerR);
348         	}        
349         } 
350         catch (ClassCastException e) 
351         {        
352             throw new RuntimeException(e);
353         }
354         try 
355         {
356         	Solenoid s = (Solenoid) detector.getFields().get("GlobalSolenoid");
357         	if (s != null) 
358         	{
359         		bfield = s.getField(new BasicHep3Vector(0, 0, 0)).z();
360         		coilMaxZ = s.getZMax();
361         	}
362         } 
363         catch (ClassCastException e) 
364         {
365             throw new RuntimeException(e);
366         }
367         
368         Element coil = new Element("coil");
369         coil.setAttribute("radLen", xlen.format(coilRadLen));
370         coil.setAttribute("intLen", xlen.format(coilIntLen));
371         coil.setAttribute("innerR", Double.toString(coilInnerR));
372         coil.setAttribute("outerR", Double.toString(coilOuterR));
373         coil.setAttribute("z", Double.toString(coilMaxZ));
374         coil.setAttribute("bfield", Double.toString(bfield));
375         root.addContent(coil);        
376 
377         Tube tube = (Tube) detector.getTrackingVolume().getLogicalVolume().getSolid();
378         Element tracking = new Element("tracking");
379         tracking.setAttribute("innerR", Double.toString(tube.getInnerRadius()));
380         tracking.setAttribute("outerR", Double.toString(tube.getOuterRadius()));
381         tracking.setAttribute("z", Double.toString(tube.getZHalfLength()));
382         root.addContent(tracking);
383 
384         return outputDoc;
385     }
386 
387     Element makeIdentifierDescription(Subdetector subdet)
388     {
389         IDDescriptor descr = subdet.getIDDecoder().getIDDescription();
390         Element id = new Element("id");
391         for (int i = 0, j = descr.fieldCount(); i < j; i++)
392         {
393             Element field = new Element("field");
394             field.setAttribute("name", descr.fieldName(i));
395             field.setAttribute("length", Integer.toString(descr.fieldLength(i)));
396             field.setAttribute("start", Integer.toString(descr.fieldStart(i)));
397             field.setAttribute("signed", Boolean.toString(descr.isSigned(i)));
398 
399             id.addContent(field);
400         }
401         return id;
402     }
403 
404     private List<Double> getCellSizes(Subdetector subdetector)
405     {
406         List<Double> cellSizes = new ArrayList<Double>();
407         BaseIDDecoder dec = (BaseIDDecoder) subdetector.getReadout().getIDDecoder();
408         if (dec instanceof AbstractCartesianGrid)
409         {
410             AbstractCartesianGrid cgrid = (AbstractCartesianGrid) dec;
411             if (cgrid.getGridSizeX() != 0)
412             {
413                 cellSizes.add(cgrid.getGridSizeX());
414             }
415             if (cgrid.getGridSizeY() != 0)
416             {
417                 cellSizes.add(cgrid.getGridSizeY());
418             }
419             if (cgrid.getGridSizeZ() != 0)
420             {
421                 cellSizes.add(cgrid.getGridSizeZ());
422             }
423         }
424         if (cellSizes.size() != 2)
425             throw new RuntimeException("Only 2 cell dimensions are allowed.");
426         return cellSizes;
427     }
428 
429     public String getOutputFormat()
430     {
431         return "pandora";
432     }
433 
434     public FileFilter getFileFilter()
435     {
436         return new PandoraFileFilter();
437     }
438 
439     private static class PandoraFileFilter extends FileFilter
440     {
441 
442         public boolean accept(java.io.File file)
443         {
444             return file.getName().endsWith(".xml");
445         }
446 
447         public String getDescription()
448         {
449             return "Pandora Geometry file (*.xml)";
450         }
451     }
452 }