Print

Print


Author: [log in to unmask]
Date: Wed Jan  7 17:03:40 2015
New Revision: 3479

Log:
Overhaul of BaseCluster class and related changes.

Modified:
    projects/lcsim/trunk/event-model/src/main/java/org/lcsim/event/Cluster.java
    projects/lcsim/trunk/event-model/src/main/java/org/lcsim/event/base/AbstractClusterPropertyCalculator.java
    projects/lcsim/trunk/event-model/src/main/java/org/lcsim/event/base/BaseCluster.java
    projects/lcsim/trunk/event-model/src/main/java/org/lcsim/event/base/ClusterPropertyCalculator.java
    projects/lcsim/trunk/event-model/src/main/java/org/lcsim/event/base/TensorClusterPropertyCalculator.java

Modified: projects/lcsim/trunk/event-model/src/main/java/org/lcsim/event/Cluster.java
 =============================================================================
--- projects/lcsim/trunk/event-model/src/main/java/org/lcsim/event/Cluster.java	(original)
+++ projects/lcsim/trunk/event-model/src/main/java/org/lcsim/event/Cluster.java	Wed Jan  7 17:03:40 2015
@@ -1,10 +1,8 @@
 package org.lcsim.event;
 import java.util.List;
 
-/** The LCIO cluster.
- * 
- * @author gaede
- * @version $Id: Cluster.java,v 1.6 2011/11/09 22:50:28 jeremy Exp $
+/** 
+ * The LCIO cluster interface.
  */
 
 public interface Cluster
@@ -14,6 +12,8 @@
      *  elsewhere, e.g. in the run header. Bits 16-31 are used internally.
      */
     public int getType();
+    
+    public int getParticleId();
 
     /** Energy of the cluster.
      */

Modified: projects/lcsim/trunk/event-model/src/main/java/org/lcsim/event/base/AbstractClusterPropertyCalculator.java
 =============================================================================
--- projects/lcsim/trunk/event-model/src/main/java/org/lcsim/event/base/AbstractClusterPropertyCalculator.java	(original)
+++ projects/lcsim/trunk/event-model/src/main/java/org/lcsim/event/base/AbstractClusterPropertyCalculator.java	Wed Jan  7 17:03:40 2015
@@ -3,6 +3,7 @@
 import java.util.List;
 
 import org.lcsim.event.CalorimeterHit;
