View Javadoc

1   package org.lcsim.recon.tracking.seedtracker;
2   
3   import hep.physics.vec.Hep3Vector;
4   
5   import java.util.ArrayList;
6   import java.util.HashMap;
7   import java.util.List;
8   import java.util.Map;
9   
10  import org.lcsim.detector.IDetectorElement;
11  import org.lcsim.detector.IPhysicalVolume;
12  import org.lcsim.detector.IPhysicalVolumeNavigator;
13  import org.lcsim.detector.IPhysicalVolumePath;
14  import org.lcsim.detector.ITransform3D;
15  import org.lcsim.detector.PhysicalVolumeNavigator;
16  import org.lcsim.detector.PhysicalVolumeNavigatorStore;
17  import org.lcsim.detector.PhysicalVolumePath;
18  import org.lcsim.detector.material.IMaterial;
19  import org.lcsim.detector.solids.Box;
20  import org.lcsim.detector.solids.ISolid;
21  import org.lcsim.detector.solids.Point3D;
22  import org.lcsim.detector.solids.Polycone;
23  import org.lcsim.detector.solids.Polycone.ZPlane;
24  import org.lcsim.detector.solids.Trd;
25  import org.lcsim.detector.solids.Tube;
26  import org.lcsim.geometry.Detector;
27  import org.lcsim.geometry.Subdetector;
28  import org.lcsim.geometry.subdetector.DiskTracker;
29  import org.lcsim.geometry.subdetector.MultiLayerTracker;
30  import org.lcsim.geometry.subdetector.PolyconeSupport;
31  import org.lcsim.geometry.subdetector.SiTrackerBarrel;
32  import org.lcsim.geometry.subdetector.SiTrackerEndcap;
33  import org.lcsim.geometry.subdetector.SiTrackerEndcap2;
34  import org.lcsim.geometry.subdetector.SiTrackerFixedTarget2;
35  import org.lcsim.geometry.subdetector.SiTrackerSpectrometer;
36  
37  /**
38   * Rewrite and refactor of Rich's {@link MaterialManager} class to handle Subdetector types. This class should now group
39   * together SiTrackerEndcap2 layers correctly.
40   *
41   * @author Rich Partridge
42   * @author Jeremy McCormick
43   * @author Matt Graham
44   * @version $Id: MaterialManager.java,v 1.21 2013/07/12 20:47:35 phansson Exp $
45   */
46  public class MaterialManager {
47  
48      /**
49       * Get the path groupings for barrel Subdetectors with physical layers one level below top. This will handle
50       * SiTrackerBarrel and MultiLayerTracker Subdetector types.
51       */
52      public static final class BarrelLayerVolumeGroup implements SubdetectorVolumeGrouper {
53  
54          @Override
55          public List<List<String>> getPathGroups(final Subdetector subdet, final IPhysicalVolume topVol) {
56              final List<List<String>> pathGroups = new ArrayList<List<String>>();
57              for (final IDetectorElement layer : subdet.getDetectorElement().getChildren()) {
58                  final List<String> layerPaths = new ArrayList<String>();
59                  final String path = "";
60                  PhysicalVolumeNavigator.getLeafPaths(layerPaths, layer.getGeometry().getPhysicalVolume(), path);
61                  pathGroups.add(layerPaths);
62              }
63              return pathGroups;
64          }
65      }
66  
67      /**
68       * Default VolumeGroup for endcaps with physical layers.
69       */
70      public static final class EndcapVolumeGrouper implements SubdetectorVolumeGrouper {
71  
72          @Override
73          public List<List<String>> getPathGroups(final Subdetector subdet, final IPhysicalVolume topVol) {
74              final List<List<String>> pathGroups = new ArrayList<List<String>>();
75              // Positive and negative endcap loop.
76              for (final IDetectorElement endcaps : subdet.getDetectorElement().getChildren()) {
77                  // Layer loop.
78                  for (final IDetectorElement layer : endcaps.getChildren()) {
79                      final List<String> layerPaths = new ArrayList<String>();
80                      final String path = "";
81                      PhysicalVolumeNavigator.getLeafPaths(layerPaths, layer.getGeometry().getPhysicalVolume(), path);
82                      pathGroups.add(layerPaths);
83                  }
84              }
85              return pathGroups;
86          }
87  
88      }
89  
90      /**
91       * Get the path groups for a PolyconeSupport, which is a single path.
92       */
93      public static final class PolyconeSupportVolumeGrouper implements SubdetectorVolumeGrouper {
94  
95          @Override
96          public List<List<String>> getPathGroups(final Subdetector subdet, final IPhysicalVolume topVol) {
97              final List<List<String>> pathGroups = new ArrayList<List<String>>();
98              final String path = "";
99              final List<String> supportPath = new ArrayList<String>();
100             final IPhysicalVolume supportPV = subdet.getDetectorElement().getChildren().get(0).getGeometry()
101                     .getPhysicalVolume();
102             PhysicalVolumeNavigator.getLeafPaths(supportPath, supportPV, path);
103             pathGroups.add(supportPath);
104             return pathGroups;
105         }
106     }
107 
108     /**
109      * Get the path groups for SiTrackerEndcap2, which has modules placed directly in the tracking volume.
110      */
111     public static final class SiTrackerEndap2VolumeGrouper implements SubdetectorVolumeGrouper {
112 
113         @Override
114         public List<List<String>> getPathGroups(final Subdetector subdet, final IPhysicalVolume topVol) {
115             final List<List<String>> pathGroups = new ArrayList<List<String>>();
116             // Positive and negative endcap loop.
117             for (final IDetectorElement endcaps : subdet.getDetectorElement().getChildren()) {
118                 // Layer loop.
119                 for (final IDetectorElement layer : endcaps.getChildren()) {
120                     final List<String> modulePaths = new ArrayList<String>();
121 
122                     // Module loop.
123                     for (final IDetectorElement module : layer.getChildren()) {
124                         final String path = "";
125                         PhysicalVolumeNavigator.getLeafPaths(modulePaths, module.getGeometry().getPhysicalVolume(),
126                                 path);
127                     }
128 
129                     // for (String p : modulePaths) {
130                     // System.out.println("adding path: " + p);
131                     // }
132 
133                     // Add module paths to this layer.
134                     pathGroups.add(modulePaths);
135                 }
136             }
137             return pathGroups;
138         }
139     }
140 
141     /**
142      * Get the path groups for SiTrackerFixedTarget2
143      */
144     public static final class SiTrackerFixedTarget2VolumeGrouper implements SubdetectorVolumeGrouper {
145 
146         @Override
147         public List<List<String>> getPathGroups(final Subdetector subdet, final IPhysicalVolume topVol) {
148             final List<List<String>> pathGroups = new ArrayList<List<String>>();
149             // Positive and negative endcap loop.
150             for (final IDetectorElement endcaps : subdet.getDetectorElement().getChildren()) {
151                 // Layer loop.
152                 for (final IDetectorElement layer : endcaps.getChildren()) {
153                     final List<String> modulePaths = new ArrayList<String>();
154                     // System.out.println(layer.getName());
155 
156                     // Module loop.
157                     for (final IDetectorElement module : layer.getChildren()) {
158                         final String path = "";
159                         PhysicalVolumeNavigator.getLeafPaths(modulePaths, module.getGeometry().getPhysicalVolume(),
160                                 path);
161                     }
162                     // Add module paths to this layer.
163                     pathGroups.add(modulePaths);
164                 }
165             }
166             return pathGroups;
167         }
168     }
169 
170     /**
171      * Interface for getting the path groupings for different Subdetector types.
172      */
173     public interface SubdetectorVolumeGrouper {
174 
175         List<List<String>> getPathGroups(Subdetector subdet, IPhysicalVolume topVol);
176     }
177 
178     /**
179      * A UniquePV is a wrapper around IPhysicalVolumePath which provides some convenience methods and caches
180      * transformations.
181      */
182     static class UniquePV {
183 
184         IPhysicalVolumeNavigator nav;
185         IPhysicalVolumePath path;
186         ITransform3D transform = null;
187 
188         /**
189          * Generates a top-level UniquePV.
190          *
191          * @param root The top-level IPhysicalVolume
192          * @param navigator The IPhysicalVolumeNavigator associated with the detector
193          */
194         public UniquePV(final IPhysicalVolume root, final IPhysicalVolumeNavigator navigator) {
195             path = new PhysicalVolumePath();
196             nav = navigator;
197             path.add(root);
198         }
199 
200         /**
201          * Generates a UniquePV from a path. (Shallow copy of path)
202          *
203          * @param path
204          * @param navigator
205          */
206         public UniquePV(final IPhysicalVolumePath path, final IPhysicalVolumeNavigator navigator) {
207             this.path = path;
208             nav = navigator;
209         }
210 
211         /**
212          * Creates a UniquePV that is a daughter of the current UniquePV (deep copy made)
213          *
214          * @param daughter
215          * @return
216          */
217         public UniquePV createDaughterUniquePV(final IPhysicalVolume daughter) {
218             final IPhysicalVolumePath np = new PhysicalVolumePath();
219             np.addAll(path);
220             np.add(daughter);
221             return new UniquePV(np, nav);
222         }
223 
224         /**
225          * Returns the local-to-global transform
226          *
227          * @return an ITransform3D from local coordinates to global coordinates.
228          */
229         public ITransform3D getLtoGTransform() {
230             if (transform == null) {
231                 transform = nav.getTransform(path);
232             }
233             return transform;
234         }
235 
236         /**
237          * Returns the IPhysicalVolume (the last element of the path)
238          */
239         public IPhysicalVolume getPV() {
240             return path.getLeafVolume();
241         }
242 
243         /**
244          * Returns the solid associated with the physical volume.
245          *
246          * @return
247          */
248         public ISolid getSolid() {
249             return this.getPV().getLogicalVolume().getSolid();
250         }
251 
252         /**
253          * Transforms the given vector from local to global coords.
254          *
255          * @param v the untransformed local Hep3Vector
256          * @return the transformed global Hep3Vector
257          */
258         public Hep3Vector localToGlobal(final Hep3Vector v) {
259 
260             return this.getLtoGTransform().transformed(v);
261         }
262 
263         @Override
264         public String toString() {
265             return path.toString();
266         }
267     }
268 
269     /**
270      * A "struct" holding geometry information about lists of physical volumes
271      */
272     class VolumeGroupInfo {
273 
274         double rmax = 0.0;
275         double rmin = 1.e10;
276         double vtot = 0.0;
277         double vtot_tube_only = 0.;
278         double weighted_r = 0.0;
279         double weighted_y = 0.0;
280         double weighted_z = 0.0;
281         double X0 = 0.0;
282         double xmax = -1.e10;
283         double xmin = 1.e10;
284         double ymax = -1.e10;
285         // mg 3/14/11 MaterialXPlane info
286         double ymin = 1.e10;
287         double zmax = -1.e10;
288         double zmin = 1.e10;
289 
290     }
291 
292     /**
293      * A "struct" holding geometry information about a single physical volume
294      */
295     class VolumeInfo {
296 
297         double rmax = 0.0;
298         double rmin = 1.e10;
299         double xmax = -1.e10;
300         double xmin = 1.e10;
301         double ymax = -1.e10;
302         // mg 3/14/11 MaterialXPlane info
303         double ymin = 1.e10;
304         double zmax = -1.e10;
305         double zmin = 1.e10;
306     }
307 
308     private static ITransform3D _detToTrk;
309     private static double _rmax;
310 
311     private static double _zmax = 1800.;
312 
313     private static final boolean TUBE_ONLY = false; // only use Tube elements
314 
315     public static double getRMax() {
316         return _rmax;
317     }
318 
319     public static double getZMax() {
320         return _zmax;
321     }
322 
323     private static List<UniquePV> makeUniquePVList(final IPhysicalVolumeNavigator nav,
324             final IPhysicalVolume trackingVol, final List<String> paths) {
325         final List<UniquePV> uniqPVs = new ArrayList<UniquePV>();
326         for (final String path : paths) {
327             /**
328              * Create the path object, prepending tracking volume name, as the paths are relative to Subdetector.
329              */
330             final IPhysicalVolumePath pvPath = nav.getPath("/" + trackingVol.getName() + path);
331 
332             /**
333              * Create the UniquePV for MaterialManager.
334              */
335             uniqPVs.add(new UniquePV(pvPath, nav));
336         }
337         return uniqPVs;
338     }
339 
340     private final List<MaterialCylinder> _matcyl = new ArrayList<MaterialCylinder>();
341 
342     private final List<MaterialDisk> _matdsk = new ArrayList<MaterialDisk>();
343 
344     // for calculating volume.
345     private final List<MaterialPolyconeSegment> _matpc = new ArrayList<MaterialPolyconeSegment>();
346 
347     private final List<MaterialXPlane> _matxpl = new ArrayList<MaterialXPlane>();
348 
349     private boolean _useDefaultXPlanes = true;
350 
351     // Variables from original MaterialManager class.
352     protected boolean DEBUG = false; // enable debug output to System.out
353 
354     private final HashMap<ISolid, Double> solid_vol_map = new HashMap<ISolid, Double>(400);
355 
356     /**
357      * VolumeGroup handlers for Subdetector types.
358      */
359     protected final Map<Class, SubdetectorVolumeGrouper> subdetGroups = new HashMap<Class, SubdetectorVolumeGrouper>();
360 
361     /**
362      * Creates a new instance of MaterialManager
363      */
364     public MaterialManager() {
365         // Barrels.
366         final SubdetectorVolumeGrouper barrelGrouper = new BarrelLayerVolumeGroup();
367         subdetGroups.put(SiTrackerBarrel.class, barrelGrouper);
368         subdetGroups.put(MultiLayerTracker.class, barrelGrouper);
369 
370         // Endcaps.
371         final SubdetectorVolumeGrouper endcapGrouper = new EndcapVolumeGrouper();
372         subdetGroups.put(SiTrackerEndcap.class, endcapGrouper);
373         subdetGroups.put(DiskTracker.class, endcapGrouper);
374 
375         // SiTrackerEndcap2.
376         final SubdetectorVolumeGrouper endcap2Grouper = new SiTrackerEndap2VolumeGrouper();
377         subdetGroups.put(SiTrackerEndcap2.class, endcap2Grouper);
378         subdetGroups.put(SiTrackerSpectrometer.class, endcap2Grouper);
379 
380         // SiTrackerFixedTarget2.
381         subdetGroups.put(SiTrackerFixedTarget2.class, new SiTrackerFixedTarget2VolumeGrouper());
382 
383         // PolyconeSupport.
384         subdetGroups.put(PolyconeSupport.class, new PolyconeSupportVolumeGrouper());
385     }
386 
387     /**
388      * Calculates the VolumeGroupInfo for a set of {@link UniquePV} objects.
389      *
390      * @param uniqPVs
391      * @param vgi
392      */
393     private void addVolumeGroupInfo(final List<UniquePV> uniqPVs, final VolumeGroupInfo vgi) {
394         double vtot;
395         if (TUBE_ONLY) {
396             vtot = vgi.vtot_tube_only;
397         } else {
398             vtot = vgi.vtot;
399         }
400 
401         // Handle Polycone.
402         if (uniqPVs.get(0).getPV().getLogicalVolume().getSolid() instanceof Polycone) {
403             this.handlePolycone(uniqPVs.get(0).getPV());
404         }
405 
406         if (vtot > 0.) {
407 
408             // Calculate the average radiation length for this volume
409 
410             // Determine if this volume should be modeled as barrel or disk
411             if (this.isXPlane(vgi.xmin, vgi.xmax)) {
412                 // Calculate the weighted radius of the elements
413                 final double zlen = vgi.zmax - vgi.zmin;
414                 final double ylen = vgi.ymax - vgi.ymin;
415                 final double thickness = vtot / (ylen * zlen * vgi.X0);
416                 final double x = (vgi.xmax + vgi.xmin) / 2;
417 
418                 if (DEBUG) {
419                     System.out.println("Treating as a XPlane...x0: " + vgi.X0 + "| zmin: " + vgi.zmin + "| zmax: "
420                             + vgi.zmax + "| vtot: " + vtot + "| thickness: " + thickness + "| rmin: " + vgi.rmin
421                             + "| rmax: " + vgi.rmax + "| xmin: " + vgi.xmin + "| xmax: " + vgi.xmax);
422                     System.out.println();
423                 }
424                 if (!_useDefaultXPlanes) {
425                     if (x > 0.1) {
426                         _matxpl.add(new MaterialXPlane(vgi.ymin, vgi.ymax, vgi.zmin, vgi.zmax, x, thickness));
427                     }
428                 } else {
429                     _matxpl.add(new MaterialXPlane(vgi.ymin, vgi.ymax, vgi.zmin, vgi.zmax, x, thickness));
430                 }
431             } else if (this.isCylinder(vgi.rmin, vgi.rmax, vgi.zmin, vgi.zmax)) {
432                 // Calculate the weighted radius of the elements
433                 final double zlen = vgi.zmax - vgi.zmin;
434                 final double thickness = vtot / (2. * Math.PI * vgi.weighted_r * zlen * vgi.X0);
435 
436                 if (DEBUG) {
437                     System.out.println("Treating as a Cylinder...x0: " + vgi.X0 + "| zmin: " + vgi.zmin + "| zmax: "
438                             + vgi.zmax + "| vtot: " + vtot + "| thickness: " + thickness + "| rmin: " + vgi.rmin
439                             + "| rmax: " + vgi.rmax);
440                     System.out.println();
441                 }
442 
443                 _matcyl.add(new MaterialCylinder(null, vgi.weighted_r, vgi.zmin, vgi.zmax, thickness));
444             } else {
445 
446                 final double thickness = vtot / (Math.PI * (vgi.rmax * vgi.rmax - vgi.rmin * vgi.rmin) * vgi.X0);
447 
448                 if (DEBUG) {
449                     System.out.println("x0: " + vgi.X0 + "| zmin: " + vgi.zmin + "| zmax: " + vgi.zmax + "| vtot: "
450                             + vtot + "| thickness: " + thickness + "| rmin: " + vgi.rmin + "| rmax: " + vgi.rmax);
451                     System.out.println();
452                 }
453 
454                 _matdsk.add(new MaterialDisk(null, vgi.rmin, vgi.rmax, vgi.weighted_z, thickness));
455             }
456         }
457     }
458 
459     /**
460      * Build model using new VolumeGroup interface for each Subdetector type.
461      */
462     public void buildModel(final Detector det) {
463         // Get the default navigator.
464         final IPhysicalVolumeNavigator nav = PhysicalVolumeNavigatorStore.getInstance().getDefaultNavigator();
465 
466         // Get the tracking volume.
467         final IPhysicalVolume trackingVol = det.getTrackingVolume();
468 
469         // Loop over subdetectors.
470         for (final Subdetector subdet : det.getSubdetectorList()) {
471             // Only look at Subdetectors in the tracking region.
472             if (subdet.isInsideTrackingVolume()) {
473                 if (DEBUG) {
474                     System.out.println();
475                     System.out.println(">>>> " + subdet.getName() + " >>>>");
476                 }
477 
478                 // Get the VolumeGrouper for this type.
479                 final SubdetectorVolumeGrouper subdetGrouper = subdetGroups.get(subdet.getClass());
480 
481                 // Can't handle this type.
482                 if (subdetGrouper == null) {
483                     System.out.println("WARNING: Can't handle Subdetector of type <"
484                             + subdet.getClass().getCanonicalName() + ">.");
485                 } else {
486                     if (DEBUG) {
487                         System.out.println("Found VolumeGrouper <" + subdetGrouper.getClass().getName() + ">.");
488                     }
489 
490                     // Make the list of path groups for this Subdetector.
491                     final List<List<String>> pathGroups = subdetGrouper.getPathGroups(subdet, trackingVol);
492 
493                     if (DEBUG) {
494                         System.out.println("Got " + pathGroups.size() + " path groups.");
495                     }
496 
497                     // Loop over path groups.
498                     for (final List<String> pathGroup : pathGroups) {
499                         if (DEBUG) {
500                             System.out.println("Adding next " + pathGroup.size() + " paths.");
501                         }
502 
503                         // Make the UniquePV list expected by MaterialManager.
504                         final List<UniquePV> uniqPVs = makeUniquePVList(nav, trackingVol, pathGroup);
505 
506                         // Calculate VolumeGroupInfo for this path group.
507                         final VolumeGroupInfo vgi = this.performVolumeGroupCalculations(uniqPVs);
508 
509                         // Debug print.
510                         if (DEBUG) {
511                             System.out.println("VolumeGroupInfo ...");
512                             System.out.println("    rmax = " + vgi.rmax);
513                             System.out.println("    rmin = " + vgi.rmin);
514                             System.out.println("    xmin = " + vgi.xmin);
515                             System.out.println("    xmax = " + vgi.xmax);
516                             System.out.println("    ymin = " + vgi.ymin);
517                             System.out.println("    ymax = " + vgi.ymax);
518                             System.out.println("    zmin = " + vgi.zmin);
519                             System.out.println("    zmax = " + vgi.zmax);
520                             System.out.println("    X0 = " + vgi.X0);
521                             System.out.println("    weighted_r = " + vgi.weighted_r);
522                             System.out.println("    weighted_z = " + vgi.weighted_z);
523                             System.out.println("    weighted_z = " + vgi.weighted_y);
524                             System.out.println("    vtot_tube_only = " + vgi.vtot_tube_only);
525                             System.out.println("    vtot = " + vgi.vtot);
526                         }
527 
528                         // Add the VolumeGroupInfo, which will setup the
529                         // material representation for this set of volumes.
530                         this.addVolumeGroupInfo(uniqPVs, vgi);
531                     }
532                 }
533             }
534         }
535 
536         // Setup the tracking volume.
537         this.setupTrackingVolume(det);
538     }
539 
540     public List<MaterialCylinder> getMaterialCylinders() {
541         return _matcyl;
542     }
543 
544     public List<MaterialDisk> getMaterialDisks() {
545         return _matdsk;
546     }
547 
548     public List<MaterialPolyconeSegment> getMaterialPolyconeSegments() {
549         return _matpc;
550     }
551 
552     public List<MaterialXPlane> getMaterialXPlanes() {
553         return _matxpl;
554     }
555 
556     private double getVolumeOfSolid(final ISolid solid) {
557         if (solid_vol_map.containsKey(solid)) {
558             return solid_vol_map.get(solid).doubleValue();
559         } else {
560             double vol;
561             try {
562                 vol = solid.getCubicVolume();
563             } catch (final Exception e) {
564                 vol = 0.0;
565             }
566 
567             solid_vol_map.put(solid, vol);
568             return vol;
569         }
570 
571     }
572 
573     // special handling for Polycone...
574     private void handlePolycone(final IPhysicalVolume pv) {
575         final Polycone pc = (Polycone) pv.getLogicalVolume().getSolid();
576         final IMaterial mat = pv.getLogicalVolume().getMaterial();
577 
578         // Loop through each segment
579         for (int i = 0; i < pc.getNumberOfZPlanes() - 1; i++) {
580             final ZPlane zp1 = pc.getZPlane(i);
581             final ZPlane zp2 = pc.getZPlane(i + 1);
582 
583             final double z1 = zp1.getZ();
584             final double z2 = zp2.getZ();
585             final double vol = Polycone.getSegmentVolume(zp1, zp2);
586             final double zlen = Math.abs(z2 - z1);
587             final double ravg = 0.25 * (zp1.getRMax() + zp1.getRMin() + zp2.getRMax() + zp2.getRMin());
588             final double ang = Math.atan2(0.5 * (zp1.getRMax() + zp1.getRMin() - zp2.getRMax() - zp2.getRMin()), zlen);
589             final double X0 = 10 * mat.getRadiationLength() / mat.getDensity();
590             final double thickness = Math.cos(ang) * vol / (2 * Math.PI * ravg * zlen * X0);
591 
592             // This is a cylinder
593             if (zp1.getRMax() == zp2.getRMax() && zp1.getRMin() == zp2.getRMin()) {
594                 _matcyl.add(new MaterialCylinder(pv, ravg, Math.min(z1, z2), Math.max(z1, z2), thickness));
595                 if (DEBUG) {
596                     System.out.println("Cylindrical segment of " + pv.getName());
597                     System.out.println("zmin = " + z1 + "| zmax = " + z2 + "| ravg = " + ravg + "| thickness = "
598                             + thickness);
599                 }
600 
601             } // Otherwise this is a non-cylindrical polycone segment
602             else {
603                 _matpc.add(new MaterialPolyconeSegment(pv, zp1, zp2, thickness, ang));
604                 if (DEBUG) {
605                     System.out.println("Non-Cylindrical segment of " + pv.getName());
606                     System.out.println("ZPlane 1: " + zp1.toString() + "| ZPlane 2: " + zp2.toString()
607                             + "| thickness = " + thickness);
608                 }
609 
610             }
611         }
612     }
613 
614     private boolean isCylinder(final double rmin, final double rmax, final double zmin, final double zmax) {
615         return (rmax - rmin) * Math.abs(zmax + zmin) < (zmax - zmin) * (rmax + rmin);
616     }
617 
618     private boolean isXPlane(final double xmin, final double xmax) {
619         if (!_useDefaultXPlanes) {
620             if (xmax - xmin < 0) {
621                 return false; // be default xmax is negative, xmin is positive
622             }
623             if (xmax + xmin < 50) {
624                 return false; // catch short modules...
625             }
626             return xmax - xmin < 50.0;// 5cm...
627         } else {
628             return xmax - xmin < 1.0;// 1mm
629         }
630     }
631 
632     private VolumeInfo performVolumeCalculations(final UniquePV pv) {
633 
634         final VolumeInfo vi = new VolumeInfo();
635         final ISolid solid = pv.getSolid();
636 
637         // ASSUMPTION: tube is along z-axis and has center at r = 0
638         if (solid instanceof Tube) {
639             final Tube tube = (Tube) solid;
640             final double z0 = pv.getLtoGTransform().getTranslation().z();
641             vi.zmax = z0 + tube.getZHalfLength();
642             vi.zmin = z0 - tube.getZHalfLength();
643             vi.rmin = tube.getInnerRadius();
644             vi.rmax = tube.getOuterRadius();
645         } else if (solid instanceof Box) {
646             final Box box = (Box) solid;
647             for (final Point3D p : box.getVertices()) {
648                 final Hep3Vector transformed = pv.localToGlobal(p.getHep3Vector());
649                 if (_detToTrk != null) {
650                     _detToTrk.transform(transformed);
651                 }
652                 vi.zmin = Math.min(transformed.z(), vi.zmin);
653                 vi.zmax = Math.max(transformed.z(), vi.zmax);
654                 final double r = Math.sqrt(transformed.x() * transformed.x() + transformed.y() * transformed.y());
655                 vi.rmin = Math.min(vi.rmin, r);
656                 vi.rmax = Math.max(vi.rmax, r);
657                 // mg 1/23/12 also store ymin,ymax
658                 vi.ymin = Math.min(transformed.y(), vi.ymin);
659                 vi.ymax = Math.max(transformed.y(), vi.ymax);
660                 vi.xmin = Math.min(transformed.x(), vi.xmin);
661                 vi.xmax = Math.max(transformed.x(), vi.xmax);
662 
663             }
664 
665         } else if (solid instanceof Trd) {
666             final Trd box = (Trd) solid;
667             for (final Point3D p : box.getVertices()) {
668                 final Hep3Vector transformed = pv.localToGlobal(p.getHep3Vector());
669                 if (_detToTrk != null) {
670                     _detToTrk.transform(transformed);
671                 }
672                 vi.zmin = Math.min(transformed.z(), vi.zmin);
673                 vi.zmax = Math.max(transformed.z(), vi.zmax);
674                 final double r = Math.sqrt(transformed.x() * transformed.x() + transformed.y() * transformed.y());
675                 vi.rmin = Math.min(vi.rmin, r);
676                 vi.rmax = Math.max(vi.rmax, r);
677                 // mg 3/14/11 also store ymin,ymax
678                 vi.ymin = Math.min(transformed.y(), vi.ymin);
679                 vi.ymax = Math.max(transformed.y(), vi.ymax);
680                 vi.xmin = Math.min(transformed.x(), vi.xmin);
681                 vi.xmax = Math.max(transformed.x(), vi.xmax);
682             }
683         } // Note: this information will NOT be used most of the time...
684         // Polycones that are top-level elements (e.g. the beampipe) are
685         // handled specially (since the radiation length is a function of z).
686         // The information here will only be used in case a top-level element
687         // has a subelement that is a Polycone, in which case it'll be
688         // approximated as the smallest possible cylinder.
689         else if (solid instanceof Polycone) {
690             final Polycone pc = (Polycone) solid;
691             final List<Polycone.ZPlane> zplanes = pc.getZPlanes();
692 
693             // For now, just take the minimum rmin and rmax of the polycone
694             for (final Polycone.ZPlane z : zplanes) {
695                 if (z.getRMax() > 0 && z.getRMin() > 0) {
696                     vi.rmin = Math.min(vi.rmin, z.getRMin());
697                     vi.rmax = vi.rmax > 0. ? Math.min(vi.rmax, z.getRMax()) : z.getRMax();
698                 }
699 
700             }
701 
702             vi.zmin = pc.getZPlanes().get(0).getZ();
703             vi.zmax = pc.getZPlanes().get(pc.getZPlanes().size() - 1).getZ();
704 
705             // check for wrong order
706             if (vi.zmin > vi.zmax) {
707                 final double temp = vi.zmin;
708                 vi.zmin = vi.zmax;
709                 vi.zmax = temp;
710             }
711 
712         }
713 
714         return vi;
715     }
716 
717     // This function performs all the calculations on lists of physical volumes
718     private VolumeGroupInfo performVolumeGroupCalculations(final List<UniquePV> volgroup) {
719 
720         final VolumeGroupInfo vgi = new VolumeGroupInfo();
721 
722         // If we have a top-level polycone, don't bother doing anything, because
723         // it'll be handled specially
724         if (volgroup.size() == 1 && volgroup.get(0).getSolid() instanceof Polycone) {
725             return vgi;
726         }
727 
728         // The normal case
729         double totwgt = 0.0;
730         if (DEBUG && volgroup.isEmpty()) {
731             System.out.println("Empty volume group...");
732         }
733         for (final UniquePV pv : volgroup) {
734 
735             // increment total volume
736             final double vol = this.getVolumeOfSolid(pv.getSolid());
737             if (pv.getSolid() instanceof Tube) {
738                 vgi.vtot_tube_only += vol;
739             }
740             vgi.vtot += vol;
741             // calculate weighted R / Z / Radiation Length
742             final VolumeInfo vi = this.performVolumeCalculations(pv);
743             final IMaterial mat = pv.getPV().getLogicalVolume().getMaterial();
744             final double matX0 = 10.0 * mat.getRadiationLength() / mat.getDensity();
745             final double wgt = vol / matX0;
746             final double z0 = pv.getLtoGTransform().getTranslation().z();
747             vgi.weighted_r += 0.5 * (vi.rmin + vi.rmax) * wgt;
748             vgi.weighted_z += z0 * wgt;
749             totwgt += wgt;
750 
751             // grab (z/r)(mins/maxes)
752             vgi.zmin = Math.min(vi.zmin, vgi.zmin);
753             vgi.zmax = Math.max(vi.zmax, vgi.zmax);
754             vgi.rmin = Math.min(vi.rmin, vgi.rmin);
755             vgi.rmax = Math.max(vi.rmax, vgi.rmax);
756             // mg 3/14/11 also store y,x information
757             vgi.ymin = Math.min(vi.ymin, vgi.ymin);
758             vgi.ymax = Math.max(vi.ymax, vgi.ymax);
759             final double y0 = pv.getLtoGTransform().getTranslation().y();
760             vgi.weighted_y += y0 * wgt;
761             vgi.xmin = Math.min(vi.xmin, vgi.xmin);
762             vgi.xmax = Math.max(vi.xmax, vgi.xmax);
763         }
764 
765         // finish weighted R/Z calculations + perform X0 calculation
766         if (totwgt > 0.) {
767             vgi.weighted_r /= totwgt;
768             vgi.weighted_z /= totwgt;
769             vgi.X0 = vgi.vtot / totwgt;
770             // mg 3/14/11 also y info
771             vgi.weighted_y /= totwgt;
772         }
773 
774         return vgi;
775     }
776 
777     /**
778      * Turn on/off debugging output.
779      *
780      * @param debug True if debugging should be enabled; false if not.
781      */
782     public void setDebug(final boolean debug) {
783         this.DEBUG = debug;
784     }
785 
786     public void setDefaultXPlaneUsage(final boolean useDefault) {
787         _useDefaultXPlanes = useDefault;
788     }
789 
790     public void setTransform(final ITransform3D transform) {
791         _detToTrk = transform;
792     }
793 
794     /**
795      * Setup tracking volume parameters.
796      *
797      * @param det The Detector.
798      */
799     private void setupTrackingVolume(final Detector det) {
800         // Find the envelope of the tracking volume
801         final ISolid trkvol = det.getTrackingVolume().getLogicalVolume().getSolid();
802         if (trkvol instanceof Tube) {
803             final Tube trktube = (Tube) trkvol;
804             _rmax = trktube.getOuterRadius();
805             _zmax = trktube.getZHalfLength();
806             if (DEBUG) {
807                 System.out.println("Ecal radius = " + _rmax);
808                 System.out.println("ECal inner Z = " + _zmax);
809             }
810         }
811     }
812 }