Print

Print


Author: [log in to unmask]
Date: Tue Oct  6 11:36:11 2015
New Revision: 3768

Log:
[HPSJAVA-615] [HPSJAVA-614] Move detector model classes from lcsim to hps-java and related changes.

Added:
    java/trunk/detector-model/src/main/java/org/hps/
    java/trunk/detector-model/src/main/java/org/hps/detector/
    java/trunk/detector-model/src/main/java/org/hps/detector/ecal/
    java/trunk/detector-model/src/main/java/org/hps/detector/ecal/EcalCrystalChannelMap.java
      - copied, changed from r3760, java/trunk/conditions/src/main/java/org/hps/conditions/ecal/EcalCrystalChannelMap.java
    java/trunk/detector-model/src/main/java/org/hps/detector/svt/
    java/trunk/detector-model/src/main/java/org/hps/detector/svt/SvtDetectorSetup.java
      - copied, changed from r3746, java/trunk/conditions/src/main/java/org/hps/conditions/svt/SvtDetectorSetup.java
    java/trunk/detector-model/src/main/java/org/lcsim/detector/converter/compact/EcalCrystal.java
    java/trunk/detector-model/src/main/java/org/lcsim/detector/converter/compact/HPSEcal2Converter.java
    java/trunk/detector-model/src/main/java/org/lcsim/detector/converter/compact/HPSEcal3Converter.java
    java/trunk/detector-model/src/main/java/org/lcsim/detector/converter/compact/HPSEcalAPI.java
    java/trunk/detector-model/src/main/java/org/lcsim/detector/converter/compact/HPSEcalConverter.java
    java/trunk/detector-model/src/main/java/org/lcsim/detector/converter/compact/HPSEcalDetectorElement.java
    java/trunk/detector-model/src/main/java/org/lcsim/detector/converter/compact/HPSMuonCalorimeterConverter.java
    java/trunk/detector-model/src/main/java/org/lcsim/detector/converter/compact/HPSTracker2Converter.java
    java/trunk/detector-model/src/main/java/org/lcsim/detector/converter/compact/HPSTrackerConverter.java
    java/trunk/detector-model/src/main/java/org/lcsim/detector/tracker/
    java/trunk/detector-model/src/main/java/org/lcsim/detector/tracker/silicon/
    java/trunk/detector-model/src/main/java/org/lcsim/detector/tracker/silicon/HpsTestRunSiSensor.java
    java/trunk/detector-model/src/main/java/org/lcsim/geometry/compact/converter/lcdd/HPSEcal.java
    java/trunk/detector-model/src/main/java/org/lcsim/geometry/compact/converter/lcdd/HPSEcal2.java
    java/trunk/detector-model/src/main/java/org/lcsim/geometry/compact/converter/lcdd/HPSEcal3.java
    java/trunk/detector-model/src/main/java/org/lcsim/geometry/compact/converter/lcdd/HPSMuonCalorimeter.java
    java/trunk/detector-model/src/main/java/org/lcsim/geometry/compact/converter/lcdd/HPSMuonCalorimeter2.java
    java/trunk/detector-model/src/main/java/org/lcsim/geometry/compact/converter/lcdd/HPSTracker.java
    java/trunk/detector-model/src/main/java/org/lcsim/geometry/compact/converter/lcdd/HPSTracker2.java
    java/trunk/detector-model/src/main/java/org/lcsim/geometry/subdetector/HPSEcal.java
    java/trunk/detector-model/src/main/java/org/lcsim/geometry/subdetector/HPSEcal2.java
    java/trunk/detector-model/src/main/java/org/lcsim/geometry/subdetector/HPSEcal3.java
    java/trunk/detector-model/src/main/java/org/lcsim/geometry/subdetector/HPSMuonCalorimeter.java
    java/trunk/detector-model/src/main/java/org/lcsim/geometry/subdetector/HPSTracker.java
    java/trunk/detector-model/src/main/java/org/lcsim/geometry/subdetector/HPSTracker2.java
    java/trunk/detector-model/src/test/java/org/hps/detector/svt/
    java/trunk/detector-model/src/test/java/org/hps/detector/svt/SvtDetectorSetupTest.java
      - copied, changed from r3746, java/trunk/conditions/src/test/java/org/hps/conditions/svt/SvtDetectorSetupTest.java
    java/trunk/detector-model/src/test/java/org/hps/detector/svt/TestRunSvtDetectorSetupTest.java
      - copied, changed from r3746, java/trunk/conditions/src/test/java/org/hps/conditions/svt/TestRunSvtDetectorSetupTest.java
    java/trunk/detector-model/src/test/java/org/lcsim/detector/
    java/trunk/detector-model/src/test/java/org/lcsim/detector/converter/
    java/trunk/detector-model/src/test/java/org/lcsim/detector/converter/compact/
    java/trunk/detector-model/src/test/java/org/lcsim/detector/converter/compact/HPSEcalAPITest.java
    java/trunk/detector-model/src/test/java/org/lcsim/detector/converter/compact/HPSMuonCalorimeterTest.java
    java/trunk/detector-model/src/test/java/org/lcsim/detector/converter/compact/HPSTracker2ConverterTest.java
    java/trunk/detector-model/src/test/java/org/lcsim/detector/converter/compact/HpsTestRunSiSensorConverterTest.java
    java/trunk/detector-model/src/test/resources/org/lcsim/geometry/subdetector/HPSEcal3Test.xml
    java/trunk/detector-model/src/test/resources/org/lcsim/geometry/subdetector/HPSEcalTest.xml
    java/trunk/detector-model/src/test/resources/org/lcsim/geometry/subdetector/HPSMuonCalorimeter2Test.xml
    java/trunk/detector-model/src/test/resources/org/lcsim/geometry/subdetector/HPSMuonCalorimeterTest.xml
    java/trunk/detector-model/src/test/resources/org/lcsim/geometry/subdetector/HPSTest.xml
    java/trunk/detector-model/src/test/resources/org/lcsim/geometry/subdetector/HPSTracker2Test.xml
    java/trunk/detector-model/src/test/resources/org/lcsim/geometry/subdetector/HpsTestRunSiSensorConverterTest.xml
Removed:
    java/trunk/conditions/src/main/java/org/hps/conditions/ecal/EcalCrystalChannelMap.java
    java/trunk/conditions/src/main/java/org/hps/conditions/svt/SvtDetectorSetup.java
    java/trunk/conditions/src/test/java/org/hps/conditions/EngRunConditionsTest.java
    java/trunk/conditions/src/test/java/org/hps/conditions/svt/SvtDetectorSetupTest.java
    java/trunk/conditions/src/test/java/org/hps/conditions/svt/TestRunSvtDetectorSetupTest.java
Modified:
    java/trunk/conditions/pom.xml
    java/trunk/conditions/src/main/java/org/hps/conditions/api/ConditionsObjectException.java
    java/trunk/conditions/src/main/java/org/hps/conditions/database/DatabaseConditionsManager.java
    java/trunk/conditions/src/main/java/org/hps/conditions/ecal/EcalChannel.java
    java/trunk/conditions/src/main/java/org/hps/conditions/ecal/EcalConditions.java
    java/trunk/conditions/src/main/java/org/hps/conditions/ecal/EcalConditionsConverter.java
    java/trunk/conditions/src/main/java/org/hps/conditions/svt/TestRunSvtDaqMapping.java
    java/trunk/conditions/src/test/java/org/hps/conditions/database/DatabaseConditionsManagerTest.java
    java/trunk/detector-model/pom.xml
    java/trunk/tracking/src/main/java/org/hps/recon/tracking/MaterialManager.java

Modified: java/trunk/conditions/pom.xml
 =============================================================================
--- java/trunk/conditions/pom.xml	(original)
+++ java/trunk/conditions/pom.xml	Tue Oct  6 11:36:11 2015
@@ -33,6 +33,9 @@
                         <exclude>org/hps/conditions/svt/TestRunSvtConditionsConverterTest.java</exclude>
                         <exclude>org/hps/conditions/svt/TestRunSvtDaqMappingTest.java</exclude>
                         <exclude>org/hps/conditions/svt/SvtTimingConstantsTest.java</exclude>
+                        <exclude>org/hps/conditions/svt/SvtAlignmentTest.java</exclude>
+                        <exclude>org/hps/conditions/api/ConditionsTagTest.java</exclude>
+                        <exclude>org/hps/conditions/HPSJAVA_529_Test.java</exclude>
                         <exclude>org/hps/conditions/dummy/**.java</exclude>
                     </excludes>
                 </configuration>

Modified: java/trunk/conditions/src/main/java/org/hps/conditions/api/ConditionsObjectException.java
 =============================================================================
--- java/trunk/conditions/src/main/java/org/hps/conditions/api/ConditionsObjectException.java	(original)
+++ java/trunk/conditions/src/main/java/org/hps/conditions/api/ConditionsObjectException.java	Tue Oct  6 11:36:11 2015
@@ -12,6 +12,15 @@
      * The associated conditions object to the error.
      */
     private ConditionsObject object;
+    
+    /**
+     * Error with a message.
+     *
+     * @param message the error message
+     */
+    public ConditionsObjectException(Exception e) {
+        super(e);
+    }
 
     /**
      * Error with a message.

Modified: java/trunk/conditions/src/main/java/org/hps/conditions/database/DatabaseConditionsManager.java
 =============================================================================
--- java/trunk/conditions/src/main/java/org/hps/conditions/database/DatabaseConditionsManager.java	(original)
+++ java/trunk/conditions/src/main/java/org/hps/conditions/database/DatabaseConditionsManager.java	Tue Oct  6 11:36:11 2015
@@ -33,7 +33,6 @@
 import org.hps.conditions.ecal.TestRunEcalConditionsConverter;
 import org.hps.conditions.svt.SvtConditions;
 import org.hps.conditions.svt.SvtConditionsConverter;
-import org.hps.conditions.svt.SvtDetectorSetup;
 import org.hps.conditions.svt.TestRunSvtConditionsConverter;
 import org.jdom.Document;
 import org.jdom.Element;
@@ -66,7 +65,7 @@
      * Name of system property that can be used to specify custom database connection parameters.
      */
     private static final String CONNECTION_PROPERTY_FILE = "org.hps.conditions.connection.file";
-    
+
     /**
      * Connection property resource.
      */
@@ -90,7 +89,8 @@
     /**
      * Initialize the logger.
      */
-    private static Logger LOGGER = LogUtil.create(DatabaseConditionsManager.class.getName(), new DefaultLogFormatter(), Level.CONFIG);
+    private static Logger LOGGER = LogUtil.create(DatabaseConditionsManager.class.getName(), new DefaultLogFormatter(),
+            Level.CONFIG);
 
     /**
      * The Test Run XML config.
@@ -116,9 +116,9 @@
 
         // Is there no manager installed yet?
         if (!ConditionsManager.isSetup() || !(ConditionsManager.defaultInstance() instanceof DatabaseConditionsManager)) {
-            
+
             // Create a new instance if necessary, which will install it globally as the default.
-            DatabaseConditionsManager dbManager = new DatabaseConditionsManager();
+            final DatabaseConditionsManager dbManager = new DatabaseConditionsManager();
 
             // Register default conditions manager.
             ConditionsManager.setDefaultConditionsManager(dbManager);
@@ -157,13 +157,13 @@
      * Reset the global static instance of the conditions manager to a new object.
      */
     public static synchronized void resetInstance() {
-                        
+
         // Create a new instance if necessary, which will install it globally as the default.
-        DatabaseConditionsManager dbManager = new DatabaseConditionsManager();
+        final DatabaseConditionsManager dbManager = new DatabaseConditionsManager();
 
         // Register default conditions manager.
         ConditionsManager.setDefaultConditionsManager(dbManager);
-        
+
         LOGGER.info("DatabaseConditionsManager instance is reset");
     }
 
@@ -178,6 +178,16 @@
     private boolean closeConnectionAfterInitialize = true;
 
     /**
+     * The current set of conditions for the run.
+     */
+    private ConditionsRecordCollection conditionsRecordCollection = null;
+
+    /**
+     * The currently active conditions tag (empty collection means no tag is active).
+     */
+    private final ConditionsTagCollection conditionsTagCollection = new ConditionsTagCollection();
+
+    /**
      * The current database connection.
      */
     private Connection connection;
@@ -258,54 +268,70 @@
     private String svtName = "Tracker";
 
     /**
-     * The helper for setting up the SVT detector with its conditions information.
-     */
-    private final SvtDetectorSetup svtSetup = new SvtDetectorSetup(this.svtName);
-
-    /**
      * Create the global registry of table meta data.
      */
     private final TableRegistry tableRegistry = TableRegistry.getTableRegistry();
 
     /**
-     * The currently active conditions tag (empty collection means no tag is active).
-     */
-    private ConditionsTagCollection conditionsTagCollection = new ConditionsTagCollection();
-    
-    /**
-     * The current set of conditions for the run.
-     */
-    private ConditionsRecordCollection conditionsRecordCollection = null;
-    
-    /**
      * The currently applied conditions tags.
      */
-    private Set<String> tags = new HashSet<String>();
-    
+    private final Set<String> tags = new HashSet<String>();
+
     /**
      * Class constructor. Calling this will automatically register this manager as the global default.
      */
     protected DatabaseConditionsManager() {
-        
+
         // Register detector conditions converter.
         this.registerConditionsConverter(new DetectorConditionsConverter());
-        
+
         // Setup connection from system property pointing to a file, if it was set.
         this.setupConnectionSystemPropertyFile();
-        
+
         // Setup connection from system property pointing to a resource, if it was set.
         this.setupConnectionSystemPropertyResource();
-                
+
         // Set run to invalid number.
         this.setRun(-1);
-        
+
         // Register conditions converters.
         for (final AbstractConditionsObjectConverter converter : this.converters.values()) {
             this.registerConditionsConverter(converter);
         }
-        
-        // Add the SVT detector setup object as a listener.
-        this.addConditionsListener(this.svtSetup);
+    }
+
+    /**
+     * Add a tag used to filter the accessible conditions records.
+     * <p>
+     * Multiple tags are OR'd together.
+     *
+     * @param tag the tag value used to filter returned conditions records
+     */
+    public void addTag(final String tag) {
+        if (!this.tags.contains(tag)) {
+            LOGGER.info("adding tag " + tag);
+            final ConditionsTagCollection findConditionsTag = this.getCachedConditions(ConditionsTagCollection.class,
+                    tag).getCachedData();
+            if (findConditionsTag.size() == 0) {
+                throw new IllegalArgumentException("The tag " + tag + " does not exist in the database.");
+            }
+            LOGGER.info("adding conditions tag " + tag + " with " + conditionsTagCollection.size() + " records");
+            this.conditionsTagCollection.addAll(findConditionsTag);
+            this.tags.add(tag);
+        } else {
+            LOGGER.warning("tag " + tag + " is already added");
+        }
+    }
+
+    /**
+     * Add one or more tags for filtering records.
+     *
+     * @param tags the <code>Set</code> of tags to add
+     */
+    public void addTags(final Set<String> tags) {
+        for (final String tag : tags) {
+            this.addTag(tag);
+        }
     }
 
     /**
@@ -321,6 +347,14 @@
                 LOGGER.warning("could not cache conditions " + meta.getKey());
             }
         }
+    }
+
+    /**
+     * Clear the tags used to filter the {@link org.hps.conditons.api.ConditionsRecord}s.
+     */
+    public void clearTags() {
+        this.tags.clear();
+        this.conditionsTagCollection.clear();
     }
 
     /**
@@ -412,9 +446,9 @@
      * @return the set of matching conditions records
      */
     public ConditionsRecordCollection findConditionsRecords(final String name) {
-        return getConditionsRecords().findByKey(name);
-    }
-  
+        return this.getConditionsRecords().findByKey(name);
+    }
+
     /**
      * Find table information from the name.
      *
@@ -436,6 +470,49 @@
         } else {
             LOGGER.warning("conditions system cannot be frozen because it is not initialized yet");
         }
+    }
+
+    /**
+     * Get the currently active conditions tags.
+     *
+     * @return the currently active conditions tags
+     */
+    public Collection<String> getActiveTags() {
+        return Collections.unmodifiableCollection(this.tags);
+    }
+
+    /**
+     * Get the set of available conditions tags from the conditions table
+     *
+     * @return the set of available conditions tags
+     */
+    public Set<String> getAvailableTags() {
+        LOGGER.fine("getting list of available conditions tags");
+        final boolean openedConnection = this.openConnection();
+        final Set<String> tags = new LinkedHashSet<String>();
+        final ResultSet rs = this
+                .selectQuery("select distinct(tag) from conditions_tags where tag is not null order by tag");
+        try {
+            while (rs.next()) {
+                tags.add(rs.getString(1));
+            }
+        } catch (final SQLException e) {
+            throw new RuntimeException(e);
+        }
+        try {
+            rs.close();
+        } catch (final SQLException e) {
+            LOGGER.log(Level.WARNING, "error closing ResultSet", e);
+        }
+        final StringBuffer sb = new StringBuffer();
+        sb.append("found unique conditions tags: ");
+        for (final String tag : tags) {
+            sb.append(tag + " ");
+        }
+        sb.setLength(sb.length() - 1);
+        LOGGER.fine(sb.toString());
+        this.closeConnection(openedConnection);
+        return tags;
     }
 
     /**
@@ -483,27 +560,26 @@
         collection.setCollectionId(collectionId);
         return collectionId;
     }
-    
+
     /**
      * Get the list of conditions records for the run, filtered by the current set of active tags.
-     * 
+     *
      * @return the list of conditions records for the run
      */
     public ConditionsRecordCollection getConditionsRecords() {
         if (this.run == -1 || this.detectorName == null) {
             throw new IllegalStateException("Conditions system is not initialized.");
-        } 
+        }
         // If the collection is null then the new conditions records need to be retrieved from the database.
         if (this.conditionsRecordCollection == null) {
-            
+
             // Get the collection of conditions that are applicable for the current run.
-            this.conditionsRecordCollection =  
-                    this.getCachedConditions(ConditionsRecordCollection.class, "conditions").getCachedData();
-            
+            this.conditionsRecordCollection = this.getCachedConditions(ConditionsRecordCollection.class, "conditions")
+                    .getCachedData();
+
             // If there is one or more tags enabled then filter the collection by the tag names.
             if (this.conditionsTagCollection.size() > 0) {
-                this.conditionsRecordCollection = 
-                        this.conditionsTagCollection.filter(this.conditionsRecordCollection);
+                this.conditionsRecordCollection = this.conditionsTagCollection.filter(this.conditionsRecordCollection);
             }
         }
         return this.conditionsRecordCollection;
@@ -592,49 +668,6 @@
     public SvtConditions getSvtConditions() {
         return this.getCachedConditions(SvtConditions.class, "svt_conditions").getCachedData();
     }