+import org.lcsim.event.Cluster;
 
 /**
  * An abstract implementation of {@link ClusterPropertyCalculator}. 
@@ -11,15 +12,30 @@
  */
 public abstract class AbstractClusterPropertyCalculator implements ClusterPropertyCalculator {
 
-    protected double[] position;
-    protected double[] positionError;
+    protected double[] position = new double[3];
+    protected double[] positionError = new double[6];
     protected double iphi;
     protected double itheta;
-    protected double[] directionError;
-    protected double[] shapeParameters;
+    protected double[] directionError = new double[6];
+    protected double[] shapeParameters = new double[6];
+          
+    @Override
+    public void calculateProperties(List<CalorimeterHit> hits) {
+    }
     
     @Override
-    public abstract void calculateProperties(List<CalorimeterHit> hits);
+    public void calculateProperties(Cluster cluster) {
+        calculateProperties(cluster.getCalorimeterHits());
+    }
+    
+    protected void reset() {
+        position = new double[3];
+        positionError = new double[6];
+        iphi = 0;
+        itheta = 0;
+        directionError = new double[6];
+        shapeParameters = new double[6];
+    }
 
     @Override
     public double[] getPosition() {
@@ -42,7 +58,7 @@
     }
 
     @Override
-    public double[] getDirectionError() {
+    public double[] getDirectionError() { 
         return directionError;
     }
 

Modified: projects/lcsim/trunk/event-model/src/main/java/org/lcsim/event/base/BaseCluster.java
 =============================================================================
--- projects/lcsim/trunk/event-model/src/main/java/org/lcsim/event/base/BaseCluster.java	(original)
+++ projects/lcsim/trunk/event-model/src/main/java/org/lcsim/event/base/BaseCluster.java	Wed Jan  7 17:03:40 2015
@@ -7,385 +7,525 @@
 
 import org.lcsim.event.CalorimeterHit;
 import org.lcsim.event.Cluster;
-import org.lcsim.event.MCParticle;
-import org.lcsim.event.SimCalorimeterHit;
-import org.lcsim.geometry.IDDecoder;
-import org.lcsim.geometry.compact.Subdetector;
-
 
 /**
- * Default implementation of Cluster Interface for Simulation
- *
- * @author cassell
+ * <p>
+ * This is a concrete implementation of the {@link org.lcsim.event.Cluster} LCIO interface.
+ * <p>
+ * This version is an overhaul of the previous base class, with the following changes:
+ * <ul>
+ * <li>Added several constructors with argument lists, including one that is fully qualified and another
+ * that takes a list of hits only.</li>
+ * <li>Removed the prior implementation of subdetector energies in favor of a simple set method.
+ * (This part of the API is basically unused anyways.)</li>
+ * <li>Added set methods for all class variables, where they were missing.</li>
+ * <li>Added access to particle ID based on the official API.</li>
+ * <li>Added a few utility methods for adding lists of hits and clusters.</li>
+ * <li>Added a method for setting a hit contribution that is different from the corrected energy.</li>
+ * <li>Simplified the {@link #calculateProperties()} method so it doesn't do a bunch of array copies.</li> 
+ * </ul>
+ * 
+ * @see org.lcsim.event.Cluster
+ * @see org.lcsim.event.CalorimeterHit
+ * @see org.lcsim.event.base.ClusterPropertyCalculator
+ * @see org.lcsim.event.base.TensorClusterPropertyCalculator
+ * 
+ * @author Jeremy McCormick <[log in to unmask]>
+ * @author Norman Graf <[log in to unmask]>
+ * 
  */
-public class BaseCluster implements Cluster
-{
+public class BaseCluster implements Cluster {
+    
     protected List<CalorimeterHit> hits = new ArrayList<CalorimeterHit>();
+    protected List<Double> hitContributions = new ArrayList<Double>();
     protected List<Cluster> clusters = new ArrayList<Cluster>();
-    protected List<Subdetector> detectors = new ArrayList<Subdetector>();
-//    protected double raw_energy;
-    protected double corrected_energy;
+        
+    protected double energy;
     protected double energyError;
-    protected double[] subdetector_raw_energies = new double[10];
-    protected double[] subdetector_corrected_energies = new double[10];
-    protected List<Double> hit_energies = new ArrayList<Double>();
-    protected ClusterPropertyCalculator cluster_property_calculator = new TensorClusterPropertyCalculator();
-    protected boolean needsPropertyCalculation = true;
-    protected double[] position;
-    protected double[] positionError;
+
+    protected double[] position = new double[3];
+    protected double[] positionError = new double[6];
+
     protected double iphi;
     protected double itheta;
-    protected double[] directionError;
+    
+    protected double[] directionError = new double[6];
     protected double[] shapeParameters;
-
-    public BaseCluster()
-    {
-//        raw_energy = 0.;
-        corrected_energy = 0.;
-        for(int i=0;i<10;i++)
-        {
-            subdetector_raw_energies[i] = 0.;
-            subdetector_corrected_energies[i] = 0.;
-        }
-        position = new double[3];
-        positionError = new double[6];
-        directionError = new double[6];
-    }
-
-    /**
-     * Add a CalorimeterHit to the cluster
-     */
-    public void addHit(CalorimeterHit hit)
-    {
-        hits.add(hit);
-        double hre = hit.getRawEnergy();
-//        raw_energy += hre;
-        double hce = hit.getCorrectedEnergy();
-        IDDecoder idc = hit.getIDDecoder();
-        idc.setID(hit.getCellID());
-        // Replace call to getValue("system").
-        // -- 2007-04-12 Jeremy McCormick
-        int detector_index = idc.getSystemID();
-        if((detector_index > 9 )|(detector_index < 0))detector_index = 0;
-        corrected_energy += hce;
-        hit_energies.add(hce);
-        subdetector_raw_energies[detector_index] += hre;
-        subdetector_corrected_energies[detector_index] += hce;
+            
+    protected double[] subdetectorEnergies = new double[0];
+        
+    protected int type;
+    protected int pid;
+    
+    protected ClusterPropertyCalculator calc = new TensorClusterPropertyCalculator();
+    protected boolean needsPropertyCalculation = true;
+    
+    /**
+     * The no argument constructor.
+     */
+    public BaseCluster() {        
+    }
+    
+    /**
+     * Almost fully qualified constructor, if the cluster's properties are already calculated.
+     * The energy given here will override the value calculated from the hits.  If this is not
+     * desired, then another constructor should be used instead.  This constructor does not
+     * allow setting hit contributions that are different from the hit energies.
+     * @param hits The list of hits.
+     * @param energy The total energy.
+     * @param energyError The energy error.
+     * @param position The position.
+     * @param positionError The position error.
+     * @param iphi The intrinsic phi.
+     * @param itheta The intrinsic theta.
+     * @param directionError The direction error.
+     * @param shapeParameters The shape parameters.
+     */
+    public BaseCluster(
+            List<CalorimeterHit> hits,
+            double energy, 
+            double energyError,
+            double[] position,
+            double[] positionError,
+            double iphi,
+            double itheta,
+            double[] directionError,
+            double[] shapeParameters,
+            int type,
+            int pid) {
+        
+        addHits(hits);        
+        
+        // This will override the energy calculated from the hits, by design!
+        this.energy = energy;
+        this.energyError = energyError;
+        
+        this.position = position;
+        this.positionError = positionError;
+        
+        this.iphi = iphi;
+        this.itheta = itheta;
+        
+        this.directionError = directionError;
+        this.shapeParameters = shapeParameters;
+        
+        this.type = type;
+        this.pid = pid;
+        
+        this.needsPropertyCalculation = false;
+    }
+    
+    /**
+     * Basic constructor that takes a list of hits.
+     * It will apply the default energy calculation.
+     * @param hits The list of CalorimeterHits.
+     */
+    public BaseCluster(List<CalorimeterHit> hits) {
+        addHits(hits);
+    }
+   
+    /**
+     *****************************************************
+     * Implementation of get methods from the interface. *
+     *****************************************************
+     */
+    
+    /**
+     * Get the list of CalorimeterHits of this cluster.
+     * The hits are not necessarily unique in the list.
+     * @return The hits comprising the cluster.
+     */
+    public List<CalorimeterHit> getCalorimeterHits() {
+        return hits;
+    }
+
+    /**
+     * Get the clusters that are part of this cluster.
+     * @return The clusters comprising the cluster.
+     */
+    public List<Cluster> getClusters() {
+        return clusters;
+    }
+
+    /**
+     * Get the energy of the cluster, which by default will be the
+     * sum of the CalorimeterHit corrected energy values.
+     * @return The energy of the cluster.
+     */
+    public double getEnergy() {
+        return energy;
+    }
+
+    /**
+     * Get the energy error.
+     * @return The energy error.
+     */
+    public double getEnergyError() {
+        return energyError;
+    }
+
+    /**
+     * Get the individual hit contribution energies.
+     * This should be an array of the same size as the hit list.
+     * By default this array contains the hit's corrected energies,
+     * but the contributions may be set to different values
+     * on a per hit basis using the {@link #addHit(CalorimeterHit, double)}
+     * method.
+     * @return The individual hit contribution energies.
+     */
+    public double[] getHitContributions() {                       
+        double[] arrayCopy = new double[hitContributions.size()];
+        for (int i = 0; i < hitContributions.size(); i++) {
+            arrayCopy[i] = hitContributions.get(i);
+        }
+        return arrayCopy;
+    }
+   
+    /**
+     * Get the list of subdetector energy contributions.
+     * The ordering and meaning of this array is unspecified by this class.
+     * @return The list of subdetector energy contributions.
+     */
+    public double[] getSubdetectorEnergies() {
+        return subdetectorEnergies;
+    }
+
+    /**
+     * Get a value defining the type of this cluster.
+     * @return The type of this cluster.
+     */
+    public int getType() {
+        return type;
+    }
+           
+    /**
+     * Get the number of hits in the cluster, including hits in sub-clusters. 
+     * Hits belonging to more than one cluster are counted once.
+     * @return The size of the cluster.
+     */
+    public int getSize() {                       
+        Set<CalorimeterHit> hitSet = new HashSet<CalorimeterHit>(this.hits);
+        for (Cluster cluster : clusters) {
+            hitSet.addAll(cluster.getCalorimeterHits());
+        }
+        int size = hitSet.size();
+        hitSet.clear();
+        return size;
+    }
+    
+    /**
+     * Get the intrinsic phi direction of the cluster.
+     * @return The intrinsic phi direction of the cluster.
+     */
+    public double getIPhi() {
+        checkCalculateProperties();
+        return iphi;
+    }
+
+    /**
+     * Get the intrinsic theta direction of the cluster.
+     * @return The intrinsic theta direction of the cluster.
+     */
+    public double getITheta() {
+        checkCalculateProperties();
+        return itheta;
+    }
+
+    /**
+     * Get the direction error of the cluster as a double array of size 6.
+     * @return The direction error of the cluster.
+     */
+    public double[] getDirectionError() {
+        checkCalculateProperties();
+        return directionError;
+    }
+
+    /**
+     * Get the position of the cluster as a double array of size 3.
+     * @return The position of the cluster.
+     */
+    public double[] getPosition() {
+        checkCalculateProperties();
+        return position;
+    }
+
+    /**
+     * Get the position error of the cluster as a double array of size 6.
+     * @return The position error of the cluster.
+     */
+    public double[] getPositionError() {
+        checkCalculateProperties();
+        return positionError;
+    }
+
+    /**
+     * Get the shape parameters of the cluster as a double array of unspecified size.
+     * @return The shape parameters of the cluster.
+     */
+    public double[] getShape() {
+        checkCalculateProperties();
+        return shapeParameters;
+    }
+    
+    /**
+     * Get the PDG ID of the particle hypothesis.
+     * @return The PID.
+     */
+    public int getParticleId() {
+        return pid;
+    }
+    
+    /**
+     **********************************
+     * Implementation of set methods. *
+     **********************************
+     */        
+    
+    /**
+     * Set the position of the cluster.    
+     * @param position The position of the cluster.
+     * @throws IllegalArgumentException if array is wrong size.
+     */
+    public void setPosition(double[] position) {
+        if (position.length != 3) {
+            throw new IllegalArgumentException("The position array argument has the wrong length: " + position.length);
+        }
+        this.position = position;
+    }
+    
+    /**
+     * Set the position error of the cluster.
+     * @param positionError The position error of the cluster.    
+     * @throws IllegalArgumentException if array is wrong size.
+     */
+    public void setPositionError(double[] positionError) {
+        if (positionError.length != 6) {
+            throw new IllegalArgumentException("The positionError array argument has the wrong length: " + position.length);
+        }
+        this.positionError = positionError;
+    }
+
+    /**
+     * Set the intrinsic phi of the cluster.
+     * @param iphi The intrinsic phi of the cluster.
+     */
+    public void setIPhi(double iphi) {
+        this.iphi = iphi;
+    }
+    
+    /**
+     * Set the intrinsic theta of the cluster.
+     * @param iphi The intrinsic theta of the cluster.
+     */
+    public void setITheta(double itheta) {
+        this.itheta = itheta;
+    }
+
+    /**
+     * Set the direction error of the cluster.    
+     * @param directionError The direction error of the cluster.
+     * @throws IllegalArgumentException if array is wrong size.
+     */
+    public void setDirectionError(double[] directionError) {
+        if (directionError.length != 6) {
+            throw new IllegalArgumentException("The directionError array argument has the wrong length: " + position.length);
+        }       
+        this.directionError = directionError;
+    }
+    
+    /**
+     * Set the shape parameters of the cluster.
+     * @param shapeParameters The shape parameters.
+     */
+    public void setShapeParameters(double[] shapeParameters) {
+        this.shapeParameters = shapeParameters;
+    }
+    
+    /**
+     * Set the type of the cluster.
+     * @param type The type of the cluster.
+     */
+    public void setType(int type) {
+        this.type = type;    
+    }
+    
+    /**
+     * Get the PDG ID of the particle hypothesis.
+     * @return The PID.
+     */
+    public void setParticleId(int pid) {
+        this.pid = pid;
+    }
+               
+    /**
+     * Set a total energy of this cluster, overriding any energy
+     * value that may have been automatically calculated from
+     * hit energies.
+     * @param energy The total energy of this cluster.
+     */
+    public void setEnergy(double energy) {
+        this.energy = energy;
+    }
+    
+    /**
+     * Set the error on the energy measurement.
+     * @param energyError The error on the energ measurement.
+     */
+    public void setEnergyError(double energyError) {
+        this.energyError = energyError;
+    }
+   
+    /**
+     * Set the subdetector energies.  
+     * @param subdetectorEnergies The subdetector energies.
+     */
+    public void setSubdetectorEnergies(double[] subdetectorEnergies) {
+        this.subdetectorEnergies = subdetectorEnergies;
+    }
+    
+    /**
+     ****************************************************
+     * Convenience methods for adding hits and clusters *
+     ****************************************************
+     */            
+    
+    /**
+     * Add a list of hits to the cluster.
+     * @param The list of hits to add.
+     */
+    public void addHits(List<CalorimeterHit> hits) {
+        for (CalorimeterHit hit : hits) {
+            addHit(hit);
+        }
+    }
+    
+    /**
+     * Add a list of sub-clusters to the cluster.
+     * @param The list of clusters to add.
+     */
+    public void addClusters(List<Cluster> clusters) {
+        for (Cluster cluster : clusters) {
+            addCluster(cluster);
+        }
+    }
+    
+    /**
+     * Add a sub-cluster to the cluster.
+     * @param cluster The cluster to add.
+     */
+    public void addCluster(Cluster cluster) {
+        clusters.add(cluster);
+        List<CalorimeterHit> clusterHits = cluster.getCalorimeterHits();
+        for (int i = 0; i < clusterHits.size(); i++) {
+            hits.add(clusterHits.get(i));
+            hitContributions.add(clusterHits.get(i).getCorrectedEnergy());
+        }                      
+        energy += cluster.getEnergy();
         needsPropertyCalculation = true;
-    }
-
-    /**
-     * Add a CalorimeterHit to the cluster from a cluster
-     */
-    public void addHitFromCluster(CalorimeterHit hit)
-    {
-        hits.add(hit);
-        double hce = hit.getCorrectedEnergy();
-        hit_energies.add(hce);
+    }    
+    
+    /**
+     * Add a hit to the cluster with default energy contribution.
+     * @param hit The hit to add.
+     */
+    public void addHit(CalorimeterHit hit) {        
+        addHit(hit, hit.getCorrectedEnergy());
         needsPropertyCalculation = true;
     }
-
-    /**
-     * Remove a CalorimeterHit from the cluster
-     */
-    public void removeHit(CalorimeterHit hit)
-    {
-        int indx = hits.indexOf(hit);
+    
+    /**
+     * Add a hit to the cluster with specified energy contribution.
+     * @param hit The hit to add.
+     * @param contribution The energy contribution of the hit [GeV].
+     */
+    public void addHit(CalorimeterHit hit, double contribution) {
+        hits.add(hit);        
+        hitContributions.add(contribution);
+        energy += contribution;
+        needsPropertyCalculation = true;
+    }
+
+    /**
+     * Remove a hit from the cluster.
+     * @param hit The hit to remove.
+     */
+    public void removeHit(CalorimeterHit hit) {
+        int index = hits.indexOf(hit);
         hits.remove(hit);
-        double hre = hit.getRawEnergy();
-//        raw_energy -= hre;
-        double hce = hit.getCorrectedEnergy();
-        IDDecoder idc = hit.getIDDecoder();
-        idc.setID(hit.getCellID());
-        int detector_index = idc.getSystemID();
-        if((detector_index > 9 )|(detector_index < 0))detector_index = 0;
-        corrected_energy -= hce;
-        hit_energies.remove(indx);
-        subdetector_raw_energies[detector_index] -= hre;
-        subdetector_corrected_energies[detector_index] -= hce;
+        double hitEnergy = hit.getCorrectedEnergy();
+        energy -= hitEnergy;
+        hitContributions.remove(index);
         needsPropertyCalculation = true;
     }
-    /**
-     * Add a Cluster to the cluster
-     */
-    public void addCluster(Cluster cluster)
-    {
-        clusters.add(cluster);
-        List<CalorimeterHit> chits = cluster.getCalorimeterHits();
-        if(chits.size() > 0)
-        {
-            for(int i=0;i<chits.size();i++)
-            {
-                addHitFromCluster(chits.get(i));
-            }
-            needsPropertyCalculation = true;
-        }
-        corrected_energy += cluster.getEnergy();
-        if(cluster instanceof BaseCluster)
-        {
-            BaseCluster bcl = (BaseCluster) cluster;
-//            raw_energy += bcl.getRawEnergy();
-        }
-        double[] sde = cluster.getSubdetectorEnergies();
-        for(int i=0;i<sde.length;i++)
-        {
-            subdetector_corrected_energies[i] += sde[i];
-            subdetector_raw_energies[i] += sde[i];
-        }
-    }
-
-    /**
-     * Do an external calculation of Energy and set it
-     */
-    public void setEnergy(double Energy)
-    {
-        corrected_energy = Energy;
-    }
-
-    /**
-     * Return the hits comprising the cluster as per interface
-     */
-    public List<CalorimeterHit> getCalorimeterHits()
-    {
-        return hits;
-    }
-
-    /**
-     * Return the clusters comprising the cluster as per interface
-     */
-    public List<Cluster> getClusters()
-    {
-        return clusters;
-    }
-
-    /**
-     * Return the corrected Energy of the cluster as per interface
-     */
-    public double getEnergy()
-    {
-        return corrected_energy;
-    }
-    
-    public double getEnergyError()
-    {
-        return energyError;
-    }
-    
-    public void setEnergyError(double energyError)
-    {
-        this.energyError = energyError;
-    }
-
-    /**
-     * Return the corrected energy contribution of each hit
-     * comprising the cluster as per interface
-     */
-    public double[] getHitContributions()
-    {
-        double[] earray = new double[hit_energies.size()];
-        for(int i=0;i<hit_energies.size();i++)
-        {
-            earray[i] = hit_energies.get(i);
-        }
-        return earray;
-    }
-
-    /**
-     * Return the phi direction of the cluster as per interface
-     */
-    public double getIPhi()
-    {
-        if(needsPropertyCalculation)
-        {
+            
+    /**
+     **************************************
+     * ClusterPropertyCalculator methods. *
+     **************************************
+     */      
+    
+    /**
+     * Calculate properties if needs property calculation.
+     */
+    void checkCalculateProperties() {
+        if (needsPropertyCalculation()) {
             calculateProperties();
         }
-        return iphi;
-    }
-
-    /**
-     * Return the theta direction of the cluster as per interface
-     */
-    public double getITheta()
-    {
-        if(needsPropertyCalculation)
-        {
-            calculateProperties();
-        }
-        return itheta;
-    }
-
-    /**
-     * Return the direction error of the cluster as per interface
-     */
-    public double[] getDirectionError()
-    {
-        if(needsPropertyCalculation)
-        {
-            calculateProperties();
-        }
-        return directionError;
-    }
-
-    /**
-     * Return the position of the cluster as per interface
-     */
-    public double[] getPosition()
-    {
-        if(needsPropertyCalculation)
-        {
-            calculateProperties();
-        }
-        return position;
-    }
-
-    /**
-     * Return the position error of the cluster as per interface
-     */
-    public double[] getPositionError()
-    {
-        if(needsPropertyCalculation)
-        {
-            calculateProperties();
-        }
-        return positionError;
-    }
-
-    /**
-     * Return the shape parameters of the cluster as per interface
-     */
-    public double[] getShape()
-    {
-        if(needsPropertyCalculation)
-        {
-            calculateProperties();
-        }
-        return shapeParameters;
-    }
-
-    /**
-     * Return the subdetector energy contributions
-     *  of the cluster as per interface
-     */
-    public double[] getSubdetectorEnergies()
-    {
-        return subdetector_corrected_energies;
-    }
-
-    /**
-     * Return a bit mask defining the type of cluster as per interface
-     */
-    public int getType()
-    {
-        return 0;
-    }
-
-//    /**
-//     * Return the sum of the raw energies from the hits in the cluster
-//     */
-//    public double getRawEnergy()
-//    {
-//        return raw_energy;
-//    }
-
-    /**
-     * Allow for recalculation of cluster properties, or
-     * prevent recalculation after adding to cluster
-     */
-    public void setNeedsPropertyCalculation(boolean tf)
-    {
-        needsPropertyCalculation = tf;
-    }
-
-    /**
-     * Override the default ClusterPropertyCalculator
-     */
-    public void setPropertyCalculator(ClusterPropertyCalculator cpc)
-    {
-        cluster_property_calculator = cpc;
-	needsPropertyCalculation = true;
-    }
-
-    /**
-     * Calculate the cluster properties from the hits
-     */
-    public void calculateProperties()
-    {
-        cluster_property_calculator.calculateProperties(hits);
-        iphi = cluster_property_calculator.getIPhi();
-        itheta = cluster_property_calculator.getITheta();
-
-	// important: store info locally, not a reference to info in calculator
-        double[] tmp = cluster_property_calculator.getPosition();
-	for(int i=0; i<3; ++i) position[i] = tmp[i];
-
-	double[] posErr = cluster_property_calculator.getPositionError();
-        double[] dirErr = cluster_property_calculator.getDirectionError();
-	for(int i=0; i<6; ++i) {
-	    positionError[i] = posErr[i];
-	    directionError[i] = dirErr[i];
-	}
-
-	// In general, assume any number of parameters needed
-        double[] shapePar = cluster_property_calculator.getShapeParameters();
-	shapeParameters = new double[ shapePar.length ];
-	for(int i=0; i<shapePar.length; ++i) {
-	    shapeParameters[i] = shapePar[i];
-	}
-
-        needsPropertyCalculation = false;
-    }
-
-    /** Return the hits comprising the cluster, including hits in
-     * subclusters, as per interface.  Hits belonging to more than one
-     * cluster/subcluster should be counted only once.
-     */
-    public int getSize()
-    {
-        Set<CalorimeterHit> hitSet = new HashSet<CalorimeterHit>(this.hits);
-        for( Cluster clus : clusters )
-        {
-            hitSet.addAll( clus.getCalorimeterHits() );
-        }
-
-	// cleanup and return
-	int size = hitSet.size();
-	hitSet.clear();
-        return size;
-    }
-
-    /**
-     * String representation of this object.
-     * @return A String representation of this object
-     */
-    public String toString()
-    {
-	double[] pos = this.getPosition();
-	double rho = Math.sqrt(pos[0]*pos[0]+pos[1]*pos[1]);
-	double theta = Math.atan2( rho, pos[2]);
-	double phi = Math.atan2( pos[1], pos[0] );
-	if(phi<0) phi += 2*Math.PI;
-        return "BaseCluster w/ "+this.getSize()+" hits at theta="+theta*180/Math.PI+", phi="+phi*180/Math.PI;
-    }
-    
-    /**
-     * Create and return a set of MCParticles from the hits of the cluster.
-     * Only looks at SimCalorimeterHits, as CalorimeterHits don't have MC information.
-     * @return The set of unique MCParticles associated with this cluster.
-     */
-    public Set<MCParticle> findUniqueMCParticlesFromHits()
-    {
-    	Set<MCParticle> mcps = new HashSet<MCParticle>();
-    	for (CalorimeterHit hit : getCalorimeterHits())
-    	{
-    		if (hit instanceof SimCalorimeterHit)
-    		{
-    			SimCalorimeterHit simhit = (SimCalorimeterHit)hit;
-    			for (int i=0; i<simhit.getMCParticleCount(); i++)
-    			{
-    				mcps.add(simhit.getMCParticle(i));
-    			}
-    		}
-    	}
-    	return mcps;
+    }
+    
+    /**
+     * Set a property calculator for computing position, etc.
+     * @param calc The property calculator.
+     */
+    public void setPropertyCalculator(ClusterPropertyCalculator calc) {
+        this.calc = calc;
+    }
+
+    /**
+     * True if property calculator is set.
+     * @return True if property calculator is set.
+     */
+    public boolean hasPropertyCalculator() {
+        return calc != null;
+    }
+    
+    /**
+     * True if cluster is flagged as needed a property calculation.
+     * @return True if cluster needs property calculation.
+     */
+    public boolean needsPropertyCalculation() {
+        return needsPropertyCalculation;
+    }
+    
+    /**
+     * Manually set whether the cluster needs property calculation.
+     * @param needsPropertyCalculation Whether the cluster needs property calculation.
+     */
+    public void setNeedsPropertyCalculation(boolean needsPropertyCalculation) {
+        this.needsPropertyCalculation = needsPropertyCalculation;
+    }
+    
+    /**
+     * 
+     * Calculate the properties of this cluster using the current 
+     * <code>ClusterPropertyCalculator</code>. The calculated properties will be
+     * set on the following class variables:<br/> 
+     * {@link #position}, {@link #positionError}, 
+     * {@link #iphi}, {@link #itheta}, {@link #directionError},   
+     * and {@link #shapeParameters}.  The cluster will then be flagged
+     * as not needing a property calculation in its current state.         
+     */
+    public void calculateProperties() {
+        if (!hasPropertyCalculator()) {
+            throw new RuntimeException("No ClusterPropertyCalculator is set on this object.");
+        }
+        calc.calculateProperties(this);
+        setPosition(calc.getPosition());
+        setPositionError(calc.getPositionError());
+        setIPhi(calc.getIPhi());
+        setITheta(calc.getITheta());
+        setDirectionError(calc.getDirectionError());                
+        setShapeParameters(calc.getShapeParameters());
+        setNeedsPropertyCalculation(false);
     }
 }

Modified: projects/lcsim/trunk/event-model/src/main/java/org/lcsim/event/base/ClusterPropertyCalculator.java
 =============================================================================
--- projects/lcsim/trunk/event-model/src/main/java/org/lcsim/event/base/ClusterPropertyCalculator.java	(original)
+++ projects/lcsim/trunk/event-model/src/main/java/org/lcsim/event/base/ClusterPropertyCalculator.java	Wed Jan  7 17:03:40 2015
@@ -1,8 +1,7 @@
 package org.lcsim.event.base;
 
-import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
+
 import org.lcsim.event.CalorimeterHit;
 import org.lcsim.event.Cluster;
 
@@ -11,41 +10,45 @@
  * 
  * @author cassell
  */
-public interface ClusterPropertyCalculator 
-{
+public interface ClusterPropertyCalculator {
 
-	/** 
-	 * Calculate properties from a CalorimeterHit list
-	 */
-	public void calculateProperties(List<CalorimeterHit> hits);
+    /**
+     * Calculate properties from a CalorimeterHit list.
+     */
+    public void calculateProperties(List<CalorimeterHit> hits);
+    
+    /**
+     * Calculate properties from a cluster.
+     */
+    public void calculateProperties(Cluster cluster);
 
-	/** 
-	 * Return position
-	 */
-	public double[] getPosition();
-	
-	/** 
-	 * Return position error
-	 */
-	public double[] getPositionError();
-	
-	/** 
-	 * Return phi direction
-	 */
-	public double getIPhi();
-	
-	/** 
-	 * Return theta direction
-	 */
-	public double getITheta();
-	
-	/** 
-	 * Return direction error
-	 */
-	public double[] getDirectionError();
-	
-	/** 
-	 * Return shape parameters
-	 */
-	public double[] getShapeParameters();
+    /**
+     * Return position
+     */
+    public double[] getPosition();
+
+    /**
+     * Return position error
+     */
+    public double[] getPositionError();
+
+    /**
+     * Return phi direction
+     */
+    public double getIPhi();
+
+    /**
+     * Return theta direction
+     */
+    public double getITheta();
+
+    /**
+     * Return direction error
+     */
+    public double[] getDirectionError();
+
+    /**
+     * Return shape parameters
+     */
+    public double[] getShapeParameters();
 }

Modified: projects/lcsim/trunk/event-model/src/main/java/org/lcsim/event/base/TensorClusterPropertyCalculator.java
 =============================================================================
--- projects/lcsim/trunk/event-model/src/main/java/org/lcsim/event/base/TensorClusterPropertyCalculator.java	(original)
+++ projects/lcsim/trunk/event-model/src/main/java/org/lcsim/event/base/TensorClusterPropertyCalculator.java	Wed Jan  7 17:03:40 2015
@@ -1,26 +1,19 @@
 package org.lcsim.event.base;
 
-import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
+
 import org.lcsim.event.CalorimeterHit;
 import org.lcsim.event.Cluster;
-// import org.lcsim.geometry.CalorimeterIDDecoder;
+
 
 /**
- * Implementation of ClusterPropertyCalculator Interface using
- * inertia Tensor calculation to derive parameters
- *
+ * Implementation of ClusterPropertyCalculator Interface using inertia Tensor calculation 
+ * to derive parameters.
+ * 
  * @author cassell
  */
-public class TensorClusterPropertyCalculator implements ClusterPropertyCalculator
-{
-    protected double[] position;
-    protected double[] positionError;
-    protected double iphi;
-    protected double itheta;
-    protected double[] directionError;
-    protected double[] shapeParameters;
+public class TensorClusterPropertyCalculator extends AbstractClusterPropertyCalculator {
+    
     protected double[] mm_NE;
     protected double[] mm_CE;
     protected double[][] mm_PA;
@@ -28,26 +21,30 @@
     /**
      * Calculate the cluster properties from the hits
      */
-    public TensorClusterPropertyCalculator()
-    {
-        position = new double[3];
-        positionError = new double[6];
-        iphi = 0.;
-        itheta = 0.;
-        directionError = new double[6];
-        shapeParameters = new double[6];
-    }
-    public void calculateProperties(List<CalorimeterHit> hits)
-    {
+    public TensorClusterPropertyCalculator() {
+    }
+    
+    protected void reset() {
+        super.reset();
         mm_NE = new double[3];
         mm_CE = new double[3];
         mm_PA = new double[3][3];
-        for(int i=0;i<3;++i)
-        {
+    }    
+    
+    public void calculateProperties(Cluster cluster) {
+        calculateProperties(cluster.getCalorimeterHits());
+    }
+
+    public void calculateProperties(List<CalorimeterHit> hits) {
+        
+        reset();
+        
+        for (int i = 0; i < 3; ++i) {
             mm_NE[i] = 0.;
             mm_CE[i] = 0.;
-            for(int j=0;j<3;++j)
-            {mm_PA[i][j]= 0.;}
+            for (int j = 0; j < 3; ++j) {
+                mm_PA[i][j] = 0.;
+            }
         }
         double Etot = 0.0;
         double Exx = 0.0;
@@ -70,53 +67,51 @@
         double M = 0.0;
         double Det = 0.0;
         int nhits = hits.size();
-        for(int i=0;i<hits.size();i++)
-        {
+        for (int i = 0; i < hits.size(); i++) {
             CalorimeterHit hit = hits.get(i);
-            //	CalorimeterIDDecoder decoder = hit.getDecoder();
-            //	decoder.setID(hit.getCellID());
-            //	double[] pos = new double[3];
-            //	pos[0] = decoder.getX();
-            //	pos[1] = decoder.getY();
-            //	pos[2] = decoder.getZ();
+            // CalorimeterIDDecoder decoder = hit.getDecoder();
+            // decoder.setID(hit.getCellID());
+            // double[] pos = new double[3];
+            // pos[0] = decoder.getX();
+            // pos[1] = decoder.getY();
+            // pos[2] = decoder.getZ();
             double[] pos = hit.getPosition();
             double E = hit.getCorrectedEnergy();
             Etot += E;
-            CEx += E*pos[0];
-            CEy += E*pos[1];
-            CEz += E*pos[2];
-            Exx += E*(pos[1]*pos[1] + pos[2]*pos[2]);
-            Eyy += E*(pos[0]*pos[0] + pos[2]*pos[2]);
-            Ezz += E*(pos[1]*pos[1] + pos[0]*pos[0]);
-            Exy -= E*pos[0]*pos[1];
-            Eyz -= E*pos[1]*pos[2];
-            Exz -= E*pos[0]*pos[2];
-        }
-        CEx = CEx/Etot;
-        CEy = CEy/Etot;
-        CEz = CEz/Etot;
-        double CErSq = CEx*CEx + CEy*CEy + CEz*CEz;
+            CEx += E * pos[0];
+            CEy += E * pos[1];
+            CEz += E * pos[2];
+            Exx += E * (pos[1] * pos[1] + pos[2] * pos[2]);
+            Eyy += E * (pos[0] * pos[0] + pos[2] * pos[2]);
+            Ezz += E * (pos[1] * pos[1] + pos[0] * pos[0]);
+            Exy -= E * pos[0] * pos[1];
+            Eyz -= E * pos[1] * pos[2];
+            Exz -= E * pos[0] * pos[2];
+        }
+        CEx = CEx / Etot;
+        CEy = CEy / Etot;
+        CEz = CEz / Etot;
+        double CErSq = CEx * CEx + CEy * CEy + CEz * CEz;
         CEr = Math.sqrt(CErSq);
         // now go to center of energy coords.
-        if (nhits > 3 )
-        {
-            Exx = Exx - Etot*(CErSq - CEx*CEx);
-            Eyy = Eyy - Etot*(CErSq - CEy*CEy);
-            Ezz = Ezz - Etot*(CErSq - CEz*CEz);
-            Exy = Exy + Etot*CEx*CEy;
-            Eyz = Eyz + Etot*CEy*CEz;
-            Exz = Exz + Etot*CEz*CEx;
+        if (nhits > 3) {
+            Exx = Exx - Etot * (CErSq - CEx * CEx);
+            Eyy = Eyy - Etot * (CErSq - CEy * CEy);
+            Ezz = Ezz - Etot * (CErSq - CEz * CEz);
+            Exy = Exy + Etot * CEx * CEy;
+            Eyz = Eyz + Etot * CEy * CEz;
+            Exz = Exz + Etot * CEz * CEx;
 
             //
             Tr = Exx + Eyy + Ezz;
-            double Dxx = Eyy*Ezz - Eyz*Eyz;
-            double Dyy = Ezz*Exx - Exz*Exz;
-            double Dzz = Exx*Eyy - Exy*Exy;
+            double Dxx = Eyy * Ezz - Eyz * Eyz;
+            double Dyy = Ezz * Exx - Exz * Exz;
+            double Dzz = Exx * Eyy - Exy * Exy;
             M = Dxx + Dyy + Dzz;
-            double Dxy = Exy*Ezz - Exz*Eyz;
-            double Dxz = Exy*Eyz - Exz*Eyy;
-            Det = Exx*Dxx - Exy*Dxy + Exz*Dxz;
-            double xt = Tr*Tr - 3*M;
+            double Dxy = Exy * Ezz - Exz * Eyz;
+            double Dxz = Exy * Eyz - Exz * Eyy;
+            Det = Exx * Dxx - Exy * Dxy + Exz * Dxz;
+            double xt = Tr * Tr - 3 * M;
             double sqrtxt = Math.sqrt(xt);
             // eqn to solve for eigenvalues is x**3 - x**2*Tr + x*M - Det = 0
             // crosses y axis at -Det and inflection points are
@@ -124,180 +119,163 @@
             double mE1 = 0.;
             double mE2 = 0.;
             double mE3 = 0.;
-            double a = (3*M - Tr*Tr)/3.;
-            double b = (-2*Tr*Tr*Tr + 9*Tr*M -27*Det)/27.;
-            double test = b*b/4. + a*a*a/27.;
-            if(test >= 0.01)
-            {
-                //System.out.println("AbstractCluster: Only 1 real root!!!");
-                //System.out.println("  nhits = " + nhits + "\n");
-                //System.out.println(" a,b,test = " + a + " " + b + " " + test + "\n");
+            double a = (3 * M - Tr * Tr) / 3.;
+            double b = (-2 * Tr * Tr * Tr + 9 * Tr * M - 27 * Det) / 27.;
+            double test = b * b / 4. + a * a * a / 27.;
+            if (test >= 0.01) {
+                // System.out.println("AbstractCluster: Only 1 real root!!!");
+                // System.out.println("  nhits = " + nhits + "\n");
+                // System.out.println(" a,b,test = " + a + " " + b + " " + test + "\n");
+            } else {
+                double temp;
+                if (test >= 0.)
+                    temp = 1.;
+                else
+                    temp = Math.sqrt(b * b * 27. / (-a * a * a * 4.));
+                if (b > 0.)
+                    temp = -temp;
+                double phi = Math.acos(temp);
+                double temp1 = 2. * Math.sqrt(-a / 3.);
+                mE1 = Tr / 3. + 2. * Math.sqrt(-a / 3.) * Math.cos(phi / 3.);
+                mE2 = Tr / 3. + 2. * Math.sqrt(-a / 3.) * Math.cos(phi / 3. + 2. * Math.PI / 3.);
+                mE3 = Tr / 3. + 2. * Math.sqrt(-a / 3.) * Math.cos(phi / 3. + 4. * Math.PI / 3.);
             }
-            else
-            {
-                double temp;
-                if(test >= 0.)temp = 1.;
-                else temp = Math.sqrt(b*b*27./(-a*a*a*4.));
-                if(b > 0.)temp = -temp;
-                double phi = Math.acos(temp);
-                double temp1 = 2.*Math.sqrt(-a/3.);
-                mE1 = Tr/3. + 2.*Math.sqrt(-a/3.)*Math.cos(phi/3.);
-                mE2 = Tr/3. + 2.*Math.sqrt(-a/3.)*Math.cos(phi/3. + 2.*Math.PI/3.);
-                mE3 = Tr/3. + 2.*Math.sqrt(-a/3.)*Math.cos(phi/3. + 4.*Math.PI/3.);
-            }
-            if(mE1 < mE2)
-            {
-                if(mE1 < mE3)
-                {
+            if (mE1 < mE2) {
+                if (mE1 < mE3) {
                     E1 = mE1;
-                    if(mE2 < mE3)
-                    {
+                    if (mE2 < mE3) {
                         E2 = mE2;
                         E3 = mE3;
-                    }
-                    else
-                    {
+                    } else {
                         E2 = mE3;
                         E3 = mE2;
                     }
-                }
-                else
-                {
+                } else {
                     E1 = mE3;
                     E2 = mE1;
                     E3 = mE2;
                 }
-            }
-            else
-            {
-                if(mE2 < mE3)
-                {
+            } else {
+                if (mE2 < mE3) {
                     E1 = mE2;
-                    if(mE1 < mE3)
-                    {
+                    if (mE1 < mE3) {
                         E2 = mE1;
                         E3 = mE3;
-                    }
-                    else
-                    {
+                    } else {
                         E2 = mE3;
                         E3 = mE1;
                     }
-                }
-                else
-                {
+                } else {
                     E1 = mE3;
                     E2 = mE2;
                     E3 = mE1;
                 }
             }
 
-            NE1 = E1/Etot;
-            NE2 = E2/Etot;
-            NE3 = E3/Etot;
+            NE1 = E1 / Etot;
+            NE2 = E2 / Etot;
+            NE3 = E3 / Etot;
             double[] EV = new double[3];
             EV[0] = E1;
             EV[1] = E2;
             EV[2] = E3;
             // Now calculate principal axes
-	    // For eigenvalue EV, the axis is (nx, ny, nz) where:
-	    //    (Exx - EV)nx + (Exy)ny + (Exz)nz = 0
-	    //    (Eyx)nx + (Eyy - EV)ny + (Eyz)nz = 0
-	    //    (Ezx)nx + (Ezy)ny + (Ezz - EV)nz = 0
-	    // Setting nx = 1, we have:
-	    //    (Exx - EV) + (Exy)ny + (Exz)nz = 0
-	    //    (Eyx) + (Eyy - EV)ny + (Eyz)nz = 0
-	    //    (Ezx) + (Ezy)ny + (Ezz - EV)nz = 0
-	    // and so
-	    //    (Exy)ny = EV - Exx - (Exz)nz  =>  ny = (EV - Exx - Exz*nz)/Exy
-	    // What if Exy = 0? Then provided Eyz is non-zero we can write:
-	    //    (Ezx) + (Ezy)ny + (Ezz - EV)nz = 0
-	    //    ny = (Exz - (Ezz-EV)*nz)/Eyz
-	    // What if Exy = 0 and Eyz = 0 but (Eyy - EV) is non-zero?
-	    //    (Eyy - EV)ny + (Eyz)nz = 0
-	    //    ny = -(Eyz*nz)/(Eyy-EV)
-
-	    // In the pathological case where Exz = Eyz = Ezz = 0:
-	    //    (Exx - EV)nx + (Exy)ny = 0  =>  ny/nx = -(Exx-EV)/Exy
-	    //    (Eyx)nx + (Eyy - EV)ny = 0  =>  ny/nx = -Eyx/(Eyy-EV)
-	    //    (EV)nz = 0
-	    // so
-	    //     -ny/nx = (EV-Exx)/Exy = Eyx/(EV-Eyy)
-	    // But watch out for order! Recalculate eigenvalues for this pathological case.
-	    //     (EV-Exx)(EV-Eyy) = Eyx*Exy
-	    //     EV^2 - EV(Exx+Eyy) + Exx*Eyy - Eyx*Exy = 0
-	    // 
-	    // In another pathological case, Exz = Exy = 0:
-	    //    (Exx - EV)nx = 0
-	    //    (Eyy - EV)ny + (Eyz)nz = 0 => ny/nz = -(Eyz)/(Eyy-EV)
-	    //    (Ezy)ny + (Ezz - EV)nz = 0 => ny/nz = -(Ezz-EV)/(Ezy)
-	    // so we cannot set nx = 1. Instead, write:
-	    //    -ny/nz = (Eyz)/(Eyy-EV) = (Ezz-EV)/(Ezy)
-	    // Then
-	    //    (Eyz)(Ezy) = (Eyy-EV)(Ezz-EV)
-	    //    (Eyz)^2 = (Eyy)(Ezz) - (Eyy)(EV) - (Ezz)(EV) + (EV)^2
-	    //    EV^2 - EV(Eyy+Ezz) + Eyy*Ezz - Eyz*Eyz = 0
-
-	    // Handle pathological case
-	    if (Exz == 0.0 && Eyz == 0.0) {
-		// Recompute eigenvectors.
-		EV[0] = 0.5*(Exx+Eyy) + 0.5*Math.sqrt((Exx+Eyy)*(Exx+Eyy) + 4.0*Exy*Exy);
-		EV[1] = 0.5*(Exx+Eyy) - 0.5*Math.sqrt((Exx+Eyy)*(Exx+Eyy) + 4.0*Exy*Exy);
-		EV[2] = 0.0;
-		for( int i = 0 ; i < 2 ; i++ ) {
-		    double nx_over_ny = Exy / (Exx-EV[i]);
-		    double nx_unnormalized = nx_over_ny;
-		    double ny_unnormalized = 1.0;
-		    double norm = Math.sqrt(nx_unnormalized*nx_unnormalized + ny_unnormalized*ny_unnormalized);
-		    mm_PA[i][0] = ny_unnormalized/norm;
-		    mm_PA[i][1] = nx_unnormalized/norm;
-		    mm_PA[i][2] = 0.0;
-		}
-		// ... and now set third eigenvector to the z direction:
-		mm_PA[2][0] = 0.0;
-		mm_PA[2][1] = 0.0;
-		mm_PA[2][2] = 1.0;
-	    } else if (Exz == 0.0 && Exy == 0.0) {
-		// Another pathological case
-		EV[0] = 0.5*(Eyy+Ezz) + 0.5*Math.sqrt((Eyy+Ezz)*(Eyy+Ezz) + 4.0*Eyz*Eyz);
-		EV[1] = 0.5*(Eyy+Ezz) - 0.5*Math.sqrt((Eyy+Ezz)*(Eyy+Ezz) + 4.0*Eyz*Eyz);
-		EV[2] = 0.0;
-		for( int i = 0 ; i < 2 ; i++ ) {
-		    double ny_over_nz = Eyz / (Eyy-EV[i]);
-		    double ny_unnormalized = ny_over_nz;
-		    double nz_unnormalized = 1.0;
-		    double norm = Math.sqrt(ny_unnormalized*ny_unnormalized + nz_unnormalized*nz_unnormalized);
-		    mm_PA[i][0] = nz_unnormalized/norm;
-		    mm_PA[i][1] = ny_unnormalized/norm;
-		    mm_PA[i][2] = 0.0;
-		}
-		mm_PA[2][0] = 0.0;
-		mm_PA[2][1] = 0.0;
-		mm_PA[2][2] = 1.0;
-	    } else {
-		for( int i = 0 ; i < 3 ; i++ )
-		    {
-			double[] C = new double[3];
-			C[0] = 1.0;
-			C[2] = (Exy*Exy + (Eyy - EV[i])*(EV[i] - Exx))/
-			    ((Eyy - EV[i])*Exz - Eyz*Exy);
-			C[1] = (EV[i] - Exx - Exz*C[2])/Exy;
-			if (Exy == 0.0) {
-			    // Recompute
-			    if (Eyz != 0.0) {
-				// ny = (Exz - (Ezz-EV)*nz)/Eyz
-				C[1] = (Exz - (Ezz-EV[i])*C[2])/Eyz;
-			    } else {
-				// ny = -(Eyz*nz)/(Eyy-EV)
-				C[1] = -(Eyz*C[2])/(Eyy-EV[i]);
-			    }
-			}
-			double norm = Math.sqrt(C[0]*C[0] + C[1]*C[1] + C[2]*C[2]);
-			mm_PA[i][0] = C[0]/norm;
-			mm_PA[i][1] = C[1]/norm;
-			mm_PA[i][2] = C[2]/norm;
-		    }
-	    }
+            // For eigenvalue EV, the axis is (nx, ny, nz) where:
+            // (Exx - EV)nx + (Exy)ny + (Exz)nz = 0
+            // (Eyx)nx + (Eyy - EV)ny + (Eyz)nz = 0
+            // (Ezx)nx + (Ezy)ny + (Ezz - EV)nz = 0
+            // Setting nx = 1, we have:
+            // (Exx - EV) + (Exy)ny + (Exz)nz = 0
+            // (Eyx) + (Eyy - EV)ny + (Eyz)nz = 0
+            // (Ezx) + (Ezy)ny + (Ezz - EV)nz = 0
+            // and so
+            // (Exy)ny = EV - Exx - (Exz)nz => ny = (EV - Exx - Exz*nz)/Exy
+            // What if Exy = 0? Then provided Eyz is non-zero we can write:
+            // (Ezx) + (Ezy)ny + (Ezz - EV)nz = 0
+            // ny = (Exz - (Ezz-EV)*nz)/Eyz
+            // What if Exy = 0 and Eyz = 0 but (Eyy - EV) is non-zero?
+            // (Eyy - EV)ny + (Eyz)nz = 0
+            // ny = -(Eyz*nz)/(Eyy-EV)
+
+            // In the pathological case where Exz = Eyz = Ezz = 0:
+            // (Exx - EV)nx + (Exy)ny = 0 => ny/nx = -(Exx-EV)/Exy
+            // (Eyx)nx + (Eyy - EV)ny = 0 => ny/nx = -Eyx/(Eyy-EV)
+            // (EV)nz = 0
+            // so
+            // -ny/nx = (EV-Exx)/Exy = Eyx/(EV-Eyy)
+            // But watch out for order! Recalculate eigenvalues for this pathological case.
+            // (EV-Exx)(EV-Eyy) = Eyx*Exy
+            // EV^2 - EV(Exx+Eyy) + Exx*Eyy - Eyx*Exy = 0
+            //
+            // In another pathological case, Exz = Exy = 0:
+            // (Exx - EV)nx = 0
+            // (Eyy - EV)ny + (Eyz)nz = 0 => ny/nz = -(Eyz)/(Eyy-EV)
+            // (Ezy)ny + (Ezz - EV)nz = 0 => ny/nz = -(Ezz-EV)/(Ezy)
+            // so we cannot set nx = 1. Instead, write:
+            // -ny/nz = (Eyz)/(Eyy-EV) = (Ezz-EV)/(Ezy)
+            // Then
+            // (Eyz)(Ezy) = (Eyy-EV)(Ezz-EV)
+            // (Eyz)^2 = (Eyy)(Ezz) - (Eyy)(EV) - (Ezz)(EV) + (EV)^2
+            // EV^2 - EV(Eyy+Ezz) + Eyy*Ezz - Eyz*Eyz = 0
+
+            // Handle pathological case
+            if (Exz == 0.0 && Eyz == 0.0) {
+                // Recompute eigenvectors.
+                EV[0] = 0.5 * (Exx + Eyy) + 0.5 * Math.sqrt((Exx + Eyy) * (Exx + Eyy) + 4.0 * Exy * Exy);
+                EV[1] = 0.5 * (Exx + Eyy) - 0.5 * Math.sqrt((Exx + Eyy) * (Exx + Eyy) + 4.0 * Exy * Exy);
+                EV[2] = 0.0;
+                for (int i = 0; i < 2; i++) {
+                    double nx_over_ny = Exy / (Exx - EV[i]);
+                    double nx_unnormalized = nx_over_ny;
+                    double ny_unnormalized = 1.0;
+                    double norm = Math.sqrt(nx_unnormalized * nx_unnormalized + ny_unnormalized * ny_unnormalized);
+                    mm_PA[i][0] = ny_unnormalized / norm;
+                    mm_PA[i][1] = nx_unnormalized / norm;
+                    mm_PA[i][2] = 0.0;
+                }
+                // ... and now set third eigenvector to the z direction:
+                mm_PA[2][0] = 0.0;
+                mm_PA[2][1] = 0.0;
+                mm_PA[2][2] = 1.0;
+            } else if (Exz == 0.0 && Exy == 0.0) {
+                // Another pathological case
+                EV[0] = 0.5 * (Eyy + Ezz) + 0.5 * Math.sqrt((Eyy + Ezz) * (Eyy + Ezz) + 4.0 * Eyz * Eyz);
+                EV[1] = 0.5 * (Eyy + Ezz) - 0.5 * Math.sqrt((Eyy + Ezz) * (Eyy + Ezz) + 4.0 * Eyz * Eyz);
+                EV[2] = 0.0;
+                for (int i = 0; i < 2; i++) {
+                    double ny_over_nz = Eyz / (Eyy - EV[i]);
+                    double ny_unnormalized = ny_over_nz;
+                    double nz_unnormalized = 1.0;
+                    double norm = Math.sqrt(ny_unnormalized * ny_unnormalized + nz_unnormalized * nz_unnormalized);
+                    mm_PA[i][0] = nz_unnormalized / norm;
+                    mm_PA[i][1] = ny_unnormalized / norm;
+                    mm_PA[i][2] = 0.0;
+                }
+                mm_PA[2][0] = 0.0;
+                mm_PA[2][1] = 0.0;
+                mm_PA[2][2] = 1.0;
+            } else {
+                for (int i = 0; i < 3; i++) {
+                    double[] C = new double[3];
+                    C[0] = 1.0;
+                    C[2] = (Exy * Exy + (Eyy - EV[i]) * (EV[i] - Exx)) / ((Eyy - EV[i]) * Exz - Eyz * Exy);
+                    C[1] = (EV[i] - Exx - Exz * C[2]) / Exy;
+                    if (Exy == 0.0) {
+                        // Recompute
+                        if (Eyz != 0.0) {
+                            // ny = (Exz - (Ezz-EV)*nz)/Eyz
+                            C[1] = (Exz - (Ezz - EV[i]) * C[2]) / Eyz;
+                        } else {
+                            // ny = -(Eyz*nz)/(Eyy-EV)
+                            C[1] = -(Eyz * C[2]) / (Eyy - EV[i]);
+                        }
+                    }
+                    double norm = Math.sqrt(C[0] * C[0] + C[1] * C[1] + C[2] * C[2]);
+                    mm_PA[i][0] = C[0] / norm;
+                    mm_PA[i][1] = C[1] / norm;
+                    mm_PA[i][2] = C[2] / norm;
+                }
+            }
         }
         mm_NE[0] = NE1;
         mm_NE[1] = NE2;
@@ -305,66 +283,57 @@
         mm_CE[0] = CEx;
         mm_CE[1] = CEy;
         mm_CE[2] = CEz;
-        for(int i=0;i<6;i++)
-        {
+        for (int i = 0; i < 6; i++) {
             positionError[i] = 0.;
             directionError[i] = 0.;
             shapeParameters[i] = 0.;
         }
-        for(int i=0;i<3;i++)
-        {
+        for (int i = 0; i < 3; i++) {
             position[i] = mm_CE[i];
             shapeParameters[i] = mm_NE[i];
         }
-        if(nhits > 3)
-        {
-            double dr = Math.sqrt(  (position[0]+mm_PA[0][0])*(position[0]+mm_PA[0][0]) +
-                    (position[1]+mm_PA[0][1])*(position[1]+mm_PA[0][1]) +
-                    (position[2]+mm_PA[0][2])*(position[2]+mm_PA[0][2]) ) -
-                    Math.sqrt(	(position[0])*(position[0]) +
-                    (position[1])*(position[1]) +
-                    (position[2])*(position[2]) ) ;
+        if (nhits > 3) {
+            double dr = Math.sqrt((position[0] + mm_PA[0][0]) * (position[0] + mm_PA[0][0]) + (position[1] + mm_PA[0][1]) * (position[1] + mm_PA[0][1]) + (position[2] + mm_PA[0][2]) * (position[2] + mm_PA[0][2])) - Math.sqrt((position[0]) * (position[0]) + (position[1]) * (position[1]) + (position[2]) * (position[2]));
             double sign = 1.;
-            if(dr < 0.)sign = -1.;
-            itheta = Math.acos(sign*mm_PA[0][2]);
-            iphi = Math.atan2(sign*mm_PA[0][1],sign*mm_PA[0][0]);
-        }
-        else
-        {
+            if (dr < 0.)
+                sign = -1.;
+            itheta = Math.acos(sign * mm_PA[0][2]);
+            iphi = Math.atan2(sign * mm_PA[0][1], sign * mm_PA[0][0]);
+        } else {
             itheta = 999.;
             iphi = 999.;
         }
     }
-    public double[] getPosition()
-    {
+
+    public double[] getPosition() {
         return position;
     }
-    public double[] getPositionError()
-    {
+
+    public double[] getPositionError() {
         return positionError;
     }
-    public double getIPhi()
-    {
+
+    public double getIPhi() {
         return iphi;
     }
-    public double getITheta()
-    {
+
+    public double getITheta() {
         return itheta;
     }
-    public double[] getDirectionError()
-    {
+
+    public double[] getDirectionError() {
         return directionError;
     }
-    public double[] getShapeParameters()
-    {
+
+    public double[] getShapeParameters() {
         return shapeParameters;
     }
-    public double[] getNormalizedEigenvalues()
-    {
+
+    public double[] getNormalizedEigenvalues() {
         return mm_NE;
     }
-    public double[][] getPrincipleAxis()
-    {
+
+    public double[][] getPrincipleAxis() {
         return mm_PA;
     }
 

########################################################################
Use REPLY-ALL to reply to list

To unsubscribe from the LCDET-SVN list, click the following link:
https://listserv.slac.stanford.edu/cgi-bin/wa?SUBED1=LCDET-SVN&A=1