LISTSERV mailing list manager LISTSERV 16.5

Help for LCDET-SVN Archives


LCDET-SVN Archives

LCDET-SVN Archives


LCDET-SVN@LISTSERV.SLAC.STANFORD.EDU


View:

Message:

[

First

|

Previous

|

Next

|

Last

]

By Topic:

[

First

|

Previous

|

Next

|

Last

]

By Author:

[

First

|

Previous

|

Next

|

Last

]

Font:

Proportional Font

LISTSERV Archives

LISTSERV Archives

LCDET-SVN Home

LCDET-SVN Home

LCDET-SVN  January 2015

LCDET-SVN January 2015

Subject:

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

From:

[log in to unmask]

Reply-To:

Notification of commits to the lcdet svn repository <[log in to unmask]>

Date:

Thu, 8 Jan 2015 01:03:48 -0000

Content-Type:

text/plain

Parts/Attachments:

Parts/Attachments

text/plain (1671 lines)

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

Top of Message | Previous Page | Permalink

Advanced Options


Options

Log In

Log In

Get Password

Get Password


Search Archives

Search Archives


Subscribe or Unsubscribe

Subscribe or Unsubscribe


Archives

January 2016
December 2015
November 2015
October 2015
September 2015
August 2015
July 2015
June 2015
May 2015
April 2015
March 2015
February 2015
January 2015
December 2014
November 2014
October 2014
September 2014
August 2014
July 2014
June 2014
May 2014
April 2014
March 2014
February 2014
January 2014
December 2013
November 2013

ATOM RSS1 RSS2



LISTSERV.SLAC.STANFORD.EDU

Secured by F-Secure Anti-Virus CataList Email List Search Powered by the LISTSERV Email List Manager

Privacy Notice, Security Notice and Terms of Use