-    
-    /**
-     * Get the currently active conditions tags.
-     * 
-     * @return the currently active conditions tags
-     */
-    public Collection<String> getActiveTags() {
-        return Collections.unmodifiableCollection(this.tags);
-    }
-
-    /**
-     * Get the set of available conditions tags from the conditions table
-     *
-     * @return the set of available conditions tags
-     */
-    public Set<String> getAvailableTags() {
-        LOGGER.fine("getting list of available conditions tags");
-        final boolean openedConnection = this.openConnection();
-        final Set<String> tags = new LinkedHashSet<String>();
-        final ResultSet rs = this
-                .selectQuery("select distinct(tag) from conditions_tags where tag is not null order by tag");
-        try {
-            while (rs.next()) {
-                tags.add(rs.getString(1));
-            }
-        } catch (final SQLException e) {
-            throw new RuntimeException(e);
-        }
-        try {
-            rs.close();
-        } catch (final SQLException e) {
-            LOGGER.log(Level.WARNING, "error closing ResultSet", e);
-        }
-        final StringBuffer sb = new StringBuffer();
-        sb.append("found unique conditions tags: ");
-        for (final String tag : tags) {
-            sb.append(tag + " ");
-        }
-        sb.setLength(sb.length() - 1);
-        LOGGER.fine(sb.toString());
-        this.closeConnection(openedConnection);
-        return tags;
-    }
 
     /**
      * True if there is a conditions record with the given name.
@@ -656,12 +689,12 @@
      * @throws ConditionsNotFoundException if there is a conditions system error
      */
     private void initialize(final String detectorName, final int runNumber) throws ConditionsNotFoundException {
-        
+
         LOGGER.config("initializing with detector " + detectorName + " and run " + runNumber);
-                
+
         // Clear the conditions cache.
-        this.clearCache();
-                
+        // this.clearCache();
+
         // Set flag if run number is from Test Run 2012 data.
         if (isTestRun(runNumber)) {
             this.isTestRun = true;
@@ -669,7 +702,7 @@
 
         // Is not configured yet?
         if (!this.isConfigured) {
-            if (isTestRun()) {
+            if (this.isTestRun()) {
                 // This looks like the Test Run so use the custom configuration for it.
                 this.setXmlConfig(DatabaseConditionsManager.TEST_RUN_CONFIG);
             } else if (runNumber > TEST_RUN_MAX_RUN) {
@@ -684,20 +717,16 @@
         // Register the converters for this initialization.
         this.registerConverters();
 
-        // Enable or disable the setup of the SVT detector.
-        LOGGER.fine("SVT setup enabled: " + this.setupSvtDetector);
-        this.svtSetup.setEnabled(this.setupSvtDetector);
-
         // Open the database connection.
         this.openConnection();
-        
+
         // Reset the conditions records to trigger a re-caching.
         this.conditionsRecordCollection = null;
-        
+
         // Call the super class's setDetector method to construct the detector object and activate conditions listeners.
         LOGGER.fine("activating default conditions manager");
         super.setDetector(detectorName, runNumber);
-        
+
         // Should all conditions sets be cached?
         if (this.cacheAllConditions) {
             // Cache the conditions sets of all registered converters.
@@ -832,7 +861,7 @@
 
     /**
      * Create a new collection with the given type.
-     * 
+     *
      * @param collectionType the collection type
      * @return the new collection
      */
@@ -858,7 +887,7 @@
 
     /**
      * Create a new collection with the given type and table name.
-     * 
+     *
      * @param collectionType the collection type
      * @param tableName the table name
      * @return the new collection
@@ -1040,7 +1069,6 @@
         LOGGER.config("setting log level to " + level);
         LOGGER.setLevel(level);
         LOGGER.getHandlers()[0].setLevel(level);
-        this.svtSetup.setLogLevel(level);
     }
 
     /**
@@ -1054,47 +1082,6 @@
         }
         this.svtName = svtName;
         LOGGER.info("SVT name set to " + this.ecalName);
-    }
-
-    /**
-     * Add a tag used to filter the accessible conditions records.
-     * <p>
-     * Multiple tags are OR'd together.
-     *
-     * @param tag the tag value used to filter returned conditions records
-     */
-    public void addTag(final String tag) {
-        if (!this.tags.contains(tag)) {
-            LOGGER.info("adding tag " + tag);
-            ConditionsTagCollection findConditionsTag = this.getCachedConditions(ConditionsTagCollection.class, tag).getCachedData();
-            if (findConditionsTag.size() == 0) {
-                throw new IllegalArgumentException("The tag " + tag + " does not exist in the database.");
-            }
-            LOGGER.info("adding conditions tag " + tag + " with " + conditionsTagCollection.size() + " records");
-            this.conditionsTagCollection.addAll(findConditionsTag);
-            this.tags.add(tag);
-        } else {
-            LOGGER.warning("tag " + tag + " is already added");
-        }
-    }
-    
-    /**
-     * Add one or more tags for filtering records.
-     * 
-     * @param tags the <code>Set</code> of tags to add
-     */
-    public void addTags(final Set<String> tags) {
-        for (String tag : tags) {
-            this.addTag(tag);
-        }
-    }
-    
-    /**
-     * Clear the tags used to filter the {@link org.hps.conditons.api.ConditionsRecord}s.
-     */
-    public void clearTags() {
-        this.tags.clear();
-        this.conditionsTagCollection.clear();
     }
 
     /**
@@ -1112,18 +1099,19 @@
             this.setConnectionProperties(f);
             LOGGER.info("connection setup from system property " + CONNECTION_PROPERTY_FILE + " = "
                     + systemPropertiesConnectionPath);
-        }                              
-    }
-    
+        }
+    }
+
     /**
      * Setup the database connection from a file specified by a Java system property setting. This could be overridden
      * by subsequent API calls to {@link #setConnectionProperties(File)} or {@link #setConnectionResource(String)}.
      */
     private void setupConnectionSystemPropertyResource() {
-        final String systemPropertiesConnectionResource = (String) System.getProperties().get(CONNECTION_PROPERTY_RESOURCE);
+        final String systemPropertiesConnectionResource = (String) System.getProperties().get(
+                CONNECTION_PROPERTY_RESOURCE);
         if (systemPropertiesConnectionResource != null) {
             this.setConnectionResource(systemPropertiesConnectionResource);
-        } 
+        }
     }
 
     /**
@@ -1160,5 +1148,5 @@
     public synchronized void unfreeze() {
         this.isFrozen = false;
         LOGGER.info("conditions system unfrozen");
-    }    
+    }
 }

Modified: java/trunk/conditions/src/main/java/org/hps/conditions/ecal/EcalChannel.java
 =============================================================================
--- java/trunk/conditions/src/main/java/org/hps/conditions/ecal/EcalChannel.java	(original)
+++ java/trunk/conditions/src/main/java/org/hps/conditions/ecal/EcalChannel.java	Tue Oct  6 11:36:11 2015
@@ -290,8 +290,11 @@
         public EcalChannelCollection getData(final ConditionsManager conditionsManager, final String name) {
             final EcalChannelCollection collection = super.getData(conditionsManager, name);
             final Subdetector ecal = DatabaseConditionsManager.getInstance().getEcalSubdetector();
-            if (ecal != null) {
+            if (ecal.getDetectorElement() != null) {
                 collection.buildGeometryMap(ecal.getDetectorElement().getIdentifierHelper(), ecal.getSystemID());
+            } else {
+                // This can happen when not running with the detector-framework jar in the classpath.
+                throw new IllegalStateException("The ECal subdetector's detector element is not setup.");
             }
             return collection;
         }

Modified: java/trunk/conditions/src/main/java/org/hps/conditions/ecal/EcalConditions.java
 =============================================================================
--- java/trunk/conditions/src/main/java/org/hps/conditions/ecal/EcalConditions.java	(original)
+++ java/trunk/conditions/src/main/java/org/hps/conditions/ecal/EcalConditions.java	Tue Oct  6 11:36:11 2015
@@ -4,8 +4,6 @@
 import java.util.Map;
 
 import org.hps.conditions.ecal.EcalChannel.EcalChannelCollection;
-import org.lcsim.detector.converter.compact.EcalCrystal;
-import org.lcsim.detector.converter.compact.HPSEcalAPI;
 import org.lcsim.geometry.Subdetector;
 
 /**
@@ -36,11 +34,6 @@
     private final Map<EcalChannel, EcalChannelConstants> channelConstants = new HashMap<EcalChannel, EcalChannelConstants>();
 
     /**
-     * Map between channels and geometric crystals.
-     */
-    private EcalCrystalChannelMap crystalMap;
-
-    /**
      * The current ECAL subdetector in the geometry.
      */
     private final Subdetector subdetector;
@@ -55,16 +48,6 @@
             throw new IllegalArgumentException("The subdetector argument is null.");
         }
         this.subdetector = subdetector;
-    }
-
-    /**
-     * Get the channel information for a geometric crystal.
-     *
-     * @param crystal the geometric crystal
-     * @return the channel information or null if does not exist
-     */
-    public EcalChannel getChannel(final EcalCrystal crystal) {
-        return this.crystalMap.getEcalChannel(crystal);
     }
 
     /**
@@ -104,10 +87,6 @@
      */
     void setChannelCollection(final EcalChannelCollection channelCollection) {
         this.channelCollection = channelCollection;
-
-        // Build the map between crystals and channels.
-        this.crystalMap = new EcalCrystalChannelMap((HPSEcalAPI) this.subdetector.getDetectorElement(),
-                channelCollection);
     }
 
     /**

Modified: java/trunk/conditions/src/main/java/org/hps/conditions/ecal/EcalConditionsConverter.java
 =============================================================================
--- java/trunk/conditions/src/main/java/org/hps/conditions/ecal/EcalConditionsConverter.java	(original)
+++ java/trunk/conditions/src/main/java/org/hps/conditions/ecal/EcalConditionsConverter.java	Tue Oct  6 11:36:11 2015
@@ -1,4 +1,7 @@
 package org.hps.conditions.ecal;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 import org.hps.conditions.api.ConditionsObjectCollection;
 import org.hps.conditions.api.ConditionsSeries;
@@ -30,6 +33,14 @@
 public class EcalConditionsConverter implements ConditionsConverter<EcalConditions> {
     
     /**
+     * Setup logger.
+     */
+    private static Logger LOGGER = Logger.getLogger(EcalConditionsConverter.class.getName());
+    static {
+        LOGGER.setLevel(Level.ALL);
+    }
+    
+    /**
      * Create combined ECAL conditions object containing all data for the current run.
      *
      * @param manager the conditions manager
@@ -40,6 +51,15 @@
        
         // Get the ECal channel map from the conditions database.
         final EcalChannelCollection channels = this.getEcalChannelCollection();
+        
+        if (channels == null) {
+            throw new IllegalStateException("The ECal channels collection is null.");
+        }
+        if (channels.size() == 0) {
+            throw new IllegalStateException("The ECal channels collection is empty.");
+        }
+        
+        LOGGER.fine("ECal channel collection has " + channels.size() + " objects");
 
         // Create the ECal conditions object that will be used to encapsulate ECal conditions collections.
         final Detector detector = getDatabaseConditionsManager().getDetectorObject();
@@ -51,9 +71,11 @@
 
         // Get the ECal gains from the conditions database and add them to the conditions set
         final EcalGainCollection gains = this.getEcalGainCollection();
+        LOGGER.fine("ECal gain collction has " + gains.size() + " objects");
         for (final EcalGain gain : gains) {
             final ChannelId channelId = new ChannelId(new int[] {gain.getChannelId()});
             final EcalChannel channel = channels.findChannel(channelId);
+            LOGGER.fine("setting channel " + channel.getChannel() + " gain to " + gain.getGain());
             conditions.getChannelConstants(channel).setGain(gain);
         }
 
@@ -70,9 +92,11 @@
 
         // Get the ECal calibrations from the conditions database and add them to the conditions set.
         final EcalCalibrationCollection calibrations = this.getEcalCalibrationCollection();
+        LOGGER.fine("ECal calibration collction has " + calibrations.size() + " objects");
         for (final EcalCalibration calibration : calibrations) {
             final ChannelId channelId = new ChannelId(new int[] {calibration.getChannelId()});
             final EcalChannel channel = channels.findChannel(channelId);
+            LOGGER.fine("setting channel " + channel.getChannel() + " ped, noise to " + calibration.getPedestal() + ", " + calibration.getNoise());
             conditions.getChannelConstants(channel).setCalibration(calibration);
         }
 

Modified: java/trunk/conditions/src/main/java/org/hps/conditions/svt/TestRunSvtDaqMapping.java
 =============================================================================
--- java/trunk/conditions/src/main/java/org/hps/conditions/svt/TestRunSvtDaqMapping.java	(original)
+++ java/trunk/conditions/src/main/java/org/hps/conditions/svt/TestRunSvtDaqMapping.java	Tue Oct  6 11:36:11 2015
@@ -4,7 +4,6 @@
 import org.hps.conditions.database.Table;
 import org.hps.util.Pair;
 import org.lcsim.detector.tracker.silicon.HpsSiSensor;
-import org.lcsim.detector.tracker.silicon.HpsTestRunSiSensor;
 
 /**
  * This class encapsulates the Test Run SVT DAQ map.

Modified: java/trunk/conditions/src/test/java/org/hps/conditions/database/DatabaseConditionsManagerTest.java
 =============================================================================
--- java/trunk/conditions/src/test/java/org/hps/conditions/database/DatabaseConditionsManagerTest.java	(original)
+++ java/trunk/conditions/src/test/java/org/hps/conditions/database/DatabaseConditionsManagerTest.java	Tue Oct  6 11:36:11 2015
@@ -47,7 +47,7 @@
         TestCase.assertTrue("The manager should be connected.", manager.isConnected());        
         TestCase.assertNotNull("The connection is null.", manager.getConnection());
         
-        // Turn off SVT detector setup becaues some classes are not available from this module.
+        // Turn off SVT detector setup because some required classes are not available from this module.
         manager.setXmlConfig("/org/hps/conditions/config/conditions_database_no_svt.xml");    
         
         // Initialize the conditions system.
@@ -59,14 +59,14 @@
         
         // Make sure that freezing the conditions system works properly.
         manager.freeze();        
-        TestCase.assertTrue("The manager should be frozen.", manager.isFrozen());                
-        manager.setDetector("HPS-EngRun2015-Nominal-v2", 1234);        
+        TestCase.assertTrue("The manager should be frozen.", manager.isFrozen());
+        manager.setDetector("HPS-EngRun2015-Nominal-v2", 1234);
         TestCase.assertEquals("The run number should be the same because system was frozen.", 5772, manager.getRun());
         
         // Check detector object state.
         Detector detector = manager.getDetectorObject();
         TestCase.assertNotNull("The detector is null.", detector);
-        TestCase.assertNotNull("The ECal conditions are null.", manager.getEcalConditions());        
+        //TestCase.assertNotNull("The ECal conditions are null.", manager.getEcalConditions());
         TestCase.assertNotNull("The ECal name is null.", manager.getEcalName());        
         TestCase.assertNotNull("The ECal subdetector is null.", manager.getEcalSubdetector());        
         TestCase.assertNotNull("The SVT conditions are null.", manager.getSvtConditions());        

Modified: java/trunk/detector-model/pom.xml
 =============================================================================
--- java/trunk/detector-model/pom.xml	(original)
+++ java/trunk/detector-model/pom.xml	Tue Oct  6 11:36:11 2015
@@ -1,5 +1,6 @@
 <?xml version="1.0"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
     <modelVersion>4.0.0</modelVersion>
     <artifactId>hps-detector-model</artifactId>
     <name>detector-model</name>
@@ -9,7 +10,7 @@
         <artifactId>hps-parent</artifactId>
         <relativePath>../parent/pom.xml</relativePath>
         <version>3.4.1-SNAPSHOT</version>
-    </parent>    
+    </parent>
     <build>
         <plugins>
             <plugin>
@@ -25,8 +26,10 @@
                             <shadedArtifactAttached>true</shadedArtifactAttached>
                             <shadedClassifierName>bin</shadedClassifierName>
                             <transformers>
-                                <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
-                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+                                <transformer
+                                    implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
+                                <transformer
+                                    implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                     <mainClass>org.lcsim.geometry.compact.converter.Main</mainClass>
                                 </transformer>
                             </transformers>
@@ -39,7 +42,17 @@
                     </execution>
                 </executions>
             </plugin>
-        </plugins>       
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <excludes>
+                        <exclude>org/hps/detector/svt/TestRunSvtDetectorSetupTest.java</exclude>
+                        <exclude>org/hps/detector/SvtAlignmentTest.java</exclude>
+                    </excludes>
+                </configuration>
+            </plugin>
+        </plugins>
     </build>
     <scm>
         <url>http://java.freehep.org/svn/repos/hps/list/java/trunk/detector-model/</url>

Copied: java/trunk/detector-model/src/main/java/org/hps/detector/ecal/EcalCrystalChannelMap.java (from r3760, java/trunk/conditions/src/main/java/org/hps/conditions/ecal/EcalCrystalChannelMap.java)
 =============================================================================
--- java/trunk/conditions/src/main/java/org/hps/conditions/ecal/EcalCrystalChannelMap.java	(original)
+++ java/trunk/detector-model/src/main/java/org/hps/detector/ecal/EcalCrystalChannelMap.java	Tue Oct  6 11:36:11 2015
@@ -1,8 +1,9 @@
-package org.hps.conditions.ecal;
+package org.hps.detector.ecal;
 
 import java.util.HashMap;
 import java.util.Map;
 
+import org.hps.conditions.ecal.EcalChannel;
 import org.hps.conditions.ecal.EcalChannel.EcalChannelCollection;
 import org.lcsim.detector.converter.compact.EcalCrystal;
 import org.lcsim.detector.converter.compact.HPSEcalAPI;

Copied: java/trunk/detector-model/src/main/java/org/hps/detector/svt/SvtDetectorSetup.java (from r3746, java/trunk/conditions/src/main/java/org/hps/conditions/svt/SvtDetectorSetup.java)
 =============================================================================
--- java/trunk/conditions/src/main/java/org/hps/conditions/svt/SvtDetectorSetup.java	(original)
+++ java/trunk/detector-model/src/main/java/org/hps/detector/svt/SvtDetectorSetup.java	Tue Oct  6 11:36:11 2015
@@ -1,4 +1,4 @@
-package org.hps.conditions.svt;
+package org.hps.detector.svt;
 
 import java.util.Collection;
 import java.util.List;
@@ -6,11 +6,19 @@
 import java.util.logging.Logger;
 
 import org.hps.conditions.database.DatabaseConditionsManager;
+import org.hps.conditions.svt.AbstractSvtDaqMapping;
+import org.hps.conditions.svt.ChannelConstants;
+import org.hps.conditions.svt.SvtChannel;
 import org.hps.conditions.svt.SvtChannel.SvtChannelCollection;
+import org.hps.conditions.svt.SvtConditions;
 import org.hps.conditions.svt.SvtDaqMapping.SvtDaqMappingCollection;
+import org.hps.conditions.svt.SvtT0Shift;
 import org.hps.conditions.svt.SvtT0Shift.SvtT0ShiftCollection;
+import org.hps.conditions.svt.TestRunSvtChannel;
 import org.hps.conditions.svt.TestRunSvtChannel.TestRunSvtChannelCollection;
+import org.hps.conditions.svt.TestRunSvtConditions;
 import org.hps.conditions.svt.TestRunSvtDaqMapping.TestRunSvtDaqMappingCollection;
+import org.hps.conditions.svt.TestRunSvtT0Shift;
 import org.hps.conditions.svt.TestRunSvtT0Shift.TestRunSvtT0ShiftCollection;
 import org.hps.util.Pair;
 import org.lcsim.conditions.ConditionsEvent;
@@ -32,6 +40,9 @@
      * Initialize logger.
      */
     private static Logger logger = LogUtil.create(SvtDetectorSetup.class);
+    static {
+        logger.setLevel(Level.ALL);
+    }
 
     /**
      * The number of noise samples.
@@ -54,11 +65,19 @@
     private String svtName = "Tracker";
 
     /**
+     * Constructor that uses the default detector name.
+     */
+    public SvtDetectorSetup() {
+        logger.info("hi");
+    }
+
+    /**
      * Constructor that takes name of SVT.
      *
      * @param svtName the name of the SVT subdetector
      */
     public SvtDetectorSetup(final String svtName) {
+        logger.info("hi");
         this.svtName = svtName;
     }
 
@@ -69,15 +88,19 @@
      */
     @Override
     public void conditionsChanged(final ConditionsEvent event) {
+        logger.info("conditions changed hook activated");
         if (this.enabled) {
+            logger.info("I am enabled");
             final DatabaseConditionsManager manager = (DatabaseConditionsManager) event.getConditionsManager();
             final Subdetector subdetector = manager.getDetectorObject().getSubdetector(this.svtName);
             if (subdetector != null) {
+                logger.info("found the SVT");
                 if (manager.isTestRun()) {
                     final TestRunSvtConditions svtConditions = manager.getCachedConditions(TestRunSvtConditions.class,
                             "test_run_svt_conditions").getCachedData();
                     this.loadTestRun(subdetector, svtConditions);
                 } else {
+                    logger.info("activating default setup (not test run)");
                     final SvtConditions svtConditions = manager.getCachedConditions(SvtConditions.class,
                             "svt_conditions").getCachedData();
                     this.loadDefault(subdetector, svtConditions);
@@ -99,7 +122,7 @@
      */
     void loadDefault(final Subdetector subdetector, final SvtConditions conditions) {
 
-        logger.info("loading SVT conditions onto subdetector " + subdetector.getName());
+        logger.info("loading default SVT conditions onto subdetector " + subdetector.getName());
 
         // Find sensor objects.
         final List<HpsSiSensor> sensors = subdetector.getDetectorElement().findDescendants(HpsSiSensor.class);

Added: java/trunk/detector-model/src/main/java/org/lcsim/detector/converter/compact/EcalCrystal.java
 =============================================================================
--- java/trunk/detector-model/src/main/java/org/lcsim/detector/converter/compact/EcalCrystal.java	(added)
+++ java/trunk/detector-model/src/main/java/org/lcsim/detector/converter/compact/EcalCrystal.java	Tue Oct  6 11:36:11 2015
@@ -0,0 +1,66 @@
+package org.lcsim.detector.converter.compact;
+
+import hep.physics.vec.BasicHep3Vector;
+import hep.physics.vec.Hep3Vector;
+import hep.physics.vec.VecOp;
+
+import org.lcsim.detector.DetectorElement;
+import org.lcsim.detector.IDetectorElement;
+import org.lcsim.detector.IGeometryInfo;
+import org.lcsim.detector.identifier.IIdentifier;
+import org.lcsim.detector.solids.Trd;
+
+/**
+ * This class implements behavior specific to the ECal crystals of the HPS experiment, 
+ * which includes access to time dependent conditions as well as DAQ setup information.
+ * @author Jeremy McCormick <[log in to unmask]>
+ */
+public class EcalCrystal extends DetectorElement {
+    
+    Hep3Vector positionFront;
+
+    /**
+     * Class constructor.
+     * @param name The name of the DetectorElement.
+     * @param parent The parent component.
+     * @param path The physical path.
+     * @param id The component's ID.
+     */
+    EcalCrystal(String name, IDetectorElement parent, String path, IIdentifier id) {
+        super(name, parent, path, id);
+    }
+    
+    /**
+     * Get the X index of this crystal.
+     * @return The X index of this crystal.
+     */
+    public int getX() {
+        return getIdentifierHelper().getValue(getIdentifier(), "ix");
+    }
+    
+    /**
+     * Get the Y index of this crystal.
+     * @return The Y index of this crystal.
+     */
+    public int getY() {
+        return getIdentifierHelper().getValue(getIdentifier(), "iy");
+    }
+               
+    /**
+     * Get the global center position of the XY plane in the front of the crystal.
+     * This is used in the reconstruction clustering algorithm for determining the 
+     * "corrected" hit position, so it is best to cache it once used, for performance 
+     * purposes.
+     * @return The center position of the XY plane in the front of the crystal.
+     */
+    public Hep3Vector getPositionFront() {
+        if (positionFront == null) {
+            IGeometryInfo geom = getGeometry();
+            double[] p = geom.transformLocalToGlobal(VecOp.add(geom.transformGlobalToLocal(geom.getPosition()), 
+                            (Hep3Vector) new BasicHep3Vector(0, 0, -1 * ((Trd) geom.getLogicalVolume().getSolid()).getZHalfLength()))).v();
+            positionFront = new BasicHep3Vector(p[0], p[1], p[2]);
+        }
+        return positionFront;
+    }
+    
+}

Added: java/trunk/detector-model/src/main/java/org/lcsim/detector/converter/compact/HPSEcal2Converter.java
 =============================================================================
--- java/trunk/detector-model/src/main/java/org/lcsim/detector/converter/compact/HPSEcal2Converter.java	(added)
+++ java/trunk/detector-model/src/main/java/org/lcsim/detector/converter/compact/HPSEcal2Converter.java	Tue Oct  6 11:36:11 2015
@@ -0,0 +1,18 @@
+package org.lcsim.detector.converter.compact;
+
+import org.lcsim.geometry.compact.Detector;
+import org.lcsim.geometry.compact.Subdetector;
+import org.lcsim.geometry.subdetector.HPSEcal2;
+
+public class HPSEcal2Converter extends AbstractSubdetectorConverter
+{
+    public void convert(Subdetector subdet, Detector detector)
+    {
+        System.out.println(this.getClass().getCanonicalName());        
+    }
+    
+    public Class getSubdetectorType()
+    {
+        return HPSEcal2.class;
+    }
+}

Added: java/trunk/detector-model/src/main/java/org/lcsim/detector/converter/compact/HPSEcal3Converter.java
 =============================================================================
--- java/trunk/detector-model/src/main/java/org/lcsim/detector/converter/compact/HPSEcal3Converter.java	(added)
+++ java/trunk/detector-model/src/main/java/org/lcsim/detector/converter/compact/HPSEcal3Converter.java	Tue Oct  6 11:36:11 2015
@@ -0,0 +1,397 @@
+package org.lcsim.detector.converter.compact;
+
+import static java.lang.Math.atan;
+import static java.lang.Math.cos;
+import static java.lang.Math.sin;
+import static java.lang.Math.tan;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Logger;
+
+import org.jdom.DataConversionException;
+import org.jdom.Element;
+import org.lcsim.detector.IDetectorElement;
+import org.lcsim.detector.ILogicalVolume;
+import org.lcsim.detector.IPhysicalVolume;
+import org.lcsim.detector.IRotation3D;
+import org.lcsim.detector.ITranslation3D;
+import org.lcsim.detector.LogicalVolume;
+import org.lcsim.detector.PhysicalVolume;
+import org.lcsim.detector.RotationGeant;
+import org.lcsim.detector.Transform3D;
+import org.lcsim.detector.Translation3D;
+import org.lcsim.detector.identifier.ExpandedIdentifier;
+import org.lcsim.detector.identifier.IExpandedIdentifier;
+import org.lcsim.detector.identifier.IIdentifier;
+import org.lcsim.detector.identifier.IIdentifierDictionary;
+import org.lcsim.detector.identifier.IIdentifierHelper;
+import org.lcsim.detector.material.MaterialStore;
+import org.lcsim.detector.solids.Trd;
+import org.lcsim.geometry.compact.Detector;
+import org.lcsim.geometry.compact.Subdetector;
+import org.lcsim.geometry.subdetector.HPSEcal3;
+
+public class HPSEcal3Converter extends AbstractSubdetectorConverter {
+
+    private static Logger LOGGER = Logger.getLogger(HPSEcal3Converter.class.toString());
+    
+    static class CrystalRange {
+
+        int xIndexMax;
+        int xIndexMin;
+        int yIndexMax;
+        int yIndexMin;
+
+        CrystalRange(final Element elem) throws Exception {
+            xIndexMin = xIndexMax = yIndexMin = yIndexMax = 0;
+
+            if (elem.getAttribute("ixmin") != null) {
+                xIndexMin = elem.getAttribute("ixmin").getIntValue();
+            } else {
+                throw new RuntimeException("Missing ixmin parameter.");
+            }
+
+            if (elem.getAttribute("ixmax") != null) {
+                xIndexMax = elem.getAttribute("ixmax").getIntValue();
+            } else {
+                throw new RuntimeException("Missing ixmax parameter.");
+            }
+
+            if (elem.getAttribute("iymin") != null) {
+                yIndexMin = elem.getAttribute("iymin").getIntValue();
+            } else {
+                throw new RuntimeException("Missing ixmax parameter.");
+            }
+
+            if (elem.getAttribute("iymax") != null) {
+                yIndexMax = elem.getAttribute("iymax").getIntValue();
+            } else {
+                throw new RuntimeException("Missing iymax parameter.");
+            }
+        }
+    }
+
+    static final double crystalToleranceX = 0.2;
+    // Tolerance factor for separating crystals to avoid overlaps.
+    static final double crystalToleranceY = 0.35;
+
+    // Margin for mother volume.
+    static final double margin = 1.1;
+
+    // Tolerance factor for moving crystals to appropriate place in mom volume.
+    static final double tolerance = 0.0;
+
+    IIdentifierDictionary dict;
+    IIdentifierHelper helper;
+
+    List<CrystalRange> ranges = new ArrayList<CrystalRange>();
+
+    private boolean checkRange(final int ix, final int iy, final List<CrystalRange> ranges) {
+        if (ranges.size() == 0) {
+            return true;
+        }
+        for (final CrystalRange range : ranges) {
+            if (ix >= range.xIndexMin && ix <= range.xIndexMax && iy >= range.yIndexMin && iy <= range.yIndexMax) {
+                return false;
+            }
+
+        }
+        return true;
+    }
+
+    @Override
+    public void convert(final Subdetector subdet, final Detector detector) {
+        
+        LOGGER.info("converting subdetector " + subdet.getName());
+
+        helper = subdet.getDetectorElement().getIdentifierHelper();
+        dict = helper.getIdentifierDictionary();
+
+        // Crystal dimensions.
+        final Element dimensions = subdet.getNode().getChild("dimensions");
+
+        double dx1, dx2, dy1, dy2, dz;
+        Element layout;
+        double beamgap = 0;
+        double beamgapTop, beamgapBottom;
+        int nx, ny;
+        double dface;
+        double tdx, tdy, tdz;
+        double bdx, bdy, bdz;
+        tdx = tdy = tdz = bdx = bdy = bdz = 0.;
+
+        try {
+            dx1 = dimensions.getAttribute("x1").getDoubleValue();
+            dx2 = dimensions.getAttribute("x2").getDoubleValue();
+            dy1 = dimensions.getAttribute("y1").getDoubleValue();
+            dy2 = dimensions.getAttribute("y2").getDoubleValue();
+            dz = dimensions.getAttribute("z").getDoubleValue();
+
+            // Layout parameters.
+            layout = subdet.getNode().getChild("layout");
+            if (layout.getAttribute("beamgap") != null) {
+                beamgap = layout.getAttribute("beamgap").getDoubleValue();
+            } else {
+                if (layout.getAttribute("beamgapTop") == null || layout.getAttribute("beamgapBottom") == null) {
+                    throw new RuntimeException(
+                            "Missing beamgap parameter in layout element, and beamgapTop or beamgapBottom was not provided.");
+                }
+            }
+            beamgapTop = beamgap;
+            if (layout.getAttribute("beamgapTop") != null) {
+                beamgapTop = layout.getAttribute("beamgapTop").getDoubleValue();
+            }
+            beamgapBottom = beamgap;
+            if (layout.getAttribute("beamgapBottom") != null) {
+                beamgapBottom = layout.getAttribute("beamgapBottom").getDoubleValue();
+            }
+            nx = layout.getAttribute("nx").getIntValue();
+            ny = layout.getAttribute("ny").getIntValue();
+            dface = layout.getAttribute("dface").getDoubleValue();
+
+            final Element topElement = layout.getChild("top");
+            final Element bottomElement = layout.getChild("bottom");
+            if (topElement != null) {
+                if (topElement.getAttribute("dx") != null) {
+                    tdx = topElement.getAttribute("dx").getDoubleValue();
+                }
+                if (topElement.getAttribute("dy") != null) {
+                    tdy = topElement.getAttribute("dy").getDoubleValue();
+                }
+                if (topElement.getAttribute("dz") != null) {
+                    tdz = topElement.getAttribute("dz").getDoubleValue();
+                }
+            }
+            if (bottomElement != null) {
+                if (bottomElement.getAttribute("dx") != null) {
+                    bdx = bottomElement.getAttribute("dx").getDoubleValue();
+                }
+                if (bottomElement.getAttribute("dy") != null) {
+                    bdy = bottomElement.getAttribute("dy").getDoubleValue();
+                }
+                if (bottomElement.getAttribute("dz") != null) {
+                    bdz = bottomElement.getAttribute("dz").getDoubleValue();
+                }
+            }
+        } catch (final DataConversionException e) {
+            throw new RuntimeException("Error converting HPSEcal3 from XML.", e);
+        }
+
+        // Crystal material.
+        final Element mat = subdet.getNode().getChild("material");
+        final String materialName = mat.getAttributeValue("name");
+
+        // Setup range of indices to be skipped.
+        for (final Object obj : layout.getChildren("remove")) {
+            final Element remove = (Element) obj;
+            try {
+                ranges.add(new CrystalRange(remove));
+            } catch (final Exception x) {
+                throw new RuntimeException(x);
+            }
+        }
+
+        if (!ranges.isEmpty()) {
+            // FIXME: Assumes single range of removed crystals.
+            ((HPSEcalDetectorElement) subdet.getDetectorElement()).setBeamGapIndices(ranges.get(0));
+        }
+
+        // Setup crystal logical volume.
+        final Trd crystalTrap = new Trd("crystal_trap", dx1, dx2, dy1, dy2, dz);
+        final ILogicalVolume crystalLogVol = new LogicalVolume("crystal_volume", crystalTrap, MaterialStore
+                .getInstance().get(materialName));
+
+        //
+        // Now we calculate parameters for crystal placement...
+        //
+
+        // Slope of the trapezoid side in X.
+        final double sx = (dx2 - dx1) / (2 * dz);
+
+        // Angle of the side of the trapezoid w.r.t. center line in X. Rotation
+        // about Y axis.
+        final double dthetay = atan(sx);
+
+        // Slope of the trapezoid side in Y.
+        final double sy = (dy2 - dy1) / (2 * dz);
+
+        // Angle of the side of the trapezoid w.r.t. center line in Y. Rotation
+        // about X axis.
+        final double dthetax = atan(sx);
+
+        // Distance between (virtual) angular origin and center of trapezoid in
+        // X.
+        final double z0x = dx1 / sx + dz;
+
+        // Distance between (virtual) angular origin and center of trapezoid in
+        // Y.
+        final double z0y = dy1 / sy + dz;
+
+        // Odd or even number of crystals in X.
+        final boolean oddx = nx % 2 != 0;
+
+        // Calculate number of X for loop.
+        if (oddx) {
+            nx -= 1;
+            nx /= 2;
+        } else {
+            nx /= 2;
+        }
+
+        double ycorrtot = 0;
+        double zcorrtoty = 0;
+
+        // Crystal sequence number used for unique volume names.
+        int crystaln = 1;
+
+        // Base name for volume.
+        final String baseName = subdet.getName() + "_crystal";
+
+        // World volume.
+        final ILogicalVolume mom = detector.getWorldVolume().getLogicalVolume();
+
+        for (int iy = 1; iy <= ny; iy++) {
+            double zcorrtotx = 0;
+            double xcorrtot = 0;
+
+            final int coeffy = 2 * iy - 1;
+            final double thetax = coeffy * dthetax;
+            final double zcorry = dy1 * (2 * sin(coeffy * dthetax));
+            final double ycorr = zcorry * tan((coeffy - 1) * dthetax);
+            final double ycenter = z0y * sin(coeffy * dthetax) + ycorr + ycorrtot + crystalToleranceY * iy;
+            final double thetaz = 0;
+
+            for (int ix = 0; ix <= nx; ix++) {
+                // Coefficient for even/odd crystal
+                int coeffx = 2 * ix;
+                if (!oddx) {
+                    coeffx -= 1;
+                    // For even number of crystals, the 0th is skipped.
+                    if (ix == 0) {
+                        continue;
+                    }
+                }
+
+                // Set parameters for next crystal placement.
+                final double thetay = coeffx * dthetay;
+                final double zcorrx = dx1 * (2 * sin(coeffx * dthetay));
+                final double xcorr = zcorrx * tan((coeffx - 1) * dthetay);
+                final double xcenter = z0x * sin(coeffx * dthetay) + xcorr + xcorrtot + crystalToleranceX * ix;
+                double zcenter = z0y * (cos(coeffy * dthetax) - 1) + z0x * (cos(coeffx * dthetay) - 1) + zcorrx
+                        + zcorrtotx + zcorry + zcorrtoty;
+                zcenter += dz;
+
+                //
+                // Bottom section.
+                //
+
+                if (this.checkRange(ix, -iy, ranges)) {
+                    // Transform of positive bottom crystal.
+                    final ITranslation3D iposBot = new Translation3D(xcenter + bdx,
+                            -(beamgapBottom + ycenter + tolerance) + bdy, zcenter + tolerance + dface + bdz);
+                    final IRotation3D irotBot = new RotationGeant(-thetax, -thetay, thetaz);
+
+                    // Place positive crystal.
+                    final IPhysicalVolume posCrystalPlacementBot = new PhysicalVolume(
+                            new Transform3D(iposBot, irotBot), baseName + crystaln, crystalLogVol, mom, crystaln);
+                    this.createDetectorElement(detector, subdet, posCrystalPlacementBot, ix, -iy);
+                    ++crystaln;
+                }
+
+                // Reflection to negative.
+                if (ix != 0) {
+                    if (this.checkRange(-ix, -iy, ranges)) {
+                        // Transform of negative.
+                        final ITranslation3D iposnegBot = new Translation3D(-xcenter + bdx,
+                                -(beamgapBottom + ycenter + tolerance) + bdy, zcenter + tolerance + dface + bdz);
+                        final IRotation3D irotnegBot = new RotationGeant(-thetax, thetay, thetaz);
+
+                        // Place negative crystal.
+                        final PhysicalVolume negCrystalPlacementBot = new PhysicalVolume(new Transform3D(iposnegBot,
+                                irotnegBot), baseName + crystaln, crystalLogVol, detector.getWorldVolume()
+                                .getLogicalVolume(), crystaln);
+                        this.createDetectorElement(detector, subdet, negCrystalPlacementBot, -ix, -iy);
+                        ++crystaln;
+                    }
+                }
+
+                if (this.checkRange(ix, iy, ranges)) {
+                    // Transform of positive top crystal.
+                    final Translation3D iposTop = new Translation3D(xcenter + tdx, beamgapTop + ycenter + tolerance
+                            + tdy, zcenter + tolerance + dface + tdz);
+                    final IRotation3D irotTop = new RotationGeant(thetax, -thetay, thetaz);
+
+                    // Place positive top crystal.
+                    final PhysicalVolume posCrystalPlacementTop = new PhysicalVolume(new Transform3D(iposTop, irotTop),
+                            baseName + crystaln, crystalLogVol, detector.getWorldVolume().getLogicalVolume(), crystaln);
+                    this.createDetectorElement(detector, subdet, posCrystalPlacementTop, ix, iy);
+                    ++crystaln;
+                }
+
+                // Reflection to negative.
+                if (ix != 0) {
+                    if (this.checkRange(-ix, iy, ranges)) {
+                        // Transform of negative.
+                        final ITranslation3D iposnegTop = new Translation3D(-xcenter + tdx, beamgapTop + ycenter
+                                + tolerance + tdy, zcenter + tolerance + dface + tdz);
+                        final IRotation3D irotnegTop = new RotationGeant(thetax, thetay, thetaz);
+
+                        // Place negative crystal.
+                        final PhysicalVolume negCrystalPlacementTop = new PhysicalVolume(new Transform3D(iposnegTop,
+                                irotnegTop), baseName + crystaln, crystalLogVol, detector.getWorldVolume()
+                                .getLogicalVolume(), crystaln);
+                        this.createDetectorElement(detector, subdet, negCrystalPlacementTop, -ix, iy);
+                        ++crystaln;
+                    }
+                }
+
+                // Increment running X and Z totals and include tolerance to
+                // avoid overlaps.
+                xcorrtot += xcorr;
+                zcorrtotx += zcorrx;
+            }
+
+            // Increment running Y totals.
+            ycorrtot += ycorr;
+            zcorrtoty += zcorry;
+        }
+
+        // Initialize state of ECal detector element.
+        ((HPSEcalDetectorElement) subdet.getDetectorElement()).initialize();
+    }
+
+    /**
+     * Create a DetectorElement for an ECal crystal.
+     *
+     * @param detector The full detector.
+     * @param subdet The subdetector.
+     * @param crystal The crystal physical volume.
+     * @param ix The value of the ix field.
+     * @param iy The value of the iy field.
+     */
+    private final void createDetectorElement(final Detector detector, final Subdetector subdet,
+            final IPhysicalVolume crystal, final int ix, final int iy) {
+        final String path = "/" + crystal.getName();
+        final IExpandedIdentifier expId = new ExpandedIdentifier(helper.getIdentifierDictionary().getNumberOfFields());
+        expId.setValue(dict.getFieldIndex("system"), subdet.getSystemID());
+        expId.setValue(dict.getFieldIndex("ix"), ix);
+        expId.setValue(dict.getFieldIndex("iy"), iy);
+        final IIdentifier id = helper.pack(expId);
+        new EcalCrystal(subdet.getName() + "_crystal" + crystal.getCopyNumber(), subdet.getDetectorElement(), path, id);
+    }
+
+    @Override
+    public Class getSubdetectorType() {
+        return HPSEcal3.class;
+    }
+
+    @Override
+    public IDetectorElement makeSubdetectorDetectorElement(final Detector detector, final Subdetector subdetector) {
+        LOGGER.info("creating detector element for subdetector " + subdetector.getName());
+        final IDetectorElement subdetectorDE = new HPSEcalDetectorElement(subdetector.getName(),
+                detector.getDetectorElement());
+        subdetector.setDetectorElement(subdetectorDE);
+        return subdetectorDE;
+    }
+}

Added: java/trunk/detector-model/src/main/java/org/lcsim/detector/converter/compact/HPSEcalAPI.java
 =============================================================================
--- java/trunk/detector-model/src/main/java/org/lcsim/detector/converter/compact/HPSEcalAPI.java	(added)
+++ java/trunk/detector-model/src/main/java/org/lcsim/detector/converter/compact/HPSEcalAPI.java	Tue Oct  6 11:36:11 2015
@@ -0,0 +1,110 @@
+package org.lcsim.detector.converter.compact;
+
+import hep.physics.vec.Hep3Vector;
+
+import java.util.List;
+
+import org.lcsim.detector.identifier.IIdentifier;
+
+/**
+ * This is a geometry API for the HPS ECAL detector.
+ * 
+ * @author Jeremy McCormick <[log in to unmask]>
+ */
+public interface HPSEcalAPI {
+    
+    /**
+     * Get the maximum X index of the crystals.
+     * @return The maximum X index of the crystals.
+     */
+    int getXIndexMax();
+    
+    /**
+     * Get the minimum X index of the crystals.
+     * @return The minimum X index of the crystals.
+     */
+    int getXIndexMin();
+    
+    /**
+     * Get the maximum Y index of the crystals.
+     * @return The maximum Y index of the crystals.
+     */
+    int getYIndexMax();
+    
+    /**
+     * Get the minimum Y index of the crystals.
+     * @return The minimum Y index of the crystals.
+     */
+    int getYIndexMin();
+    
+    /**
+     * Get an array with all the valid X indices.
+     * @return An array with the X indices.
+     */
+    List<Integer> getXIndices();
+    
+    /**
+     * Get an array with all the valid Y indices.
+     * @return An array with the Y indices.
+     */
+    List<Integer> getYIndices();
+    
+    /**
+     * True if the given indices are located in the beam gap
+     * and so do not have crystals associated with them.
+     * @param x The X index.
+     * @param y The Y index.
+     * @return True if indices reference a position in the beam gap.
+     */
+    boolean isInBeamGap(int x, int y);
+       
+    /**
+     * Get the crystal at the given indices or null if it does not exist.
+     * @param x The X index.
+     * @param y The Y index.
+     * @return The crystal at the given indices or null if it does not exist.
+     */
+    EcalCrystal getCrystal(int x, int y);
+       
+    /**
+     * Get the crystal with the given ID or null if it does not exist
+     * @param id The packed ID of the crystal.
+     * @return The packed ID of the crystal.
+     */
+    EcalCrystal getCrystal(IIdentifier id);
+    
+    /**
+     * Get the crystal at the given position in global coordinates or null if position
+     * is not inside a crystal's volume.
+     * @param position The position of the crystal.
+     * @return The crystal at the given position or null if position is not inside crystal.
+     */
+    EcalCrystal getCrystal(Hep3Vector position);
+    
+    /**
+     * Get the list of crystal objects.
+     * @return The list of crystal objects.
+     */
+    List<EcalCrystal> getCrystals();
+    
+    /**
+     * Get a row of crystals from the Y index.
+     * @param y The Y index.
+     * @return The row of crystals.
+     */
+    List<EcalCrystal> getRow(int y);
+    
+    /**
+     * Get a column of crystals from the X index.
+     * @param x The X index. 
+     * @return The column of crystals.
+     */
+    List<EcalCrystal> getColumn(int x);
+            
+    /**
+     * Get the neighbors of a crystal.
+     * @param crystal A crystal object.
+     * @return The list of neighbor objects.
+     */
+    List<EcalCrystal> getNeighbors(EcalCrystal crystal);
+}

Added: java/trunk/detector-model/src/main/java/org/lcsim/detector/converter/compact/HPSEcalConverter.java
 =============================================================================
--- java/trunk/detector-model/src/main/java/org/lcsim/detector/converter/compact/HPSEcalConverter.java	(added)
+++ java/trunk/detector-model/src/main/java/org/lcsim/detector/converter/compact/HPSEcalConverter.java	Tue Oct  6 11:36:11 2015
@@ -0,0 +1,18 @@
+package org.lcsim.detector.converter.compact;
+
+import org.lcsim.geometry.compact.Detector;
+import org.lcsim.geometry.compact.Subdetector;
+import org.lcsim.geometry.subdetector.HPSEcal;
+
+public class HPSEcalConverter extends AbstractSubdetectorConverter
+{
+    public void convert(Subdetector subdet, Detector detector)
+    {
+        System.out.println(this.getClass().getCanonicalName());        
+    }
+    
+    public Class getSubdetectorType()
+    {
+        return HPSEcal.class;
+    }
+}

Added: java/trunk/detector-model/src/main/java/org/lcsim/detector/converter/compact/HPSEcalDetectorElement.java
 =============================================================================
--- java/trunk/detector-model/src/main/java/org/lcsim/detector/converter/compact/HPSEcalDetectorElement.java	(added)
+++ java/trunk/detector-model/src/main/java/org/lcsim/detector/converter/compact/HPSEcalDetectorElement.java	Tue Oct  6 11:36:11 2015
@@ -0,0 +1,299 @@
+package org.lcsim.detector.converter.compact;
+
+import hep.physics.vec.Hep3Vector;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.lcsim.detector.IDetectorElement;
+import org.lcsim.detector.IDetectorElementContainer;
+import org.lcsim.detector.converter.compact.HPSEcal3Converter.CrystalRange;
+import org.lcsim.detector.identifier.IExpandedIdentifier;
+import org.lcsim.detector.identifier.IIdentifier;
+import org.lcsim.detector.identifier.IIdentifierHelper;
+
+/**
+ * <p>
+ * This is an implementation of a basic geometry API for the HPS ECAL.
+ * <p>
+ * The neighboring API and conventions are based on the page 7 diagram from the 
+ * <a href="https://wiki.jlab.org/hps-run/images/f/f4/Ecal_manual_annex.pdf">ECAL Manual Annex</a>
+ * in which the viewpoint is from the beam towards the detector.
+ * 
+ * @author Jeremy McCormick <[log in to unmask]>
+ * 
+ * @see HPSEcalAPI
+ * @see EcalCrystal
+ * @see org.lcsim.detector.IDetectorElement
+ * @see SubdetectorDetectorElement
+ */
+public final class HPSEcalDetectorElement extends SubdetectorDetectorElement implements HPSEcalAPI {
+        
+    Map<EcalCrystal, List<EcalCrystal>> neighborMap;
+    
+    int xIndexMax = Integer.MIN_VALUE;
+    int xIndexMin = Integer.MAX_VALUE;
+    int yIndexMax = Integer.MIN_VALUE;
+    int yIndexMin = Integer.MAX_VALUE;
+    
+    List<Integer> xIndices;
+    List<Integer> yIndices;
+    
+    CrystalRange beamGap;
+                         
+    public HPSEcalDetectorElement(String name, IDetectorElement parent) {
+        super(name, parent);
+    }
+    
+    /**
+     * Set the index range for the beam gap.
+     * @param beamGap The beam gap index range.
+     */
+    void setBeamGapIndices(CrystalRange beamGap) {
+        this.beamGap = beamGap;
+    }
+    
+    @Override
+    public int getXIndexMax() {
+        return xIndexMax;
+    }
+
+    @Override
+    public int getXIndexMin() {
+        return xIndexMin;
+    }
+
+    @Override
+    public int getYIndexMax() {
+        return yIndexMax;
+    }
+
+    @Override
+    public int getYIndexMin() {
+        return yIndexMin;
+    }
+    
+    @Override
+    public List<Integer> getXIndices() {
+        return Collections.unmodifiableList(xIndices);
+    }
+
+    @Override
+    public List<Integer> getYIndices() {
+        return Collections.unmodifiableList(yIndices);
+    }
+    
+    @Override
+    public boolean isInBeamGap(int xIndex, int yIndex) {
+        if((xIndex >= beamGap.xIndexMin && xIndex <= beamGap.xIndexMax) && 
+                (yIndex >= beamGap.yIndexMin && yIndex <= beamGap.yIndexMax)) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+    
+    @Override
+    public List<EcalCrystal> getRow(int yIndex) {
+        List<EcalCrystal> row = new ArrayList<EcalCrystal>();
+        for (int ix = xIndexMin; ix <= xIndexMax; ix++) {
+            if (ix == 0)
+                continue;
+            EcalCrystal crystal = getCrystal(ix, yIndex);
+            if (crystal != null) {
+                row.add(crystal);
+            }
+        }
+        return row;
+    }
+    
+    @Override
+    public List<EcalCrystal> getColumn(int xIndex) {
+        List<EcalCrystal> column = new ArrayList<EcalCrystal>();
+        for (int iy = yIndexMin; iy <= yIndexMax; iy++) {
+            if (iy == 0)
+                continue;
+            EcalCrystal crystal = getCrystal(xIndex, iy);
+            if (crystal != null) {
+                column.add(crystal);
+            }           
+        }
+        return column;
+    }
+                                         
+    @Override
+    public EcalCrystal getCrystal(int xIndex, int yIndex) {
+        IIdentifierHelper helper = getIdentifierHelper();
+        IExpandedIdentifier expId = helper.createExpandedIdentifier();
+        expId.setValue(helper.getFieldIndex("ix"), xIndex);
+        expId.setValue(helper.getFieldIndex("iy"), yIndex);
+        expId.setValue(helper.getFieldIndex("system"), getSystemID());
+        return getCrystal(helper.pack(expId));
+    }
+
+    @Override
+    public EcalCrystal getCrystal(IIdentifier id) {
+        IDetectorElementContainer de = findDetectorElement(id);
+        if (de == null || de.isEmpty()) {
+            return null;
+        } else {
+            return (EcalCrystal) de.get(0);
+        }
+    }    
+          
+    @Override
+    public EcalCrystal getCrystal(Hep3Vector position) {
+        IDetectorElement de = findDetectorElement(position);
+        if (de instanceof EcalCrystal) {
+            return (EcalCrystal) de;        
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public List<EcalCrystal> getNeighbors(EcalCrystal crystal) {
+        return neighborMap.get(crystal);
+    }
+           
+    @Override
+    public List<EcalCrystal> getCrystals() {
+        return findDescendants(EcalCrystal.class);
+    }
+            
+    @Override
+    public void initialize() {
+        computeIndexRanges();
+        createNeighborMap();
+    }
+    
+    void computeIndexRanges() {
+        for (EcalCrystal crystal : getCrystals()) {
+            if (crystal.getX() > xIndexMax) {
+                xIndexMax = crystal.getX();
+            }
+            if (crystal.getX() < xIndexMin) {
+                xIndexMin = crystal.getX();
+            }
+            if (crystal.getY() > yIndexMax) {
+                yIndexMax = crystal.getY();
+            }
+            if (crystal.getY() < yIndexMin) {
+                yIndexMin = crystal.getY();
+            }
+        }
+        //System.out.println("computed index boundaries ...");
+        //System.out.println("maxIndexX = " + xIndexMax);
+        //System.out.println("minIndexX = " + xIndexMin);
+        //System.out.println("maxIndexY = " + yIndexMax);
+        //System.out.println("minIndexY = " + yIndexMin);
+        
+        xIndices = new ArrayList<Integer>();
+        for (int ix = xIndexMin; ix <= xIndexMax; ix++) {
+            if (ix == 0)
+                continue;
+            //System.out.println("adding ix = " + ix);
+            xIndices.add(ix);
+        }
+        
+        yIndices = new ArrayList<Integer>();
+        for (int iy = yIndexMin; iy <= yIndexMax; iy++) {
+            if (iy == 0)
+                continue;
+            //System.out.println("adding iy = " + iy);
+            yIndices.add(iy);
+        }
+    }
+    
+    // Constants for neighboring, relative to the beam direction as per diagram.
+    enum NeighborDirection {
+        NORTH,
+        NORTHEAST,
+        EAST,
+        SOUTHEAST,
+        SOUTH,
+        SOUTHWEST,
+        WEST,
+        NORTHWEST
+    }   
+    
+    /**
+     * Create a map of a crystal to its adjacent neighbors in all eight cardinal directions.
+     * Non-existent crystals are filtered out if the the geometry object does not exist, 
+     * which automatically takes care of edge crystals and missing crystals from the beam gap
+     * without explicitly needing to check those indices for validity.
+     */
+    void createNeighborMap() {
+        neighborMap = new HashMap<EcalCrystal, List<EcalCrystal>>();
+        for (EcalCrystal crystal : getCrystals()) {            
+            //System.out.println("find neighbors for " + crystal.getName() + " @ " + crystal.getX() + " " + crystal.getY());            
+            List<EcalCrystal> neighborCrystals = new ArrayList<EcalCrystal>();                        
+            for (NeighborDirection neighborDirection : NeighborDirection.values()) {
+                int[] xy = getNeighborIndices(crystal, neighborDirection);
+                EcalCrystal neighborCrystal = getCrystal(xy[0], xy[1]);
+                if (neighborCrystal != null) {
+                    //System.out.println("  adding neighbor @ " + neighborCrystal.getX() + " " + neighborCrystal.getY());
+                    neighborCrystals.add(neighborCrystal);
+                } 
+            }            
+            neighborMap.put(crystal, neighborCrystals);            
+        }
+    }
+              
+    /**     
+     * Get the neighbor indices of a crystal.
+     * @param crystal The ECAL crystal geometry object.
+     * @param direction The direction of the neighbor from integer encoding.
+     * @return The neighbor indices.
+     */   
+    int[] getNeighborIndices(EcalCrystal crystal, NeighborDirection direction) {
+        int[] xy = new int[2];
+        int ix = crystal.getX();
+        int iy = crystal.getY();
+        switch (direction) {
+            case NORTH:
+                xy[0] = ix;
+                xy[1] = iy + 1;
+                break;
+            case NORTHEAST:
+                xy[0] = ix - 1;
+                if (xy[0] == 0) xy[0] = -1;
+                xy[1] = iy + 1;
+                break;          
+            case EAST:
+                xy[0] = ix - 1;
+                if (xy[0] == 0) xy[0] = -1;
+                xy[1] = iy;                
+                break;
+            case SOUTHEAST:
+                xy[0] = ix - 1;
+                if (xy[0] == 0) xy[0] = -1;
+                xy[1] = iy - 1;
+                break;
+            case SOUTH:
+                xy[0] = ix;
+                xy[1] = iy - 1;
+                break;
+            case SOUTHWEST:
+                xy[0] = ix + 1;
+                if (xy[0] == 0) xy[0] = 1;
+                xy[1] = iy - 1;
+                break;
+            case WEST:
+                xy[0] = ix + 1;
+                if (xy[0] == 0) xy[0] = 1;
+                xy[1] = iy;
+                break;
+            case NORTHWEST:
+                xy[0] = ix + 1;
+                if (xy[0] == 0) xy[0] = 1;
+                xy[1] = iy + 1;
+                break;
+        }
+        return xy;
+    }
+  
+}

Added: java/trunk/detector-model/src/main/java/org/lcsim/detector/converter/compact/HPSMuonCalorimeterConverter.java
 =============================================================================
--- java/trunk/detector-model/src/main/java/org/lcsim/detector/converter/compact/HPSMuonCalorimeterConverter.java	(added)
+++ java/trunk/detector-model/src/main/java/org/lcsim/detector/converter/compact/HPSMuonCalorimeterConverter.java	Tue Oct  6 11:36:11 2015
@@ -0,0 +1,132 @@
+package org.lcsim.detector.converter.compact;
+
+import org.jdom.DataConversionException;
+import org.jdom.Element;
+import org.lcsim.detector.DetectorElement;
+import org.lcsim.detector.ILogicalVolume;
+import org.lcsim.detector.IRotation3D;
+import org.lcsim.detector.ITranslation3D;
+import org.lcsim.detector.LogicalVolume;
+import org.lcsim.detector.PhysicalVolume;
+import org.lcsim.detector.RotationGeant;
+import org.lcsim.detector.Transform3D;
+import org.lcsim.detector.Translation3D;
+import org.lcsim.detector.identifier.ExpandedIdentifier;
+import org.lcsim.detector.identifier.IExpandedIdentifier;
+import org.lcsim.detector.identifier.IIdentifier;
+import org.lcsim.detector.identifier.IIdentifierDictionary;
+import org.lcsim.detector.identifier.IIdentifierHelper;
+import org.lcsim.detector.material.IMaterial;
+import org.lcsim.detector.material.MaterialStore;
+import org.lcsim.detector.solids.Box;
+import org.lcsim.geometry.compact.Detector;
+import org.lcsim.geometry.compact.Subdetector;
+import org.lcsim.geometry.subdetector.HPSMuonCalorimeter;
+
+
+/**
+ * @author jeremym
+ * @version $Id: HPSMuonCalorimeterConverter.java,v 1.2 2013/01/25 00:13:44 jeremy Exp $
+ */
+public class HPSMuonCalorimeterConverter extends AbstractSubdetectorConverter 
+{
+
+    public Class getSubdetectorType() 
+    {
+        return HPSMuonCalorimeter.class;
+    }
+
+    public void convert(Subdetector subdet, Detector detector) 
+    {               
+        
+        IIdentifierHelper helper = subdet.getDetectorElement().getIdentifierHelper();
+        IIdentifierDictionary dict = helper.getIdentifierDictionary();
+                
+        try {
+            Element node = subdet.getNode();
+            String name = node.getAttributeValue("name");
+            ILogicalVolume mother = detector.getWorldVolume().getLogicalVolume();
+
+            for (Object layerObject : node.getChildren("layer")) {
+
+                Element layer = (Element) layerObject;
+                int layerId = layer.getAttribute("id").getIntValue();
+
+                int slice = 1;
+                for (Object boxObject : layer.getChildren("box")) {
+
+                    Element element = (Element) boxObject;
+
+                    double x, y, z, px, py, pz, rx, ry, rz;
+                    x = y = z = px = py = pz = rx = ry = rz = 0.;
+
+                    if (element.getAttribute("x") != null) {
+                        x = element.getAttribute("x").getDoubleValue();
+                    } else {
+                        throw new RuntimeException("x is required.");
+                    }
+                    if (element.getAttribute("y") != null) {
+                        y = element.getAttribute("y").getDoubleValue();
+                    } else {
+                        throw new RuntimeException("y is required.");
+                    }
+                    if (element.getAttribute("z") != null) {
+                        z = element.getAttribute("z").getDoubleValue();
+                    } else {
+                        throw new RuntimeException("z is required.");
+                    }
+
+                    if (element.getAttribute("px") != null)
+                        px = element.getAttribute("px").getDoubleValue();
+                    if (element.getAttribute("py") != null)
+                        py = element.getAttribute("py").getDoubleValue();
+                    if (element.getAttribute("pz") != null)
+                        pz = element.getAttribute("pz").getDoubleValue();
+
+                    if (element.getAttribute("rx") != null)
+                        rx = element.getAttribute("rx").getDoubleValue();
+                    if (element.getAttribute("ry") != null)
+                        ry = element.getAttribute("ry").getDoubleValue();
+                    if (element.getAttribute("rz") != null)
+                        rz = element.getAttribute("rz").getDoubleValue();
+
+                    String materialName = element.getAttributeValue("material");
+                    IMaterial material = MaterialStore.getInstance().get(materialName);
+
+                    String shapeBaseName = name + "_layer" + layerId + "_sublayer" + slice;
+
+                    Box box = new Box(shapeBaseName + "_box", x/2, y/2, z/2);
+
+                    ITranslation3D pos = new Translation3D(px, py, pz);
+                    IRotation3D rot = new RotationGeant(rx, ry, rz);
+                    ILogicalVolume vol = new LogicalVolume(shapeBaseName + "_vol", box, material);
+                                       
+                    String physVolName = shapeBaseName + "_pv"; 
+                    new PhysicalVolume(new Transform3D(pos, rot), physVolName, vol, mother, 0);
+                    
+                    final IExpandedIdentifier expId = new ExpandedIdentifier(helper.getIdentifierDictionary().getNumberOfFields());
+                    expId.setValue(dict.getFieldIndex("system"), subdet.getSystemID());
+                    expId.setValue(dict.getFieldIndex("layer"), layerId);
+                    expId.setValue(dict.getFieldIndex("slice"), slice);                    
+                    int side = 1;
+                    if (py < 0) {
+                        side = -1;
+                    }
+                    expId.setValue(dict.getFieldIndex("side"), side);                                       
+                    final IIdentifier id = helper.pack(expId);                    
+                    new DetectorElement(shapeBaseName, subdet.getDetectorElement(), "/" + physVolName, id);
+                    
+                    ++slice;
+                }
+            }
+        } catch (DataConversionException e) {
+            throw new RuntimeException(e);
+        }
+
+    }
+    
+    public boolean isCalorimeter() 
+    {
+        return true;
+    }
+}

Added: java/trunk/detector-model/src/main/java/org/lcsim/detector/converter/compact/HPSTracker2Converter.java
 =============================================================================
--- java/trunk/detector-model/src/main/java/org/lcsim/detector/converter/compact/HPSTracker2Converter.java	(added)
+++ java/trunk/detector-model/src/main/java/org/lcsim/detector/converter/compact/HPSTracker2Converter.java	Tue Oct  6 11:36:11 2015
@@ -0,0 +1,407 @@
+package org.lcsim.detector.converter.compact;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.jdom.DataConversionException;
+import org.jdom.Element;
+import org.jdom.JDOMException;
+import org.lcsim.detector.DetectorElement;
+import org.lcsim.detector.DetectorIdentifierHelper;
+import org.lcsim.detector.DetectorIdentifierHelper.SystemMap;
+import org.lcsim.detector.IDetectorElement;
+import org.lcsim.detector.ILogicalVolume;
+import org.lcsim.detector.IPhysicalVolume;
+import org.lcsim.detector.IRotation3D;
+import org.lcsim.detector.ITranslation3D;
+import org.lcsim.detector.LogicalVolume;
+import org.lcsim.detector.PhysicalVolume;
+import org.lcsim.detector.RotationGeant;
+import org.lcsim.detector.Transform3D;
+import org.lcsim.detector.Translation3D;
+import org.lcsim.detector.converter.compact.subdetector.HpsTracker2;
+import org.lcsim.detector.identifier.ExpandedIdentifier;
+import org.lcsim.detector.identifier.IExpandedIdentifier;
+import org.lcsim.detector.identifier.IIdentifier;
+import org.lcsim.detector.identifier.IIdentifierDictionary;
+import org.lcsim.detector.identifier.IIdentifierHelper;
+import org.lcsim.detector.material.IMaterial;
+import org.lcsim.detector.material.MaterialStore;
+import org.lcsim.detector.solids.Box;
+import org.lcsim.detector.tracker.silicon.HpsSiSensor;
+import org.lcsim.detector.tracker.silicon.HpsTestRunSiSensor;
+import org.lcsim.detector.tracker.silicon.SiTrackerIdentifierHelper;
+import org.lcsim.detector.tracker.silicon.SiTrackerModule;
+import org.lcsim.geometry.compact.Detector;
+import org.lcsim.geometry.compact.Subdetector;
+import org.lcsim.geometry.subdetector.HPSTracker2;
+
+/**
+ * Converts an HPSTracker2 XML description into Java runtime objects.
+ * @author Jeremy McCormick <[log in to unmask]>
+ *
+ */
+public class HPSTracker2Converter extends AbstractSubdetectorConverter {
+    
+    private Map<String, ModuleParameters> moduleParameters = new HashMap<String, ModuleParameters>();
+    private Map<String, LogicalVolume> modules = new HashMap<String, LogicalVolume>();
+    private IMaterial trackingMaterial;
+    private static final boolean debug = false;    
+    
+    public Class getSubdetectorType() {
+        return HPSTracker2.class;
+    }
+    
+    public IIdentifierHelper makeIdentifierHelper(Subdetector subdetector, SystemMap systemMap) {
+        return new SiTrackerIdentifierHelper(subdetector.getDetectorElement(), makeIdentifierDictionary(subdetector), systemMap);
+    }
+
+    public void convert(Subdetector subdet, Detector detector) {
+        trackingMaterial = MaterialStore.getInstance().get("TrackingMaterial");
+        if (trackingMaterial == null) {
+            trackingMaterial = MaterialStore.getInstance().get("Air");
+        }
+        
+        // Get the tracking volume for module placement.
+        ILogicalVolume trackingVolume = detector.getTrackingVolume().getLogicalVolume();
+                
+        // Get ID helper and dictionary for subdetector.
+        DetectorIdentifierHelper helper = (DetectorIdentifierHelper) subdet.getDetectorElement().getIdentifierHelper();
+        IIdentifierDictionary iddict = subdet.getDetectorElement().getIdentifierHelper().getIdentifierDictionary();
+        int nfields = helper.getIdentifierDictionary().getNumberOfFields();
+        
+        // Get XML node for this subdetector.
+        Element node = subdet.getNode();
+        
+        // Create the module logical volumes.
+        for (Iterator i = node.getChildren("module").iterator(); i.hasNext();) {
+            Element module = (Element) i.next();
+            String moduleName = module.getAttributeValue("name");
+            moduleParameters.put(moduleName, new ModuleParameters(module));
+            modules.put(moduleName, makeModule(moduleParameters.get(moduleName)));
+        }
+        
+        try {
+            for (Iterator i = node.getChildren("layer").iterator(); i.hasNext();) {
+                
+                Element layerElement = (Element) i.next();
+                int layerId = -1;
+                layerId = layerElement.getAttribute("id").getIntValue();
+                
+                // Layer identifier.
+                IExpandedIdentifier layerPosId = new ExpandedIdentifier(nfields);
+                layerPosId.setValue(helper.getFieldIndex("system"), subdet.getSystemID());
+                layerPosId.setValue(helper.getFieldIndex("barrel"), helper.getBarrelValue());
+                layerPosId.setValue(helper.getFieldIndex("layer"), layerId);
+                
+                // DetectorElement for layer.
+                IDetectorElement layerDe = new DetectorElement(subdet.getName() + "_layer" + layerId, subdet.getDetectorElement(), helper.pack(layerPosId));
+
+                // Loop over modules within layer.
+                for (Iterator j = layerElement.getChildren("module_placement").iterator(); j.hasNext();) {
+
+                    Element modulePlacementElement = (Element)j.next();
+                    String moduleName = modulePlacementElement.getAttributeValue("name");
+                    int moduleNumber = modulePlacementElement.getAttribute("id").getIntValue();
+
+                    // Get the position and rotation parameters.  All must be explicitly specified.
+                    double x, y, z;
+                    double rx, ry, rz;
+                    x = modulePlacementElement.getAttribute("x").getDoubleValue();
+                    y = modulePlacementElement.getAttribute("y").getDoubleValue();
+                    z = modulePlacementElement.getAttribute("z").getDoubleValue();
+                    rx = modulePlacementElement.getAttribute("rx").getDoubleValue();
+                    ry = modulePlacementElement.getAttribute("ry").getDoubleValue();
+                    rz = modulePlacementElement.getAttribute("rz").getDoubleValue();
+                    
+                    ITranslation3D pos = new Translation3D(x, y, z);
+                    IRotation3D rot = new RotationGeant(rx, ry, rz);
+                    
+                    String modulePlacementName = subdet.getName() + "_" + moduleName + "_layer" + layerId + "_module" + moduleNumber;
+                    
+                    LogicalVolume lv = modules.get(moduleName);
+                    IPhysicalVolume modulePhysVol = new PhysicalVolume(new Transform3D(pos,rot), modulePlacementName, lv, trackingVolume, 0);
+                    
+                    if (debug)
+                        System.out.println("made module: " + modulePhysVol.getName());
+                    
+                    // Module DetectorElement.
+                    String modulePath = "/" + detector.getTrackingVolume().getName() + "/" + modulePlacementName;                                                        
+                    SiTrackerModule moduleDe = new SiTrackerModule(modulePlacementName, layerDe, modulePath, moduleNumber);
+                    
+                    if (debug)
+                        System.out.println("created new SiTrackerModule called " + modulePlacementName + " with path: " + modulePath);
+                                        
+                    // Make SiSensor DetectorElements.
+                    int sensorNumber = 0;
+                    for (IPhysicalVolume componentPhysVol : modulePhysVol.getLogicalVolume().getDaughters()) {
+                        // Setup the sensor.
+                        if (componentPhysVol.getLogicalVolume().getDaughters().size() != 0) {
+                            
+                            // Sensor physical volume.
+                            IPhysicalVolume sensorPhysVol = componentPhysVol.getLogicalVolume().getDaughters().get(0);
+                            
+                            ExpandedIdentifier expId = new ExpandedIdentifier(iddict.getNumberOfFields());
+                            
+                            // Setup SiSensor's identifier.
+                            expId.setValue(iddict.getFieldIndex("system"), subdet.getSystemID());
+                            expId.setValue(iddict.getFieldIndex("barrel"), 0);                            
+                            expId.setValue(iddict.getFieldIndex("layer"), helper.getValue(layerDe.getIdentifier(), "layer"));
+                            expId.setValue(iddict.getFieldIndex("module"), ((SiTrackerModule) moduleDe).getModuleId());
+                            expId.setValue(iddict.getFieldIndex("sensor"), sensorNumber);
+
+                            // Packed identifier.
+                            IIdentifier sensorId = iddict.pack(expId);
+
+                            // Sensor paths.
+                            String sensorPath = modulePath.toString() + "/" + componentPhysVol.getName() + "/" + sensorPhysVol.getName();
+                            String sensorName = moduleDe.getName() + "_sensor" + sensorNumber;
+                            
+                            // Create the sensor.
+                            HpsSiSensor sensor =  null; 
+                            if(moduleParameters.get(moduleName).getType().equals(HpsTestRunSiSensor.class.getSimpleName())){
+                                sensor = new HpsTestRunSiSensor(sensorNumber, sensorName, moduleDe, sensorPath, sensorId);
+                            } else { 
+                                sensor = new HpsSiSensor(sensorNumber, sensorName, moduleDe, sensorPath, sensorId);
+                            }
+                                      
+                            if (debug)
+                                System.out.println("created sensor " + sensor.getName());
+                            
+                            // Increment sensor numbering.
+                            ++sensorNumber;
+                        }
+                    }
+                }
+            }
+        } 
+        catch (DataConversionException e) {
+            throw new RuntimeException(e);
+        }
+        
+        // Create the stereo layers 
+        ((HpsTracker2) subdet.getDetectorElement()).createStereoLayers();
+    }    
+    
+    private LogicalVolume makeModule(ModuleParameters params)
+    {
+        double thickness = params.getThickness();
+        double x, y;
+        y = params.getDimension(0); // Y is long dimension along world's X axis.
+        x = params.getDimension(1); // X is short dimension along world Y axis.
+        
+        Box box = new Box(params.getName() + "Box", x / 2, y / 2, thickness / 2);
+        LogicalVolume volume = new LogicalVolume(params.getName() + "Volume", box, trackingMaterial);
+        
+        makeModuleComponents(volume, params);
+        
+        return volume;
+    }
+
+    private void makeModuleComponents(LogicalVolume moduleVolume, ModuleParameters moduleParameters)
+    {
+        double moduleY = moduleParameters.getDimension(0);
+        double moduleX = moduleParameters.getDimension(1);
+        Box box = (Box)moduleVolume.getSolid();
+        double moduleZ = box.getZHalfLength() * 2;                        
+        double posZ = -moduleZ / 2;
+        String moduleName = moduleVolume.getName();
+        int sensorNumber = 0;       
+        for (ModuleComponentParameters component : moduleParameters)
+        {
+            double componentThickness = component.getThickness();
+            IMaterial material = MaterialStore.getInstance().get(component.getMaterialName());
+            if (material == null)
+            {
+                throw new RuntimeException("The material " + component.getMaterialName() + " does not exist in the materials database.");
+            }           
+            boolean sensitive = component.isSensitive();            
+            int componentNumber = component.getComponentNumber();
+            posZ += componentThickness / 2;
+            String componentName = moduleName + "_component" + componentNumber;
+            Box componentBox = new Box(componentName + "Box", moduleX / 2, moduleY / 2, componentThickness / 2);
+            LogicalVolume componentVolume = new LogicalVolume(componentName, componentBox, material);
+            double zrot = 0;
+            if (sensitive)
+            {
+                if (sensorNumber > 1)
+                {
+                    throw new RuntimeException("Exceeded maximum of 2 sensors per module.");
+                }
+                // Flip 180 deg for 1st sensor.
+                if (sensorNumber == 0)
+                {
+                    zrot = Math.PI;
+                }                
+                String sensorName = componentName + "Sensor" + sensorNumber;
+                double sensorX = component.getDimensionY(); // Flipped so X is actually Y.
+                double sensorY = component.getDimensionX(); // Flipped so Y is actually X.
+                Box sensorBox = new Box(sensorName + "Box", sensorX / 2, sensorY / 2, componentThickness / 2);
+                LogicalVolume sensorVol = new LogicalVolume(sensorName, sensorBox, material);                
+                Translation3D sensorPosition = new Translation3D(0, 0, 0);
+                RotationGeant sensorRotation = new RotationGeant(0, 0, zrot);
+                new PhysicalVolume(new Transform3D(sensorPosition, sensorRotation), sensorName, sensorVol, componentVolume, sensorNumber);                
+                ++sensorNumber;
+            }
+            Translation3D position = new Translation3D(0., 0., posZ);
+            RotationGeant rotation = new RotationGeant(0, 0, zrot);
+            PhysicalVolume pv = new PhysicalVolume(new Transform3D(position, rotation), componentName, componentVolume, moduleVolume, componentNumber);
+            pv.setSensitive(sensitive);
+            posZ += componentThickness / 2;
+        }
+    }
+    
+    private static class ModuleComponentParameters {
+        private String materialName;
+        private double thickness;
+        private boolean sensitive;
+        private int componentNumber;
+        private String vis;
+        private double dimX, dimY;
+
+        ModuleComponentParameters(double dimX, double dimY, double thickness, String materialName, int componentNumber, boolean sensitive, String vis) {
+            this.dimX = dimX;
+            this.dimY = dimY;
+            this.thickness = thickness;
+            this.materialName = materialName;
+            this.sensitive = sensitive;
+            this.componentNumber = componentNumber;
+            this.vis = vis;
+        }
+
+        double getThickness() {
+            return thickness;
+        }
+
+        double getDimensionX() {
+            return dimX;
+        }
+
+        double getDimensionY() {
+            return dimY;
+        }
+
+        String getMaterialName() {
+            return materialName;
+        }
+
+        boolean isSensitive() {
+            return sensitive;
+        }
+
+        int getComponentNumber() {
+            return componentNumber;
+        }
+
+        String getVis() {
+            return vis;
+        }
+    }
+
+    private static class ModuleParameters extends ArrayList<ModuleComponentParameters> {
+        private double thickness;
+        private String name;
+        private double dimensions[] = new double[3];
+        private String vis;
+        private String type = ""; 
+
+        ModuleParameters(Element element) {
+            name = element.getAttributeValue("name");
+            
+            if (element.getAttribute("vis") != null)
+                this.vis = element.getAttribute("vis").getValue();
+            
+            if(element.getAttribute("type") != null)
+                this.type = element.getAttributeValue("type");
+            
+            // Optional dimension parameters (not always present).
+            if (element.getChild("trd") != null) {
+                Element trd = element.getChild("trd");
+                try {
+                    dimensions[0] = trd.getAttribute("x1").getDoubleValue();
+                    dimensions[1] = trd.getAttribute("x2").getDoubleValue();
+                    dimensions[2] = trd.getAttribute("z").getDoubleValue();
+                } catch (DataConversionException x) {
+                    throw new RuntimeException(x);
+                }
+            } else if (element.getChild("box") != null) {
+                Element box = element.getChild("box");
+                try {
+                    dimensions[0] = box.getAttribute("x").getDoubleValue();
+                    dimensions[1] = box.getAttribute("y").getDoubleValue();
+                } catch (DataConversionException x) {
+                    throw new RuntimeException(x);
+                }
+            }
+            
+            int cntr = 0;
+            for (Object o : element.getChildren("module_component")) {
+                try {
+
+                    Element e = (Element) o;
+
+                    double thickness = e.getAttribute("thickness").getDoubleValue();
+
+                    String materialName = e.getAttributeValue("material");
+
+                    boolean sensitive = false;
+                    if (e.getAttribute("sensitive") != null)
+                        sensitive = e.getAttribute("sensitive").getBooleanValue();
+                    String componentVis = null;
+                    if (e.getAttribute("vis") != null)
+                        componentVis = e.getAttribute("vis").getValue();
+
+                    // Sensors may have reduced dimensions for dead area.
+                    double x = dimensions[0]; // default
+                    double y = dimensions[1]; // default
+                    if (sensitive && e.getChild("dimensions") != null) {
+                        Element dimensions = e.getChild("dimensions");
+                        x = dimensions.getAttribute("x").getDoubleValue();
+                        y = dimensions.getAttribute("y").getDoubleValue();
+                    }
+                    add(new ModuleComponentParameters(x, y, thickness, materialName, cntr, sensitive, componentVis));
+                } catch (JDOMException x) {
+                    throw new RuntimeException(x);
+                }
+                ++cntr;
+            }
+            calculateThickness();
+        }
+
+        void calculateThickness() {
+            thickness = 0.; // reset thickness
+            for (ModuleComponentParameters p : this) {
+                thickness += p.getThickness();
+            }
+        }
+
+        double getThickness() {
+            return thickness;
+        }
+
+        String getName() {
+            return name;
+        }
+
+        double getDimension(int i) {
+            if (i > (dimensions.length - 1) || i < 0)
+                throw new RuntimeException("Invalid dimensions index: " + i);
+            return dimensions[i];
+        }
+        
+        String getType() { 
+            return type;
+        }
+    }
+    
+    public IDetectorElement makeSubdetectorDetectorElement(Detector detector, Subdetector subdetector)
+    {
+        IDetectorElement subdetectorDE =
+                new HpsTracker2(subdetector.getName(), detector.getDetectorElement());
+        subdetector.setDetectorElement(subdetectorDE);
+        return subdetectorDE;
+    }
+    
+}

Added: java/trunk/detector-model/src/main/java/org/lcsim/detector/converter/compact/HPSTrackerConverter.java
 =============================================================================
--- java/trunk/detector-model/src/main/java/org/lcsim/detector/converter/compact/HPSTrackerConverter.java	(added)
+++ java/trunk/detector-model/src/main/java/org/lcsim/detector/converter/compact/HPSTrackerConverter.java	Tue Oct  6 11:36:11 2015
@@ -0,0 +1,584 @@
+package org.lcsim.detector.converter.compact;
+
+import hep.physics.matrix.BasicMatrix;
+import hep.physics.vec.BasicHep3Vector;
+import hep.physics.vec.VecOp;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.jdom.DataConversionException;
+import org.jdom.Element;
+import org.jdom.JDOMException;
+import org.lcsim.detector.DetectorElement;
+import org.lcsim.detector.DetectorIdentifierHelper;
+import org.lcsim.detector.DetectorIdentifierHelper.SystemMap;
+import org.lcsim.detector.IDetectorElement;
+
+import org.lcsim.detector.IPhysicalVolume;
+import org.lcsim.detector.IPhysicalVolumePath;
+import org.lcsim.detector.IRotation3D;
+import org.lcsim.detector.ITranslation3D;
+import org.lcsim.detector.LogicalVolume;
+import org.lcsim.detector.PhysicalVolume;
+import org.lcsim.detector.RotationGeant;
+import org.lcsim.detector.RotationPassiveXYZ;
+import org.lcsim.detector.Transform3D;
+import org.lcsim.detector.Translation3D;
+import org.lcsim.detector.identifier.ExpandedIdentifier;
+import org.lcsim.detector.identifier.IExpandedIdentifier;
+import org.lcsim.detector.identifier.IIdentifier;
+import org.lcsim.detector.identifier.IIdentifierDictionary;
+import org.lcsim.detector.identifier.IIdentifierHelper;
+import org.lcsim.detector.material.IMaterial;
+import org.lcsim.detector.material.MaterialStore;
+import org.lcsim.detector.solids.Box;
+import org.lcsim.detector.solids.Polygon3D;
+import org.lcsim.detector.tracker.silicon.ChargeCarrier;
+import org.lcsim.detector.tracker.silicon.SiSensor;
+import org.lcsim.detector.tracker.silicon.SiStrips;
+import org.lcsim.detector.tracker.silicon.SiTrackerIdentifierHelper;
+import org.lcsim.detector.tracker.silicon.SiTrackerModule;
+import org.lcsim.geometry.compact.Detector;
+import org.lcsim.geometry.compact.Subdetector;
+import org.lcsim.geometry.subdetector.HPSTracker;
+
+public class HPSTrackerConverter extends AbstractSubdetectorConverter
+{
+    Map<String, ModuleParameters> moduleParameters = new HashMap<String, ModuleParameters>();
+    Map<String, LogicalVolume> modules = new HashMap<String, LogicalVolume>();
+    IMaterial vacuum;
+    
+    public Class getSubdetectorType()
+    {
+        return HPSTracker.class;
+    }
+
+    public IIdentifierHelper makeIdentifierHelper(Subdetector subdetector, SystemMap systemMap)
+    {
+        return new SiTrackerIdentifierHelper(subdetector.getDetectorElement(), makeIdentifierDictionary(subdetector), systemMap);
+    }
+
+    public void convert(Subdetector subdet, Detector detector)
+    {
+        try
+        {
+            Element node = subdet.getNode();
+            String subdetName = node.getAttributeValue("name");
+            vacuum = MaterialStore.getInstance().get("Air");
+
+            boolean reflect = true;
+            if (node.getAttribute("reflect") != null)
+            {
+                reflect = node.getAttribute("reflect").getBooleanValue();
+            }
+
+           
+
+            IDetectorElement subdetDetElem = subdet.getDetectorElement();
+            DetectorIdentifierHelper helper = (DetectorIdentifierHelper) subdetDetElem.getIdentifierHelper();
+            int nfields = helper.getIdentifierDictionary().getNumberOfFields();
+            IDetectorElement endcapPos = null;
+            IDetectorElement endcapNeg = null;
+            try
+            {
+                // Positive endcap DE
+                IExpandedIdentifier endcapPosId = new ExpandedIdentifier(nfields);
+                endcapPosId.setValue(helper.getFieldIndex("system"), subdet.getSystemID());
+                endcapPosId.setValue(helper.getFieldIndex("barrel"), helper.getBarrelValue());
+                endcapPos = new DetectorElement(subdet.getName() + "_positive", subdetDetElem);
+                endcapPos.setIdentifier(helper.pack(endcapPosId));
+                if (reflect)
+                {
+                    IExpandedIdentifier endcapNegId = new ExpandedIdentifier(nfields);
+                    endcapNegId.setValue(helper.getFieldIndex("system"), subdet.getSystemID());
+                    endcapNegId.setValue(helper.getFieldIndex("barrel"), helper.getBarrelValue());
+                    endcapNeg = new DetectorElement(subdet.getName() + "_negative", subdetDetElem);
+                    endcapNeg.setIdentifier(helper.pack(endcapNegId));
+                }
+            }
+            catch (Exception x)
+            {
+                throw new RuntimeException(x);
+            }
+
+            for (Iterator i = node.getChildren("module").iterator(); i.hasNext();)
+            {
+                Element module = (Element) i.next();
+                String moduleName = module.getAttributeValue("name");
+                moduleParameters.put(moduleName, new ModuleParameters(module));
+                modules.put(moduleName, makeModule(moduleParameters.get(moduleName)));
+            }
+
+            for (Iterator i = node.getChildren("layer").iterator(); i.hasNext();)
+            {
+                Element layerElement = (Element) i.next();
+
+                int layerId = layerElement.getAttribute("id").getIntValue();
+
+                // Positive endcap layer.
+                IExpandedIdentifier layerPosId = new ExpandedIdentifier(nfields);
+                layerPosId.setValue(helper.getFieldIndex("system"), subdet.getSystemID());
+                layerPosId.setValue(helper.getFieldIndex("barrel"), helper.getBarrelValue());
+                layerPosId.setValue(helper.getFieldIndex("layer"), layerId);
+                IDetectorElement layerPos = new DetectorElement(endcapPos.getName() + "_layer" + layerId, endcapPos, helper.pack(layerPosId));
+
+                // Negative endcap layer.
+                IDetectorElement layerNeg = null;
+                if (reflect)
+                {
+                    IExpandedIdentifier layerNegId = new ExpandedIdentifier(nfields);
+                    layerNegId.setValue(helper.getFieldIndex("system"), subdet.getSystemID());
+                    layerNegId.setValue(helper.getFieldIndex("barrel"), helper.getBarrelValue());
+                    layerNegId.setValue(helper.getFieldIndex("layer"), layerId);
+                    layerNeg = new DetectorElement(endcapNeg.getName() + "_layer_reflected" + layerId, endcapNeg, helper.pack(layerNegId));
+                }
+
+                int moduleNumber = 0;
+                for (Iterator j = layerElement.getChildren("quadrant").iterator(); j.hasNext();)
+                {
+                    Element ringElement = (Element) j.next();
+                    double zLayer = ringElement.getAttribute("z").getDoubleValue();
+                    double dz = ringElement.getAttribute("dz").getDoubleValue();
+                    double xStart = ringElement.getAttribute("xStart").getDoubleValue();
+                    double xStep = ringElement.getAttribute("xStep").getDoubleValue();
+                    int nx = ringElement.getAttribute("nx").getIntValue();
+                    double yStart = ringElement.getAttribute("yStart").getDoubleValue();
+                    int ny = ringElement.getAttribute("ny").getIntValue();
+                    double yStep = ringElement.getAttribute("yStep").getDoubleValue();
+
+                    double top_phi0 = 0;
+                    if (ringElement.getAttribute("top_phi0") != null)
+                    {
+                        top_phi0 = ringElement.getAttribute("top_phi0").getDoubleValue();
+                    }
+                    double bot_phi0 = 0;
+                    if (ringElement.getAttribute("bot_phi0") != null)
+                    {
+                        bot_phi0 = ringElement.getAttribute("bot_phi0").getDoubleValue();
+                    }
+                    String module = ringElement.getAttributeValue("module");
+                    LogicalVolume moduleVolume = modules.get(module);
+                    if (moduleVolume == null)
+                    {
+                        throw new RuntimeException("Module " + module + " was not found.");
+                    }
+                    double theta = 0;
+                    if (ringElement.getAttribute("theta") != null)
+                    {
+                        theta = ringElement.getAttribute("theta").getDoubleValue();
+                    }
+                    
+                    ModuleParameters modPars = moduleParameters.get(module);
+
+                    double x, y, z;
+                    z = zLayer;
+                    x = xStart;
+                    // System.out.println("Making modules...nx=" + nx + ";ny=" + ny);
+                    for (int k = 0; k < nx; k++ )
+                    {
+                        y = yStart;
+                        for (int kk = 0; kk < ny; kk++ )
+                        {
+                            String moduleBaseName = subdetName + "_layer" + layerId + "_module" + moduleNumber;
+                            Translation3D p = new Translation3D(x, y, z + dz);
+                            //RotationGeant rot = new RotationGeant(0, theta,-(Math.PI / 2 + top_phi0));
+                            RotationGeant rot = new RotationGeant(0, theta,-Math.PI/2 - top_phi0);
+                            new PhysicalVolume(new Transform3D(p, rot), moduleBaseName, moduleVolume, detector.getTrackingVolume().getLogicalVolume(), 0);
+                            String path = "/" + detector.getTrackingVolume().getName() + "/" + moduleBaseName;
+                            new SiTrackerModule(moduleBaseName, layerPos, path, moduleNumber);
+                            ++moduleNumber;                           
+                            if (reflect)
+                            {
+                                Translation3D pr = new Translation3D(x, -y, z + dz);                                                         
+                                  //first x, then y, then z...
+                                //RotationGeant rotr = new RotationGeant(0, theta, Math.PI/2 - bot_phi0);                                                                
+                                RotationGeant rotr = new RotationGeant(0, theta, -Math.PI/2 - bot_phi0);
+                                String path2 = "/" + detector.getTrackingVolume().getName() + "/" + moduleBaseName + "_reflected";
+                                new PhysicalVolume(new Transform3D(pr, rotr), moduleBaseName + "_reflected", moduleVolume, detector.getTrackingVolume().getLogicalVolume(), k);
+                                new SiTrackerModule(moduleBaseName + "_reflected", layerNeg, path2, moduleNumber);
+                            }
+
+                            dz = -dz;
+                            y += yStep;
+                            ++moduleNumber;
+                        }
+                        x += xStep;
+                    }
+                }
+            }
+        }
+        catch (JDOMException except)
+        {
+            throw new RuntimeException(except);
+        }
+        makeSensors(subdet);
+    }
+
+    private LogicalVolume makeModule(ModuleParameters params)
+    {
+        double thickness = params.getThickness();
+        double x, y;
+        //x = params.getDimension(0);
+        //y = params.getDimension(1);        
+        y = params.getDimension(0); // Y is long dimension along world's X axis.
+        x = params.getDimension(1); // X is short dimension along world Y axis.
+        
+        Box box = new Box(params.getName() + "Box", x / 2, y / 2, thickness / 2);
+        LogicalVolume volume = new LogicalVolume(params.getName() + "Volume", box, vacuum);
+        
+        makeModuleComponents(volume, params);
+        
+        return volume;
+    }
+
+    private void makeModuleComponents(LogicalVolume moduleVolume, ModuleParameters moduleParameters)
+    {
+        //double moduleX = moduleParameters.getDimension(0);
+        //double moduleY = moduleParameters.getDimension(1);        
+    	double moduleY = moduleParameters.getDimension(0);
+        double moduleX = moduleParameters.getDimension(1);
+        Box box = (Box)moduleVolume.getSolid();
+        double moduleZ = box.getZHalfLength() * 2;                        
+        double posZ = -moduleZ / 2;
+        String moduleName = moduleVolume.getName();
+        int sensorNumber = 0;       
+        for (ModuleComponentParameters component : moduleParameters)
+        {
+            double componentThickness = component.getThickness();
+            IMaterial material = MaterialStore.getInstance().get(component.getMaterialName());
+            if (material == null)
+            {
+                throw new RuntimeException("The material " + component.getMaterialName() + " does not exist in the materials database.");
+            }           
+            boolean sensitive = component.isSensitive();            
+            int componentNumber = component.getComponentNumber();
+            posZ += componentThickness / 2;
+            String componentName = moduleName + "_component" + componentNumber;
+            Box componentBox = new Box(componentName + "Box", moduleX / 2, moduleY / 2, componentThickness / 2);
+            LogicalVolume componentVolume = new LogicalVolume(componentName, componentBox, material);
+            double zrot = 0;
+            if (sensitive)
+            {
+                if (sensorNumber > 1)
+                {
+                    throw new RuntimeException("Exceeded maximum of 2 sensors per module.");
+                }
+                // Flip 180 deg for 1st sensor.
+                if (sensorNumber == 0)
+                {
+                    zrot = Math.PI;
+                }                
+                String sensorName = componentName + "Sensor" + sensorNumber;
+                double sensorX = component.getDimensionY(); // Flipped so X is actually Y.
+                //if (sensorX > moduleX)
+                //    throw new RuntimeException("Sensor X dimension " + sensorX + " is too big for module.");
+                double sensorY = component.getDimensionX(); // Flipped so Y is actually X.
+                //if (sensorY > moduleY)
+                //    throw new RuntimeException("Sensor Y dimension " + sensorY + " is too big for module.");
+                Box sensorBox = new Box(sensorName + "Box", sensorX / 2, sensorY / 2, componentThickness / 2);
+                LogicalVolume sensorVol = new LogicalVolume(sensorName, sensorBox, material);                
+                Translation3D sensorPosition = new Translation3D(0, 0, 0);
+                RotationGeant sensorRotation = new RotationGeant(0, 0, zrot);
+                //PhysicalVolume sensorPhysVol = 
+                new PhysicalVolume(new Transform3D(sensorPosition, sensorRotation), sensorName, sensorVol, componentVolume, sensorNumber);                
+                // TODO Could make sensors here?
+                ++sensorNumber;
+            }
+            Translation3D position = new Translation3D(0., 0., posZ);
+            RotationGeant rotation = new RotationGeant(0, 0, zrot);
+            PhysicalVolume pv = new PhysicalVolume(new Transform3D(position, rotation), componentName, componentVolume, moduleVolume, componentNumber);
+            pv.setSensitive(sensitive);
+            posZ += componentThickness / 2;
+        }
+    }
+
+    // Called after geometry is in place to create the Sensor DetectorElements.
+    private void makeSensors(Subdetector subdet)
+    {
+        SiTrackerIdentifierHelper helper = (SiTrackerIdentifierHelper) subdet.getDetectorElement().getIdentifierHelper();
+        for (IDetectorElement endcap : subdet.getDetectorElement().getChildren())
+        {
+            for (IDetectorElement layer : endcap.getChildren())
+            {
+                for (IDetectorElement module : layer.getChildren())
+                {
+                    IPhysicalVolume modulePhysVol = module.getGeometry().getPhysicalVolume();
+                    IPhysicalVolumePath modulePath = module.getGeometry().getPath();
+                    int sensorId = 0;
+                    for (IPhysicalVolume componentPhysVol : modulePhysVol.getLogicalVolume().getDaughters())
+                    {
+                        // Setup the sensor.
+                        if (componentPhysVol.getLogicalVolume().getDaughters().size() != 0)
+                        {
+                            IPhysicalVolume sensorPhysVol = componentPhysVol.getLogicalVolume().getDaughters().get(0);
+                            
+                            IIdentifierDictionary iddict = subdet.getDetectorElement().getIdentifierHelper().getIdentifierDictionary();
+
+                            ExpandedIdentifier expId = new ExpandedIdentifier(iddict.getNumberOfFields());
+                            expId.setValue(iddict.getFieldIndex("system"), subdet.getSystemID());
+
+                            if (helper.isEndcapPositive(endcap.getIdentifier()))
+                            {
+                                expId.setValue(iddict.getFieldIndex("barrel"), helper.getEndcapPositiveValue());
+                            }
+                            else if (helper.isEndcapNegative(endcap.getIdentifier()))
+                            {
+                                expId.setValue(iddict.getFieldIndex("barrel"), helper.getEndcapNegativeValue());
+                            }
+                            else if (helper.isBarrel(endcap.getIdentifier()))
+                            {
+                                expId.setValue(iddict.getFieldIndex("barrel"), helper.getBarrelValue());
+                            }
+                            else
+                            {
+                                throw new RuntimeException(endcap.getName() + " is not a positive or negative endcap!");
+                            }
+                            expId.setValue(iddict.getFieldIndex("layer"), layer.getIdentifierHelper().getValue(layer.getIdentifier(), "layer"));
+                            expId.setValue(iddict.getFieldIndex("module"), ((SiTrackerModule) module).getModuleId());
+                            expId.setValue(iddict.getFieldIndex("sensor"), sensorId);
+
+                            IIdentifier id = iddict.pack(expId);
+
+                            String sensorPath = modulePath.toString() + "/" + componentPhysVol.getName() + "/" + sensorPhysVol.getName();
+                            String sensorName = module.getName() + "_sensor" + sensorId;
+
+                            // Create the sensor.
+                            SiSensor sensor = new SiSensor(sensorId, sensorName, module, sensorPath, id);
+                            
+                            // Configure parameters of strips, etc.                              
+                            //configSensor(sensor);                            
+
+                            // Increment sensor numbering.
+                            ++sensorId;
+                        }
+                    //    }
+                    }
+                }
+            }
+        }
+    }
+   
+    // Parameters...    
+    private double readoutCapacitanceIntercept = 0;
+    private double readoutCapacitanceSlope = 0.16;
+    private double senseCapacitanceIntercept = 0;
+    private double senseCapacitanceSlope = 0.16;
+    private double readoutStripPitch = 0.060;
+    private double senseStripPitch = 0.030;
+    private double readoutTransferEfficiency = 0.986;
+    private double senseTransferEfficiency =  0.419;
+
+    // TODO: Move this method to a Driver class.
+    /*
+    private void configSensor(SiSensor sensor)
+    {
+    	//
+        Box sensorSolid = (Box) sensor.getGeometry().getLogicalVolume().getSolid();                                                        
+        
+        Polygon3D pside = sensorSolid.getFacesNormalTo(new BasicHep3Vector(0, 0, 1)).get(0);
+        Polygon3D nside = sensorSolid.getFacesNormalTo(new BasicHep3Vector(0, 0, -1)).get(0);         
+
+        sensor.setBiasSurface(ChargeCarrier.HOLE, pside);     // P side collects holes.
+        sensor.setBiasSurface(ChargeCarrier.ELECTRON, nside); // N side collects electrons.
+
+        // Setup electrodes on the XY front and back surfaces of the Box. 
+        ITranslation3D electrodesPosition = new Translation3D(VecOp.mult(-pside.getDistance(), pside.getNormal()));  // Translate to outside of polygon (always the same).
+        
+        // Strip angle.
+        IRotation3D electrodesRotation = new RotationPassiveXYZ(0.0, 0.0, 0.0); // Strips aligned to edge of sensor, ALWAYS.
+        Transform3D electrodesTransform = new Transform3D(electrodesPosition, electrodesRotation);
+        
+        // Free calculation of readout electrodes, sense electrodes determined thereon.
+        SiStrips readoutElectrodes = new SiStrips(ChargeCarrier.HOLE, readoutStripPitch, sensor, electrodesTransform);
+        SiStrips senseElectrodes = new SiStrips(ChargeCarrier.HOLE, senseStripPitch, (readoutElectrodes.getNCells()*2-1), sensor, electrodesTransform);
+        //
+
+        // Readout electrode parameters.
+        readoutElectrodes.setCapacitanceIntercept(readoutCapacitanceIntercept);
+        readoutElectrodes.setCapacitanceSlope(readoutCapacitanceSlope);
+        
+        // Sense electrode parameters.
+        senseElectrodes.setCapacitanceIntercept(senseCapacitanceIntercept);
+        senseElectrodes.setCapacitanceSlope(senseCapacitanceSlope); 
+
+        // Set sense and readout electrodes.
+        sensor.setSenseElectrodes(senseElectrodes);
+        sensor.setReadoutElectrodes(readoutElectrodes);
+
+        // Charge transfer efficiency.
+        double[][] transferEfficiencies = {{readoutTransferEfficiency, senseTransferEfficiency}};
+        sensor.setTransferEfficiencies(ChargeCarrier.HOLE, new BasicMatrix(transferEfficiencies));
+    }
+    */
+    
+    static class ModuleComponentParameters
+    {
+        protected String materialName;
+        protected double thickness;
+        protected boolean sensitive;
+        protected int componentNumber;
+        protected String vis;
+        protected double dimX, dimY;
+
+        public ModuleComponentParameters(double dimX, double dimY, double thickness, String materialName, int componentNumber, boolean sensitive, String vis)
+        {
+            this.dimX = dimX;
+            this.dimY = dimY;
+            this.thickness = thickness;
+            this.materialName = materialName;
+            this.sensitive = sensitive;
+            this.componentNumber = componentNumber;
+            this.vis = vis;
+        }
+
+        public double getThickness()
+        {
+            return thickness;
+        }
+
+        public double getDimensionX()
+        {
+            return dimX;
+        }
+
+        public double getDimensionY()
+        {
+            return dimY;
+        }
+
+        public String getMaterialName()
+        {
+            return materialName;
+        }
+
+        public boolean isSensitive()
+        {
+            return sensitive;
+        }
+
+        public int getComponentNumber()
+        {
+            return componentNumber;
+        }
+
+        public String getVis()
+        {
+            return vis;
+        }
+    }
+
+    static class ModuleParameters extends ArrayList<ModuleComponentParameters>
+    {
+        double thickness;
+        String name;
+        double dimensions[] = new double[3];
+        String vis;
+
+        public ModuleParameters(Element element)
+        {
+            name = element.getAttributeValue("name");
+            if (element.getAttribute("vis") != null)
+                this.vis = element.getAttribute("vis").getValue();
+            // Optional dimension parameters (not always present).
+            if (element.getChild("trd") != null)
+            {
+                Element trd = element.getChild("trd");
+                try
+                {
+                    dimensions[0] = trd.getAttribute("x1").getDoubleValue();
+                    dimensions[1] = trd.getAttribute("x2").getDoubleValue();
+                    dimensions[2] = trd.getAttribute("z").getDoubleValue();
+                }
+                catch (DataConversionException x)
+                {
+                    throw new RuntimeException(x);
+                }
+            }
+            else if (element.getChild("box") != null)
+            {
+                Element box = element.getChild("box");
+                try
+                {
+                    dimensions[0] = box.getAttribute("x").getDoubleValue();
+                    dimensions[1] = box.getAttribute("y").getDoubleValue();
+                }
+                catch (DataConversionException x)
+                {
+                    throw new RuntimeException(x);
+                }
+            }
+            int cntr = 0;
+            for (Object o : element.getChildren("module_component"))
+            {
+                try
+                {
+
+                    Element e = (Element) o;
+
+                    double thickness = e.getAttribute("thickness").getDoubleValue();
+
+                    String materialName = e.getAttributeValue("material");
+
+                    boolean sensitive = false;
+                    if (e.getAttribute("sensitive") != null)
+                        sensitive = e.getAttribute("sensitive").getBooleanValue();
+                    String componentVis = null;
+                    if (e.getAttribute("vis") != null)
+                        componentVis = e.getAttribute("vis").getValue();
+
+                    // Sensors may have reduced dimensions for dead area.
+                    double x = dimensions[0]; // default
+                    double y = dimensions[1]; // default
+                    if (sensitive && e.getChild("dimensions") != null)
+                    {
+                        Element dimensions = e.getChild("dimensions");
+                        x = dimensions.getAttribute("x").getDoubleValue();
+                        y = dimensions.getAttribute("y").getDoubleValue();
+                        // System.out.println("x,y="+x+","+y);
+                    }
+                    add(new ModuleComponentParameters(x, y, thickness, materialName, cntr, sensitive, componentVis));
+                }
+                catch (JDOMException x)
+                {
+                    throw new RuntimeException(x);
+                }
+                ++cntr;
+            }
+            calculateThickness();
+        }
+
+        public void calculateThickness()
+        {
+            thickness = 0.; // reset thickness
+            for (ModuleComponentParameters p : this)
+            {
+                thickness += p.getThickness();
+            }
+        }
+
+        public double getThickness()
+        {
+            return thickness;
+        }
+
+        public String getName()
+        {
+            return name;
+        }
+
+        public double getDimension(int i)
+        {
+            if (i > (dimensions.length - 1) || i < 0)
+                throw new RuntimeException("Invalid dimensions index: " + i);
+            return dimensions[i];
+        }
+
+        public String getVis()
+        {
+            return vis;
+        }
+    }
+}

Added: java/trunk/detector-model/src/main/java/org/lcsim/detector/tracker/silicon/HpsTestRunSiSensor.java
 =============================================================================
--- java/trunk/detector-model/src/main/java/org/lcsim/detector/tracker/silicon/HpsTestRunSiSensor.java	(added)
+++ java/trunk/detector-model/src/main/java/org/lcsim/detector/tracker/silicon/HpsTestRunSiSensor.java	Tue Oct  6 11:36:11 2015
@@ -0,0 +1,98 @@
+package org.lcsim.detector.tracker.silicon;
+
+import org.lcsim.detector.IDetectorElement;
+import org.lcsim.detector.identifier.IIdentifier;
+
+
+/**
+ * This class extends {@link HpsSiSensor} with conditions specific to HPS SVT half-modules
+ * (sensors) used during the test run.  Each half-module is uniquely identified by 
+ * an FPGA/Hybrid ID pair which is then related to calibration conditions such as
+ * baseline, noise gain etc.
+ * 
+ * @author Jeremy McCormick <[log in to unmask]>
+ * @author Omar Moreno <[log in to unmask]>
+ */
+public class HpsTestRunSiSensor extends HpsSiSensor {
+	
+	
+	protected int fpgaID;
+	protected int hybridID;
+			
+	
+	/**
+	 * This class constructor matches the signature of <code>SiSensor</code>.
+	 * @param sensorid The sensor ID.
+	 * @param name The name of the sensor.
+	 * @param parent The parent DetectorElement.
+	 * @param support The physical support path.
+	 * @param id The identifier of the sensor.
+	 */
+	public HpsTestRunSiSensor(
+            int sensorid,
+            String name,
+            IDetectorElement parent,
+            String support,
+            IIdentifier id)
+    {
+		super(sensorid, name, parent, support, id);
+    }
+	
+	
+
+	/**
+	 * Get the FPGA ID associated with this sensor.
+	 * 
+	 * @return The FPGA ID
+	 */
+	public int getFpgaID() {
+		return fpgaID;
+	}
+
+	/**
+	 * Get the hybrid ID associated with this sensor.
+	 * 
+	 * @return The hybrid ID
+	 */
+	public int getHybridID() {
+		return hybridID;
+	}
+
+	@Override
+	public int getFebID(){
+	    throw new RuntimeException("This method is not supported for the HpsTestRunSiSensor.");
+	}
+	
+	@Override
+	public int getFebHybridID(){
+	    throw new RuntimeException("This method is not supported for the HpsTestRunSiSensor.");
+	}
+
+	/**
+	 * Set the FPGA ID associated with this sensor.
+	 * 
+	 * @param The FPGA ID
+	 */
+	public void setFpgaID(int fpgaID) {
+		this.fpgaID = fpgaID;
+	}
+
+	/**
+	 * Set the hybrid ID associated with this sensor.
+	 * 
+	 * @param The hybrid ID.
+	 */
+	public void setHybridID(int hybridID) {
+		this.hybridID = hybridID;
+	}
+	
+	@Override
+	public void setFebID(int febID) {
+	    throw new RuntimeException("This method is not supported for the HpsTestRunSiSensor.");
+	}
+	
+	@Override
+	public void setFebHybridID(int febHybridID) {
+	    throw new RuntimeException("This method is not supported for the HpsTestRunSiSensor.");
+	}
+}

Added: java/trunk/detector-model/src/main/java/org/lcsim/geometry/compact/converter/lcdd/HPSEcal.java
 =============================================================================
--- java/trunk/detector-model/src/main/java/org/lcsim/geometry/compact/converter/lcdd/HPSEcal.java	(added)
+++ java/trunk/detector-model/src/main/java/org/lcsim/geometry/compact/converter/lcdd/HPSEcal.java	Tue Oct  6 11:36:11 2015
@@ -0,0 +1,258 @@
+package org.lcsim.geometry.compact.converter.lcdd;
+
+import static java.lang.Math.atan;
+import static java.lang.Math.cos;
+import static java.lang.Math.sin;
+import static java.lang.Math.tan;
+
+import org.jdom.Element;
+import org.jdom.JDOMException;
+import org.lcsim.geometry.compact.converter.lcdd.util.Box;
+import org.lcsim.geometry.compact.converter.lcdd.util.Define;
+import org.lcsim.geometry.compact.converter.lcdd.util.LCDD;
+import org.lcsim.geometry.compact.converter.lcdd.util.PhysVol;
+import org.lcsim.geometry.compact.converter.lcdd.util.Position;
+import org.lcsim.geometry.compact.converter.lcdd.util.Rotation;
+import org.lcsim.geometry.compact.converter.lcdd.util.SensitiveDetector;
+import org.lcsim.geometry.compact.converter.lcdd.util.Trapezoid;
+import org.lcsim.geometry.compact.converter.lcdd.util.Volume;
+
+/**
+ * LCDD model for the HPS inner ECal.
+ * 
+ * Coordinate System Notes:
+ * 
+ * Beam travels in +X direction.
+ * Magnetic field is -Z (????).
+ * Y is the beam bend plane.
+ * 
+ * The dimensions element defines the crystal HALF dimensions: x1, x2, y1, y2, and z.
+ * 
+ * The layout element defines the placement of the crystals.
+ * 
+ * <ul>
+ * <li>beamgap - offset from the beamline in the Y coordinate</li>
+ * <li>nx - number of crystals in X</li>
+ * <li>ny - number of crystals in Y along each side of beam</li>
+ * <li>dface - distance from origin to the face of the calorimeter along X</li>
+ * </ul>
+ * 
+ * @author Jeremy McCormick <[log in to unmask]>
+ * @author Tim Nelson <[log in to unmask]>
+ * 
+ * @version $Id: HPSEcal.java,v 1.12 2011/07/18 21:01:53 jeremy Exp $
+ */
+public class HPSEcal extends LCDDSubdetector
+{
+    // Tolerance factor for moving crystals to appropriate place in mom volume.
+    static final double tolerance = 0.1;
+    
+    // Tolerance factor for separating crystals to avoid overlaps.
+    static final double crystalTolerance = 0.1;
+    
+    HPSEcal(Element node) throws JDOMException
+    {
+        super(node); 
+    }
+    
+    void addToLCDD(LCDD lcdd, SensitiveDetector sens) throws JDOMException
+    {
+        if (sens == null)
+            throw new RuntimeException("SensitiveDetector is null!");
+        
+        // Crystal dimensions.
+        Element dimensions = node.getChild("dimensions");
+        double dx1 = dimensions.getAttribute("x1").getDoubleValue();
+        double dx2 = dimensions.getAttribute("x2").getDoubleValue();
+        double dy1 = dimensions.getAttribute("y1").getDoubleValue();
+        double dy2 = dimensions.getAttribute("y2").getDoubleValue();
+        double dz = dimensions.getAttribute("z").getDoubleValue();
+                
+        // Crystal material.
+        Element mat = node.getChild("material");
+        String materialName = mat.getAttributeValue("name");
+        
+        // Layout parameters.
+        Element layout = node.getChild("layout");
+        double beamgap = layout.getAttribute("beamgap").getDoubleValue();
+        int nx = layout.getAttribute("nx").getIntValue();
+        int ny = layout.getAttribute("ny").getIntValue();
+        double dface = layout.getAttribute("dface").getDoubleValue();
+                
+        // Setup crystal logical volume.
+        Trapezoid crystalTrap = new Trapezoid("crystal_trap", dx1, dx2, dy1, dy2, dz);
+        Volume crystalLogVol = new Volume("crystal_volume", crystalTrap, lcdd.getMaterial(materialName));
+        crystalLogVol.setSensitiveDetector(sens);
+        
+        lcdd.add(crystalTrap);
+        lcdd.add(crystalLogVol);
+                      
+        // Mother volume dimensions.
+        double mx, my, mz;
+        double margin = 1.1;
+        mx = nx*Math.max(dx2,dx1)*margin;
+        my = ny*Math.max(dy2, dy1)*margin;
+        mz = dz*margin;  
+
+        // Envelope box and logical volume for one section of ECal.
+        Box momBox = new Box("ecal_env_box", mx*2, my*2, mz*2);
+        Volume momVol = new Volume("ecal_env_volume", momBox, lcdd.getMaterial("Air"));       
+        
+        lcdd.add(momBox);
+        lcdd.add(momVol);
+        
+        // Slope of the trapezoid side in X.
+        double sx = (dx2-dx1)/(2*dz);
+        
+        // Angle of the side of the trapezoid w.r.t. center line in X.  Rotation about Y axis.
+        double dthetay = atan(sx);        
+        
+        // Slope of the trapezoid side in Y.
+        double sy = (dy2-dy1)/(2*dz);      
+        
+        // Angle of the side of the trapezoid w.r.t. center line in Y.  Rotation about X axis.        
+        double dthetax = atan(sx);
+        
+        // Distance between (virtual) angular origin and center of trapezoid in X.
+        double z0x = dx1/sx+dz;
+        
+        // Distance between (virtual) angular origin and center of trapezoid in Y.
+        double z0y = dy1/sy+dz;
+                
+        // Odd or even number of crystals in X.
+        boolean oddx = (nx % 2 != 0);
+        
+        // Calculate number of X for loop.
+        if (oddx)
+        {
+            nx -= 1;
+            nx /= 2;
+        }
+        else
+        {
+            nx /= 2;
+        }
+
+        double ycorrtot = 0;
+        double zcorrtoty = 0;
+        
+        Define define = lcdd.getDefine();
+
+        for (int iy=1; iy<=ny; iy++)
+        {
+            double zcorrtotx = 0;
+            double xcorrtot = 0;
+            
+            int coeffy = 2*iy-1;
+            double thetax = coeffy*dthetax;
+            double zcorry = dy1*(2*sin(coeffy*dthetax));
+            double ycorr = zcorry*tan((coeffy-1)*dthetax);
+            double ycenter = z0y*sin(coeffy*dthetax)+ycorr+ycorrtot+(crystalTolerance*iy);
+            
+            for (int ix=0; ix<=nx; ix++)
+            {                             
+                // Coefficient for even/odd crystal
+                int coeffx = 2*ix;
+                if (!oddx)
+                {
+                    coeffx -= 1;
+                    // For even number of crystals, the 0th is skipped.
+                    if (ix==0)
+                        continue;
+                }
+                
+                // Set parameters for next crystal placement.
+                // FIXME Need documentation here.
+                double thetay = coeffx*dthetay;
+                double zcorrx = dx1*(2*sin(coeffx*dthetay));
+                double xcorr = zcorrx*tan((coeffx-1)*dthetay);
+                double xcenter = z0x*sin(coeffx*dthetay)+xcorr+xcorrtot+(crystalTolerance*ix);
+                double zcenter = z0y*(cos(coeffy*dthetax)-1)+z0x*(cos(coeffx*dthetay)-1)+zcorrx+zcorrtotx+zcorry+zcorrtoty;
+                zcenter += dz;
+                                
+                double thetaz = 0;
+                                
+                String baseName = "crystal"+ix+"-"+iy;
+                
+                // Transform of positive.
+                Position ipos = 
+                    new Position(baseName+"_pos_pos", xcenter, ycenter-my+tolerance, zcenter-mz+tolerance);
+                Rotation irot = 
+                    new Rotation(baseName+"_rot_pos", thetax, -thetay, thetaz);
+
+                define.addPosition(ipos);
+                define.addRotation(irot);            
+
+                // Place positive crystal.
+                PhysVol posCrystalPlacement = new PhysVol(crystalLogVol, momVol, ipos, irot);
+                
+                // Add volume IDs.
+                posCrystalPlacement.addPhysVolID("ix", ix);
+                posCrystalPlacement.addPhysVolID("iy", iy);            
+
+                // DEBUG
+                System.out.println(ix + ", " + iy);
+
+                // Reflection to negative.
+                if (ix != 0)
+                {
+                    // Transform of negative.
+                    Position iposneg = 
+                        new Position(baseName+"_pos_neg", -xcenter, ycenter-my+tolerance, zcenter-mz+tolerance);
+                    Rotation irotneg =                 
+                        new Rotation(baseName+"_rot_neg", thetax, thetay, thetaz);
+
+                    define.addPosition(iposneg);
+                    define.addRotation(irotneg);
+
+                    // Place negative crystal.
+                    PhysVol negCrystalPlacement = new PhysVol(crystalLogVol, momVol, iposneg, irotneg);
+                    
+                    // Add volume IDs.
+                    negCrystalPlacement.addPhysVolID("ix", -ix);
+                    negCrystalPlacement.addPhysVolID("iy", iy);
+                    
+                    // DEBUG
+                    System.out.println(-ix + ", " + iy);
+                }                
+
+                // Increment running X and Z totals and include tolerance to avoid overlaps.
+                xcorrtot += xcorr;                
+                zcorrtotx += zcorrx;
+            }        
+                        
+            // Increment running Y totals.
+            ycorrtot += ycorr;
+            zcorrtoty += zcorry;
+        }
+                
+        // Get mother volume for envelope.
+        Volume world = lcdd.pickMotherVolume(this);
+        
+        // Place the top section.
+        Position mpostop = new Position(momVol.getVolumeName() + "_top_pos", dface+mz, 0, my+beamgap);
+        Rotation mrottop = new Rotation(momVol.getVolumeName() + "_top_rot", 0, -Math.PI/2, (3*Math.PI)/2);
+        define.addPosition(mpostop);
+        define.addRotation(mrottop);
+        PhysVol topSide = new PhysVol(momVol, world, mpostop, mrottop);
+        topSide.addPhysVolID("system", this.getSystemID());
+        topSide.addPhysVolID("side", 1);
+                
+        // Place the bottom section.        
+        Position mposbot = new Position(momVol.getVolumeName() + "_bot_pos", dface+mz, 0, -my-beamgap);
+        Rotation mrotbot = new Rotation(momVol.getVolumeName() + "_bot_rot", 0, -Math.PI/2, Math.PI/2);
+        define.addPosition(mposbot);
+        define.addRotation(mrotbot);
+        PhysVol botSide = new PhysVol(momVol, world, mposbot, mrotbot);        
+        botSide.addPhysVolID("system", this.getSystemID());
+        botSide.addPhysVolID("side", -1);
+        
+        // Make the section box invisible.
+        momVol.setVisAttributes(lcdd.getVisAttributes("InvisibleWithDaughters"));
+    }
+    
+    public boolean isCalorimeter()
+    {
+        return true;
+    }
+}

Added: java/trunk/detector-model/src/main/java/org/lcsim/geometry/compact/converter/lcdd/HPSEcal2.java
 =============================================================================
--- java/trunk/detector-model/src/main/java/org/lcsim/geometry/compact/converter/lcdd/HPSEcal2.java	(added)
+++ java/trunk/detector-model/src/main/java/org/lcsim/geometry/compact/converter/lcdd/HPSEcal2.java	Tue Oct  6 11:36:11 2015
@@ -0,0 +1,253 @@
+package org.lcsim.geometry.compact.converter.lcdd;
+
+import static java.lang.Math.atan;
+import static java.lang.Math.cos;
+import static java.lang.Math.sin;
+import static java.lang.Math.tan;
+
+import org.jdom.Element;
+import org.jdom.JDOMException;
+import org.lcsim.geometry.compact.converter.lcdd.util.Box;
+import org.lcsim.geometry.compact.converter.lcdd.util.Define;
+import org.lcsim.geometry.compact.converter.lcdd.util.LCDD;
+import org.lcsim.geometry.compact.converter.lcdd.util.PhysVol;
+import org.lcsim.geometry.compact.converter.lcdd.util.Position;
+import org.lcsim.geometry.compact.converter.lcdd.util.Rotation;
+import org.lcsim.geometry.compact.converter.lcdd.util.SensitiveDetector;
+import org.lcsim.geometry.compact.converter.lcdd.util.Trapezoid;
+import org.lcsim.geometry.compact.converter.lcdd.util.Volume;
+
+/**
+ * LCDD model for the HPS inner ECal.
+ * 
+ * Coordinate System Notes:
+ * 
+ * Beam travels in +X direction.
+ * Magnetic field is -Z (????).
+ * Y is the beam bend plane.
+ * 
+ * The dimensions element defines the crystal HALF dimensions: x1, x2, y1, y2, and z.
+ * 
+ * The layout element defines the placement of the crystals.
+ * 
+ * <ul>
+ * <li>beamgap - offset from the beamline in the Y coordinate</li>
+ * <li>nx - number of crystals in X</li>
+ * <li>ny - number of crystals in Y along each side of beam</li>
+ * <li>dface - distance from origin to the face of the calorimeter along X</li>
+ * </ul>
+ * 
+ * @author Jeremy McCormick
+ * @author Tim Nelson
+ * 
+ * @version $Id: HPSEcal2.java,v 1.1 2011/07/14 22:45:55 jeremy Exp $
+ */
+public class HPSEcal2 extends LCDDSubdetector
+{
+    // Tolerance factor for moving crystals to appropriate place in mom volume.
+    static final double tolerance = 0.1;
+    
+    // Tolerance factor for separating crystals to avoid overlaps.
+    static final double crystalTolerance = 0.1;
+    
+    HPSEcal2(Element node) throws JDOMException
+    {
+        super(node); 
+    }
+    
+    void addToLCDD(LCDD lcdd, SensitiveDetector sens) throws JDOMException
+    {
+        if (sens == null)
+            throw new RuntimeException("SensitiveDetector is null!");
+        
+        // Crystal dimensions.
+        Element dimensions = node.getChild("dimensions");
+        double dx1 = dimensions.getAttribute("x1").getDoubleValue();
+        double dx2 = dimensions.getAttribute("x2").getDoubleValue();
+        double dy1 = dimensions.getAttribute("y1").getDoubleValue();
+        double dy2 = dimensions.getAttribute("y2").getDoubleValue();
+        double dz = dimensions.getAttribute("z").getDoubleValue();
+                
+        // Crystal material.
+        Element mat = node.getChild("material");
+        String materialName = mat.getAttributeValue("name");
+        
+        // Layout parameters.
+        Element layout = node.getChild("layout");
+        double beamgap = layout.getAttribute("beamgap").getDoubleValue();
+        int nx = layout.getAttribute("nx").getIntValue();
+        int ny = layout.getAttribute("ny").getIntValue();
+        double dface = layout.getAttribute("dface").getDoubleValue();
+                
+        // Setup crystal logical volume.
+        Trapezoid crystalTrap = new Trapezoid("crystal_trap", dx1, dx2, dy1, dy2, dz);
+        Volume crystalLogVol = new Volume("crystal_volume", crystalTrap, lcdd.getMaterial(materialName));
+        crystalLogVol.setSensitiveDetector(sens);
+        
+        lcdd.add(crystalTrap);
+        lcdd.add(crystalLogVol);
+                      
+        // Mother volume dimensions.
+        double mx, my, mz;
+        double margin = 1.1;
+        mx = nx*Math.max(dx2,dx1)*margin;
+        my = ny*Math.max(dy2, dy1)*margin;
+        mz = dz*margin;  
+
+        // Envelope box and logical volume for one section of ECal.
+        Box momBox = new Box("ecal_env_box", mx*2, my*2, mz*2);
+        Volume momVol = new Volume("ecal_env_volume", momBox, lcdd.getMaterial("Air"));       
+        
+        lcdd.add(momBox);
+        lcdd.add(momVol);
+        
+        // Slope of the trapezoid side in X.
+        double sx = (dx2-dx1)/(2*dz);
+        
+        // Angle of the side of the trapezoid w.r.t. center line in X.  Rotation about Y axis.
+        double dthetay = atan(sx);        
+        
+        // Slope of the trapezoid side in Y.
+        double sy = (dy2-dy1)/(2*dz);      
+        
+        // Angle of the side of the trapezoid w.r.t. center line in Y.  Rotation about X axis.        
+        double dthetax = atan(sx);
+        
+        // Distance between (virtual) angular origin and center of trapezoid in X.
+        double z0x = dx1/sx+dz;
+        
+        // Distance between (virtual) angular origin and center of trapezoid in Y.
+        double z0y = dy1/sy+dz;
+                
+        // Odd or even number of crystals in X.
+        boolean oddx = (nx % 2 != 0);
+        
+        // Calculate number of X for loop.
+        if (oddx)
+        {
+            nx -= 1;
+            nx /= 2;
+        }
+        else
+        {
+            nx /= 2;
+        }
+
+        double ycorrtot = 0;
+        double zcorrtoty = 0;
+        
+        Define define = lcdd.getDefine();
+
+        for (int iy=1; iy<=ny; iy++)
+        {
+            double zcorrtotx = 0;
+            double xcorrtot = 0;
+            
+            int coeffy = 2*iy-1;
+            double thetax = coeffy*dthetax;
+            double zcorry = dy1*(2*sin(coeffy*dthetax));
+            double ycorr = zcorry*tan((coeffy-1)*dthetax);
+            double ycenter = z0y*sin(coeffy*dthetax)+ycorr+ycorrtot+(crystalTolerance*iy);
+            
+            for (int ix=0; ix<=nx; ix++)
+            {                             
+                // Coefficient for even/odd crystal
+                int coeffx = 2*ix;
+                if (!oddx)
+                {
+                    coeffx -= 1;
+                    // For even number of crystals, the 0th is skipped.
+                    if (ix==0)
+                        continue;
+                }
+                
+                // Set parameters for next crystal placement.
+                // FIXME Need documentation here.
+                double thetay = coeffx*dthetay;
+                double zcorrx = dx1*(2*sin(coeffx*dthetay));
+                double xcorr = zcorrx*tan((coeffx-1)*dthetay);
+                double xcenter = z0x*sin(coeffx*dthetay)+xcorr+xcorrtot+(crystalTolerance*ix);
+                double zcenter = z0y*(cos(coeffy*dthetax)-1)+z0x*(cos(coeffx*dthetay)-1)+zcorrx+zcorrtotx+zcorry+zcorrtoty;
+                zcenter += dz;
+                                
+                double thetaz = 0;
+                                
+                String baseName = "crystal"+ix+"-"+iy;
+                
+                // Transform of positive.
+                Position ipos = 
+                    new Position(baseName+"_pos_pos", xcenter, ycenter-my+tolerance, zcenter-mz+tolerance);
+                Rotation irot = 
+                    new Rotation(baseName+"_rot_pos", thetax, -thetay, thetaz);
+
+                define.addPosition(ipos);
+                define.addRotation(irot);            
+
+                // Place positive crystal.
+                PhysVol posCrystalPlacement = new PhysVol(crystalLogVol, momVol, ipos, irot);
+                
+                // Add volume IDs.
+                posCrystalPlacement.addPhysVolID("ix", ix);
+                posCrystalPlacement.addPhysVolID("iy", iy);                               
+
+                // Reflection to negative.
+                if (ix != 0)
+                {
+                    // Transform of negative.
+                    Position iposneg = 
+                        new Position(baseName+"_pos_neg", -xcenter, ycenter-my+tolerance, zcenter-mz+tolerance);
+                    Rotation irotneg =                 
+                        new Rotation(baseName+"_rot_neg", thetax, thetay, thetaz);
+
+                    define.addPosition(iposneg);
+                    define.addRotation(irotneg);
+
+                    // Place negative crystal.
+                    PhysVol negCrystalPlacement = new PhysVol(crystalLogVol, momVol, iposneg, irotneg);
+                    
+                    // Add volume IDs.
+                    negCrystalPlacement.addPhysVolID("ix", -ix);
+                    negCrystalPlacement.addPhysVolID("iy", iy);
+                }                
+
+                // Increment running X and Z totals and include tolerance to avoid overlaps.
+                xcorrtot += xcorr;                
+                zcorrtotx += zcorrx;
+            }        
+                        
+            // Increment running Y totals.
+            ycorrtot += ycorr;
+            zcorrtoty += zcorry;
+        }
+                
+        // Get mother volume for envelope.
+        Volume world = lcdd.pickMotherVolume(this);
+        
+        // Place the top section.
+        Position mpostop = new Position(momVol.getVolumeName() + "_top_pos", 0, my+beamgap, dface+mz);
+        //Rotation mrottop = new Rotation(momVol.getVolumeName() + "_top_rot", 0, -Math.PI/2, (3*Math.PI)/2);
+        Rotation mrottop = new Rotation(momVol.getVolumeName() + "_top_rot", 0, 0, 0);
+        define.addPosition(mpostop);
+        define.addRotation(mrottop);
+        PhysVol topSide = new PhysVol(momVol, world, mpostop, mrottop);
+        topSide.addPhysVolID("system", this.getSystemID());
+        topSide.addPhysVolID("side", 1);
+                
+        // Place the bottom section.        
+        Position mposbot = new Position(momVol.getVolumeName() + "_bot_pos", 0, -my-beamgap, dface+mz);
+        Rotation mrotbot = new Rotation(momVol.getVolumeName() + "_bot_rot", 0, 0, Math.PI);
+        define.addPosition(mposbot);
+        define.addRotation(mrotbot);
+        PhysVol botSide = new PhysVol(momVol, world, mposbot, mrotbot);        
+        botSide.addPhysVolID("system", this.getSystemID());
+        botSide.addPhysVolID("side", -1);        
+        
+        // Make the section box invisible.
+        momVol.setVisAttributes(lcdd.getVisAttributes("InvisibleWithDaughters"));
+    }
+    
+    public boolean isCalorimeter()
+    {
+        return true;
+    }
+}

Added: java/trunk/detector-model/src/main/java/org/lcsim/geometry/compact/converter/lcdd/HPSEcal3.java
 =============================================================================
--- java/trunk/detector-model/src/main/java/org/lcsim/geometry/compact/converter/lcdd/HPSEcal3.java	(added)
+++ java/trunk/detector-model/src/main/java/org/lcsim/geometry/compact/converter/lcdd/HPSEcal3.java	Tue Oct  6 11:36:11 2015
@@ -0,0 +1,378 @@
+package org.lcsim.geometry.compact.converter.lcdd;
+
+import static java.lang.Math.atan;
+import static java.lang.Math.cos;
+import static java.lang.Math.sin;
+import static java.lang.Math.tan;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.jdom.Element;
+import org.jdom.JDOMException;
+import org.lcsim.geometry.compact.converter.lcdd.util.Define;
+import org.lcsim.geometry.compact.converter.lcdd.util.LCDD;
+import org.lcsim.geometry.compact.converter.lcdd.util.PhysVol;
+import org.lcsim.geometry.compact.converter.lcdd.util.Position;
+import org.lcsim.geometry.compact.converter.lcdd.util.Rotation;
+import org.lcsim.geometry.compact.converter.lcdd.util.SensitiveDetector;
+import org.lcsim.geometry.compact.converter.lcdd.util.Trapezoid;
+import org.lcsim.geometry.compact.converter.lcdd.util.Volume;
+
+/**
+ * LCDD model for the HPS inner ECal.
+ * 
+ * Coordinate System defined as follows:<br>
+ * 
+ * <ul>
+ * <li>Beam travels in +X direction.</li>
+ * <li>Magnetic field is in -Z.</li>
+ * <li>Y is the beam bend plane.</li>
+ * </ul>
+ * 
+ * The <dimensions> element defines the crystal HALF dimensions: x1, x2, y1, y2, and z.<br>
+ * 
+ * The <layout> element defines the number of crystals and (optionally) crystals to be left out.<br>
+ * 
+ * <ul>
+ * <li><b>beamgap</b> - offset from the beamline in the Y coordinate of the top and bottom halves</li>
+ * <li><b>beamgapTop</b> - offset from the beamline in the Y coordinate for the top half (defaults to <b>beamgap</b>)</li>
+ * <li><b>beamgapBottom</b> - offset from the beamline in the Y coordinate for the bottom half (defaults to <b>beamgap</b>)</li>
+ * <li><b>nx</b> - number of crystals in X</li>
+ * <li><b>ny</b> - number of crystals in Y along each side of beam</li>
+ * <li><b>dface</b> - distance from origin to the face of the calorimeter along Z</li>
+ * </ul>
+ * 
+ * Under the layout element, <remove> tags can be included to exclude crystal placement by range. This element
+ * has the following parameters.<br>
+ * 
+ * <ul>
+ * <li><b>ixmin</b> - minimum x index to exclude (inclusive)</li>
+ * <li><b>ixmax</b> - maximum x index to exclude (inclusive)</li>
+ * <li><b>iymin</b> - minimum y index to exclude (inclusive)</li>
+ * <li><b>iymax</b> - maximum y index to exclude (inclusive)</li>
+ * </ul>
+ * 
+ * To be excluded, a crystal's ID must pass all four of these min/max checks.<br>
+ * 
+ * @author Jeremy McCormick
+ * @author Tim Nelson
+ * 
+ * @version $Id: HPSEcal3.java,v 1.9 2012/06/11 22:55:31 jeremy Exp $
+ */
+public class HPSEcal3 extends LCDDSubdetector {
+    // Tolerance factor for moving crystals to appropriate place in mom volume.
+    static final double tolerance = 0.0;
+
+    // Tolerance factor for separating crystals to avoid overlaps.
+    static final double crystalToleranceY = 0.35;
+    static final double crystalToleranceX = 0.2;
+
+    // Margin for mother volume.
+    static final double margin = 1.1;
+
+    List<CrystalRange> ranges = new ArrayList<CrystalRange>();
+
+    private static class CrystalRange {
+        int ixmin;
+        int ixmax;
+        int iymin;
+        int iymax;
+
+        CrystalRange(Element elem) throws Exception {
+            ixmin = ixmax = iymin = iymax = 0;
+
+            if (elem.getAttribute("ixmin") != null) {
+                ixmin = elem.getAttribute("ixmin").getIntValue();
+            } else {
+                throw new RuntimeException("Missing ixmin parameter.");
+            }
+
+            if (elem.getAttribute("ixmax") != null) {
+                ixmax = elem.getAttribute("ixmax").getIntValue();
+            } else {
+                throw new RuntimeException("Missing ixmax parameter.");
+            }
+
+            if (elem.getAttribute("iymin") != null) {
+                iymin = elem.getAttribute("iymin").getIntValue();
+            } else {
+                throw new RuntimeException("Missing ixmax parameter.");
+            }
+
+            if (elem.getAttribute("iymax") != null) {
+                iymax = elem.getAttribute("iymax").getIntValue();
+            } else {
+                throw new RuntimeException("Missing iymax parameter.");
+            }
+        }
+    }
+
+    private boolean checkRange(int ix, int iy, List<CrystalRange> ranges) {
+        if (ranges.size() == 0)
+            return true;
+        for (CrystalRange range : ranges) {
+            if ((ix >= range.ixmin && ix <= range.ixmax) && ((iy >= range.iymin) && (iy <= range.iymax))) {
+                return false;
+            }
+
+        }
+        return true;
+    }
+
+    HPSEcal3(Element node) throws JDOMException {
+        super(node);
+    }
+
+    void addToLCDD(LCDD lcdd, SensitiveDetector sens) throws JDOMException {
+        if (sens == null)
+            throw new RuntimeException("SensitiveDetector parameter points to null.");
+
+        // Crystal dimensions.
+        Element dimensions = node.getChild("dimensions");
+        double dx1 = dimensions.getAttribute("x1").getDoubleValue();
+        double dx2 = dimensions.getAttribute("x2").getDoubleValue();
+        double dy1 = dimensions.getAttribute("y1").getDoubleValue();
+        double dy2 = dimensions.getAttribute("y2").getDoubleValue();
+        double dz = dimensions.getAttribute("z").getDoubleValue();
+
+        int system = this.getSystemID();
+
+        // Crystal material.
+        Element mat = node.getChild("material");
+        String materialName = mat.getAttributeValue("name");
+
+        // Layout parameters.
+        Element layout = node.getChild("layout");
+        double beamgap = 0;
+        if (layout.getAttribute("beamgap") != null) {
+            beamgap = layout.getAttribute("beamgap").getDoubleValue();
+        } else {
+            if (layout.getAttribute("beamgapTop") == null || layout.getAttribute("beamgapBottom") == null) {
+                throw new RuntimeException("Missing beamgap parameter in layout element, and beamgapTop or beamgapBottom was not provided.");
+            }
+        }
+        double beamgapTop = beamgap;
+        if (layout.getAttribute("beamgapTop") != null) {
+            beamgapTop = layout.getAttribute("beamgapTop").getDoubleValue();
+        }
+        double beamgapBottom = beamgap;
+        if (layout.getAttribute("beamgapBottom") != null) {
+            beamgapBottom = layout.getAttribute("beamgapBottom").getDoubleValue();
+        } 
+        int nx = layout.getAttribute("nx").getIntValue();
+        int ny = layout.getAttribute("ny").getIntValue();
+        double dface = layout.getAttribute("dface").getDoubleValue();
+        
+        double tdx, tdy, tdz;        
+        double bdx, bdy, bdz;
+        tdx = tdy = tdz = bdx = bdy = bdz = 0.;
+        Element topElement = layout.getChild("top");
+        Element bottomElement = layout.getChild("bottom");
+        if (topElement != null) {
+            if (topElement.getAttribute("dx") != null)
+                tdx = topElement.getAttribute("dx").getDoubleValue();
+            if (topElement.getAttribute("dy") != null)
+                tdy = topElement.getAttribute("dy").getDoubleValue();
+            if (topElement.getAttribute("dz") != null)
+                tdz = topElement.getAttribute("dz").getDoubleValue();
+        }
+        if (bottomElement != null) {
+            if (bottomElement.getAttribute("dx") != null)
+                bdx = bottomElement.getAttribute("dx").getDoubleValue();
+            if (bottomElement.getAttribute("dy") != null)
+                bdy = bottomElement.getAttribute("dy").getDoubleValue();
+            if (bottomElement.getAttribute("dz") != null)
+                bdz = bottomElement.getAttribute("dz").getDoubleValue();
+        }
+
+        // Setup range of indices to be skipped.
+        for (Object obj : layout.getChildren("remove")) {
+            Element remove = (Element) obj;
+            try {
+                ranges.add(new CrystalRange(remove));
+            } catch (Exception x) {
+                throw new RuntimeException(x);
+            }
+        }
+
+        // Setup crystal logical volume.
+        Trapezoid crystalTrap = new Trapezoid("crystal_trap", dx1, dx2, dy1, dy2, dz);
+        Volume crystalLogVol = new Volume("crystal_volume", crystalTrap, lcdd.getMaterial(materialName));
+        crystalLogVol.setSensitiveDetector(sens);
+
+        // Set vis attributes on crystal log vol.
+        setVisAttributes(lcdd, this.getNode(), crystalLogVol);
+
+        // Add shape and log vol to lcdd.
+        lcdd.add(crystalTrap);
+        lcdd.add(crystalLogVol);
+
+        // Place crystals in world volume.
+        Volume world = lcdd.pickMotherVolume(this);
+
+        //
+        // Now we calculate parameters for crystal placement...
+        //
+
+        // Slope of the trapezoid side in X.
+        double sx = (dx2 - dx1) / (2 * dz);
+
+        // Angle of the side of the trapezoid w.r.t. center line in X. Rotation about Y axis.
+        double dthetay = atan(sx);
+
+        // Slope of the trapezoid side in Y.
+        double sy = (dy2 - dy1) / (2 * dz);
+
+        // Angle of the side of the trapezoid w.r.t. center line in Y. Rotation about X axis.
+        double dthetax = atan(sx);
+
+        // Distance between (virtual) angular origin and center of trapezoid in X.
+        double z0x = dx1 / sx + dz;
+
+        // Distance between (virtual) angular origin and center of trapezoid in Y.
+        double z0y = dy1 / sy + dz;
+
+        // Odd or even number of crystals in X.
+        boolean oddx = (nx % 2 != 0);
+
+        // Calculate number of X for loop.
+        if (oddx) {
+            nx -= 1;
+            nx /= 2;
+        } else {
+            nx /= 2;
+        }
+
+        double ycorrtot = 0;
+        double zcorrtoty = 0;
+
+        Define define = lcdd.getDefine();
+
+        for (int iy = 1; iy <= ny; iy++) {
+            double zcorrtotx = 0;
+            double xcorrtot = 0;
+
+            int coeffy = 2 * iy - 1;
+            double thetax = coeffy * dthetax;
+            double zcorry = dy1 * (2 * sin(coeffy * dthetax));
+            double ycorr = zcorry * tan((coeffy - 1) * dthetax);
+            double ycenter = z0y * sin(coeffy * dthetax) + ycorr + ycorrtot + (crystalToleranceY * iy);
+            double thetaz = 0;
+
+            for (int ix = 0; ix <= nx; ix++) {
+                // Coefficient for even/odd crystal
+                int coeffx = 2 * ix;
+                if (!oddx) {
+                    coeffx -= 1;
+                    // For even number of crystals, the 0th is skipped.
+                    if (ix == 0)
+                        continue;
+                }
+
+                // Set parameters for next crystal placement.
+                double thetay = coeffx * dthetay;
+                double zcorrx = dx1 * (2 * sin(coeffx * dthetay));
+                double xcorr = zcorrx * tan((coeffx - 1) * dthetay);
+                double xcenter = z0x * sin(coeffx * dthetay) + xcorr + xcorrtot + (crystalToleranceX * ix);
+                double zcenter = z0y * (cos(coeffy * dthetax) - 1) + z0x * (cos(coeffx * dthetay) - 1) + zcorrx + zcorrtotx + zcorry + zcorrtoty;
+                zcenter += dz;
+
+                String baseName = "crystal" + ix + "-" + iy;
+
+                //
+                // Bottom section.
+                //
+
+                if (checkRange(ix, -iy, ranges)) {
+                    // Transform of positive bottom crystal.
+                    Position iposBot = new Position(baseName + "_pos_pos_bot", xcenter + bdx, -(beamgapBottom + ycenter + tolerance) + bdy, zcenter + tolerance + dface + bdz);
+                    
+                    //System.out.println("iposBot = " + iposBot.x() + ", " + iposBot.y() + " , " + iposBot.z() + " --> " + ix + ", " + -iy);
+                    Rotation irotBot = new Rotation(baseName + "_rot_pos_bot", -thetax, -thetay, thetaz);
+                    define.addPosition(iposBot);
+                    define.addRotation(irotBot);
+
+                    // Place positive crystal.
+                    PhysVol posCrystalPlacementBot = new PhysVol(crystalLogVol, world, iposBot, irotBot);
+
+                    // Add volume IDs.
+                    posCrystalPlacementBot.addPhysVolID("system", system);
+                    posCrystalPlacementBot.addPhysVolID("ix", ix);
+                    posCrystalPlacementBot.addPhysVolID("iy", -iy);
+                }
+
+                // Reflection to negative.
+                if (ix != 0) {
+                    if (checkRange(-ix, -iy, ranges)) {
+                        // Transform of negative.
+                        Position iposnegBot = new Position(baseName + "_pos_neg_bot", -xcenter + bdx, -(beamgapBottom + ycenter + tolerance) + bdy, zcenter + tolerance + dface + bdz);
+                        //System.out.println("iposnegBot = " + iposnegBot.x() + ", " + iposnegBot.y() + " , " + iposnegBot.z() + " --> " + -ix + ", " + -iy);
+                        Rotation irotnegBot = new Rotation(baseName + "_rot_neg_bot", -thetax, thetay, thetaz);
+
+                        define.addPosition(iposnegBot);
+                        define.addRotation(irotnegBot);
+
+                        // Place negative crystal.
+                        PhysVol negCrystalPlacementBot = new PhysVol(crystalLogVol, world, iposnegBot, irotnegBot);
+
+                        // Add volume IDs.
+                        negCrystalPlacementBot.addPhysVolID("system", system);
+                        negCrystalPlacementBot.addPhysVolID("ix", -ix);
+                        negCrystalPlacementBot.addPhysVolID("iy", -iy);
+                    }
+                }
+
+                if (checkRange(ix, iy, ranges)) {
+                    // Transform of positive top crystal.
+                    Position iposTop = new Position(baseName + "_pos_pos_top", xcenter + tdx, beamgapTop + ycenter + tolerance + tdy, zcenter + tolerance + dface + tdz);
+                    //System.out.println("iposTop = " + iposTop.x() + ", " + iposTop.y() + " , " + iposTop.z() + " --> " + ix + ", " + iy);
+                    Rotation irotTop = new Rotation(baseName + "_rot_pos_top", thetax, -thetay, thetaz);
+                    define.addPosition(iposTop);
+                    define.addRotation(irotTop);
+
+                    // Place positive top crystal.
+                    PhysVol posCrystalPlacementTop = new PhysVol(crystalLogVol, world, iposTop, irotTop);
+
+                    // Add volume IDs.
+                    posCrystalPlacementTop.addPhysVolID("system", system);
+                    posCrystalPlacementTop.addPhysVolID("ix", ix);
+                    posCrystalPlacementTop.addPhysVolID("iy", iy);
+                }
+
+                // Reflection to negative.
+                if (ix != 0) {
+                    if (checkRange(-ix, iy, ranges)) {
+                        // Transform of negative.
+                        Position iposnegTop = new Position(baseName + "_pos_neg_top", -xcenter + tdx, beamgapTop + ycenter + tolerance + tdy, zcenter + tolerance + dface + tdz);
+                        //System.out.println("iposTop = " + iposnegTop.x() + ", " + iposnegTop.y() + " , " + iposnegTop.z() + " --> " + -ix + ", " + iy);
+                        Rotation irotnegTop = new Rotation(baseName + "_rot_neg_top", thetax, thetay, thetaz);
+
+                        define.addPosition(iposnegTop);
+                        define.addRotation(irotnegTop);
+
+                        // Place negative crystal.
+                        PhysVol negCrystalPlacementTop = new PhysVol(crystalLogVol, world, iposnegTop, irotnegTop);
+
+                        // Add volume IDs.
+                        negCrystalPlacementTop.addPhysVolID("system", system);
+                        negCrystalPlacementTop.addPhysVolID("ix", -ix);
+                        negCrystalPlacementTop.addPhysVolID("iy", iy);
+                    }
+                }
+
+                // Increment running X and Z totals and include tolerance to avoid overlaps.
+                xcorrtot += xcorr;
+                zcorrtotx += zcorrx;
+            }
+
+            // Increment running Y totals.
+            ycorrtot += ycorr;
+            zcorrtoty += zcorry;
+        }
+    }
+
+    public boolean isCalorimeter() {
+        return true;
+    }
+}

Added: java/trunk/detector-model/src/main/java/org/lcsim/geometry/compact/converter/lcdd/HPSMuonCalorimeter.java
 =============================================================================
--- java/trunk/detector-model/src/main/java/org/lcsim/geometry/compact/converter/lcdd/HPSMuonCalorimeter.java	(added)
+++ java/trunk/detector-model/src/main/java/org/lcsim/geometry/compact/converter/lcdd/HPSMuonCalorimeter.java	Tue Oct  6 11:36:11 2015
@@ -0,0 +1,122 @@
+package org.lcsim.geometry.compact.converter.lcdd;
+
+import org.jdom.Element;
+import org.jdom.JDOMException;
+import org.lcsim.geometry.compact.converter.lcdd.util.Box;
+import org.lcsim.geometry.compact.converter.lcdd.util.LCDD;
+import org.lcsim.geometry.compact.converter.lcdd.util.Material;
+import org.lcsim.geometry.compact.converter.lcdd.util.PhysVol;
+import org.lcsim.geometry.compact.converter.lcdd.util.Position;
+import org.lcsim.geometry.compact.converter.lcdd.util.Rotation;
+import org.lcsim.geometry.compact.converter.lcdd.util.SensitiveDetector;
+import org.lcsim.geometry.compact.converter.lcdd.util.Volume;
+
+/**
+ * @author jeremym
+ * @version $Id: HPSMuonCalorimeter.java,v 1.5 2013/01/25 00:13:44 jeremy Exp $
+ */
+public class HPSMuonCalorimeter extends LCDDSubdetector
+{
+
+    HPSMuonCalorimeter(Element node) throws JDOMException
+    {
+        super(node);
+    }
+
+    void addToLCDD(LCDD lcdd, SensitiveDetector sens) throws JDOMException
+    {
+
+        String name = node.getAttributeValue("name");
+        int id = node.getAttribute("id").getIntValue();
+        Volume mother = lcdd.pickMotherVolume(this);
+
+        for (Object layerObject : node.getChildren("layer")) {
+
+            Element layer = (Element) layerObject;
+            int layerId = layer.getAttribute("id").getIntValue();
+
+            int slice = 1;
+            for (Object boxObject : layer.getChildren("box")) {
+
+                Element element = (Element) boxObject;
+
+                double x, y, z, px, py, pz, rx, ry, rz;
+                x = y = z = px = py = pz = rx = ry = rz = 0.;
+
+                if (element.getAttribute("x") != null) {
+                    x = element.getAttribute("x").getDoubleValue();
+                } else {
+                    throw new RuntimeException("x is required.");
+                }
+                if (element.getAttribute("y") != null) {
+                    y = element.getAttribute("y").getDoubleValue();
+                } else {
+                    throw new RuntimeException("y is required.");
+                }
+                if (element.getAttribute("z") != null) {
+                    z = element.getAttribute("z").getDoubleValue();
+                } else {
+                    throw new RuntimeException("z is required.");
+                }
+
+                if (element.getAttribute("px") != null)
+                    px = element.getAttribute("px").getDoubleValue();
+                if (element.getAttribute("py") != null)
+                    py = element.getAttribute("py").getDoubleValue();
+                if (element.getAttribute("pz") != null)
+                    pz = element.getAttribute("pz").getDoubleValue();
+
+                if (element.getAttribute("rx") != null)
+                    rx = element.getAttribute("rx").getDoubleValue();
+                if (element.getAttribute("ry") != null)
+                    ry = element.getAttribute("ry").getDoubleValue();
+                if (element.getAttribute("rz") != null)
+                    rz = element.getAttribute("rz").getDoubleValue();
+
+                String materialString = element.getAttributeValue("material");
+                Material material = lcdd.getMaterial(materialString);
+
+                String shapeBaseName = name + "_layer" + layerId + "_sublayer" + slice;
+
+                Box box = new Box(shapeBaseName + "_box", x, y, z);
+                lcdd.add(box);
+
+                Position pos = new Position(shapeBaseName + "_pos", px, py, pz);
+                lcdd.add(pos);
+
+                Rotation rot = new Rotation(shapeBaseName + "_rot", rx, ry, rz);
+                lcdd.add(rot);
+
+                Volume vol = new Volume(shapeBaseName + "_vol", box, material);
+
+                boolean sensitive = false;
+                if (element.getAttribute("sensitive") != null)
+                    sensitive = element.getAttribute("sensitive").getBooleanValue();
+
+                if (sensitive) {
+                    vol.setSensitiveDetector(sens);
+                }
+
+                lcdd.add(vol);
+
+                PhysVol physVol = new PhysVol(vol, mother, pos, rot);
+                physVol.addPhysVolID("layer", layerId);
+                physVol.addPhysVolID("slice", slice);
+                if (py >= 0) {
+                    physVol.addPhysVolID("side", 1);
+                } else {
+                    physVol.addPhysVolID("side", -1);
+                }
+                physVol.addPhysVolID("system", id);
+
+                ++slice;
+            }
+        }
+
+    }
+
+    public boolean isCalorimeter()
+    {
+        return true;
+    }
+}

Added: java/trunk/detector-model/src/main/java/org/lcsim/geometry/compact/converter/lcdd/HPSMuonCalorimeter2.java
 =============================================================================
--- java/trunk/detector-model/src/main/java/org/lcsim/geometry/compact/converter/lcdd/HPSMuonCalorimeter2.java	(added)
+++ java/trunk/detector-model/src/main/java/org/lcsim/geometry/compact/converter/lcdd/HPSMuonCalorimeter2.java	Tue Oct  6 11:36:11 2015
@@ -0,0 +1,55 @@
+package org.lcsim.geometry.compact.converter.lcdd;
+
+import org.jdom.Element;
+import org.jdom.JDOMException;
+import org.lcsim.geometry.compact.converter.lcdd.util.LCDD;
+import org.lcsim.geometry.compact.converter.lcdd.util.SensitiveDetector;
+import org.lcsim.geometry.compact.converter.lcdd.util.Volume;
+
+public class HPSMuonCalorimeter2 extends LCDDSubdetector
+{
+	HPSMuonCalorimeter2(Element e) throws JDOMException 
+	{
+		super(e);
+	}
+
+	void addToLCDD(LCDD lcdd, SensitiveDetector sens) throws JDOMException 
+	{
+        String name = node.getAttributeValue("name");
+        System.out.println("HPSMuonCalorimeter2.addToLCDD - " + name);
+        int id = node.getAttribute("id").getIntValue();
+        Volume mother = lcdd.pickMotherVolume(this);
+        
+        Element parameters = node.getChild("parameters");
+        if (parameters == null) {
+        	throw new RuntimeException("parameters element missing");
+        }
+                
+        double frontFaceToTarget = parameters.getAttribute("front_face_to_target").getDoubleValue();
+        double deadZoneAngle = parameters.getAttribute("dead_zone_angle").getDoubleValue();
+        double stripThickness = parameters.getAttribute("strip_thickness").getDoubleValue();
+        double stripSpacingZ = parameters.getAttribute("strip_spacing_z").getDoubleValue();
+        double stripSpacingY = parameters.getAttribute("strip_spacing_y").getDoubleValue();
+        double stripSpacingX = parameters.getAttribute("strip_spacing_x").getDoubleValue();
+        
+        System.out.println("frontFaceToTarget = " + frontFaceToTarget);
+        System.out.println("deadZoneAngle = " + deadZoneAngle);
+        System.out.println("stripThickness = " + stripThickness);
+        System.out.println("stripSpacingX = " + stripSpacingX);
+        System.out.println("stripSpacingY = " + stripSpacingY);        
+        System.out.println("stripSpacingZ = " + stripSpacingZ);
+        
+        for (Object layerObject : node.getChildren("layer")) {
+        	Element layerElement = (Element)layerObject;
+        	int layerId = layerElement.getAttribute("id").getIntValue();
+        	System.out.println("layer = " + layerId);
+        	for (Object sliceObject : layerElement.getChildren("slice")) {
+        		Element sliceElement = (Element)sliceObject;
+        		if (sliceElement.getAttribute("thickness") != null) {
+        			double thickness = sliceElement.getAttribute("thickness").getDoubleValue();
+        			System.out.println("slice thickness = " + thickness);
+        		}
+        	}
+        }
+	}
+}

Added: java/trunk/detector-model/src/main/java/org/lcsim/geometry/compact/converter/lcdd/HPSTracker.java
 =============================================================================
--- java/trunk/detector-model/src/main/java/org/lcsim/geometry/compact/converter/lcdd/HPSTracker.java	(added)
+++ java/trunk/detector-model/src/main/java/org/lcsim/geometry/compact/converter/lcdd/HPSTracker.java	Tue Oct  6 11:36:11 2015
@@ -0,0 +1,512 @@
+package org.lcsim.geometry.compact.converter.lcdd;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.jdom.DataConversionException;
+import org.jdom.Element;
+import org.jdom.JDOMException;
+import org.lcsim.geometry.compact.converter.lcdd.util.Box;
+import org.lcsim.geometry.compact.converter.lcdd.util.LCDD;
+import org.lcsim.geometry.compact.converter.lcdd.util.Material;
+import org.lcsim.geometry.compact.converter.lcdd.util.PhysVol;
+import org.lcsim.geometry.compact.converter.lcdd.util.Position;
+import org.lcsim.geometry.compact.converter.lcdd.util.Rotation;
+import org.lcsim.geometry.compact.converter.lcdd.util.SensitiveDetector;
+import org.lcsim.geometry.compact.converter.lcdd.util.Volume;
+
+/**
+ * An LCDD converter for a Silicon endcap tracker model based on Bill Cooper's design from <a href=
+ * "http://ilcagenda.linearcollider.org/materialDisplay.py?contribId=58&sessionId=1&materialId=slides&confId=2784"
+ * >Boulder SiD Workshop 2008</a>.
+ * 
+ * @author jeremym
+ */
+public class HPSTracker extends LCDDSubdetector
+{
+
+    Map<String, ModuleParameters> moduleParameters = new HashMap<String, ModuleParameters>();
+    Map<String, Volume> modules = new HashMap<String, Volume>();
+    Material vacuum;
+
+    public HPSTracker(Element node) throws JDOMException
+    {
+        super(node);
+    }
+    
+    public boolean isTracker()
+    {
+        return true;
+    }
+
+    void addToLCDD(LCDD lcdd, SensitiveDetector sd) throws JDOMException
+    {
+        int sysId = node.getAttribute("id").getIntValue();
+        String subdetName = node.getAttributeValue("name");
+        vacuum = lcdd.getMaterial("Vacuum");
+        boolean reflect;
+
+        if (node.getAttribute("reflect") != null)
+        {
+            reflect = node.getAttribute("reflect").getBooleanValue();
+        }
+        else
+        {
+            reflect = true;
+        }
+    
+
+        for (Iterator i = node.getChildren("module").iterator(); i.hasNext();)
+        {
+            Element module = (Element) i.next();
+            String moduleName = module.getAttributeValue("name");
+            moduleParameters.put(moduleName, new ModuleParameters(module));
+            modules.put(moduleName, makeModule(moduleParameters.get(moduleName), sd, lcdd));
+        }
+        
+        // layer
+        for (Iterator i = node.getChildren("layer").iterator(); i.hasNext();)
+        {
+        	// Modules are numbered from 0 starting in each layer.
+            int moduleNumber = 0;
+
+            Element layerElement = (Element) i.next();
+            int layerId = layerElement.getAttribute("id").getIntValue();
+            
+            System.out.println("<layer id=\"" + layerId + "\">");
+            
+            int ringCount = 0;
+
+            // quadrant (???)
+            for (Iterator j = layerElement.getChildren("quadrant").iterator(); j.hasNext();)
+            {
+                Element ringElement = (Element) j.next();
+                
+                double zLayer = ringElement.getAttribute("z").getDoubleValue();
+                double dz = ringElement.getAttribute("dz").getDoubleValue();
+                double xStart = ringElement.getAttribute("xStart").getDoubleValue();
+                double xStep = ringElement.getAttribute("xStep").getDoubleValue();
+                int nx = ringElement.getAttribute("nx").getIntValue();
+                double yStart = ringElement.getAttribute("yStart").getDoubleValue();
+                int ny = ringElement.getAttribute("ny").getIntValue();
+                double yStep = ringElement.getAttribute("yStep").getDoubleValue();
+                double top_phi0 = 0;                
+                if (ringElement.getAttribute("top_phi0") != null)
+                {
+                    top_phi0 = ringElement.getAttribute("top_phi0").getDoubleValue();
+                }
+                 double bot_phi0 = 0;                
+                if (ringElement.getAttribute("bot_phi0") != null)
+                {
+                    bot_phi0 = ringElement.getAttribute("bot_phi0").getDoubleValue();
+                }
+                 double theta = 0;                
+                if (ringElement.getAttribute("theta") != null)
+                {
+                    theta = ringElement.getAttribute("theta").getDoubleValue();
+                }
+                
+                String module = ringElement.getAttributeValue("module");
+
+                Volume moduleVolume = modules.get(module);
+                
+                if (moduleVolume == null)
+                {
+                    throw new RuntimeException("Module " + module + " was not found.");
+                }
+
+                double x, y, z;
+                z = zLayer;
+                x = xStart;
+
+                // nx
+                for (int k = 0; k < nx; k++ )
+                {
+                    y = yStart;
+                    // ny
+                    for (int kk = 0; kk < ny; kk++ )
+                    {
+                        String moduleBaseName = subdetName + "_layer" + layerId + "_module" + moduleNumber;
+
+                        Position p = new Position(moduleBaseName + "_position");
+
+                        p.setX(x);
+                        p.setY(y);
+                        p.setZ(z + dz);
+                        //System.out.println("layer, module = " + layerId + ", " + moduleNumber);
+                        //System.out.println("module pos = " + x + " " + y + " " + z);
+                        Rotation rot = new Rotation(moduleBaseName + "_rotation");
+                        rot.setX(0);
+                        rot.setY(theta);
+                        //rot.setZ(phi0);
+                        // Y side along world's X axis.  
+                        // FIXME Should phi0 actually be subtracted???
+                        rot.setZ(-(Math.PI / 2 + top_phi0));
+                        //System.out.println("module rot = " + 0 + ", " + theta + "," + -(Math.PI / 2 + top_phi0));
+
+                        lcdd.add(p);
+                        lcdd.add(rot);
+
+                        PhysVol pv = new PhysVol(moduleVolume, lcdd.getTrackingVolume(), p, rot);
+                        pv.addPhysVolID("system", sysId);
+                        pv.addPhysVolID("barrel", 0);
+                        pv.addPhysVolID("layer", layerId);
+                        pv.addPhysVolID("module", moduleNumber);
+                                                
+                        System.out.print("    <module_placement name=\"" + module + "\" ");
+                        System.out.print("id=\"" + moduleNumber + "\" ");
+                        System.out.print("x=\"" + p.getAttribute("x").getDoubleValue() + "\" ");
+                        System.out.print("y=\"" + p.getAttribute("y").getDoubleValue() + "\" ");
+                        System.out.print("z=\"" + p.getAttribute("z").getDoubleValue() + "\" ");
+                        System.out.print("rx=\"" + rot.getAttribute("x").getDoubleValue() + "\" ");
+                        System.out.print("ry=\"" + rot.getAttribute("y").getDoubleValue() + "\" ");
+                        System.out.print("rz=\"" + rot.getAttribute("z").getDoubleValue() + "\"/>");
+                        System.out.println();
+                        
+                        ++moduleNumber;     
+                        
+                        if (reflect)
+                        {
+                            Position pr = new Position(moduleBaseName + "_reflect_position");
+                            pr.setX(x);
+                            pr.setY(-y);
+                            pr.setZ(z + dz);
+                            //System.out.println("module @ " + x + " " + -y + " " + (z + dz));
+                            Rotation rotr = new Rotation(moduleBaseName + "_reflect_rotation");                          
+                            rotr.setX(0);
+                            rotr.setY(theta);
+                            rotr.setZ(-Math.PI/2 - bot_phi0);
+
+                            lcdd.add(pr);
+                            lcdd.add(rotr);
+
+                            PhysVol pvr = new PhysVol(moduleVolume, lcdd.getTrackingVolume(), pr, rotr);
+                            pvr.addPhysVolID("system", sysId);
+                            pvr.addPhysVolID("barrel", 0);
+                            pvr.addPhysVolID("layer", layerId);
+                            pvr.addPhysVolID("module", moduleNumber);
+                            
+                            System.out.print("    <module_placement name=\"" + module + "\" ");
+                            System.out.print("id=\"" + moduleNumber + "\" ");
+                            System.out.print("x=\"" + pr.getAttribute("x").getDoubleValue() + "\" ");
+                            System.out.print("y=\"" + pr.getAttribute("y").getDoubleValue() + "\" ");
+                            System.out.print("z=\"" + pr.getAttribute("z").getDoubleValue() + "\" ");
+                            System.out.print("rx=\"" + rotr.getAttribute("x").getDoubleValue() + "\" ");
+                            System.out.print("ry=\"" + rotr.getAttribute("y").getDoubleValue() + "\" ");
+                            System.out.print("rz=\"" + rotr.getAttribute("z").getDoubleValue() + "\"/>");
+                            System.out.println();
+                            
+                            ++moduleNumber;                            
+                        }
+                        
+                        dz = -dz;
+                        y += yStep;                        
+                    }
+                    x += xStep;
+                }
+            }
+            System.out.println("</layer>");
+        }
+    }
+
+    private Volume makeModule(ModuleParameters params, SensitiveDetector sd, LCDD lcdd)
+    {
+        double thickness = params.getThickness();
+        double x, y;
+        //x = params.getDimension(0);
+        //y = params.getDimension(1);
+        y = params.getDimension(0); // Y is in X plane in world coordinates.
+        x = params.getDimension(1); // X is in Y plane in world coordinates.
+        //System.out.println("making module with x = " + x + " and y = " + y);
+        Box box = new Box(params.getName() + "Box", x, y, thickness);
+        lcdd.add(box);
+
+        Volume moduleVolume = new Volume(params.getName() + "Volume", box, vacuum);
+        makeModuleComponents(moduleVolume, params, sd, lcdd);
+        lcdd.add(moduleVolume);
+
+        if (params.getVis() != null)
+        {
+            moduleVolume.setVisAttributes(lcdd.getVisAttributes(params.getVis()));
+        }
+
+        return moduleVolume;
+    }
+
+    private void makeModuleComponents(Volume moduleVolume, ModuleParameters moduleParameters, SensitiveDetector sd, LCDD lcdd)
+    {
+        Box envelope = (Box) lcdd.getSolid(moduleVolume.getSolidRef());
+
+        double moduleX = envelope.getX();
+        double moduleY = envelope.getY();
+
+        double posZ = -moduleParameters.getThickness() / 2;
+
+        String moduleName = moduleVolume.getVolumeName();
+
+        int sensor = 0;
+        for (ModuleComponentParameters component : moduleParameters)
+        {
+
+            double thickness = component.getThickness();
+
+            Material material = null;
+            try
+            {
+                material = lcdd.getMaterial(component.getMaterialName());
+            }
+            catch (JDOMException except)
+            {
+                throw new RuntimeException(except);
+            }
+            boolean sensitive = component.isSensitive();
+            int componentNumber = component.getComponentNumber();
+
+            posZ += thickness / 2;
+
+            String componentName = moduleName + "_component" + componentNumber;
+
+            //System.out.println("making " + componentName + " with x = " + moduleX + " and y = " + moduleY);
+            Box componentBox = new Box(componentName + "Box", moduleX, moduleY, thickness);
+            lcdd.add(componentBox);
+
+            Volume componentVolume = new Volume(componentName, componentBox, material);
+
+            Position position = new Position(componentName + "_position", 0., 0., posZ);
+            lcdd.add(position);
+            Rotation rotation = new Rotation(componentName + "_rotation", 0., 0., 0.);
+            lcdd.add(rotation);
+
+            PhysVol pv = new PhysVol(componentVolume, moduleVolume, position, rotation);
+            pv.addPhysVolID("component", componentNumber);
+
+            if (sensitive)
+            {
+                if (sensor > 1)
+                {
+                    throw new RuntimeException("Maximum of 2 sensors per module.");
+                }
+
+                // Build a child sensor volume to allow dead areas.
+
+                String sensorName = componentName + "Sensor" + sensor;
+
+                // Flipped these around!!!
+                double sensorX = component.getDimensionY();
+                double sensorY = component.getDimensionX();
+                /*
+                
+                if (sensorX > moduleX)
+                    throw new RuntimeException("Sensor X dimension " + sensorX + " is too big for module.");
+
+                
+                if (sensorY > moduleY)
+                    throw new RuntimeException("Sensor Y dimension " + sensorY + " is too big for module.");
+                    */
+
+                Box sensorBox = new Box(sensorName + "Box", sensorX, sensorY, thickness);
+                lcdd.add(sensorBox);
+
+                Volume sensorVol = new Volume(sensorName, sensorBox, material);
+                sensorVol.setSensitiveDetector(sd);
+                lcdd.add(sensorVol);
+
+                Position sensorPosition = new Position(sensorName + "Position", 0, 0, 0);
+                lcdd.add(sensorPosition);
+                Rotation sensorRotation = new Rotation(sensorName + "Rotation", 0, 0, 0);
+                lcdd.add(sensorRotation);
+
+                PhysVol sensorPhysVol = new PhysVol(sensorVol, componentVolume, sensorPosition, sensorRotation);
+                sensorPhysVol.addPhysVolID("sensor", sensor);
+
+                ++sensor;
+            }
+
+            // Add component volume after (possible) sensor child volume.
+            lcdd.add(componentVolume);
+
+            // Set vis attributes of component.
+            if (component.getVis() != null)
+            {
+                componentVolume.setVisAttributes(lcdd.getVisAttributes(component.getVis()));
+            }
+
+            // Step to next component placement position.
+            posZ += thickness / 2;
+        }
+    }
+
+    static class ModuleComponentParameters
+    {
+        protected String materialName;
+        protected double thickness;
+        protected boolean sensitive;
+        protected int componentNumber;
+        protected String vis;
+        protected double dimX, dimY;
+
+        public ModuleComponentParameters(double dimX, double dimY, double thickness, String materialName, int componentNumber, boolean sensitive, String vis)
+        {
+            this.dimX = dimX;
+            this.dimY = dimY;
+            this.thickness = thickness;
+            this.materialName = materialName;
+            this.sensitive = sensitive;
+            this.componentNumber = componentNumber;
+            this.vis = vis;
+        }
+
+        public double getThickness()
+        {
+            return thickness;
+        }
+
+        public double getDimensionX()
+        {
+            return dimX;
+        }
+
+        public double getDimensionY()
+        {
+            return dimY;
+        }
+
+        public String getMaterialName()
+        {
+            return materialName;
+        }
+
+        public boolean isSensitive()
+        {
+            return sensitive;
+        }
+
+        public int getComponentNumber()
+        {
+            return componentNumber;
+        }
+
+        public String getVis()
+        {
+            return vis;
+        }
+    }
+
+    static class ModuleParameters extends ArrayList<ModuleComponentParameters>
+    {
+        double thickness;
+        String name;
+        double dimensions[] = new double[3];
+        String vis;
+
+        public ModuleParameters(Element element)
+        {
+            name = element.getAttributeValue("name");
+
+            if (element.getAttribute("vis") != null)
+                this.vis = element.getAttribute("vis").getValue();
+
+            // Optional dimension parameters (not always present).
+            if (element.getChild("trd") != null)
+            {
+                Element trd = element.getChild("trd");
+                try
+                {
+                    dimensions[0] = trd.getAttribute("x1").getDoubleValue();
+                    dimensions[1] = trd.getAttribute("x2").getDoubleValue();
+                    dimensions[2] = trd.getAttribute("z").getDoubleValue();
+                }
+                catch (DataConversionException x)
+                {
+                    throw new RuntimeException(x);
+                }
+            }
+            else if (element.getChild("box") != null)
+            {
+                Element box = element.getChild("box");
+                try
+                {
+                    dimensions[0] = box.getAttribute("x").getDoubleValue();
+                    dimensions[1] = box.getAttribute("y").getDoubleValue();
+                }
+                catch (DataConversionException x)
+                {
+                    throw new RuntimeException(x);
+                }
+            }
+
+            int cntr = 0;
+            for (Object o : element.getChildren("module_component"))
+            {
+                try
+                {
+
+                    Element e = (Element) o;
+
+                    double thickness = e.getAttribute("thickness").getDoubleValue();
+
+                    String materialName = e.getAttributeValue("material");
+
+                    boolean sensitive = false;
+                    if (e.getAttribute("sensitive") != null)
+                        sensitive = e.getAttribute("sensitive").getBooleanValue();
+                    String componentVis = null;
+                    if (e.getAttribute("vis") != null)
+                        componentVis = e.getAttribute("vis").getValue();
+
+                    // Sensors may have reduced dimensions for dead area.
+                    double x = dimensions[0]; // default
+                    double y = dimensions[1]; // default
+                    if (sensitive && e.getChild("dimensions") != null)
+                    {
+                        Element dimensions = e.getChild("dimensions");
+                        x = dimensions.getAttribute("x").getDoubleValue();
+                        y = dimensions.getAttribute("y").getDoubleValue();
+                        // System.out.println("x,y="+x+","+y);
+                    }
+                    add(new ModuleComponentParameters(x, y, thickness, materialName, cntr, sensitive, componentVis));
+                }
+                catch (JDOMException x)
+                {
+                    throw new RuntimeException(x);
+                }
+                ++cntr;
+            }
+
+            calculateThickness();
+        }
+
+        public void calculateThickness()
+        {
+            thickness = 0.; // reset thickness
+            for (ModuleComponentParameters p : this)
+            {
+                thickness += p.getThickness();
+            }
+        }
+
+        public double getThickness()
+        {
+            return thickness;
+        }
+
+        public String getName()
+        {
+            return name;
+        }
+
+        public double getDimension(int i)
+        {
+            if (i > (dimensions.length - 1) || i < 0)
+                throw new RuntimeException("Invalid dimensions index: " + i);
+            return dimensions[i];
+        }
+
+        public String getVis()
+        {
+            return vis;
+        }
+    }
+}

Added: java/trunk/detector-model/src/main/java/org/lcsim/geometry/compact/converter/lcdd/HPSTracker2.java
 =============================================================================
--- java/trunk/detector-model/src/main/java/org/lcsim/geometry/compact/converter/lcdd/HPSTracker2.java	(added)
+++ java/trunk/detector-model/src/main/java/org/lcsim/geometry/compact/converter/lcdd/HPSTracker2.java	Tue Oct  6 11:36:11 2015
@@ -0,0 +1,355 @@
+package org.lcsim.geometry.compact.converter.lcdd;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.jdom.DataConversionException;
+import org.jdom.Element;
+import org.jdom.JDOMException;
+import org.lcsim.geometry.compact.converter.lcdd.util.Box;
+import org.lcsim.geometry.compact.converter.lcdd.util.LCDD;
+import org.lcsim.geometry.compact.converter.lcdd.util.Material;
+import org.lcsim.geometry.compact.converter.lcdd.util.PhysVol;
+import org.lcsim.geometry.compact.converter.lcdd.util.Position;
+import org.lcsim.geometry.compact.converter.lcdd.util.Rotation;
+import org.lcsim.geometry.compact.converter.lcdd.util.SensitiveDetector;
+import org.lcsim.geometry.compact.converter.lcdd.util.Volume;
+
+/**
+ * 
+ * SVT geometry for HPS Test Run.
+ * 
+ * @author Jeremy McCormick <[log in to unmask]>
+ * @author Matt Graham <[log in to unmask]>
+ */
+public class HPSTracker2 extends LCDDSubdetector {
+
+    Map<String, ModuleParameters> moduleParameters = new HashMap<String, ModuleParameters>();
+    Map<String, Volume> modules = new HashMap<String, Volume>();
+    Material vacuum;
+
+    public HPSTracker2(Element node) throws JDOMException {
+        super(node);
+    }
+    
+    public boolean isTracker() {
+        return true;
+    }
+
+    void addToLCDD(LCDD lcdd, SensitiveDetector sd) throws JDOMException {
+        
+        // Get parameters.
+        int sysId = node.getAttribute("id").getIntValue();
+        String subdetName = node.getAttributeValue("name");
+        vacuum = lcdd.getMaterial("Vacuum");
+   
+        // Create module logical volumes.
+        createModules(lcdd, sd);
+        
+        // Create module placements in tracking volume.
+        createModulePlacements(lcdd, sysId, subdetName);
+    }
+
+    // Place modules within the tracking volume.
+	private void createModulePlacements(LCDD lcdd, int sysId, String subdetName) throws DataConversionException {
+		//Volume trackingVolume = lcdd.getTrackingVolume();
+		Volume momVolume = lcdd.pickMotherVolume(this);
+		// Loop over layers.
+        for (Iterator i = node.getChildren("layer").iterator(); i.hasNext();) {        
+        	Element layerElement = (Element)i.next();
+        	int layerNumber = layerElement.getAttribute("id").getIntValue();
+        	// Loop over modules within layer.
+        	for (Iterator j = layerElement.getChildren("module_placement").iterator(); j.hasNext();) {
+        	    
+        		Element modulePlacementElement = (Element)j.next();
+        		String moduleName = modulePlacementElement.getAttributeValue("name");
+        		int moduleNumber = modulePlacementElement.getAttribute("id").getIntValue();
+        		
+        		// Get the position and rotation parameters.  All must be explicitly specified.
+        		double x, y, z;
+        		double rx, ry, rz;
+        		x = modulePlacementElement.getAttribute("x").getDoubleValue();
+        		y = modulePlacementElement.getAttribute("y").getDoubleValue();
+        		z = modulePlacementElement.getAttribute("z").getDoubleValue();
+        		rx = modulePlacementElement.getAttribute("rx").getDoubleValue();
+        		ry = modulePlacementElement.getAttribute("ry").getDoubleValue();
+        		rz = modulePlacementElement.getAttribute("rz").getDoubleValue();
+        		
+        		// Place the module with position and rotation from above.
+        		String modulePlacementName = subdetName + "_" + moduleName + "_layer" + layerNumber + "_module" + moduleNumber;
+        		Position p = new Position(modulePlacementName + "_position", x, y, z);
+        		Rotation r = new Rotation(modulePlacementName + "_rotation", rx, ry, rz);
+        		lcdd.add(p);
+        		lcdd.add(r);        		        		
+        		//PhysVol modulePhysVol = new PhysVol(modules.get(moduleName), trackingVolume, p, r);
+        		PhysVol modulePhysVol = new PhysVol(modules.get(moduleName), momVolume, p, r);
+        		
+        		// Add identifier values to the placement volume.
+        		modulePhysVol.addPhysVolID("system", sysId);
+        		modulePhysVol.addPhysVolID("barrel", 0);
+        		modulePhysVol.addPhysVolID("layer", layerNumber);
+        		modulePhysVol.addPhysVolID("module", moduleNumber);        		
+        	}
+        }
+	}
+
+    // Create the module logical volumes.
+	private void createModules(LCDD lcdd, SensitiveDetector sd) {
+        for (Iterator i = node.getChildren("module").iterator(); i.hasNext();) {
+            Element module = (Element) i.next();
+            String moduleName = module.getAttributeValue("name");
+            moduleParameters.put(moduleName, new ModuleParameters(module));
+            modules.put(moduleName, makeModule(moduleParameters.get(moduleName), sd, lcdd));
+        }
+	}
+
+	private Volume makeModule(ModuleParameters params, SensitiveDetector sd, LCDD lcdd) {
+		double thickness = params.getThickness();
+		double x, y;
+		// x = params.getDimension(0);
+		// y = params.getDimension(1);
+		y = params.getDimension(0); // Y is in X plane in world coordinates.
+		x = params.getDimension(1); // X is in Y plane in world coordinates.
+		// System.out.println("making module with x = " + x + " and y = " + y);
+		Box box = new Box(params.getName() + "Box", x, y, thickness);
+		lcdd.add(box);
+
+		Volume moduleVolume = new Volume(params.getName() + "Volume", box, vacuum);
+		makeModuleComponents(moduleVolume, params, sd, lcdd);
+		lcdd.add(moduleVolume);
+
+		if (params.getVis() != null) {
+			moduleVolume.setVisAttributes(lcdd.getVisAttributes(params.getVis()));
+		}
+
+		return moduleVolume;
+	}
+
+    private void makeModuleComponents(Volume moduleVolume, ModuleParameters moduleParameters, SensitiveDetector sd, LCDD lcdd) {
+        Box envelope = (Box) lcdd.getSolid(moduleVolume.getSolidRef());
+
+        double moduleX = envelope.getX();
+        double moduleY = envelope.getY();
+
+        double posZ = -moduleParameters.getThickness() / 2;
+
+        String moduleName = moduleVolume.getVolumeName();
+
+        int sensor = 0;
+        for (ModuleComponentParameters component : moduleParameters) {
+
+            double thickness = component.getThickness();
+
+            Material material = null;
+            try {
+                material = lcdd.getMaterial(component.getMaterialName());
+            } catch (JDOMException except) {
+                throw new RuntimeException(except);
+            }
+            boolean sensitive = component.isSensitive();
+            int componentNumber = component.getComponentNumber();
+
+            posZ += thickness / 2;
+
+            String componentName = moduleName + "_component" + componentNumber;
+
+            Box componentBox = new Box(componentName + "Box", moduleX, moduleY, thickness);
+            lcdd.add(componentBox);
+
+            Volume componentVolume = new Volume(componentName, componentBox, material);
+
+            Position position = new Position(componentName + "_position", 0., 0., posZ);
+            lcdd.add(position);
+            Rotation rotation = new Rotation(componentName + "_rotation", 0., 0., 0.);
+            lcdd.add(rotation);
+
+            PhysVol pv = new PhysVol(componentVolume, moduleVolume, position, rotation);
+            pv.addPhysVolID("component", componentNumber);
+
+            if (sensitive) {
+                if (sensor > 1) {
+                    throw new RuntimeException("Maximum of 2 sensors per module.");
+                }
+
+                // Build a child sensor volume to allow dead areas.
+
+                String sensorName = componentName + "Sensor" + sensor;
+
+                // Flipped these around!!!
+                double sensorX = component.getDimensionY();
+                double sensorY = component.getDimensionX();
+                
+                Box sensorBox = new Box(sensorName + "Box", sensorX, sensorY, thickness);
+                lcdd.add(sensorBox);
+
+                Volume sensorVol = new Volume(sensorName, sensorBox, material);
+                sensorVol.setSensitiveDetector(sd);
+                lcdd.add(sensorVol);
+
+                Position sensorPosition = new Position(sensorName + "Position", 0, 0, 0);
+                lcdd.add(sensorPosition);
+                Rotation sensorRotation = new Rotation(sensorName + "Rotation", 0, 0, 0);
+                lcdd.add(sensorRotation);
+
+                PhysVol sensorPhysVol = new PhysVol(sensorVol, componentVolume, sensorPosition, sensorRotation);
+                sensorPhysVol.addPhysVolID("sensor", sensor);
+
+                ++sensor;
+            }
+
+            // Add component volume after (possible) sensor child volume.
+            lcdd.add(componentVolume);
+
+            // Set vis attributes of component.
+            if (component.getVis() != null) {
+                componentVolume.setVisAttributes(lcdd.getVisAttributes(component.getVis()));
+            }
+
+            // Step to next component placement position.
+            posZ += thickness / 2;
+        }
+    }
+
+    private static class ModuleComponentParameters {
+        protected String materialName;
+        protected double thickness;
+        protected boolean sensitive;
+        protected int componentNumber;
+        protected String vis;
+        protected double dimX, dimY;
+
+        ModuleComponentParameters(double dimX, double dimY, double thickness, String materialName, int componentNumber, boolean sensitive, String vis) {
+            this.dimX = dimX;
+            this.dimY = dimY;
+            this.thickness = thickness;
+            this.materialName = materialName;
+            this.sensitive = sensitive;
+            this.componentNumber = componentNumber;
+            this.vis = vis;
+        }
+
+        double getThickness() {
+            return thickness;
+        }
+
+        double getDimensionX() {
+            return dimX;
+        }
+
+        double getDimensionY() {
+            return dimY;
+        }
+
+        String getMaterialName() {
+            return materialName;
+        }
+
+        boolean isSensitive() {
+            return sensitive;
+        }
+
+        int getComponentNumber() {
+            return componentNumber;
+        }
+
+        String getVis() {
+            return vis;
+        }
+    }
+
+    private static class ModuleParameters extends ArrayList<ModuleComponentParameters> {
+        double thickness;
+        String name;
+        double dimensions[] = new double[3];
+        String vis;
+
+        public ModuleParameters(Element element) {
+            name = element.getAttributeValue("name");
+
+            if (element.getAttribute("vis") != null)
+                this.vis = element.getAttribute("vis").getValue();
+
+            // Optional dimension parameters (not always present).
+            if (element.getChild("trd") != null) {
+                Element trd = element.getChild("trd");
+                try {
+                    dimensions[0] = trd.getAttribute("x1").getDoubleValue();
+                    dimensions[1] = trd.getAttribute("x2").getDoubleValue();
+                    dimensions[2] = trd.getAttribute("z").getDoubleValue();
+                } catch (DataConversionException x) {
+                    throw new RuntimeException(x);
+                }
+            } else if (element.getChild("box") != null) {
+                Element box = element.getChild("box");
+                try {
+                    dimensions[0] = box.getAttribute("x").getDoubleValue();
+                    dimensions[1] = box.getAttribute("y").getDoubleValue();
+                } catch (DataConversionException x) {
+                    throw new RuntimeException(x);
+                }
+            }
+
+            int cntr = 0;
+            for (Object o : element.getChildren("module_component")) {
+                try {
+
+                    Element e = (Element) o;
+
+                    double thickness = e.getAttribute("thickness").getDoubleValue();
+
+                    String materialName = e.getAttributeValue("material");
+
+                    boolean sensitive = false;
+                    if (e.getAttribute("sensitive") != null)
+                        sensitive = e.getAttribute("sensitive").getBooleanValue();
+                    String componentVis = null;
+                    if (e.getAttribute("vis") != null)
+                        componentVis = e.getAttribute("vis").getValue();
+
+                    // Sensors may have reduced dimensions for dead area.
+                    double x = dimensions[0]; // default
+                    double y = dimensions[1]; // default
+                    if (sensitive && e.getChild("dimensions") != null) {
+                        Element dimensions = e.getChild("dimensions");
+                        x = dimensions.getAttribute("x").getDoubleValue();
+                        y = dimensions.getAttribute("y").getDoubleValue();
+                        // System.out.println("x,y="+x+","+y);
+                    }
+                    add(new ModuleComponentParameters(x, y, thickness, materialName, cntr, sensitive, componentVis));
+                } catch (JDOMException x) {
+                    throw new RuntimeException(x);
+                }
+                ++cntr;
+            }
+
+            calculateThickness();
+        }
+
+        void calculateThickness() {
+            thickness = 0.; // reset thickness
+            for (ModuleComponentParameters p : this) {
+                thickness += p.getThickness();
+            }
+        }
+
+        double getThickness() {
+            return thickness;
+        }
+
+        String getName() {
+            return name;
+        }
+
+        double getDimension(int i) {
+            if (i > (dimensions.length - 1) || i < 0)
+                throw new RuntimeException("Invalid dimensions index: " + i);
+            return dimensions[i];
+        }
+
+        String getVis() {
+            return vis;
+        }
+    }
+}

Added: java/trunk/detector-model/src/main/java/org/lcsim/geometry/subdetector/HPSEcal.java
 =============================================================================
--- java/trunk/detector-model/src/main/java/org/lcsim/geometry/subdetector/HPSEcal.java	(added)
+++ java/trunk/detector-model/src/main/java/org/lcsim/geometry/subdetector/HPSEcal.java	Tue Oct  6 11:36:11 2015
@@ -0,0 +1,363 @@
+package org.lcsim.geometry.subdetector;
+
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.jdom.Element;
+import org.jdom.JDOMException;
+import org.lcsim.detector.identifier.IIdentifierHelper;
+import org.lcsim.detector.identifier.Identifier;
+import org.lcsim.geometry.IDDecoder;
+import org.lcsim.geometry.util.IDEncoder;
+
+/**
+ * Reconstruction version of HPS ECal with crystal array.
+ * 
+ * @author Jeremy McCormick <[log in to unmask]>
+ * @author Timothy Nelson <[log in to unmask]>
+ * @version $Id: HPSEcal.java,v 1.6 2011/07/28 20:20:18 jeremy Exp $
+ */
+public class HPSEcal extends AbstractSubdetector
+{
+    private int nx;
+    private int ny;
+    private double beamgap;
+    private double dface;
+    private boolean oddX;
+
+    public static class NeighborMap extends HashMap<Long,Set<Long>>
+    {
+        IIdentifierHelper helper;
+        public NeighborMap(IIdentifierHelper helper)
+        {
+            this.helper = helper;
+        }
+        
+        public String toString()
+        {
+            System.out.println("NeighborMap has " + this.size() + " entries.");
+            StringBuffer buff = new StringBuffer();
+            for (long id : this.keySet())
+            {
+                buff.append(helper.unpack(new Identifier(id)))
+                    .append("\n");
+                Set<Long> nei = this.get(id); 
+                for (long nid : nei)
+                {
+                    buff.append("  " + helper.unpack(new Identifier(nid)))
+                        .append("\n");
+                }
+            }
+            return buff.toString();
+        }
+    }
+
+    private NeighborMap neighborMap = null;
+
+    HPSEcal(Element node) throws JDOMException
+    {
+        super(node);
+        
+        Element layout = node.getChild("layout");
+        
+        nx = layout.getAttribute("nx").getIntValue();
+        ny = layout.getAttribute("ny").getIntValue();
+        beamgap = layout.getAttribute("beamgap").getDoubleValue();
+        dface = layout.getAttribute("dface").getDoubleValue();
+        
+        if (nx % 2 != 0)
+            oddX = true;
+     }
+    
+    public double distanceToFace()
+    {
+        return dface;
+    }
+    
+    public double beamGap()
+    {
+        return beamgap;
+    }
+    
+    /**
+     * The number of crystals in X in one section.
+     * @return
+     */
+    public double nx()
+    {
+        return nx;
+    }
+    
+    /**
+     * The number of crystals in y in one section.
+     * @return
+     */
+    public double ny()
+    {
+        return ny;
+    }   
+    
+    // Class for storing neighbor incides in XY and side.
+    static class XYSide implements Comparator<XYSide>
+    {
+        int x;
+        int y;
+        int side;
+        
+        public XYSide(int x, int y, int side)
+        {
+            this.x = x;
+            this.y = y;
+            this.side = side;
+        }
+        
+        public int x()
+        {
+            return x;
+        }
+        
+        public int y()
+        {
+            return y;
+        }
+        
+        public int side()
+        {
+            return side;
+        }
+        
+        public boolean equals(Object o)
+        {
+            XYSide xy = (XYSide)o;
+            return xy.x() == x && xy.y() == y && xy.side() == side; 
+        }
+
+        public int compare(XYSide o1, XYSide o2)
+        {
+            if (o1.equals(o2))
+            {
+                return 0;
+            }
+            else
+            {
+                return -1;
+            }
+        }
+    }
+    
+    /**
+     * Get the neighbors for a given cell ID.  Each crystal not on an edge 
+     * has 8 neighbors.  Edge crystals have fewer.
+     * @param id The cell ID.
+     * @return A <code>Set</code> containing the cell's neighbors.
+     */
+    Set<Long> getNeighbors(Long id)
+    {        
+        // Get the IDDecoder.
+        IDDecoder dec = getIDDecoder();        
+        
+        // Set the ID.
+        dec.setID(id);
+        
+        // Get ID field values.
+        int x = dec.getValue("ix");
+        int y = dec.getValue("iy");
+        int side = dec.getValue("side");
+       
+        // Get field indices.
+        int ix = dec.getFieldIndex("ix");
+        int iy = dec.getFieldIndex("iy");
+        int iside = dec.getFieldIndex("side");
+        
+        // Get X, Y, & side neighbor data for this crystal.
+        Set<XYSide> neighbors = getNeighbors(x, y, side);
+
+        // Get buffer with values from current ID.
+        int[] buffer = new int[dec.getFieldCount()];
+        dec.getValues(buffer);       
+        
+        // Create an encoder to make neighbor IDs.
+        IDEncoder enc = new IDEncoder(dec.getIDDescription());
+     
+        // Set to hold neighbor IDs.
+        Set<Long> ids = new HashSet<Long>();
+        
+        // Loop over neighbor objects to make IDs.
+        for (XYSide xyside : neighbors)
+        {
+            buffer[ix] = xyside.x;
+            buffer[iy] = xyside.y;
+            buffer[iside] = xyside.side;
+            long nId = enc.setValues(buffer);
+            ids.add(nId);
+        }
+        
+        return ids;
+    }
+    
+    Set<XYSide> getNeighbors(int ix, int iy, int side)
+    {
+        Set<Integer> xneighbors = getXNeighbors(ix);
+        Set<Integer> yneighbors = getYNeighbors(iy);
+        
+        Set<XYSide> neighbors = new HashSet<XYSide>();
+        
+        for (Integer jx : xneighbors)
+        {
+            for (Integer jy : yneighbors)
+            {                
+                // Filter out self.
+                if (jx == ix && jy == iy)
+                {
+                    continue;
+                }
+                
+                neighbors.add(new XYSide(jx,jy,side));
+            }
+        }
+        
+        return neighbors;
+    }
+    
+    Set<Integer> getXNeighbors(int ix)
+    {
+        Set<Integer> neighbors = new HashSet<Integer>();
+        
+        // Add self.
+        neighbors.add(ix);
+        
+        // Left neighbor.
+        if (isValidX(ix - 1))
+        {
+            neighbors.add(ix - 1);
+        }
+        else if (isValidX(ix - 2))
+        {
+            neighbors.add(ix - 2);
+        }
+        
+        // Right neighbor.
+        if (isValidX(ix + 1))
+        {
+            neighbors.add(ix + 1);
+        }
+        else if (isValidX(ix + 2))
+        {
+            neighbors.add(ix + 2);
+        }                
+        
+        return neighbors;
+    }
+    
+    Set<Integer> getYNeighbors(int iy)
+    {
+        Set<Integer> neighbors = new HashSet<Integer>();
+        
+        // Add self.
+        neighbors.add(iy);
+        
+        // Lower neighbor.
+        if (isValidY(iy - 1))
+        {
+            neighbors.add(iy - 1);
+        }
+        // Upper neighbor.
+        if (isValidY(iy + 1))
+        {
+            neighbors.add(iy + 1);
+        }
+        
+        return neighbors;
+    }
+    
+    boolean isValidY(int iy)
+    {
+        // Zero is not valid because ID scheme goes from 1.
+        return iy > 0 && iy <= ny;
+    }
+    
+    boolean isValidX(int ix)
+    {
+        // Even case.
+        if (!oddX)
+        {
+            return ix >= -nx/2 && ix <= nx/2 && ix != 0;
+        }
+        // Odd case.
+        else
+        {
+            return ix >= (-nx-1)/2 && ix <= (nx+1)/2;
+        }
+    }
+        
+    /**
+     * Create a map of crystal IDs to the <code>Set</code> of neighbor crystal IDs.
+     * @return A map of neighbors for each crystal ID.
+     */
+    public NeighborMap getNeighborMap()
+    {
+        if (neighborMap != null)
+        {
+            return neighborMap;
+        }
+       
+        // Setup the private instance of the map. 
+        neighborMap = new NeighborMap(this.getDetectorElement().getIdentifierHelper());
+        
+        IDDecoder dec = getIDDecoder();
+        IDEncoder enc = new IDEncoder(dec.getIDDescription());
+        
+        int nfields = dec.getFieldCount();
+        int[] vals = new int[nfields];
+
+        vals[dec.getFieldIndex("system")] = getSystemID();
+        
+        int idxx = dec.getFieldIndex("ix");
+        int idxy = dec.getFieldIndex("iy");
+        
+        int hnx = nx;        
+        
+        // Calculate number of X for loop.  (from LCDD conv)
+        if (oddX)
+        {
+            hnx -= 1;
+            hnx /= 2;
+        }
+        else
+        {
+            hnx /= 2;
+        }
+                
+        for (int side=-1; side <=1; side++)
+        {            
+            if (side == 0) continue;            
+            vals[dec.getFieldIndex("side")] = side;
+            // Loop over y.
+            for (int iy=1; iy<=ny; iy++)
+            {       
+                // Loop over x.
+                for (int ix=0; ix<=hnx; ix++)
+                {                                                                                       
+                    // Loop for positive and negative x.
+                    for (int j=-1; j<=1; j++)
+                    {
+                        if (j == 0)
+                            continue;
+                                                
+                        vals[idxx] = ix*j;
+                        vals[idxy] = iy;
+                                                        
+                        Long id = enc.setValues(vals);
+                        Set<Long> neighbors = getNeighbors(id);
+                                        
+                        neighborMap.put(id, neighbors);
+                    }
+                }
+            }
+        }
+        
+        return neighborMap;
+    }    
+}

Added: java/trunk/detector-model/src/main/java/org/lcsim/geometry/subdetector/HPSEcal2.java
 =============================================================================
--- java/trunk/detector-model/src/main/java/org/lcsim/geometry/subdetector/HPSEcal2.java	(added)
+++ java/trunk/detector-model/src/main/java/org/lcsim/geometry/subdetector/HPSEcal2.java	Tue Oct  6 11:36:11 2015
@@ -0,0 +1,12 @@
+package org.lcsim.geometry.subdetector;
+
+import org.jdom.Element;
+import org.jdom.JDOMException;
+
+public class HPSEcal2 extends HPSEcal
+{
+    HPSEcal2(Element node) throws JDOMException
+    {
+        super(node);
+    }
+}

Added: java/trunk/detector-model/src/main/java/org/lcsim/geometry/subdetector/HPSEcal3.java
 =============================================================================
--- java/trunk/detector-model/src/main/java/org/lcsim/geometry/subdetector/HPSEcal3.java	(added)
+++ java/trunk/detector-model/src/main/java/org/lcsim/geometry/subdetector/HPSEcal3.java	Tue Oct  6 11:36:11 2015
@@ -0,0 +1,387 @@
+package org.lcsim.geometry.subdetector;
+
+import hep.graphics.heprep.HepRep;
+import hep.graphics.heprep.HepRepFactory;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.jdom.Element;
+import org.jdom.JDOMException;
+import org.lcsim.detector.converter.heprep.DetectorElementToHepRepConverter;
+import org.lcsim.detector.identifier.IIdentifierHelper;
+import org.lcsim.detector.identifier.Identifier;
+import org.lcsim.geometry.IDDecoder;
+import org.lcsim.geometry.util.IDEncoder;
+
+/**
+ * Reconstruction version of HPS ECal with crystal array.
+ * 
+ * @author Jeremy McCormick <[log in to unmask]>
+ * @author Timothy Nelson <[log in to unmask]>
+ * @version $Id: HPSEcal3.java,v 1.3 2012/04/30 18:04:38 jeremy Exp $
+ */
+public class HPSEcal3 extends AbstractSubdetector {
+    private int nx;
+    private int ny;
+    //private double beamgap;
+    //private double dface;
+    private boolean oddX;
+    List<CrystalRange> removeCrystals = new ArrayList<CrystalRange>();
+
+    public static class NeighborMap extends HashMap<Long, Set<Long>> {
+        IIdentifierHelper helper;
+
+        public NeighborMap(IIdentifierHelper helper) {
+            this.helper = helper;
+        }
+
+        public String toString() {
+            System.out.println("NeighborMap has " + this.size() + " entries.");
+            StringBuffer buff = new StringBuffer();
+            for (long id : this.keySet()) {
+                buff.append(helper.unpack(new Identifier(id))).append("\n");
+                Set<Long> nei = this.get(id);
+                for (long nid : nei) {
+                    buff.append("  " + helper.unpack(new Identifier(nid))).append("\n");
+                }
+            }
+            return buff.toString();
+        }
+    }
+
+    private NeighborMap neighborMap = null;
+
+    HPSEcal3(Element node) throws JDOMException {
+        super(node);
+
+        Element layout = node.getChild("layout");
+
+        nx = layout.getAttribute("nx").getIntValue();
+        ny = layout.getAttribute("ny").getIntValue();        
+        //beamgap = layout.getAttribute("beamgap").getDoubleValue();
+        //dface = layout.getAttribute("dface").getDoubleValue();
+
+        if (nx % 2 != 0)
+            oddX = true;
+
+        // Setup range of indices to be skipped.
+        for (Object obj : layout.getChildren("remove")) {
+            Element remove = (Element) obj;
+            try {
+                removeCrystals.add(new CrystalRange(remove));
+            } catch (Exception x) {
+                throw new RuntimeException(x);
+            }
+        }
+
+        /*
+         * <remove ixmin="2" ixmax="10" iymin="-1" iymax="1"/>
+         */
+    }
+
+    private static class CrystalRange {
+        int ixmin;
+        int ixmax;
+        int iymin;
+        int iymax;
+
+        CrystalRange(Element elem) throws Exception {
+            ixmin = ixmax = iymin = iymax = 0;
+
+            if (elem.getAttribute("ixmin") != null) {
+                ixmin = elem.getAttribute("ixmin").getIntValue();
+            } else {
+                throw new RuntimeException("Missing ixmin parameter.");
+            }
+
+            if (elem.getAttribute("ixmax") != null) {
+                ixmax = elem.getAttribute("ixmax").getIntValue();
+            } else {
+                throw new RuntimeException("Missing ixmax parameter.");
+            }
+
+            if (elem.getAttribute("iymin") != null) {
+                iymin = elem.getAttribute("iymin").getIntValue();
+            } else {
+                throw new RuntimeException("Missing ixmax parameter.");
+            }
+
+            if (elem.getAttribute("iymax") != null) {
+                iymax = elem.getAttribute("iymax").getIntValue();
+            } else {
+                throw new RuntimeException("Missing iymax parameter.");
+            }
+        }
+    }
+
+    private boolean isValidXY(int ix, int iy) {
+        if (!isValidX(ix))
+            return false;
+        if (!isValidY(iy))
+            return false;
+        return checkRange(ix, iy, this.removeCrystals);
+    }
+
+    private boolean checkRange(int ix, int iy, List<CrystalRange> ranges) {
+        if (ranges.size() == 0)
+            return true;
+        for (CrystalRange range : ranges) {
+            if ((ix >= range.ixmin && ix <= range.ixmax) && ((iy >= range.iymin) && (iy <= range.iymax))) {
+                return false;
+            }
+
+        }
+        return true;
+    }
+
+    //public double distanceToFace() {
+    //    return dface;
+    //}
+
+    //public double beamGap() {
+    //    return beamgap;
+    //}
+
+    /**
+     * The number of crystals in X in one section.
+     * 
+     * @return
+     */
+    public double nx() {
+        return nx;
+    }
+
+    /**
+     * The number of crystals in y in one section.
+     * 
+     * @return
+     */
+    public double ny() {
+        return ny;
+    }
+
+    // Class for storing neighbor indices in XY and side.
+    static class XY implements Comparator<XY> {
+        int x;
+        int y;
+
+        public XY(int x, int y) {
+            this.x = x;
+            this.y = y;
+        }
+
+        public int x() {
+            return x;
+        }
+
+        public int y() {
+            return y;
+        }
+
+        public boolean equals(Object o) {
+            XY xy = (XY) o;
+            return xy.x() == x && xy.y() == y;
+        }
+
+        public int compare(XY o1, XY o2) {
+            if (o1.equals(o2)) {
+                return 0;
+            } else {
+                return -1;
+            }
+        }
+    }
+
+    /**
+     * Get the neighbors for a given cell ID. Each crystal not on an edge has 8 neighbors. Edge crystals have fewer. 
+     * @param id The cell ID.
+     * @return A <code>Set</code> containing the cell's neighbors.
+     */
+    Set<Long> getNeighbors(Long id) {
+        // Get the IDDecoder.
+        IDDecoder dec = getIDDecoder();
+
+        // Set the ID.
+        dec.setID(id);
+
+        // Get ID field values.
+        int x = dec.getValue("ix");
+        int y = dec.getValue("iy");
+
+        // Get field indices.
+        int ix = dec.getFieldIndex("ix");
+        int iy = dec.getFieldIndex("iy");
+
+        // Get X, Y, & side neighbor data for this crystal.
+        Set<XY> neighbors = getNeighbors(x, y);
+
+        // Get buffer with values from current ID.
+        int[] buffer = new int[dec.getFieldCount()];
+        dec.getValues(buffer);
+
+        // Create an encoder to make neighbor IDs.
+        IDEncoder enc = new IDEncoder(dec.getIDDescription());
+
+        // Set to hold neighbor IDs.
+        Set<Long> ids = new HashSet<Long>();
+
+        // Loop over neighbor objects to make IDs.
+        for (XY xyside : neighbors) {
+            buffer[ix] = xyside.x;
+            buffer[iy] = xyside.y;
+            long nId = enc.setValues(buffer);
+            ids.add(nId);
+        }
+
+        return ids;
+    }
+
+    Set<XY> getNeighbors(int ix, int iy) {
+        Set<Integer> xneighbors = getXNeighbors(ix);
+        Set<Integer> yneighbors = getYNeighbors(iy);
+
+        Set<XY> neighbors = new HashSet<XY>();
+
+        for (Integer jx : xneighbors) {
+            for (Integer jy : yneighbors) {
+                // Filter out self.
+                if (jx == ix && jy == iy) {
+                    continue;
+                }
+
+                // Check for valid neighbor.
+                // FIXME: Duplication of isValidX + isValidY.
+                if (!isValidXY(jx, jy))
+                    continue;
+
+                neighbors.add(new XY(jx, jy));
+            }
+        }
+
+        return neighbors;
+    }
+
+    Set<Integer> getXNeighbors(int ix) {
+        Set<Integer> neighbors = new HashSet<Integer>();
+
+        // Add self.
+        neighbors.add(ix);
+
+        // Left neighbor.
+        if (isValidX(ix - 1)) {
+            neighbors.add(ix - 1);
+        } else if (isValidX(ix - 2)) {
+            neighbors.add(ix - 2);
+        }
+
+        // Right neighbor.
+        if (isValidX(ix + 1)) {
+            neighbors.add(ix + 1);
+        } else if (isValidX(ix + 2)) {
+            neighbors.add(ix + 2);
+        }
+
+        return neighbors;
+    }
+
+    Set<Integer> getYNeighbors(int iy) {
+        Set<Integer> neighbors = new HashSet<Integer>();
+
+        // Add self.
+        neighbors.add(iy);
+
+        // Lower neighbor.
+        if (isValidY(iy - 1)) {
+            neighbors.add(iy - 1);
+        }
+
+        // Upper neighbor.
+        if (isValidY(iy + 1)) {
+            neighbors.add(iy + 1);
+        }
+
+        return neighbors;
+    }
+
+    boolean isValidY(int iy) {
+        // Zero is not valid because ID scheme goes from 1.
+        return iy >= -ny && iy <= ny && iy != 0;
+    }
+
+    boolean isValidX(int ix) {
+        // Even case.
+        if (!oddX) {
+            return ix >= -nx / 2 && ix <= nx / 2 && ix != 0;
+        }
+        // Odd case.
+        else {
+            return ix >= (-nx - 1) / 2 && ix <= (nx + 1) / 2;
+        }
+    }
+
+    /**
+     * Create a map of crystal IDs to the <code>Set</code> of neighbor crystal IDs.
+     * 
+     * @return A map of neighbors for each crystal ID.
+     */
+    public NeighborMap getNeighborMap() {
+        if (neighborMap != null) {
+            return neighborMap;
+        }
+
+        // Setup the private instance of the map.
+        neighborMap = new NeighborMap(this.getDetectorElement().getIdentifierHelper());
+
+        IDDecoder dec = getIDDecoder();
+        IDEncoder enc = new IDEncoder(dec.getIDDescription());
+
+        int nfields = dec.getFieldCount();
+        int[] vals = new int[nfields];
+
+        vals[dec.getFieldIndex("system")] = getSystemID();
+
+        int idxx = dec.getFieldIndex("ix");
+        int idxy = dec.getFieldIndex("iy");
+
+        /*
+        int hnx = nx;
+
+        // Calculate number of X for loop. (from LCDD conv)
+        if (oddX) {
+            hnx -= 1;
+            hnx /= 2;
+        } else {
+            hnx /= 2;
+        }
+        */
+
+        // Loop over y.
+        for (int iy = -ny; iy <= ny; iy++) {
+            int loopx = (int) Math.floor(nx / 2);
+            // Loop over x.
+            for (int ix = -loopx; ix <= loopx; ix++) {
+                if (!isValidXY(ix, iy))
+                    continue;
+
+                vals[idxx] = ix;
+                vals[idxy] = iy;
+
+                Long id = enc.setValues(vals);
+                Set<Long> neighbors = getNeighbors(id);
+
+                neighborMap.put(id, neighbors);
+            }
+        }
+
+        return neighborMap;
+    }
+
+    public void appendHepRep(HepRepFactory factory, HepRep heprep) {
+        DetectorElementToHepRepConverter.convert(getDetectorElement(), factory, heprep, -1, false, getVisAttributes().getColor());
+    }
+}

Added: java/trunk/detector-model/src/main/java/org/lcsim/geometry/subdetector/HPSMuonCalorimeter.java
 =============================================================================
--- java/trunk/detector-model/src/main/java/org/lcsim/geometry/subdetector/HPSMuonCalorimeter.java	(added)
+++ java/trunk/detector-model/src/main/java/org/lcsim/geometry/subdetector/HPSMuonCalorimeter.java	Tue Oct  6 11:36:11 2015
@@ -0,0 +1,31 @@
+package org.lcsim.geometry.subdetector;
+
+import hep.graphics.heprep.HepRep;
+import hep.graphics.heprep.HepRepFactory;
+
+import org.jdom.Element;
+import org.jdom.JDOMException;
+import org.lcsim.detector.converter.heprep.DetectorElementToHepRepConverter;
+
+/**
+ * @author Jeremy McCormick <[log in to unmask]>
+ * @version $Id: HPSMuonCalorimeter.java,v 1.2 2013/01/25 00:13:14 jeremy Exp $
+ */
+public class HPSMuonCalorimeter extends AbstractSubdetector 
+{
+    HPSMuonCalorimeter(Element node) throws JDOMException 
+    {
+        super(node);
+    }
+    
+    public void appendHepRep(HepRepFactory factory, HepRep heprep) 
+    {
+        DetectorElementToHepRepConverter.convert(
+                getDetectorElement(), 
+                factory, 
+                heprep, 
+                -1, 
+                false, 
+                getVisAttributes().getColor());
+    }
+}

Added: java/trunk/detector-model/src/main/java/org/lcsim/geometry/subdetector/HPSTracker.java
 =============================================================================
--- java/trunk/detector-model/src/main/java/org/lcsim/geometry/subdetector/HPSTracker.java	(added)
+++ java/trunk/detector-model/src/main/java/org/lcsim/geometry/subdetector/HPSTracker.java	Tue Oct  6 11:36:11 2015
@@ -0,0 +1,32 @@
+package org.lcsim.geometry.subdetector;
+
+import hep.graphics.heprep.HepRep;
+import hep.graphics.heprep.HepRepFactory;
+
+import org.jdom.Element;
+import org.jdom.JDOMException;
+import org.lcsim.detector.converter.heprep.DetectorElementToHepRepConverter;
+
+public class HPSTracker extends AbstractTracker
+{
+    HPSTracker(Element node) throws JDOMException
+    {
+        super(node);
+    }
+
+    public void appendHepRep(HepRepFactory factory, HepRep heprep)
+    {
+        DetectorElementToHepRepConverter.convert(getDetectorElement(), factory, heprep, -1, false, getVisAttributes().getColor());
+    }
+
+    public boolean isEndcap()
+    {
+        return false;
+    }
+
+    public boolean isBarrel()
+    {
+        return true;
+    }
+
+}

Added: java/trunk/detector-model/src/main/java/org/lcsim/geometry/subdetector/HPSTracker2.java
 =============================================================================
--- java/trunk/detector-model/src/main/java/org/lcsim/geometry/subdetector/HPSTracker2.java	(added)
+++ java/trunk/detector-model/src/main/java/org/lcsim/geometry/subdetector/HPSTracker2.java	Tue Oct  6 11:36:11 2015
@@ -0,0 +1,27 @@
+package org.lcsim.geometry.subdetector;
+
+import hep.graphics.heprep.HepRep;
+import hep.graphics.heprep.HepRepFactory;
+
+import org.jdom.Element;
+import org.jdom.JDOMException;
+import org.lcsim.detector.converter.heprep.DetectorElementToHepRepConverter;
+
+public class HPSTracker2 extends AbstractTracker {
+    
+    HPSTracker2(Element node) throws JDOMException {
+        super(node);
+    }
+
+    public void appendHepRep(HepRepFactory factory, HepRep heprep) {
+        DetectorElementToHepRepConverter.convert(getDetectorElement(), factory, heprep, -1, false, getVisAttributes().getColor());
+    }
+
+    public boolean isEndcap() {
+        return false;
+    }
+
+    public boolean isBarrel() {
+        return true;
+    }
+}

Copied: java/trunk/detector-model/src/test/java/org/hps/detector/svt/SvtDetectorSetupTest.java (from r3746, java/trunk/conditions/src/test/java/org/hps/conditions/svt/SvtDetectorSetupTest.java)
 =============================================================================
--- java/trunk/conditions/src/test/java/org/hps/conditions/svt/SvtDetectorSetupTest.java	(original)
+++ java/trunk/detector-model/src/test/java/org/hps/detector/svt/SvtDetectorSetupTest.java	Tue Oct  6 11:36:11 2015
@@ -1,10 +1,12 @@
-package org.hps.conditions.svt;
+package org.hps.detector.svt;
 
 import java.util.List;
+import java.util.logging.Logger;
 
 import junit.framework.TestCase;
 
 import org.hps.conditions.database.DatabaseConditionsManager;
+import org.hps.conditions.svt.SvtConditions;
 import org.lcsim.detector.tracker.silicon.HpsSiSensor;
 import org.lcsim.geometry.Detector;
 
@@ -17,7 +19,7 @@
  */
 // TODO: Update this test with more meaningful assertions.
 public final class SvtDetectorSetupTest extends TestCase {
-
+    
     /**
      * Maximum channel number.
      */
@@ -60,6 +62,7 @@
     public void test() throws Exception {
 
         final DatabaseConditionsManager conditionsManager = DatabaseConditionsManager.getInstance();
+        conditionsManager.addConditionsListener(new SvtDetectorSetup());
         conditionsManager.setDetector("HPS-Proposal2014-v7-2pt2", 0);
 
         // Get the detector.

Copied: java/trunk/detector-model/src/test/java/org/hps/detector/svt/TestRunSvtDetectorSetupTest.java (from r3746, java/trunk/conditions/src/test/java/org/hps/conditions/svt/TestRunSvtDetectorSetupTest.java)
 =============================================================================
--- java/trunk/conditions/src/test/java/org/hps/conditions/svt/TestRunSvtDetectorSetupTest.java	(original)
+++ java/trunk/detector-model/src/test/java/org/hps/detector/svt/TestRunSvtDetectorSetupTest.java	Tue Oct  6 11:36:11 2015
@@ -1,10 +1,11 @@
-package org.hps.conditions.svt;
+package org.hps.detector.svt;
 
 import java.util.List;
 
 import junit.framework.TestCase;
 
 import org.hps.conditions.database.DatabaseConditionsManager;
+import org.hps.conditions.svt.TestRunSvtConditions;
 import org.lcsim.detector.tracker.silicon.HpsTestRunSiSensor;
 import org.lcsim.geometry.Detector;
 

Added: java/trunk/detector-model/src/test/java/org/lcsim/detector/converter/compact/HPSEcalAPITest.java
 =============================================================================
--- java/trunk/detector-model/src/test/java/org/lcsim/detector/converter/compact/HPSEcalAPITest.java	(added)
+++ java/trunk/detector-model/src/test/java/org/lcsim/detector/converter/compact/HPSEcalAPITest.java	Tue Oct  6 11:36:11 2015
@@ -0,0 +1,74 @@
+package org.lcsim.detector.converter.compact;
+
+import java.io.InputStream;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.lcsim.geometry.Detector;
+import org.lcsim.geometry.GeometryReader;
+
+/**
+ * @author Jeremy McCormick <[log in to unmask]>
+ */
+public class HPSEcalAPITest extends TestCase {
+
+    public void testHPSEcalAPI() throws Exception {
+
+        final GeometryReader geometryReader = new GeometryReader();
+        final InputStream in = this.getClass().getResourceAsStream("/org/lcsim/geometry/subdetector/HPSEcal3Test.xml");
+        final Detector detector = geometryReader.read(in);
+
+        final HPSEcalDetectorElement ecal = (HPSEcalDetectorElement) detector.getSubdetector("Ecal")
+                .getDetectorElement();
+
+        final HPSEcalAPI api = ecal;
+
+        assertEquals("The max X index is wrong.", 23, api.getXIndexMax());
+        assertEquals("The min X index is wrong.", -23, api.getXIndexMin());
+        assertEquals("The max Y index is wrong.", 5, api.getYIndexMax());
+        assertEquals("The min Y index is wrong.", -5, api.getYIndexMin());
+
+        for (final Integer yIndex : api.getYIndices()) {
+            if (yIndex == 0) {
+                System.out.println("skipping yIndex = 0");
+                continue;
+            }
+            for (final Integer xIndex : api.getXIndices()) {
+                System.out.println("checking crystal " + xIndex + ", " + yIndex);
+                if (xIndex == 0) {
+                    System.out.println("skipping xIndex = 0");
+                    continue;
+                }
+                if ((yIndex == 1 || yIndex == -1) && xIndex <= -2 && xIndex >= -10) {
+                    System.out.println("crystal " + xIndex + ", " + yIndex + " should be in the gap");
+                    assertTrue("Indices should be in gap: " + xIndex + ", " + yIndex, api.isInBeamGap(xIndex, yIndex));
+                    // Crystal is in the beam gap.
+                    continue;
+                }
+                final EcalCrystal crystal = api.getCrystal(xIndex, yIndex);
+                assertNotNull("Failed to find crystal at ix = " + xIndex + ", iy = " + yIndex, crystal);
+            }
+        }
+
+        for (final Integer yIndex : api.getYIndices()) {
+            final List<EcalCrystal> row = api.getRow(yIndex);
+            System.out.println("found " + row.size() + " crystals in row " + yIndex);
+            if (Math.abs(yIndex) != 1) {
+                assertEquals("Wrong number of crystals in row.", 46, row.size());
+            } else {
+                assertEquals("Wrong number of crystals in row.", 37, row.size());
+            }
+        }
+
+        for (final Integer xIndex : api.getXIndices()) {
+            final List<EcalCrystal> column = api.getColumn(xIndex);
+            System.out.println("found " + column.size() + " crystals in column " + xIndex);
+            if (xIndex > -2 || xIndex < -10) {
+                assertEquals("Wrong number of crystals in column.", 10, column.size());
+            } else {
+                assertEquals("Wrong number of crystals in column.", 8, column.size());
+            }
+        }
+    }
+}

Added: java/trunk/detector-model/src/test/java/org/lcsim/detector/converter/compact/HPSMuonCalorimeterTest.java
 =============================================================================
--- java/trunk/detector-model/src/test/java/org/lcsim/detector/converter/compact/HPSMuonCalorimeterTest.java	(added)
+++ java/trunk/detector-model/src/test/java/org/lcsim/detector/converter/compact/HPSMuonCalorimeterTest.java	Tue Oct  6 11:36:11 2015
@@ -0,0 +1,48 @@
+package org.lcsim.detector.converter.compact;
+
+import java.io.InputStream;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.lcsim.detector.IDetectorElement;
+import org.lcsim.detector.identifier.IIdentifierDictionary;
+import org.lcsim.detector.identifier.IIdentifierHelper;
+import org.lcsim.geometry.Detector;
+import org.lcsim.geometry.GeometryReader;
+
+public class HPSMuonCalorimeterTest extends TestCase 
+{
+    
+    Detector detector = null;
+    private static final String resource = "/org/lcsim/geometry/subdetector/HPSMuonCalorimeterTest.xml";
+    
+    public static Test suite() 
+    {
+        return new TestSuite(HPSMuonCalorimeterTest.class);
+    }
+
+    public void setUp() 
+    {
+        InputStream in = this.getClass().getResourceAsStream(resource);
+        GeometryReader reader = new GeometryReader();
+        try {
+            detector = reader.read(in);
+        } catch (Throwable x) {
+            throw new RuntimeException(x);
+        }
+    }
+    
+    public void testMuon() 
+    {
+        IDetectorElement de = detector.getSubdetector("MUON").getDetectorElement();
+        System.out.println("MUON has " + de.getChildren().size() + " children.");
+        IIdentifierHelper helper = de.getIdentifierHelper();
+        IIdentifierDictionary dict = helper.getIdentifierDictionary();        
+        System.out.println(dict.toString());
+        for (IDetectorElement child : de.getChildren()) {
+            System.out.println(child.getName() + " => " + helper.unpack(child.getIdentifier()));
+        }
+    }
+}

Added: java/trunk/detector-model/src/test/java/org/lcsim/detector/converter/compact/HPSTracker2ConverterTest.java
 =============================================================================
--- java/trunk/detector-model/src/test/java/org/lcsim/detector/converter/compact/HPSTracker2ConverterTest.java	(added)
+++ java/trunk/detector-model/src/test/java/org/lcsim/detector/converter/compact/HPSTracker2ConverterTest.java	Tue Oct  6 11:36:11 2015
@@ -0,0 +1,117 @@
+package org.lcsim.detector.converter.compact;
+
+import java.io.InputStream;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.lcsim.detector.converter.compact.subdetector.HpsTracker2;
+import org.lcsim.detector.converter.compact.subdetector.SvtStereoLayer;
+import org.lcsim.detector.tracker.silicon.ChargeCarrier;
+import org.lcsim.detector.tracker.silicon.HpsSiSensor;
+import org.lcsim.geometry.Detector;
+import org.lcsim.geometry.GeometryReader;
+import org.lcsim.util.log.DefaultLogFormatter;
+import org.lcsim.util.log.LogUtil;
+
+/**
+ * Unit test for the HPSTracker2Coverter.
+ * 
+ * @author Jeremy McCormick <[log in to unmask]>
+ * @author Omar Moreno <[log in to unmask]> 
+ */
+public class HPSTracker2ConverterTest extends TestCase {
+   
+    // Initialize the logger
+    private static Logger logger = LogUtil.create(HPSTracker2Converter.class.getName(),
+                                                  new DefaultLogFormatter(), Level.INFO);
+    
+    Detector detector = null;
+   
+    //-----------------//
+    //--- Constants ---//
+    //-----------------//
+
+    private static final int TOTAL_NUMBER_OF_SENSORS = 20; 
+    private static final int TOTAL_NUMBER_OF_STEREO_LAYERS = 10; 
+    private static final String SUBDETECTOR_NAME = "Tracker";
+    
+	public static final int NUMBER_OF_READOUT_STRIPS = 639;
+	public static final int NUMBER_OF_SENSE_STRIPS = 1277;
+    
+    //-----------------//
+    //-----------------//
+    
+    public static Test suite() {
+        return new TestSuite(HPSTracker2ConverterTest.class);
+    }
+
+    private static final String resource = "/org/lcsim/geometry/subdetector/HPSTracker2Test.xml";
+    public void setUp() {
+        InputStream in = this.getClass().getResourceAsStream(resource);
+
+        GeometryReader reader = new GeometryReader();
+
+        try {
+            detector = reader.read(in);
+        }
+        catch (Throwable x) {
+            throw new RuntimeException(x);
+        }
+    }
+    
+    public void testHPSTracker2Converter() {
+       
+        // Test if the correct number of sensors was created.
+        logger.info("Checking if the correct number of sensors were created.");
+        List<HpsSiSensor> sensors = detector.getSubdetector(SUBDETECTOR_NAME).getDetectorElement().findDescendants(HpsSiSensor.class);
+        assertTrue("[ " + this.getClass().getSimpleName() + " ]: The wrong number of sensors were created.",
+                sensors.size() == TOTAL_NUMBER_OF_SENSORS);
+        logger.info("Total number of sensors that were created: " + sensors.size());
+       
+        // Test if the sensors that were created are instances of HpsSiSensor
+        logger.info("Checking if sensors were initialized correctly");
+        for(HpsSiSensor sensor : sensors) {
+            assertTrue("[ " + this.getClass().getSimpleName() + " ]: Sensor is of wrong type: " + sensor.getClass().getSimpleName(),
+                        sensor instanceof HpsSiSensor);
+			assertTrue("[ " + this.getClass().getSimpleName() + " ]: Wrong number of readout electrodes found.",
+					sensor.getReadoutElectrodes(ChargeCarrier.HOLE).getNCells() == NUMBER_OF_READOUT_STRIPS);
+			
+			assertTrue("[ " + this.getClass().getSimpleName() + " ]: Wrong number of sense electrodes found.",
+					sensor.getSenseElectrodes(ChargeCarrier.HOLE).getNCells() == NUMBER_OF_SENSE_STRIPS);
+			logger.info(sensor.toString());
+        }
+        logger.info("Sensors were all initialized correctly.");
+        
+        // Check that the correct number of stereo layers were created
+        logger.info("Checking if the correct number of stereo layers were created.");
+        List<SvtStereoLayer> stereoLayers = ((HpsTracker2) detector.getSubdetector(SUBDETECTOR_NAME).getDetectorElement()).getStereoPairs();
+        // Check that the number of stereo layers created is as expected
+        assertTrue("[ " + this.getClass().getSimpleName() + " ]: The wrong number of stereo layers were created.",
+                stereoLayers.size() == TOTAL_NUMBER_OF_STEREO_LAYERS);
+        logger.info("Total number of stereo layers created: " + stereoLayers.size());
+        
+        for(SvtStereoLayer stereoLayer : stereoLayers){
+            logger.fine(stereoLayer.toString());
+            
+            // The sensors comprising the stereo layer should belong to the same detector volume
+            assertTrue("[ " + this.getClass().getSimpleName() + " ]: Sensors belong to different detector volumes.", 
+                    stereoLayer.getAxialSensor().getModuleNumber() == stereoLayer.getStereoSensor().getModuleNumber());
+            
+            // If the stereo layer is part of the top detector volume, the axial layers have an odd layer number.
+            // If the stereo layer is part of the bottom detector volume, the axial layers have an even layer number.
+            logger.info("Checking if the layers are oriented correctly."); 
+            if(stereoLayer.getAxialSensor().isTopLayer()){
+                assertTrue("[ " + this.getClass().getSimpleName() + " ]: Sensors composing the stereo layer are flipped",
+                        stereoLayer.getAxialSensor().getLayerNumber()%2 == 1);
+            } else { 
+                assertTrue("[ " + this.getClass().getSimpleName() + " ]: Sensors composing the stereo layer are flipped",
+                        stereoLayer.getAxialSensor().getLayerNumber()%2 == 0);
+            }
+        }
+    }
+}

Added: java/trunk/detector-model/src/test/java/org/lcsim/detector/converter/compact/HpsTestRunSiSensorConverterTest.java
 =============================================================================
--- java/trunk/detector-model/src/test/java/org/lcsim/detector/converter/compact/HpsTestRunSiSensorConverterTest.java	(added)
+++ java/trunk/detector-model/src/test/java/org/lcsim/detector/converter/compact/HpsTestRunSiSensorConverterTest.java	Tue Oct  6 11:36:11 2015
@@ -0,0 +1,95 @@
+package org.lcsim.detector.converter.compact;
+
+import java.io.InputStream;
+import java.util.List;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.lcsim.detector.converter.compact.subdetector.HpsTracker2;
+import org.lcsim.detector.converter.compact.subdetector.SvtStereoLayer;
+import org.lcsim.detector.tracker.silicon.HpsSiSensor;
+import org.lcsim.detector.tracker.silicon.HpsTestRunSiSensor;
+import org.lcsim.geometry.Detector;
+import org.lcsim.geometry.GeometryReader;
+
+/**
+ * Unit test for the {@link HPSTracker2Converter} when the sensor type
+ * is equal to {@link HpsTestRunSiSensor} 
+ * 
+ * @author Omar Moreno <[log in to unmask]>
+ */
+public class HpsTestRunSiSensorConverterTest extends TestCase {
+    
+    Detector detector = null;
+   
+    //-----------------//
+    //--- Constants ---//
+    //-----------------//
+    private static final int TOTAL_NUMBER_OF_SENSORS = 20; 
+    private static final int TOTAL_NUMBER_OF_STEREO_LAYERS = 10; 
+    private static final String SUBDETECTOR_NAME = "Tracker";
+    private static final String RESOURCE = "/org/lcsim/geometry/subdetector/HpsTestRunSiSensorConverterTest.xml";
+    
+    /*public static Test suite() {
+        return new TestSuite(HPSTracker2ConverterTest.class);
+    }*/
+    
+    public void setUp() { 
+        
+        InputStream in = this.getClass().getResourceAsStream(RESOURCE);
+
+        GeometryReader reader = new GeometryReader();
+
+        try {
+            detector = reader.read(in);
+        }
+        catch (Throwable x) {
+            throw new RuntimeException(x);
+        }
+    }
+    
+    
+    public void testHPSTracker2Converter() { 
+        
+        System.out.println("[ " + this.getClass().getSimpleName() + " ]: Checking if the correct number of sensors were created.");
+        List<HpsSiSensor> sensors = detector.getSubdetector(SUBDETECTOR_NAME).getDetectorElement().findDescendants(HpsSiSensor.class);
+        assertTrue("[ " + this.getClass().getSimpleName() + " ]: The wrong number of sensors were created.", sensors.size() == TOTAL_NUMBER_OF_SENSORS);
+        System.out.println("[ " + this.getClass().getSimpleName() + " ]: Total number of sensors that were created: " + sensors.size());
+        
+        
+        System.out.println("[ " + this.getClass().getSimpleName() + " ]: Checking if sensor is instance of HpsTestRunSiSensor.");
+        for(HpsSiSensor sensor : sensors) {
+            assertTrue("[ " + this.getClass().getSimpleName() + " ]: Sensor is of wrong type: " + sensor.getClass().getSimpleName(),
+                        sensor instanceof HpsTestRunSiSensor);
+        }
+        System.out.println("[ " + this.getClass().getSimpleName() + " ]: Sensors are all instances of HpsTestRunSiSensor.");
+        
+        
+        // Check that the correct number of stereo layers were created
+        System.out.println("[ HPSTracker2ConverterTest ]: Checking if the correct number of stereo layers were created.");
+        List<SvtStereoLayer> stereoLayers = ((HpsTracker2) detector.getSubdetector(SUBDETECTOR_NAME).getDetectorElement()).getStereoPairs();
+        // Check that the number of stereo layers created is as expected
+        assertTrue("The wrong number of stereo layers were created.", stereoLayers.size() == TOTAL_NUMBER_OF_STEREO_LAYERS);
+        System.out.println("[ " + this.getClass().getSimpleName() + " ]: Total number of stereo layers created: " + stereoLayers.size());
+
+        for(SvtStereoLayer stereoLayer : stereoLayers){
+            System.out.println("[ " + this.getClass().getSimpleName() + " ]: " + stereoLayer.toString());
+            
+            // The sensors comprising the stereo layer should belong to the same detector volume
+            assertTrue("Sensors belong to different detector volumes.", 
+                    stereoLayer.getAxialSensor().getModuleNumber() == stereoLayer.getStereoSensor().getModuleNumber());
+            
+            // If the stereo layer is part of the top detector volume, the axial layers have an odd layer number.
+            // If the stereo layer is part of the bottom detector volumen, the axial layers have an even layer number. 
+            System.out.println("[ " + this.getClass().getSimpleName() + " ]: check if the layers are oriented correctly."); 
+            if(stereoLayer.getAxialSensor().isTopLayer()){
+                assertTrue("Sensors composing the stereo layer are flipped", stereoLayer.getAxialSensor().getLayerNumber()%2 == 1);
+            } else { 
+                assertTrue("Sensors composing the stereo layer are flipped", stereoLayer.getAxialSensor().getLayerNumber()%2 == 0);
+                
+            }
+        } 
+    }
+}

Added: java/trunk/detector-model/src/test/resources/org/lcsim/geometry/subdetector/HPSEcal3Test.xml
 =============================================================================
--- java/trunk/detector-model/src/test/resources/org/lcsim/geometry/subdetector/HPSEcal3Test.xml	(added)
+++ java/trunk/detector-model/src/test/resources/org/lcsim/geometry/subdetector/HPSEcal3Test.xml	Tue Oct  6 11:36:11 2015
@@ -0,0 +1,56 @@
+<lccdd xmlns:compact="http://www.lcsim.org/schemas/compact/1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" xs:noNamespaceSchemaLocation="http://www.lcsim.org/schemas/compact/1.0/compact.xsd">
+    <info name="HPSEcal3Test" />
+    <define>   
+        <!-- world volume -->
+        <constant name="world_side" value="10000.0*cm" />
+        <constant name="world_x" value="world_side" />
+        <constant name="world_y" value="world_side" />
+        <constant name="world_z" value="world_side" />
+        <!-- tracking region -->
+        <constant name="tracking_region_radius" value="200.0*cm" />
+        <constant name="tracking_region_min" value="5.0*cm" />
+        <constant name="tracking_region_zmax" value="100.0*cm" />
+        <!-- ECal placement parameters -->
+        <constant name="beam_angle" value="0.03052"/>
+         <constant name="ecal_front" value="13.3/2*mm" />
+        <constant name="ecal_back" value="16/2*mm" />
+        <constant name="ecal_z" value="160/2*mm" />
+        <constant name="ecal_dface" value="139.3*cm"/>
+    </define>
+    <materials>
+        <material name="LeadTungstate">
+            <D value="8.28" unit="g/cm3" />
+            <composite n="1" ref="Pb" />
+            <composite n="1" ref="W" />
+            <composite n="4" ref="O" />
+        </material>
+    </materials>
+    <detectors>
+    
+    <detector id="13" name="Ecal" type="HPSEcal3" insideTrackingVolume="false" readout="EcalHits">
+        <comment>The crystal ECal</comment>
+        <material name="LeadTungstate" />
+        <dimensions x1="ecal_front" y1="ecal_front" x2="ecal_back" y2="ecal_back" z="ecal_z" />          
+            <layout beamgap="20.0*mm" nx="46" ny="5" dface="ecal_dface">
+                <remove ixmin="-10" ixmax="-2" iymin="-1" iymax="1" />
+                <top dx="ecal_dface*tan(beam_angle)" dy="0." dz="0."/>
+                <bottom dx="ecal_dface*tan(beam_angle)" dy="0." dz="0."/>
+            </layout>
+        </detector>
+<!--     
+        <detector id="2" name="ECAL" type="HPSEcal3" insideTrackingVolume="false" readout="ECAL_HITS">
+            <material name="LeadTungstate" />
+            <dimensions x1="ecal_front" y1="ecal_front" x2="ecal_back" y2="ecal_back" z="ecal_z" />
+            <layout beamgapBottom="40.0*mm" beamgapTop="20.0*mm" nx="46" ny="5" dface="120.0*cm">
+                <remove ixmin="2" ixmax="7" iymin="-1" iymax="1" />
+            </layout>
+        </detector>
+ -->        
+    </detectors>
+    <readouts>
+        <readout name="EcalHits">
+            <segmentation type="GridXYZ" gridSizeX="0.0" gridSizeY="0.0" gridSizeZ="0.0" />
+            <id>system:6,side:-2,layer:4,ix:-8,iy:-6</id>
+        </readout>
+    </readouts>
+</lccdd>

Added: java/trunk/detector-model/src/test/resources/org/lcsim/geometry/subdetector/HPSEcalTest.xml
 =============================================================================
--- java/trunk/detector-model/src/test/resources/org/lcsim/geometry/subdetector/HPSEcalTest.xml	(added)
+++ java/trunk/detector-model/src/test/resources/org/lcsim/geometry/subdetector/HPSEcalTest.xml	Tue Oct  6 11:36:11 2015
@@ -0,0 +1,57 @@
+<lccdd xmlns:compact="http://www.lcsim.org/schemas/compact/1.0"
+       xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
+       xs:noNamespaceSchemaLocation="http://www.lcsim.org/schemas/compact/1.0/compact.xsd">
+
+    <info name="HPSEcalTest">
+        <comment>Test of class org.lcsim.geometry.compact.converter.lcdd.TestBeamCalorimeter</comment>
+    </info>
+    
+    <define>
+                
+        <!-- world -->
+        <constant name="world_side" value="30000" />
+        <constant name="world_x" value="world_side" />
+        <constant name="world_y" value="world_side" />
+        <constant name="world_z" value="world_side" />
+        
+        <!-- tracking region -->
+        <constant name="tracking_region_radius" value="150.0*cm"/>
+        <constant name="tracking_region_zmax" value="200.0*cm"/>
+        
+    </define>
+    
+    <materials>
+    </materials>
+    
+    <detectors>
+        
+        <detector id="1" 
+                  name="HPSEcalTest" 
+                  type="HPSEcal" 
+                  insideTrackingVolume="false">
+            <!-- stuff goes here -->
+        </detector>
+    </detectors>
+
+<!--
+                  readout="CalHits"
+-->
+    
+    <readouts>
+<!--
+        <readout name="CalHits">
+            <segmentation type="GridXYZ" gridSizeX="10.0" gridSizeY="10.0" />
+            <id>system:3,barrel:2,layer:7,x:32:-16,y:-16</id>
+        </readout>
+-->
+    </readouts>
+    <fields>
+<!--
+        <field type="Solenoid" name="GlobalSolenoid"
+               inner_field="5.0"
+               outer_field="-0.6"
+               zmax="1000"
+               outer_radius="(221.0+ 5.0 + 17.5 + 40./2.)*cm"/>
+-->
+    </fields>
+</lccdd>

Added: java/trunk/detector-model/src/test/resources/org/lcsim/geometry/subdetector/HPSMuonCalorimeter2Test.xml
 =============================================================================
--- java/trunk/detector-model/src/test/resources/org/lcsim/geometry/subdetector/HPSMuonCalorimeter2Test.xml	(added)
+++ java/trunk/detector-model/src/test/resources/org/lcsim/geometry/subdetector/HPSMuonCalorimeter2Test.xml	Tue Oct  6 11:36:11 2015
@@ -0,0 +1,57 @@
+<lccdd xmlns:compact="http://www.lcsim.org/schemas/compact/1.0"
+       xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
+       xs:noNamespaceSchemaLocation="http://www.lcsim.org/schemas/compact/1.0/compact.xsd">
+    
+    <info name="HPSMuonCalorimeter2Test" />
+    
+    <define>   
+        
+        <!-- world volume -->
+        <constant name="world_side" value="10000.0*cm" />
+        <constant name="world_x" value="world_side" />
+        <constant name="world_y" value="world_side" />
+        <constant name="world_z" value="world_side" />
+        
+        <!-- tracking region -->
+        <constant name="tracking_region_radius" value="5.0*cm" />
+        <constant name="tracking_region_min" value="0.0*cm" />
+        <constant name="tracking_region_zmax" value="100.0*cm" />
+        
+    </define>
+    
+    <materials>
+        <material name="Scintillator">
+            <D value="1.032" unit="g/cm3"/>
+            <composite n="9" ref="C"/>
+            <composite n="10" ref="H"/>
+        </material>
+    </materials>
+    
+    <detectors>        
+        <detector id="2" name="MUON" type="HPSMuonCalorimeter2" insideTrackingVolume="false" readout="MUON_HITS">
+            <parameters front_face_to_target="177.0*cm" 
+                        dead_zone_angle="0.015" 
+                        strip_thickness="1.0*cm" 
+                        strip_spacing_z="1.0"
+                        strip_spacing_y="0.1"
+                        strip_spacing_x="0.1" 
+                        strip_spacing_z_outer="1.0*cm" 
+                        center_x="0." />
+             
+             <layer id="1">
+                 <slice material="Iron" sensitive="false" thickness="30.0*cm" />
+                 <slice material="Scintillator" sensitive="true" />
+                 <slice material="Scintillator" sensitive="true" />
+             </layer>
+             
+        </detector>
+    </detectors>
+    
+    <readouts>
+        <readout name="MUON_HITS">
+            <segmentation type="GridXYZ" gridSizeX="5.0" gridSizeY="5.0" gridSizeZ="0.0" />
+            <id>system:6,side:-2,layer:4,slice:3,x:32:-8,y:-6</id>
+        </readout>
+    </readouts>
+            
+</lccdd>

Added: java/trunk/detector-model/src/test/resources/org/lcsim/geometry/subdetector/HPSMuonCalorimeterTest.xml
 =============================================================================
--- java/trunk/detector-model/src/test/resources/org/lcsim/geometry/subdetector/HPSMuonCalorimeterTest.xml	(added)
+++ java/trunk/detector-model/src/test/resources/org/lcsim/geometry/subdetector/HPSMuonCalorimeterTest.xml	Tue Oct  6 11:36:11 2015
@@ -0,0 +1,151 @@
+<lccdd xmlns:compact="http://www.lcsim.org/schemas/compact/1.0"
+       xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
+       xs:noNamespaceSchemaLocation="http://www.lcsim.org/schemas/compact/1.0/compact.xsd">
+    
+    <info name="HPSMuonCalorimeterTest" />
+    
+    <define>   
+        
+        <!-- world volume -->
+        <constant name="world_side" value="10000.0*cm" />
+        <constant name="world_x" value="world_side" />
+        <constant name="world_y" value="world_side" />
+        <constant name="world_z" value="world_side" />
+        
+        <!-- tracking region -->
+        <constant name="tracking_region_radius" value="5.0*cm" />
+        <constant name="tracking_region_min" value="0.0*cm" />
+        <constant name="tracking_region_zmax" value="100.0*cm" />
+        
+        <!-- muon parameters -->
+        <!--<constant name="beam_gap" value="22.785 + 1.0" />-->
+        <constant name="beam_gap" value="33.0" />
+        <constant name="muon_abs_z" value="150.0" />
+        <constant name="muon_sens_z" value="10.0" />
+        <constant name="muon_strip_gap" value="1.0" />
+                
+        <!-- muon layer 1 -->                
+        
+        <constant name="muon_abs1_x" value="1182.0" />
+        <constant name="muon_abs1_y" value="131.4" />
+        <constant name="muon_abs1_z" value="300.0" />        
+        <constant name="muon_abs1_pz" value="1920.0" />
+        <constant name="muon_abs1_py" value="beam_gap + muon_abs1_y / 2" />
+
+        <constant name="muon_sens1_x" value="muon_abs1_x" />
+        <constant name="muon_sens1_y" value="muon_abs1_y" />
+        <constant name="muon_sens1_pz" value="muon_abs1_pz + muon_abs1_z / 2 + muon_strip_gap + muon_sens_z / 2" />
+        <constant name="muon_sens1_py" value="beam_gap + muon_sens1_y / 2" />
+        
+        <constant name="muon_sens2_x" value="muon_sens1_x" />
+        <constant name="muon_sens2_y" value="muon_sens1_y" />
+        <constant name="muon_sens2_pz" value="muon_sens1_pz + muon_sens_z + muon_strip_gap" />
+        <constant name="muon_sens2_py" value="beam_gap + muon_sens2_y / 2" />
+        
+        <!-- muon layer 2 -->
+        
+        <constant name="muon_abs2_x" value="1276.0" />
+        <constant name="muon_abs2_y" value="130.72" />
+        <constant name="muon_abs2_pz" value="2168.0" />
+        <constant name="muon_abs2_py" value="beam_gap + muon_abs2_y / 2" />
+        
+        <constant name="muon_sens3_x" value="muon_abs2_x" />
+        <constant name="muon_sens3_y" value="muon_abs2_y" />
+        <constant name="muon_sens3_pz" value="muon_abs2_pz + muon_abs_z / 2 + muon_strip_gap + muon_sens_z / 2" />
+        <constant name="muon_sens3_py" value="beam_gap + muon_sens3_y / 2" />
+        
+        <constant name="muon_sens4_x" value="muon_sens3_x" />
+        <constant name="muon_sens4_y" value="muon_sens3_y" />
+        <constant name="muon_sens4_pz" value="muon_sens3_pz + muon_sens_z + muon_strip_gap" />
+        <constant name="muon_sens4_py" value="beam_gap + muon_sens4_y / 2" />
+        
+        <!-- muon layer 3 -->
+        
+        <constant name="muon_abs3_x" value="1406.0" />
+        <constant name="muon_abs3_y" value="140.7" />
+        <constant name="muon_abs3_pz" value="2341.0" />
+        <constant name="muon_abs3_py" value="beam_gap + muon_abs3_y / 2" />
+        
+        <constant name="muon_sens5_x" value="muon_abs3_x" />
+        <constant name="muon_sens5_y" value="muon_abs3_y" />
+        <constant name="muon_sens5_pz" value="muon_abs3_pz + muon_abs_z / 2 + muon_strip_gap + muon_sens_z / 2" />
+        <constant name="muon_sens5_py" value="beam_gap + muon_sens5_y / 2" />
+        
+        <constant name="muon_sens6_x" value="muon_sens5_x" />
+        <constant name="muon_sens6_y" value="muon_sens5_y" />
+        <constant name="muon_sens6_pz" value="muon_sens5_pz + muon_sens_z + muon_strip_gap" />    
+        <constant name="muon_sens6_py" value="beam_gap + muon_sens6_y / 2" />    
+
+        <!-- muon layer 4 -->
+               
+        <constant name="muon_abs4_x" value="1546.0" />
+        <constant name="muon_abs4_y" value="150.0" />
+        <constant name="muon_abs4_pz" value="2514.0" />
+        <constant name="muon_abs4_py" value="beam_gap + muon_abs4_y / 2" />
+        
+        <constant name="muon_sens7_x" value="muon_abs4_x" />
+        <constant name="muon_sens7_y" value="muon_abs4_y" />
+        <constant name="muon_sens7_pz" value="muon_abs4_pz + muon_abs_z / 2 + muon_strip_gap + muon_sens_z / 2" />
+        <constant name="muon_sens7_py" value="beam_gap + muon_sens7_y / 2" />
+        
+        <constant name="muon_sens8_x" value="muon_sens7_x" />
+        <constant name="muon_sens8_y" value="muon_sens7_y" />
+        <constant name="muon_sens8_pz" value="muon_sens7_pz + muon_sens_z + muon_strip_gap" />
+        <constant name="muon_sens8_py" value="beam_gap + muon_sens8_y / 2" />
+                     
+    </define>
+    
+    <materials>
+    </materials>
+    
+    <detectors>        
+        <detector id="2" name="MUON" type="HPSMuonCalorimeter" insideTrackingVolume="false" readout="MUON_HITS">
+            <layer id="1">
+                <!-- nstrips="26" strip_orientation="x" strip_gap="0.01" -->
+                <box x="muon_abs1_x"  y="muon_abs1_y"  z="muon_abs1_z" py="muon_abs1_py"   pz="muon_abs1_pz"  material="Steel235" />
+                <box x="muon_abs1_x"  y="muon_abs1_y"  z="muon_abs1_z" py="-muon_abs1_py"  pz="muon_abs1_pz"  material="Steel235" />
+                <box x="muon_sens1_x" y="muon_sens1_y" z="muon_sens_z" py="muon_sens1_py"  pz="muon_sens1_pz" material="Polystyrene" sensitive="true" />
+                <box x="muon_sens1_x" y="muon_sens1_y" z="muon_sens_z" py="-muon_sens1_py" pz="muon_sens1_pz" material="Polystyrene" sensitive="true" />
+                <box x="muon_sens2_x" y="muon_sens2_y" z="muon_sens_z" py="muon_sens2_py"  pz="muon_sens2_pz" material="Polystyrene" sensitive="true" />
+                <box x="muon_sens2_x" y="muon_sens2_y" z="muon_sens_z" py="-muon_sens2_py" pz="muon_sens2_pz" material="Polystyrene" sensitive="true" />
+            </layer>
+            <layer id="2">
+                <box x="muon_abs2_x"  y="muon_abs2_y"  z="muon_abs_z"  py="muon_abs2_py"   pz="muon_abs2_pz"  material="Steel235" />
+                <box x="muon_abs2_x"  y="muon_abs2_y"  z="muon_abs_z"  py="-muon_abs2_py"  pz="muon_abs2_pz"  material="Steel235" />
+                <box x="muon_sens3_x" y="muon_sens3_y" z="muon_sens_z" py="muon_sens3_py"  pz="muon_sens3_pz" material="Polystyrene" sensitive="true" />
+                <box x="muon_sens3_x" y="muon_sens3_y" z="muon_sens_z" py="-muon_sens3_py" pz="muon_sens3_pz" material="Polystyrene" sensitive="true" />
+                <box x="muon_sens4_x" y="muon_sens4_y" z="muon_sens_z" py="muon_sens4_py"  pz="muon_sens4_pz" material="Polystyrene" sensitive="true" />                 
+                <box x="muon_sens4_x" y="muon_sens4_y" z="muon_sens_z" py="-muon_sens4_py" pz="muon_sens4_pz" material="Polystyrene" sensitive="true" />
+            </layer>
+            <layer id="3">
+                <box x="muon_abs3_x"  y="muon_abs3_y"  z="muon_abs_z"  py="muon_abs3_py"   pz="muon_abs3_pz"  material="Steel235" />
+                <box x="muon_abs3_x"  y="muon_abs3_y"  z="muon_abs_z"  py="-muon_abs3_py"  pz="muon_abs3_pz"  material="Steel235" />
+                <box x="muon_sens5_x" y="muon_sens5_y" z="muon_sens_z" py="muon_sens5_py"  pz="muon_sens5_pz" material="Polystyrene" sensitive="true" />
+                <box x="muon_sens5_x" y="muon_sens5_y" z="muon_sens_z" py="-muon_sens5_py" pz="muon_sens5_pz" material="Polystyrene" sensitive="true" />
+                <box x="muon_sens6_x" y="muon_sens6_y" z="muon_sens_z" py="muon_sens6_py"  pz="muon_sens6_pz" material="Polystyrene" sensitive="true" />                
+                <box x="muon_sens6_x" y="muon_sens6_y" z="muon_sens_z" py="-muon_sens6_py" pz="muon_sens6_pz" material="Polystyrene" sensitive="true" />
+            </layer>
+            <layer id="4">
+                <box x="muon_abs4_x"  y="muon_abs4_y"  z="muon_abs_z"  py="muon_abs4_py"   pz="muon_abs4_pz"  material="Steel235" />
+                <box x="muon_abs4_x"  y="muon_abs4_y"  z="muon_abs_z"  py="-muon_abs4_py"  pz="muon_abs4_pz"  material="Steel235" />
+                <box x="muon_sens7_x" y="muon_sens7_y" z="muon_sens_z" py="muon_sens7_py"  pz="muon_sens7_pz" material="Polystyrene" sensitive="true" />
+                <box x="muon_sens7_x" y="muon_sens7_y" z="muon_sens_z" py="-muon_sens7_py" pz="muon_sens7_pz" material="Polystyrene" sensitive="true" /> 
+                <box x="muon_sens8_x" y="muon_sens8_y" z="muon_sens_z" py="muon_sens8_py"  pz="muon_sens8_pz" material="Polystyrene" sensitive="true" />
+                <box x="muon_sens8_x" y="muon_sens8_y" z="muon_sens_z" py="-muon_sens8_py" pz="muon_sens8_pz" material="Polystyrene" sensitive="true" />
+            </layer>
+        </detector>
+    </detectors>
+    
+    <readouts>
+        <readout name="MUON_HITS">
+            <segmentation type="GridXYZ" gridSizeX="5.0" gridSizeY="5.0" gridSizeZ="0.0" />
+            <!--<id>system:6,side:-2,layer:4,slice:3,ix:32:-8,iy:-6</id>-->
+            <id>system:6,side:-2,layer:4,slice:3,x:32:-8,y:-6</id>
+        </readout>
+    </readouts>
+    
+    <includes>
+        <gdmlFile file="./testResources/org/lcsim/geometry/subdetector/hps_2014_muon_vacuum_flange.gdml" />
+    </includes>
+        
+</lccdd>

Added: java/trunk/detector-model/src/test/resources/org/lcsim/geometry/subdetector/HPSTest.xml
 =============================================================================
--- java/trunk/detector-model/src/test/resources/org/lcsim/geometry/subdetector/HPSTest.xml	(added)
+++ java/trunk/detector-model/src/test/resources/org/lcsim/geometry/subdetector/HPSTest.xml	Tue Oct  6 11:36:11 2015
@@ -0,0 +1,254 @@
+<lccdd xmlns:compact="http://www.lcsim.org/schemas/compact/1.0"
+       xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
+       xs:noNamespaceSchemaLocation="http://www.lcsim.org/schemas/compact/1.0/compact.xsd">
+
+    <info name="HPSTest">
+        <comment>HPS test setup</comment>
+    </info>
+
+    <define>   
+
+<!--        <constant name="pi" value="3.14159" />-->
+        <constant name="SA"  value="0.10" />
+        <constant name="SA2"  value="0.05" />
+
+        <!-- world -->
+        <constant name="world_side" value="10000.0*cm" />
+        <constant name="world_x" value="world_side" />
+        <constant name="world_y" value="world_side" />
+        <constant name="world_z" value="world_side" />
+    
+        <!-- tracking region -->
+        <constant name="tracking_region_radius" value="200.0*cm"/>
+        <constant name="tracking_region_min" value="5.0*cm"/>
+        <constant name="tracking_region_zmax" value="100.0*cm"/>
+    
+        <constant name="xCent1" value="10*cm"/>
+        <constant name="xCent2" value="20*cm"/>
+        <constant name="xCent3" value="30*cm"/>
+        <constant name="xCent4" value="50*cm"/>
+        <constant name="xCent5" value="70*cm"/>
+        
+        <constant name="zgap1" value="0.30*cm" />
+        <constant name="zgap2" value="0.60*cm" />
+        <constant name="zgap3" value="0.90*cm" />
+        <constant name="zgap4" value="1.5*cm" />
+        <constant name="zgap5" value="2.1*cm" />
+        
+        <constant name="zPlaneDist" value="1.0*cm"/>
+        
+        <constant name="modLength" value="10.0*cm"/>
+        <constant name="modWidth" value="4.0*cm"/>
+
+        <constant name="ecal_front" value="13.3/2*mm" />
+        <constant name="ecal_back" value="16/2*mm" />
+        <!--<constant name="ecal_back" value="30/2*mm"/>-->
+        <constant name="ecal_z" value="160/2*mm" />
+        
+
+        <!-- tracking region -->
+<!--
+        <constant name="tracking_region_radius" value="200.0*cm"/>
+        <constant name="tracking_region_min" value="5.0*cm"/>
+        <constant name="tracking_region_zmax" value="100.0*cm"/>	
+
+        <constant name="xCent1" value="10*cm" />
+        <constant name="xCent2" value="20*cm" />
+
+        <constant name="xCent3" value="30*cm" />
+        <constant name="xCent4" value="50*cm" />
+        <constant name="xCent5" value="70*cm" />
+        <constant name="xCent6" value="90*cm" />
+
+        <constant name="zgap1" value="0.30*cm" />
+        <constant name="zgap2" value="0.60*cm" />
+
+        <constant name="zgap3" value="0.498*cm" />
+        <constant name="zgap4" value="0.830*cm" />
+        <constant name="zgap5" value="1.162*cm" />
+        <constant name="zgap6" value="1.494*cm" />
+
+        <constant name="zPlaneDist" value="1.0*cm" />
+
+        <constant name="modLength" value="10.0*cm" />
+        <constant name="modWidth" value="4.0*cm" />
+-->
+
+    </define>
+
+    <materials>
+        <material name="LeadTungstate">
+            <D value="8.28" unit="g/cm3"/>
+            <composite n="1" ref="Pb"/>
+            <composite n="1" ref="W"/>
+            <composite n="4" ref="O"/>
+        </material>
+    </materials>
+
+    <detectors>
+
+        <detector id="13" 
+                  name="HPSEcalTest" 
+                  type="HPSEcal" 
+                  insideTrackingVolume="true" 
+                  readout="EcalHits"
+                  >
+            <material name="LeadTungstate" />
+            <dimensions x1="ecal_front" y1="ecal_front" x2="ecal_back"
+                        y2="ecal_back" z="ecal_z" />
+            <layout beamgap="20.0*mm" nx="46" ny="5" dface="120.0*cm" />
+        </detector>
+
+        <detector id="1" name="Tracker" type="SiTrackerFixedTarget2" readout="TrackerHits" combineHits="true" reflect="true"  flipSA="true">
+            <module name="Module1">
+                <trd x1="modWidth/2" x2="modWidth/2" z="modLength/2" />
+                <module_component   thickness="0.032*cm" material = "Silicon" sensitive="true"/><!-- X0=0.32% -->
+                <module_component   thickness="0.02*cm" material = "Carbon" sensitive="false"/> <!-- X0=0.1% -->
+            </module> 
+            
+            <layer id="1">
+                <quadrant yStart="0" ny="1" yStep="modLength" zStart="(zgap1+modWidth)/2" nz="1" zStep="modWidth" phi0="0.0" x="xCent1" dx="0.0*cm"  module="Module1"/>
+            </layer>
+            <layer id="2">
+                <quadrant yStart="0" ny="1" yStep="modLength" zStart="(zgap1+modWidth)/2" nz="1" zStep="modWidth" phi0="SA" x="xCent1+zPlaneDist" dx="0.0*cm"  module="Module1"/>
+            </layer>
+
+            <layer id="3">
+                <quadrant yStart="0" ny="1" yStep="modLength" zStart="(zgap2+modWidth)/2" nz="1" zStep="modWidth" phi0="0.0" x="xCent2" dx="0.0*cm"  module="Module1"/>
+            </layer>
+            <layer id="4">
+                <quadrant yStart="0" ny="1" yStep="modLength" zStart="(zgap2+modWidth)/2" nz="1" zStep="modWidth" phi0="SA" x="xCent2+zPlaneDist" dx="0.0*cm"  module="Module1"/>
+            </layer>
+            
+            
+            <layer id="5">
+                <quadrant yStart="0" ny="1" yStep="modLength" zStart="(zgap3+modWidth)/2" nz="1" zStep="modWidth" phi0="0.0" x="xCent3" dx="0.0*cm"  module="Module1"/>
+            </layer>
+            <layer id="6">
+                <quadrant yStart="0" ny="1" yStep="modLength" zStart="(zgap3+modWidth)/2" nz="1" zStep="modWidth" phi0="SA" x="xCent3+zPlaneDist" dx="0.0*cm"  module="Module1"/>
+            </layer>
+
+            <layer id="7">
+                <quadrant yStart="0" ny="1" yStep="modLength" zStart="(zgap4+modWidth)/2" nz="1" zStep="modWidth" phi0="0.0" x="xCent4" dx="0.0*cm"  module="Module1"/>
+            </layer>
+            <layer id="8">
+                <quadrant yStart="0" ny="1" yStep="modLength" zStart="(zgap4+modWidth)/2" nz="1" zStep="modWidth" phi0="SA2" x="xCent4+zPlaneDist" dx="0.0*cm"  module="Module1"/>
+            </layer>
+
+            <layer id="9">
+                <quadrant yStart="0" ny="1" yStep="modLength" zStart="(zgap5+modWidth)/2" nz="1" zStep="modWidth" phi0="0.0" x="xCent5" dx="0.0*cm"  module="Module1"/>
+            </layer>
+            <layer id="10">
+                <quadrant yStart="0" ny="1" yStep="modLength" zStart="(zgap5+modWidth)/2" nz="1" zStep="modWidth" phi0="SA2" x="xCent5+zPlaneDist" dx="0.0*cm"  module="Module1"/>
+            </layer>
+
+
+        </detector>     
+
+<!--                
+Proposal detector
+        <detector id="1" name="Tracker" type="SiTrackerFixedTarget2"
+                  readout="TrackerHits" combineHits="true" reflect="true">
+            <module name="Module1">
+                <trd x1="modWidth/2" x2="modWidth/2" z="modLength/2" />
+                <module_component thickness="0.032*cm" material="Silicon"
+                                  sensitive="true" />
+                <module_component thickness="0.02*cm" material="Carbon"
+                                  sensitive="false" />
+            </module>
+            
+            <layer id="1">
+                <quadrant yStart="0" ny="1" yStep="modLength" zStart="(zgap1+modWidth)/2"
+                          nz="1" zStep="modWidth" phi0="0.0" x="xCent1" dx="0.0*cm" module="Module1" />
+            </layer>
+            <layer id="2">
+                <quadrant yStart="-1*modWidth/2" ny="2" yStep="modWidth"
+                          zStart="(zgap1+modLength)/2" nz="1" zStep="modLength" phi0="pi/2.0"
+                          x="xCent1+zPlaneDist" dx="0.0*cm" module="Module1" />
+            </layer>
+            
+            
+            <layer id="3">
+                <quadrant yStart="0" ny="1" yStep="modLength" zStart="(zgap2+modWidth)/2"
+                          nz="2" zStep="modWidth" phi0="0.0" x="xCent2" dx="0.0*cm" module="Module1" />
+            </layer>
+            <layer id="4">
+                <quadrant yStart="-1*modWidth" ny="3" yStep="modWidth"
+                          zStart="(zgap2+modLength)/2" nz="1" zStep="modLength" phi0="pi/2.0"
+                          x="xCent2+zPlaneDist" dx="0.0*cm" module="Module1" />
+            </layer>
+            
+            <layer id="5">
+                <quadrant yStart="0" ny="1" yStep="modLength" zStart="(zgap3+modWidth)/2"
+                          nz="2" zStep="modWidth" phi0="0.0" x="xCent3" dx="0.0*cm" module="Module1" />
+            </layer>
+            <layer id="6">
+                <quadrant yStart="-1*modWidth" ny="3" yStep="modWidth"
+                          zStart="(zgap3+modLength)/2" nz="1" zStep="modLength" phi0="pi/2.0"
+                          x="xCent3+zPlaneDist" dx="0.0*cm" module="Module1" />
+            </layer>
+            
+            
+            
+            <layer id="7">
+                <quadrant yStart="-2*modWidth" ny="5" yStep="modWidth"
+                          zStart="(zgap4+modLength)/2" nz="1" zStep="modLength" phi0="pi/2.0-SA"
+                          x="xCent4" dx="0.0*cm" module="Module1" />
+            </layer>
+            <layer id="8">
+                <quadrant yStart="-2*modWidth" ny="5" yStep="modWidth"
+                          zStart="(zgap4+modLength)/2" nz="1" zStep="modLength" phi0="pi/2.0"
+                          x="xCent4+zPlaneDist" dx="0.0*cm" module="Module1" />
+            </layer>
+            
+            <layer id="9">
+                <quadrant yStart="-3*modWidth" ny="7" yStep="modWidth"
+                          zStart="(zgap5+modLength)/2" nz="1" zStep="modLength" phi0="pi/2.0+SA"
+                          x="xCent5" dx="0.0*cm" module="Module1" />
+            </layer>
+            <layer id="10">
+                <quadrant yStart="-3*modWidth" ny="7" yStep="modWidth"
+                          zStart="(zgap5+modLength)/2" nz="1" zStep="modLength" phi0="pi/2.0"
+                          x="xCent5+zPlaneDist" dx="0.0*cm" module="Module1" />
+            </layer>
+
+            <layer id="11">
+                <quadrant yStart="-7*modWidth/2" ny="8" yStep="modWidth"
+                          zStart="(zgap6+modLength)/2" nz="1" zStep="modLength" phi0="pi/2.0-SA"
+                          x="xCent6" dx="0.0*cm" module="Module1" />
+            </layer>
+            <layer id="12">
+                <quadrant yStart="-7*modWidth/2" ny="8" yStep="modWidth"
+                          zStart="(zgap6+modLength)/2" nz="1" zStep="modLength" phi0="pi/2.0"
+                          x="xCent6+zPlaneDist" dx="0.0*cm" module="Module1" />
+            </layer>
+
+        </detector>
+-->
+
+    </detectors>
+
+    <readouts>
+        <readout name="EcalHits">
+            <segmentation type="GridXYZ" gridSizeX="0.0" gridSizeY="0.0" gridSizeZ="0.0" />
+            <id>system:6,side:-2,layer:4,ix:9,iy:9</id>
+        </readout>
+        <readout name="TargetHits">
+            <id>system:6,barrel:3,layer:4,wedge:4,module:12,sensor:1,side:32:-2,strip:12</id>
+        </readout>
+        <readout name="TrackerHits">
+            <id>system:6,barrel:3,layer:4,wedge:4,module:12,sensor:1,side:32:-2,strip:12</id>
+        </readout>
+    </readouts>
+    <fields>
+        <field type="Solenoid" 
+               name="GlobalSolenoid" 
+               inner_field="0.0"
+               outer_field="1.0" 
+               zmax="17.78*cm"
+               inner_radius="10.0*cm"
+               outer_radius="101.44*cm"
+               />
+    </fields>
+
+</lccdd>

Added: java/trunk/detector-model/src/test/resources/org/lcsim/geometry/subdetector/HPSTracker2Test.xml
 =============================================================================
--- java/trunk/detector-model/src/test/resources/org/lcsim/geometry/subdetector/HPSTracker2Test.xml	(added)
+++ java/trunk/detector-model/src/test/resources/org/lcsim/geometry/subdetector/HPSTracker2Test.xml	Tue Oct  6 11:36:11 2015
@@ -0,0 +1,145 @@
+<lccdd xmlns:compact="http://www.lcsim.org/schemas/compact/1.0"
+       xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
+       xs:noNamespaceSchemaLocation="http://www.lcsim.org/schemas/compact/1.0/compact.xsd">
+    
+    <info name="HPS-Test-JLAB-v4pt0">
+        <comment>HPS Test Proposal detector</comment>
+    </info>
+
+    <define>
+        
+        <!-- world -->
+        <constant name="world_side" value="500.0*cm" />
+        <constant name="world_x" value="world_side" />
+        <constant name="world_y" value="world_side" />
+        <constant name="world_z" value="world_side" />
+
+        <!-- tracking region -->
+        <constant name="tracking_region_radius" value="200.0*cm"/>
+        <constant name="tracking_region_min" value="5.0*cm"/>
+        <constant name="tracking_region_zmax" value="100.0*cm"/>
+
+        <!-- SVT module dimensions -->
+        <constant name="moduleLength" value="100.0"/>
+        <constant name="moduleWidth" value="40.34"/>
+
+        <!-- SVT sensor dimensions -->
+        <constant name="sensorLength" value="98.33"/>
+        <!-- Sensor width is slightly under the real value of 38.34 mm so that sisim doesn't break. -->
+        <constant name="sensorWidth" value="38.3399"/>
+
+        <!-- module tilt for stereo angle -->
+        <constant name="SA"  value="0.10" />
+        <constant name="SA2"  value="0.05" />
+        
+        <constant name="pi"  value="3.14159" />
+        <!-- module z placement -->
+        <constant name="zCent1" value="10*cm"/>
+        <constant name="zCent2" value="20*cm"/>
+        <constant name="zCent3" value="30*cm"/>
+        <constant name="zCent4" value="50*cm"/>
+        <constant name="zCent5" value="70*cm"/>
+
+        <!-- module z gaps -->
+        <constant name="ygap1" value="0.30*cm" />
+        <constant name="ygap2" value="0.60*cm" />
+        <constant name="ygap3" value="0.90*cm" />
+        <constant name="ygap4" value="1.5*cm" />
+        <constant name="ygap5" value="2.1*cm" />
+
+        <!-- module z plane distance -->
+        <constant name="zPlaneDist" value="1.0*cm"/>
+
+        <!-- ecal -->
+        <constant name="ecal_front" value="13.3/2*mm" />
+        <constant name="ecal_back" value="16/2*mm" />
+        <constant name="ecal_z" value="160/2*mm" />
+
+    </define>
+    
+    <materials>
+
+        <!-- Set tracking material to vacuum. -->
+        <material name="TrackingMaterial">
+            <D type="density" unit="g/cm3" value="0.0000000000000001"/>
+            <fraction n="1.0" ref="Air" />
+        </material>
+        
+    </materials>
+      
+    <detectors>
+       
+        <detector id="1" name="Tracker" type="HPSTracker2" readout="TrackerHits" combineHits="true">
+            <comment> The Silicon Vertex Tracker.</comment>            
+            <module name="TestRunModule">
+                <box x="moduleLength" y="moduleWidth" />
+                <module_component thickness="0.032*cm" material = "Silicon" sensitive="true">
+                    <dimensions x="sensorLength" y="sensorWidth" /> 
+                </module_component>
+                <module_component thickness="0.02*cm" material = "Carbon" sensitive="false"/> 
+            </module>            
+            <layer id="1">
+                <module_placement name="TestRunModule" id="0" x="0.0" y="21.67" z="100.0" rx="0.0" ry="0.0" rz="-1.5707963267948966"/>
+                <module_placement name="TestRunModule" id="1" x="0.0" y="-21.67" z="100.0" rx="0.0" ry="0.0" rz="-1.6707963267948966"/>
+            </layer>
+            <layer id="2">
+                <module_placement name="TestRunModule" id="0" x="0.0" y="21.67" z="110.0" rx="0.0" ry="3.14159" rz="-4.812386326794897"/>
+                <module_placement name="TestRunModule" id="1" x="0.0" y="-21.67" z="110.0" rx="0.0" ry="3.14159" rz="-4.712386326794896"/>
+            </layer>
+            <layer id="3">
+                <module_placement name="TestRunModule" id="0" x="0.0" y="23.17" z="200.0" rx="0.0" ry="0.0" rz="-1.5707963267948966"/>
+                <module_placement name="TestRunModule" id="1" x="0.0" y="-23.17" z="200.0" rx="0.0" ry="0.0" rz="-1.6707963267948966"/>
+            </layer>
+            <layer id="4">
+                <module_placement name="TestRunModule" id="0" x="0.0" y="23.17" z="210.0" rx="0.0" ry="3.14159" rz="-4.812386326794897"/>
+                <module_placement name="TestRunModule" id="1" x="0.0" y="-23.17" z="210.0" rx="0.0" ry="3.14159" rz="-4.712386326794896"/>
+            </layer>
+            <layer id="5">
+                <module_placement name="TestRunModule" id="0" x="0.0" y="24.67" z="300.0" rx="0.0" ry="0.0" rz="-1.5707963267948966"/>
+                <module_placement name="TestRunModule" id="1" x="0.0" y="-24.67" z="300.0" rx="0.0" ry="0.0" rz="-1.6707963267948966"/>
+            </layer>
+            <layer id="6">
+                <module_placement name="TestRunModule" id="0" x="0.0" y="24.67" z="310.0" rx="0.0" ry="3.14159" rz="-4.812386326794897"/>
+                <module_placement name="TestRunModule" id="1" x="0.0" y="-24.67" z="310.0" rx="0.0" ry="3.14159" rz="-4.712386326794896"/>
+            </layer>
+            <layer id="7">
+                <module_placement name="TestRunModule" id="0" x="0.0" y="27.67" z="500.0" rx="0.0" ry="0.0" rz="-1.5707963267948966"/>
+                <module_placement name="TestRunModule" id="1" x="0.0" y="-27.67" z="500.0" rx="0.0" ry="0.0" rz="-1.6207963267948966"/>
+            </layer>
+            <layer id="8">
+                <module_placement name="TestRunModule" id="0" x="0.0" y="27.67" z="510.0" rx="0.0" ry="3.14159" rz="-4.762386326794896"/>
+                <module_placement name="TestRunModule" id="1" x="0.0" y="-27.67" z="510.0" rx="0.0" ry="3.14159" rz="-4.712386326794896"/>
+            </layer>
+            <layer id="9">
+                <module_placement name="TestRunModule" id="0" x="0.0" y="30.67" z="700.0" rx="0.0" ry="0.0" rz="-1.5707963267948966"/>
+                <module_placement name="TestRunModule" id="1" x="0.0" y="-30.67" z="700.0" rx="0.0" ry="0.0" rz="-1.6207963267948966"/>
+            </layer>
+            <layer id="10">
+                <module_placement name="TestRunModule" id="0" x="0.0" y="30.67" z="710.0" rx="0.0" ry="3.14159" rz="-4.762386326794896"/>
+                <module_placement name="TestRunModule" id="1" x="0.0" y="-30.67" z="710.0" rx="0.0" ry="3.14159" rz="-4.712386326794896"/>
+            </layer>                                      
+        </detector>     
+    </detectors>
+    
+    <readouts>   
+        <readout name="TrackerHits">
+            <id>system:6,barrel:3,layer:4,module:12,sensor:1,side:32:-2,strip:12</id> 
+        </readout>
+    </readouts>
+
+    <fields>
+        <field type="BoxDipole" 
+               name="AnalyzingDipole"
+               x="0*cm"
+               y="0*cm"
+               z="45.22*cm"
+               dx="22.86*cm"
+               dy="7.62*cm"
+               dz="46.22*cm"
+               bx="0.0"
+               by="-0.5"
+               bz="0.0">     
+        </field>
+    </fields>
+
+</lccdd>

Added: java/trunk/detector-model/src/test/resources/org/lcsim/geometry/subdetector/HpsTestRunSiSensorConverterTest.xml
 =============================================================================
--- java/trunk/detector-model/src/test/resources/org/lcsim/geometry/subdetector/HpsTestRunSiSensorConverterTest.xml	(added)
+++ java/trunk/detector-model/src/test/resources/org/lcsim/geometry/subdetector/HpsTestRunSiSensorConverterTest.xml	Tue Oct  6 11:36:11 2015
@@ -0,0 +1,145 @@
+<lccdd xmlns:compact="http://www.lcsim.org/schemas/compact/1.0"
+       xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
+       xs:noNamespaceSchemaLocation="http://www.lcsim.org/schemas/compact/1.0/compact.xsd">
+    
+    <info name="HPS-Test-JLAB-v4pt0">
+        <comment>HPS Test Proposal detector</comment>
+    </info>
+
+    <define>
+        
+        <!-- world -->
+        <constant name="world_side" value="500.0*cm" />
+        <constant name="world_x" value="world_side" />
+        <constant name="world_y" value="world_side" />
+        <constant name="world_z" value="world_side" />
+
+        <!-- tracking region -->
+        <constant name="tracking_region_radius" value="200.0*cm"/>
+        <constant name="tracking_region_min" value="5.0*cm"/>
+        <constant name="tracking_region_zmax" value="100.0*cm"/>
+
+        <!-- SVT module dimensions -->
+        <constant name="moduleLength" value="100.0"/>
+        <constant name="moduleWidth" value="40.34"/>
+
+        <!-- SVT sensor dimensions -->
+        <constant name="sensorLength" value="98.33"/>
+        <!-- Sensor width is slightly under the real value of 38.34 mm so that sisim doesn't break. -->
+        <constant name="sensorWidth" value="38.3399"/>
+
+        <!-- module tilt for stereo angle -->
+        <constant name="SA"  value="0.10" />
+        <constant name="SA2"  value="0.05" />
+        
+        <constant name="pi"  value="3.14159" />
+        <!-- module z placement -->
+        <constant name="zCent1" value="10*cm"/>
+        <constant name="zCent2" value="20*cm"/>
+        <constant name="zCent3" value="30*cm"/>
+        <constant name="zCent4" value="50*cm"/>
+        <constant name="zCent5" value="70*cm"/>
+
+        <!-- module z gaps -->
+        <constant name="ygap1" value="0.30*cm" />
+        <constant name="ygap2" value="0.60*cm" />
+        <constant name="ygap3" value="0.90*cm" />
+        <constant name="ygap4" value="1.5*cm" />
+        <constant name="ygap5" value="2.1*cm" />
+
+        <!-- module z plane distance -->
+        <constant name="zPlaneDist" value="1.0*cm"/>
+
+        <!-- ecal -->
+        <constant name="ecal_front" value="13.3/2*mm" />
+        <constant name="ecal_back" value="16/2*mm" />
+        <constant name="ecal_z" value="160/2*mm" />
+
+    </define>
+    
+    <materials>
+
+        <!-- Set tracking material to vacuum. -->
+        <material name="TrackingMaterial">
+            <D type="density" unit="g/cm3" value="0.0000000000000001"/>
+            <fraction n="1.0" ref="Air" />
+        </material>
+        
+    </materials>
+      
+    <detectors>
+       
+        <detector id="1" name="Tracker" type="HPSTracker2" readout="TrackerHits" combineHits="true">
+            <comment> The Silicon Vertex Tracker.</comment>            
+            <module name="TestRunModule" type="HpsTestRunSiSensor">
+                <box x="moduleLength" y="moduleWidth" />
+                <module_component thickness="0.032*cm" material = "Silicon" sensitive="true">
+                    <dimensions x="sensorLength" y="sensorWidth" /> 
+                </module_component>
+                <module_component thickness="0.02*cm" material = "Carbon" sensitive="false"/> 
+            </module>            
+            <layer id="1">
+                <module_placement name="TestRunModule" id="0" x="0.0" y="21.67" z="100.0" rx="0.0" ry="0.0" rz="-1.5707963267948966"/>
+                <module_placement name="TestRunModule" id="1" x="0.0" y="-21.67" z="100.0" rx="0.0" ry="0.0" rz="-1.6707963267948966"/>
+            </layer>
+            <layer id="2">
+                <module_placement name="TestRunModule" id="0" x="0.0" y="21.67" z="110.0" rx="0.0" ry="3.14159" rz="-4.812386326794897"/>
+                <module_placement name="TestRunModule" id="1" x="0.0" y="-21.67" z="110.0" rx="0.0" ry="3.14159" rz="-4.712386326794896"/>
+            </layer>
+            <layer id="3">
+                <module_placement name="TestRunModule" id="0" x="0.0" y="23.17" z="200.0" rx="0.0" ry="0.0" rz="-1.5707963267948966"/>
+                <module_placement name="TestRunModule" id="1" x="0.0" y="-23.17" z="200.0" rx="0.0" ry="0.0" rz="-1.6707963267948966"/>
+            </layer>
+            <layer id="4">
+                <module_placement name="TestRunModule" id="0" x="0.0" y="23.17" z="210.0" rx="0.0" ry="3.14159" rz="-4.812386326794897"/>
+                <module_placement name="TestRunModule" id="1" x="0.0" y="-23.17" z="210.0" rx="0.0" ry="3.14159" rz="-4.712386326794896"/>
+            </layer>
+            <layer id="5">
+                <module_placement name="TestRunModule" id="0" x="0.0" y="24.67" z="300.0" rx="0.0" ry="0.0" rz="-1.5707963267948966"/>
+                <module_placement name="TestRunModule" id="1" x="0.0" y="-24.67" z="300.0" rx="0.0" ry="0.0" rz="-1.6707963267948966"/>
+            </layer>
+            <layer id="6">
+                <module_placement name="TestRunModule" id="0" x="0.0" y="24.67" z="310.0" rx="0.0" ry="3.14159" rz="-4.812386326794897"/>
+                <module_placement name="TestRunModule" id="1" x="0.0" y="-24.67" z="310.0" rx="0.0" ry="3.14159" rz="-4.712386326794896"/>
+            </layer>
+            <layer id="7">
+                <module_placement name="TestRunModule" id="0" x="0.0" y="27.67" z="500.0" rx="0.0" ry="0.0" rz="-1.5707963267948966"/>
+                <module_placement name="TestRunModule" id="1" x="0.0" y="-27.67" z="500.0" rx="0.0" ry="0.0" rz="-1.6207963267948966"/>
+            </layer>
+            <layer id="8">
+                <module_placement name="TestRunModule" id="0" x="0.0" y="27.67" z="510.0" rx="0.0" ry="3.14159" rz="-4.762386326794896"/>
+                <module_placement name="TestRunModule" id="1" x="0.0" y="-27.67" z="510.0" rx="0.0" ry="3.14159" rz="-4.712386326794896"/>
+            </layer>
+            <layer id="9">
+                <module_placement name="TestRunModule" id="0" x="0.0" y="30.67" z="700.0" rx="0.0" ry="0.0" rz="-1.5707963267948966"/>
+                <module_placement name="TestRunModule" id="1" x="0.0" y="-30.67" z="700.0" rx="0.0" ry="0.0" rz="-1.6207963267948966"/>
+            </layer>
+            <layer id="10">
+                <module_placement name="TestRunModule" id="0" x="0.0" y="30.67" z="710.0" rx="0.0" ry="3.14159" rz="-4.762386326794896"/>
+                <module_placement name="TestRunModule" id="1" x="0.0" y="-30.67" z="710.0" rx="0.0" ry="3.14159" rz="-4.712386326794896"/>
+            </layer>                                      
+        </detector>     
+    </detectors>
+    
+    <readouts>   
+        <readout name="TrackerHits">
+            <id>system:6,barrel:3,layer:4,module:12,sensor:1,side:32:-2,strip:12</id> 
+        </readout>
+    </readouts>
+
+    <fields>
+        <field type="BoxDipole" 
+               name="AnalyzingDipole"
+               x="0*cm"
+               y="0*cm"
+               z="45.22*cm"
+               dx="22.86*cm"
+               dy="7.62*cm"
+               dz="46.22*cm"
+               bx="0.0"
+               by="-0.5"
+               bz="0.0">     
+        </field>
+    </fields>
+
+</lccdd>

Modified: java/trunk/tracking/src/main/java/org/hps/recon/tracking/MaterialManager.java
 =============================================================================
--- java/trunk/tracking/src/main/java/org/hps/recon/tracking/MaterialManager.java	(original)
+++ java/trunk/tracking/src/main/java/org/hps/recon/tracking/MaterialManager.java	Tue Oct  6 11:36:11 2015
@@ -1,30 +1,63 @@
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
 package org.hps.recon.tracking;
 
 import java.util.ArrayList;
 import java.util.List;
 
+import org.lcsim.detector.IDetectorElement;
+import org.lcsim.detector.IPhysicalVolume;
+import org.lcsim.detector.PhysicalVolumeNavigator;
+import org.lcsim.geometry.Subdetector;
+import org.lcsim.geometry.subdetector.HPSTracker;
+import org.lcsim.geometry.subdetector.HPSTracker2;
 import org.lcsim.recon.tracking.seedtracker.MaterialXPlane;
 
 /**
- * 
  * Extension to lcsim MaterialManager to allow more flexibility in track reconstruction
- * 
+ *
  * @author Per Hansson <[log in to unmask]>
  */
 public class MaterialManager extends org.lcsim.recon.tracking.seedtracker.MaterialManager {
 
+    /**
+     * Get the path groups for SiTrackerEndcap2, which has modules placed directly in the tracking volume.
+     */
+    static private class HPSTracker2VolumeGrouper implements SubdetectorVolumeGrouper {
+
+        @Override
+        public List<List<String>> getPathGroups(final Subdetector subdet, final IPhysicalVolume topVol) {
+            // System.out.println(this.getClass().getSimpleName() + ".getPathGroups()");
+            final List<List<String>> pathGroups = new ArrayList<List<String>>();
+            // Layer loop.
+            for (final IDetectorElement layer : subdet.getDetectorElement().getChildren()) {
+                final List<String> modulePaths = new ArrayList<String>();
+
+                // Module loop.
+                for (final IDetectorElement module : layer.getChildren()) {
+                    final String path = "";
+                    PhysicalVolumeNavigator.getLeafPaths(modulePaths, module.getGeometry().getPhysicalVolume(), path);
+                }
+
+                // Add module paths to this layer.
+                pathGroups.add(modulePaths);
+            }
+            return pathGroups;
+        }
+    }
+
+    private final static List<MaterialXPlane> _emptyMaterialXPlaneList = new ArrayList<MaterialXPlane>();
+
     protected boolean _includeMS = true;
-    private final static List<MaterialXPlane> _emptyMaterialXPlaneList = new ArrayList<MaterialXPlane>();
 
     public MaterialManager() {
         super();
+
+        // Add volume groupers for HPS tracker types.
+        final SubdetectorVolumeGrouper endcap2Grouper = new SiTrackerEndap2VolumeGrouper();
+        subdetGroups.put(HPSTracker.class, endcap2Grouper);
+        subdetGroups.put(HPSTracker2.class, new HPSTracker2VolumeGrouper());
     }
 
-    public MaterialManager(boolean includeMS) {
+    public MaterialManager(final boolean includeMS) {
         super();
         this._includeMS = includeMS;
     }
@@ -35,7 +68,7 @@
     }
 
     @Override
-    public void setDebug(boolean debug) {
+    public void setDebug(final boolean debug) {
         super.setDebug(debug);
     }