Print

Print


Commit in lcd/hep/lcd/recon/cluster/MST on MAIN
DummyDecisionMaker.java+41added 1.1
MSTCluster.java+257added 1.1
MSTDecisionMakerPair.java+26added 1.1
MSTDecisionMakerSingle.java+26added 1.1
OmitEndcap.java+35added 1.1
SizeDecision.java+77added 1.1
GeometricalDistance.java+34-271.1 -> 1.2
MSTAbstractMetrics.java+19-61.1 -> 1.2
MSTClusterBuilder.java+155-731.1 -> 1.2
MSTClusterList.java+162-481.1 -> 1.2
MSTClusterProcessor.java+136-481.1 -> 1.2
MSTHit.java+110-131.1 -> 1.2
MSTClusterCore.java-741.1 removed
+1078-289
6 added + 1 removed + 6 modified, total 13 files
An updated and very much extended version of the MST cluster algorithm.
It was not possible to implement all updates without changing internal and
external interfaces. 

For help with your user code, contact [log in to unmask]

lcd/hep/lcd/recon/cluster/MST
DummyDecisionMaker.java added at 1.1
diff -N DummyDecisionMaker.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ DummyDecisionMaker.java	4 Aug 2005 22:58:27 -0000	1.1
@@ -0,0 +1,41 @@
+package hep.lcd.recon.cluster.MST; 
+
+import java.io.*; 
+import java.util.*; 
+
+/**
+   Default decision maker which passes all non-empty clusters.
+
+
+   @version 1.0
+   @author [log in to unmask]
+
+   Package: MST
+   
+   Modification Log: 
+      -- 04 Aug 2005: Version 1.0  by  [log in to unmask]
+
+*/
+
+public class DummyDecisionMaker implements MSTDecisionMakerSingle, MSTDecisionMakerPair {
+    /**
+       Blank constructor.
+    */
+    public DummyDecisionMaker(){}
+
+    /**
+       Check if a single cluster is valid, i.e. not empty.
+     */
+    public boolean valid(MSTCluster cluster){
+	if ( cluster.getNHits()<=0 ) return false;
+	return true;}
+
+    /**
+       Check if a pair of clusters forms a valid pair, i.e. neither is empty.
+     */
+    public boolean valid(MSTCluster aCluster, MSTCluster bCluster){
+	if ( aCluster.getNHits()<=0 ) return false;
+	if ( bCluster.getNHits()<=0 ) return false;
+	return true;}
+
+}

lcd/hep/lcd/recon/cluster/MST
MSTCluster.java added at 1.1
diff -N MSTCluster.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ MSTCluster.java	4 Aug 2005 22:58:28 -0000	1.1
@@ -0,0 +1,257 @@
+package hep.lcd.recon.cluster.MST; 
+
+import java.io.*; 
+import java.util.*; 
+
+import hep.lcd.event.MCParticle; 
+import hep.lcd.event.LCDEvent;
+import hep.lcd.event.CalorimeterHit;
+import hep.lcd.geometry.CalorimeterCell; 
+import hep.lcd.recon.cluster.util.AbstractCluster; 
+
+/**
+   Extension of AbstractCluster for use with the MST package
+
+   @version 1.0
+   @author [log in to unmask]
+
+   Package: MST
+   
+   Modification Log: 
+      -- 04 Aug 2005: Version 1.0   by  [log in to unmask]
+
+*/
+
+public class MSTCluster extends AbstractCluster {
+
+    /**
+       Construct an empty cluster.
+    */
+    public MSTCluster(LCDEvent event){
+	super(event.getCalorimeterCell());
+	thisEvent = event;
+	thisCell = event.getCalorimeterCell();
+    }
+
+    /**
+       Construct a cluster containing one MSTHit.
+    */
+    public MSTCluster(CalorimeterHit hit, LCDEvent event){
+	super(event.getCalorimeterCell());
+	thisEvent = event;;
+	thisCell = event.getCalorimeterCell();
+	addHit(hit);
+    }
+
+    /**
+       Construct a cluster and fill it with the hits contained
+       in 'cluster'.
+    */
+    public MSTCluster (AbstractCluster cluster, LCDEvent event){
+	super(event.getCalorimeterCell());
+	thisEvent = event;
+	thisCell = event.getCalorimeterCell();
+	addCluster(cluster);
+    }
+
+    private int clusterNo = 0;
+    /**
+       Return the cluster index.
+    */
+    public int getNo(){return clusterNo;}
+    /**
+       Set the cluster index. This is optional information set
+       by the MSTClusterBuilder.
+    */
+    public void setNo(int no){clusterNo = no;}
+
+    /**
+       Given an input cluster, add its hits.
+    */
+    public void addCluster(AbstractCluster cluster){
+	Enumeration e = cluster.getHits();
+	while ( e.hasMoreElements() ){
+	    Object obj = e.nextElement();
+	    if (obj instanceof MSTHit){
+		addHit((MSTHit) obj);
+	    } else {
+		addHit(new MSTHit((CalorimeterHit)obj, thisEvent));
+	    }
+	    lCalculated = false;
+	}
+    }
+
+    /**
+       Calculate internal quantities.
+    */
+    private void calculateExtendedQuantities(){
+	calculatePurity(); 
+	fillLayerEnergy();
+	lCalculated = true; 
+    }
+    
+    /**
+       Fraction of energy deposited by AlphaParticle relative 
+       to total energy in the cluster, where 'AlphaParticle'
+       is the particle that contributes the greatest amount
+       of energy to the cluster.
+     */
+    public double getPurity(){
+	if ( !lCalculated ) calculateExtendedQuantities(); 
+	return ((double) purity); 
+    }
+
+    /**
+       Internal routine to calculate the purity and find the AlphaParticle.
+     */
+    private void calculatePurity(){
+
+	int pMax = -1; 
+	double eMax = 0.; 
+	double eSum = 0.; 
+
+	MCParticle[] particles = getMCParticles(); 
+	double[] energies = getContributedEnergy(); 
+
+	for ( int i=0; i<particles.length; i++ ){
+	    eSum += energies[i]; 
+
+	    if ( energies[i] > eMax ){
+		eMax = energies[i]; 
+		pMax = i; 
+	    }
+	}
+	if (pMax>=0){
+	    purity = energies[pMax]/eSum; 
+	    setAlphaParticle(particles[pMax]); 
+	}else{
+	    System.out.println("Something is wrong in MSTCluster::calculatePurity(). Cluster has no MC particles assigned.");
+	}
+    }
+
+    private void setAlphaParticle(MCParticle particle){
+	alphaParticle = particle; 
+    }
+    
+    /**
+       Get MCParticle contributing the most energy to the MSTClusterCore
+     */
+    public MCParticle getAlphaParticle(){
+	if ( !lCalculated ) calculateExtendedQuantities(); 
+	return ((MCParticle) alphaParticle); 
+    }
+
+    /**
+       Returns true if the particle has no hits in the endcap.
+    */
+    public boolean inBarrel(){
+	//  returns true if no hit of cluster is in endcap
+	boolean tmp = true;
+	for (Iterator iter=hits.iterator(); iter.hasNext(); ){
+	    MSTHit hit = (MSTHit)iter.next();
+	    thisCell.setTowerID(hit.getTowerID());
+	    tmp = (tmp && (!thisCell.isEndcap()) );
+	}
+	return tmp;
+    }
+
+
+    /**
+       Returns true only if the particle has no hits in the barrel.
+    */
+    public boolean inEndcap(){
+	//  returns true if no hit of cluster is in barrel
+	boolean tmp = true;
+	for (Iterator iter=hits.iterator(); iter.hasNext(); ){
+	    MSTHit hit = (MSTHit)iter.next();
+	    thisCell.setTowerID(hit.getTowerID());
+	    tmp = (tmp && (thisCell.isEndcap()) );
+	}
+	return tmp;
+    }
+
+    public CalorimeterCell getCalorimeterCell(){
+	return thisCell;
+    }
+
+    /**
+       Find the layer corresponding to the center of energy.
+    */
+    public int getLayer(){
+	double[] pos = getCenterofEnergy();
+	int cell = thisCell.findTowerIDfromCartesian(pos[0],pos[1],pos[2]);
+	thisCell.setTowerID(cell);
+	int tmp = thisCell.getLayer();
+	return tmp;
+    }
+
+    /**
+       Compute the energy-weighted RMS of all hits in the cluster
+       about the center of energy.
+    */
+    public double getWeightedRMSSize(){
+	double tmp = 0;
+	double etot = 0;
+	double[] ctr = getCenterofEnergy();
+	Iterator iter = hits.iterator();
+	while (iter.hasNext()){
+	    MSTHit hit = (MSTHit) iter.next();
+	    thisCell.setTowerID(hit.getTowerID());
+	    double pos[] = thisCell.getPosition();
+	    double dist = 0.;
+	    for (int i=0; i<3; i++){
+		dist += ((pos[i]-ctr[i])*(pos[i]-ctr[i]));}
+	    tmp += hit.getEnergy()*dist;
+	    etot += hit.getEnergy();
+	}
+	tmp = Math.sqrt(tmp/etot);
+	return tmp;
+    }
+
+    private void fillLayerEnergy(){
+	for (int i=0; i<100; i++) energyLayer[i]=0.;
+	Iterator iter = hits.iterator();
+	while (iter.hasNext()){
+	    MSTHit hit = (MSTHit) iter.next();
+	    thisCell.setTowerID(hit.getTowerID());
+	    int layer = thisCell.getLayer();
+	    // System.out.println("Layer :"+layer);
+	    energyLayer[layer]+=hit.getEnergy();
+	}
+    }
+
+    /**
+       Get the total energy in the given layer.
+    */
+    public double getLayerEnergy(int i){
+	if (i>=100) return 0.;
+	if ( !lCalculated ) calculateExtendedQuantities(); 
+	return energyLayer[i];
+    }
+
+    /**
+       Returns the co-ordinates of the center of energy.
+       In the general case this is the same as
+       AbstractCluster.getCenterofEnergy(), but in the
+       special case that there is exactly one hit we
+       just return the co-ordinates of that hit.
+    */
+    public double[] getCenterofEnergy(){
+	if (getNHits()==1){
+	    Enumeration hits = getHits();
+	    MSTHit h = (MSTHit) hits.nextElement();
+	    return h.getCoordinates();
+	}else{
+	    return super.getCenterofEnergy();
+	}
+    }
+
+    private double purity = -1.; 
+    private boolean lCalculated = false; 
+    private MCParticle alphaParticle; 
+    private LCDEvent thisEvent;
+    private CalorimeterCell thisCell;
+    private double[] energyLayer = new double[100];
+
+}
+

lcd/hep/lcd/recon/cluster/MST
MSTDecisionMakerPair.java added at 1.1
diff -N MSTDecisionMakerPair.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ MSTDecisionMakerPair.java	4 Aug 2005 22:58:28 -0000	1.1
@@ -0,0 +1,26 @@
+package hep.lcd.recon.cluster.MST; 
+
+import java.io.*; 
+import java.util.*; 
+
+/**
+   AbstractDecisionMaker Class for MST Clustering
+   
+   @version 1.0
+   @author [log in to unmask]
+
+   Package: MST
+   
+   Modification Log: 
+      -- 04 Aug 2005: Version 1.0   by  [log in to unmask]
+
+*/
+
+public abstract interface MSTDecisionMakerPair{
+
+    /**
+       Determines whether a pair of clusters clusters should be used.
+    */
+    public abstract boolean valid(MSTCluster aCluster, MSTCluster bCluster);
+
+}

lcd/hep/lcd/recon/cluster/MST
MSTDecisionMakerSingle.java added at 1.1
diff -N MSTDecisionMakerSingle.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ MSTDecisionMakerSingle.java	4 Aug 2005 22:58:28 -0000	1.1
@@ -0,0 +1,26 @@
+package hep.lcd.recon.cluster.MST; 
+
+import java.io.*; 
+import java.util.*; 
+
+/**
+   AbstractDecisionMaker Class for MST Clustering
+   
+   @version 1.0
+   @author [log in to unmask]
+
+   Package: MST
+   
+   Modification Log: 
+      -- 04 Aug 2005: Version 1.0   by  [log in to unmask]
+
+*/
+
+public abstract interface MSTDecisionMakerSingle{
+
+    /**
+       Determines whether a cluster should be used.
+    */
+    public abstract boolean valid(MSTCluster cluster);
+
+}

lcd/hep/lcd/recon/cluster/MST
OmitEndcap.java added at 1.1
diff -N OmitEndcap.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ OmitEndcap.java	4 Aug 2005 22:58:28 -0000	1.1
@@ -0,0 +1,35 @@
+package hep.lcd.recon.cluster.MST; 
+
+import java.io.*; 
+import java.util.*; 
+import hep.lcd.geometry.CalorimeterCell;
+import hep.lcd.event.CalorimeterHit;
+
+/**
+   Implements MSTDecisionMakerSingle to define clusters which are entirely contained
+   in the barrel of the calorimeter.
+
+   @version 1.0
+   @author [log in to unmask]
+
+   Package: MST
+   
+   Modification Log: 
+      -- 04 Aug 2005: Version 1.0   by  [log in to unmask]
+
+ */
+public class OmitEndcap implements MSTDecisionMakerSingle {
+    /**
+       Blank constructor.
+     */
+    public OmitEndcap(){}
+
+    /**
+       Returns true if all hits of a cluster lie in the barrel part of the calorimeter.
+       @param cluster Cluster to be evaluated
+    */
+    public boolean valid(MSTCluster cluster){
+	return cluster.inBarrel();
+    }
+
+}

lcd/hep/lcd/recon/cluster/MST
SizeDecision.java added at 1.1
diff -N SizeDecision.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ SizeDecision.java	4 Aug 2005 22:58:28 -0000	1.1
@@ -0,0 +1,77 @@
+package hep.lcd.recon.cluster.MST;
+
+import java.io.*; 
+import java.util.*; 
+import hep.lcd.geometry.CalorimeterCell;
+import hep.lcd.event.CalorimeterHit;
+
+
+public class SizeDecision implements MSTDecisionMakerSingle {
+    
+    /**
+       Constructor. Default is to require at least one hit
+       in the cluster (no upper limit on number of hits).
+    */
+    public SizeDecision(){
+	setSize(1);
+    }
+
+    /**
+       Constructor specifying minium size of cluster.
+       @param newMinSize Require cluster size greater than or equal to this
+    */
+    public SizeDecision(int newMinSize){
+	setSize(newMinSize);
+    }
+
+    /**
+       Constructor specifying minium and maximum sizes of cluster.
+       @param newMinSize Require cluster size greater than or equal to this
+       @param newMaxSize Require cluster size less than or equal to this
+    */
+    public SizeDecision(int newMinSize, int newMaxSize){
+	setSize(newMinSize, newMaxSize);
+    }
+
+    /**
+       Specify minium size of cluster.
+       @param newMinSize Require cluster size greater than or equal to this
+    */
+    public void setSize(int newMinSize){
+	minSize = newMinSize;
+        applyMinimum = true;
+        applyMaximum = false;
+    }
+
+    /**
+       Specify minium and maximum sizes of cluster.
+       @param newMinSize Require cluster size greater than or equal to this
+       @param newMaxSize Require cluster size less than or equal to this
+    */
+    public void setSize(int newMinSize, int newMaxSize){
+	minSize = newMinSize;
+        maxSize = newMaxSize;
+        applyMinimum = true;
+        applyMaximum = true;
+    }
+    
+    /**
+       Check if cluster passes size cuts.
+       @param cluster Cluster to check
+    */
+    public boolean valid(MSTCluster cluster){
+	int nHits = cluster.getNHits();
+	if (applyMinimum && nHits<minSize) {
+	    return false; // too few hits
+        }
+        if (applyMaximum && nHits>maxSize) {
+	    return false; // too many hits
+        }
+        return true; // Pass!
+    }
+
+    private int minSize;
+    private int maxSize;
+    private boolean applyMinimum = false;
+    private boolean applyMaximum = false;
+}

lcd/hep/lcd/recon/cluster/MST
GeometricalDistance.java 1.1 -> 1.2
diff -u -r1.1 -r1.2
--- GeometricalDistance.java	12 Apr 2005 16:44:20 -0000	1.1
+++ GeometricalDistance.java	4 Aug 2005 22:58:28 -0000	1.2
@@ -3,51 +3,58 @@
 import java.io.*; 
 import java.util.*; 
 
-import hep.physics.BasicHep3Vector; 
 /**
    Geometrical Distance
        -- Defines proximity metric for MSTAbstractMetrics
 
+   @version 1.01
+   @author [log in to unmask], [log in to unmask]
+
    Package: MST
    
-   Version 1.0
-
    Modification Log: 
-      -- 23 March 2005: First Version
+      -- 23 Mar 2005: Version 1.0   by  [log in to unmask]
+      -- 04 Aug 2005: Version 1.01  by  [log in to unmask]
+                      - Generalize hits to clusters;
+		      - use carthesian distance
 
-   (c) [log in to unmask]
 */
 
 public class GeometricalDistance extends MSTAbstractMetrics {
-    public GeometricalDistance(){}
 
-    public double getDistance( MSTHit hit1, MSTHit hit2 ){
+    /**
+       Blank constructor.
+    */
+    public GeometricalDistance(){}
 
-	double[] coOne = hit1.getCoordinates(); 
-	double[] coTwo = hit2.getCoordinates(); 
+    /**
+       Compute the 3D distance between the centers of energy of two
+       clusters. (In the case of single-hit clusters, this is just
+       the co-ordinates of that hit.) If the distance is
+       greater than the threshold, the return value of the routine 
+       may differ from the actual distance but is guaranteed to be
+       over the threshold.
+    */
+    public double getDistance( MSTCluster cluster1, MSTCluster cluster2 ){
+
+	double[] coOne = cluster1.getCenterofEnergy(); 
+	double[] coTwo = cluster2.getCenterofEnergy(); 
+
+	double distance = Math.sqrt((coOne[0]-coTwo[0])*(coOne[0]-coTwo[0])
+	                          + (coOne[1]-coTwo[1])*(coOne[1]-coTwo[1])
+	                          + (coOne[2]-coTwo[2])*(coOne[2]-coTwo[2]));
 
-	if ( Math.abs(coOne[0]-coTwo[0])>threshold ||
-	     Math.abs(coOne[1]-coTwo[1])>threshold ||
-	     Math.abs(coOne[2]-coTwo[2])>threshold ) return 999.; 
-
-	double radOne = hit1.getRadius(); 
-	double radTwo = hit2.getRadius(); 
-	if ( Math.abs(radOne - radTwo) > threshold ) return 999.; 
-
-	double thetaXY = (coOne[0]/radOne) * (coTwo[0]/radTwo)
-	    +            (coOne[1]/radOne) * (coTwo[1]/radTwo); 
-	thetaXY = Math.acos( Math.max(-1.0,Math.min(1.0,thetaXY)) ); 
-
-	double distance = Math.pow(radOne*thetaXY,2) 
-	    +             Math.pow(coOne[2]-coTwo[2],2)
-	    +             Math.pow(radOne-radTwo,2); 
-	distance = Math.sqrt(distance); 
-	return ((double) distance); 
+	return ((double) distance);  
     }
+
+    /**
+       Set the threshold. This may be used for performance reasons to
+       speed up the distance calculation.
+    */
     public void setThreshold(double value){
 	threshold = value; 
     }
 
-    private double threshold; 
+    private double threshold=9999; 
 
 }

lcd/hep/lcd/recon/cluster/MST
MSTAbstractMetrics.java 1.1 -> 1.2
diff -u -r1.1 -r1.2
--- MSTAbstractMetrics.java	12 Apr 2005 16:44:20 -0000	1.1
+++ MSTAbstractMetrics.java	4 Aug 2005 22:58:28 -0000	1.2
@@ -6,19 +6,32 @@
 /**
    AbstractMetrics Class for MST Clustering
    
-   @version 1.0
-   @author [log in to unmask]
+
+   @version 1.01
+   @author [log in to unmask], [log in to unmask]
+
+   Package: MST
+   
+   Modification Log: 
+      -- 23 Mar 2005: Version 1.0   by  [log in to unmask]
+      -- 04 Aug 2005: Version 1.01  by  [log in to unmask]
+                      - Generalize hits to clusters;
+
 */
 
 public abstract class MSTAbstractMetrics{
 
     public MSTAbstractMetrics(){ }
     /**
-       Get distance between hits according to implemented metrics
-     */
-    public abstract double getDistance(MSTHit aHit, MSTHit bHit); 
+       Get distance between hits according to implemented metrics.
+       If the distance is greater than the threshold, the
+       return value of the routine may differ from the actual
+       distance but is guaranteed to be over the threshold.
+    */
+    public abstract double getDistance(MSTCluster aCluster, MSTCluster bCluster); 
+
     /**
-       For performace reasons, the metrics class should know the threshold
+       For performace reasons, the metrics class should know the threshold.
      */
     public abstract void setThreshold(double value); 
 }

lcd/hep/lcd/recon/cluster/MST
MSTClusterBuilder.java 1.1 -> 1.2
diff -u -r1.1 -r1.2
--- MSTClusterBuilder.java	12 Apr 2005 16:44:20 -0000	1.1
+++ MSTClusterBuilder.java	4 Aug 2005 22:58:28 -0000	1.2
@@ -4,122 +4,204 @@
 import java.text.*; 
 import java.util.*;
 
+import hep.lcd.event.LCDEvent; 
 import hep.lcd.geometry.CalorimeterCell; 
-import hep.lcd.event.CalorimeterHit; 
-import hep.lcd.event.CalorimeterHits; 
 import hep.lcd.recon.cluster.util.AbstractCluster; 
 
 /**
-   Recursive MSTClustering Algorimthm
+   Recursive MSTClustering Algorimthm <br>
+
+   More details on the feaatures of the algorithm can be found in the description of
+   MSTClusterProcessor. The user is highly encouraged to use this wrapper class instead
+   of the MSTClusterBuilder itself.
+
+   @version 1.01
+   @author [log in to unmask], [log in to unmask]
+
+   Package: MST
+   
+   Modification Log: 
+      -- 23 Mar 2005: Version 1.0   by  [log in to unmask]
+      -- 04 Aug 2005: Version 1.01  by  [log in to unmask]
+                      - Generalize hits to clusters;
+		      - Fix bug in recursion algorithm
+		      - introduce filters in form of MSTDecisionMakerSingle an
+		        MSTDecisionMakerPair
 
-   @version 1.0
-   @author [log in to unmask]
 */
 
 public class MSTClusterBuilder{
 
-    public MSTClusterBuilder(  CalorimeterHits hits
-			     , CalorimeterCell cell ){
-	thisCell = cell; 
-	thisHits = hits; 
-    }
-
     /**
-       Initialize MSTClusterBuilder (required)
-     */
-    public void initialize(){
-	Enumeration e = thisHits.getHits(); 
-	while ( e.hasMoreElements() ){
-
-	    CalorimeterHit hit = (CalorimeterHit) e.nextElement(); 
-	    thisCell.setTowerID(hit.getTowerID()); 
-
-	    if ( !thisCell.isEndcap() | (thisCell.isEndcap() & lUseEndcap) ) {
-		MSTHit mstHit = new MSTHit(hit,thisCell); 
-
-		vHits.add(mstHit); 
-		hash.put(mstHit,hit); 
-	    }
-	}
+       Constructor.
+       @param inList A Vector of input objects of type MSTCluster
+       @param event  The current LCDEent
+    */
+    public MSTClusterBuilder ( Vector inList, LCDEvent event ){
+	inputList = inList;
+	thisEvent = event;
     }
 
-    private CalorimeterCell thisCell; 
-    private CalorimeterHits thisHits; 
-    private Hashtable hash = new Hashtable(); 
-
-    private Vector vHits = new Vector(); 
+    protected Vector inputList;
+    protected Collection usedList;;
+    protected LCDEvent thisEvent;
     private double threshold = -1; 
+    private boolean thresholdSet = false;
+    protected MSTDecisionMakerSingle seedDecision = new DummyDecisionMaker();
+    protected MSTDecisionMakerPair pairDecision = new DummyDecisionMaker();
+    protected MSTDecisionMakerSingle outputDecision = new DummyDecisionMaker();
 
     /**
-       Main call to clustering routine
+       Main call to clustering routine.
+       @return A Vector of MSTCluster objects.
      */
     public Vector doClustering(){
-	Vector clusterList = new Vector(); 
+	Vector outputList = new Vector(); 
+	usedList = new HashSet();
 
-	if ( vHits == null ) throw new AssertionError("No Hits Provided"); 
+	if ( inputList == null ) throw new AssertionError("Empty list of input objects"); 
 
 	if ( theMetrics == null ) 
-	    throw new AssertionError("No Metrics Defined"); 
+	    throw new AssertionError("No metrics defined"); 
 	if ( threshold == -1 )
-	    throw new AssertionError("No Threshold Defined"); 
-	
-	while ( vHits.size() != 0 ) {
-	    MSTClusterCore clusterCore = new MSTClusterCore(thisCell); 
-
-	    MSTHit hitOne = (MSTHit) vHits.firstElement(); 
-	    clusterCore.addHit((CalorimeterHit) hash.get(hitOne)); 
-	    clusterVector(hitOne, clusterCore); 
+	    throw new AssertionError("No threshold defined"); 
+	if ( !thresholdSet ) { throw new AssertionError("Threshold not set"); }
 
-	    clusterList.add(clusterCore); 
+	// We will loop over each of the input objects. We check that the object
+	// is useable (not used in a previous cluster, valid seed) and if so
+	// we try to form a cluster around it.
+	int index=0; // Used to number each cluster sequentially
+	Iterator iter = inputList.listIterator();
+	while ( iter.hasNext() ) {
+	    MSTCluster inOne = (MSTCluster) iter.next();
+	    if ( !usedList.contains(inOne) && seedDecision.valid(inOne) ){
+		MSTCluster out = new MSTCluster(thisEvent); 
+		out.setNo(index);index++;
+		clusterVector(inOne, out); // Call to the recursive algorithm
+		if (outputDecision.valid(out)) {
+		    // Cluster is OK => add it to the output Vector
+		    outputList.add(out);
+		}
+	    } 
 	}
-	return ((Vector) clusterList); 
+	return ((Vector) outputList); 
     }
 
-    private void clusterVector(MSTHit hitOne, MSTClusterCore cluster){
 
-	int i = vHits.indexOf(hitOne); 
-	vHits.remove(hitOne); 
 
-	while ( i < vHits.size() ){
-	    MSTHit hitTwo = (MSTHit) vHits.elementAt(i); 
-	    double distance = theMetrics.getDistance(hitOne, hitTwo);
-
-	    if ( distance < threshold ) {
-		clusterVector(hitTwo,cluster);
-		
-		cluster.addHit((CalorimeterHit) hash.get(hitTwo)); 
-		vHits.remove(hitTwo); 
-	    } else {
-		i += 1; 
+    /**
+       The recursive clustering routine.
+       @param inOne The component we are adding
+       @param out   The output, merged cluster
+    */
+    //
+    // Comments on the algorithm:
+    //   We need at some point to compare every hit to every other
+    //   hit. If we try and skip some in the name of speed, we will
+    //   surely not reconstruct the clusters properly. For example,
+    //   consider the following case:
+    //
+    //     hit #1 is close to hit #3 but not hit #2
+    //     hit #2 is close to hit #3 but not hit #1
+    //     hit #3 is close to hit #1 and to hit #2
+    //
+    //   We start with hit #1 and then pick up hit #3. But then we
+    //   have to go back and compare #3 to all previous hits...
+    //
+    //   Thus, we need an absolute minimum of 0.5*n*n comparisons,
+    //   where n is the number of hits in the detector. It's
+    //   easiest to be up-front about this. (OK, so we could get
+    //   clever and save some, but it's very hard not to make a
+    //   mistake as soon as you do less than 0.5*n*n.)
+    //
+    //   The approach we use is recursive. At each step, we start with
+    //   a hit and look for other, nearby hits to add to its cluster.
+    //    * We store a list of ALL hits in inputList and loop over that
+    //      when looking for hits to add to the cluster. Otherwise, the
+    //      algorithm gets messy and we will probably miss some.
+    //    * When we have a hit to look at the distance of, we first
+    //      check whether the hit has already been used -- obviously,
+    //      if it's already been clustered we shouldn't waste time on
+    //      it. We do this with a set "usedList", which starts out
+    //      empty and gradually fills. This is a HashSet, so that
+    //      the add(), remove(), contains(), and size() member
+    //      functions take constant time.
+    //
+    private void clusterVector(MSTCluster inOne, MSTCluster out){
+	// We are adding a new object. It should not have been used
+	// before, nor be in the cluster already.
+	out.addCluster(inOne);
+	boolean addedOK = usedList.add(inOne);
+	if (!addedOK) { throw new AssertionError("Failed to add object -- tried to use is twice"); }
+
+	// Now, check all other objects to see if they're close enough to add.
+	// (inputList is the set of ALL input objects and is never altered)
+	Iterator iter = inputList.listIterator();
+	while ( iter.hasNext() ){
+	    MSTCluster inTwo = (MSTCluster) iter.next(); 
+	    if ( !usedList.contains(inTwo) ){
+		if ( pairDecision.valid(inOne,inTwo) ){
+		    double distance = theMetrics.getDistance(inOne, inTwo);
+		    if ( distance < threshold ) {
+			// Not used before, useable, and distance is within
+			// threshold => add it to the cluster & recurse:
+			clusterVector(inTwo,out);
+		    }
+		}
 	    }
 	}
+	return;
     }
 
     /**
-       Set threshold for starting a new ClusterCore 
-       (default threshold=3.0)
+       Set threshold for starting a new Cluster.
+       @param value The new threshold
      */
-    protected void setThreshold(double value){
+    public void setThreshold(double value){
 	if ( theMetrics == null ) 
 	    throw new AssertionError("No Metrics Defined"); 
 	threshold = value; 
 	theMetrics.setThreshold(threshold); 
+	thresholdSet = true;
     }
 
     /**
-       Register metrics to be used in clustering
-    */
-    protected void registerMetrics(MSTAbstractMetrics m){
+       Register metrics to be used in clustering. The metric must be of a
+       class extending MSTAbstractMetrics and defines a distance between two
+       MSTCluster.
+       @param m The metric to use
+     */
+    public void registerMetrics(MSTAbstractMetrics m){
 	theMetrics = m; 
     }
 
+    protected MSTAbstractMetrics theMetrics = null; 
+
     /**
-       Include Hits in the Endcaps according to the value of "bool"
-     */
-    protected void useEndcap(boolean bool){
-	lUseEndcap = bool; 
-    }
+       Only objects passing the seed filter are used to start a new cluster.
+       The seed filter must be of a class implementing MSTDecisionMakerSingle.
+       Default is DummyDecisionMaker().
+       @param decision The decision maker to use
+    */
+    public void setSeedDecision (MSTDecisionMakerSingle decision){
+	seedDecision = decision;}
+
+    /**
+       Two objects are only clustered together, if they pass the pair filter.
+       The pair filter must be of a class implementing MSTDecisionMakerPair.
+       Default is DummyDecisionMaker().
+       @param decision The decision maker to use
+    */
+    public void setPairDecision (MSTDecisionMakerPair decision){
+	pairDecision = decision;}
+
+    /**
+       Only objects passing the output filter get written to the ouput list.
+       The output filter must be of a class implementing MSTDecisionMakerSingle.
+       Default is DummyDecisionMaker().
+       @param decision The decision maker to use
+    */
+    public void setOutputDecision(MSTDecisionMakerSingle decision){
+	outputDecision = decision;}
 
-    private MSTAbstractMetrics theMetrics = null; 
-    private boolean lUseEndcap = true; 
 }

lcd/hep/lcd/recon/cluster/MST
MSTClusterList.java 1.1 -> 1.2
diff -u -r1.1 -r1.2
--- MSTClusterList.java	12 Apr 2005 16:44:20 -0000	1.1
+++ MSTClusterList.java	4 Aug 2005 22:58:28 -0000	1.2
@@ -4,81 +4,195 @@
 import java.util.*; 
 
 import hep.lcd.event.ClusterList; 
+import hep.lcd.recon.cluster.util.AbstractCluster;
+
+import hep.lcd.event.CalorimeterHit;
+import hep.lcd.event.CalorimeterHits;
+import hep.lcd.event.ClusterList;
+import hep.lcd.event.LCDEvent;
+
 
 /**
-   Defines ClusterList for MST. 
+   An extention of ClusterList for MSTCluster objects.
    
-   @author  [log in to unmask]
-   @version 1.0
+   @version 1.01
+   @author [log in to unmask], [log in to unmask]
+
+   Package: MST
+   
+   Modification Log: 
+      -- 23 Mar 2005: Version 1.0   by  [log in to unmask]
+      -- 04 Aug 2005: Version 1.01  by  [log in to unmask]
+                      - add constructors
+		      - introduce filters in form of MSTDecisonMakerSingle
+		      - introduce add and remove funtions
+
 */
 public class MSTClusterList implements ClusterList, Serializable
 {
-    MSTClusterList(Vector vListIn)
-    {
+
+    /**
+       Constructor (blank list).
+    */
+    public MSTClusterList(){
+	this.vList = new Vector();
+    }
+
+    /**
+       Constructor (taking list from named object).
+       @param userTag Name of object in the event store
+       @param event   Current event
+    */
+    public MSTClusterList(String userTag, LCDEvent event){
+	this.vList = new Vector();
+	Object obj = event.get(userTag);
+	if (obj == null) { throw new AssertionError("Object '"+userTag+"' not found in the event."); }
+	if ( obj instanceof CalorimeterHits ) {
+	    fillVector((CalorimeterHits) obj, event);
+	} else if ( obj instanceof ClusterList ) {
+	    fillVector((ClusterList) obj, event);
+	} else {
+	    throw new AssertionError("Object named '"+userTag
+				     +"' found but not recognized. Its class is '"
+				     +obj.getClass().getName()+"'");
+	}
+    }
+
+    /**
+       Constructor (taking list from a Vector of MSTCluster objects).
+       @param vListIn Vector of MSTCluster objects
+    */
+    public MSTClusterList(Vector vListIn){
 	this.vList = vListIn;
+	// Verify that these are MSTCluster objects:
+	Enumeration eClusters = vList.elements();
+	while (eClusters.hasMoreElements()) {
+	    Object obj = eClusters.nextElement();
+	    if ( ! (obj instanceof MSTCluster) ) {
+		throw new AssertionError("Vector must only contain MSTCluster objects");
+	    }
+	}
     }
     
-    public int getNClusters() { 
-	return ((int) getNClusters(0)); 
+    /**
+       Constructor (taking a list of CalorimeterHits).
+       @param hits CalorimeterHits object holding hits to wrap and add to the list
+       @param event Current event
+    */
+    public MSTClusterList(CalorimeterHits hits, LCDEvent event){
+	this.vList = new Vector();
+	fillVector(hits,event);
+    }
+
+    /**
+       Constructor (taking a ClusterList of MSTCluster objects). [verify]
+       @param list ClusterList object holding clusters to add to the list
+       @param event Current event
+    */
+    public MSTClusterList(ClusterList list, LCDEvent event){
+	this.vList = new Vector();
+	fillVector(list,event);
     }
+
+    /**
+       Convert from a CalorimeterHits input.
+    */
+    private void fillVector(CalorimeterHits hits, LCDEvent event){
+	Enumeration iter = hits.getHits();
+	while (iter.hasMoreElements()){
+	    Object obj = (Object) iter.nextElement();
+	    if (obj instanceof CalorimeterHit){
+		MSTCluster mstc = new MSTCluster((CalorimeterHit)obj, event);
+	    } else {
+		throw new AssertionError("MSTClusterList.fillVector(CalorimeterHits,LCDEvent): The CalorimeterHits object contains elements of un-recognized type '"+obj.getClass().getName()+"'");
+	    }
+	}
+    }
+
     /**
-       Get number of ClusterCores with a minimum of <tt> minHits </tt> hits.
-     */
-    public int getNClusters(int minHits) { 
-	return ((int) getVMin(minHits).size() ); 
+       Convert from a ClusterList input.
+    */
+    private void fillVector(ClusterList list, LCDEvent event){
+	Enumeration iter = list.getClusters();
+	while (iter.hasMoreElements()){
+	    Object obj = (Object) iter.nextElement();
+	    if (obj instanceof AbstractCluster){
+		MSTCluster mstc = new MSTCluster((AbstractCluster) obj, event);
+		this.vList.add(mstc);
+	    } else {
+		throw new AssertionError("MSTClusterList.fillVector(ClusterList,LCDEvent): The Clusterist object contains elements of un-recognized type '"+obj.getClass().getName()+"'");
+	    }
+	}
+    }
+
+    /**
+       Number of clusters in the list.
+    */
+    public int getNClusters() { 
+	return ((int) vList.size()); 
     }
+
     /**
-       Get number of ClusterCores with a minimum of <tt> minHits </tt> 
-       and a maximum of <tt> maxHits</tt> hits.
-     */
-    public int getNClusters(int minHits, int maxHits) { 
-	return ((int) getVMin(minHits, maxHits).size() ); 
+       Number of clusters in the list which pass the decision maker.
+    */
+    public int getNClusters(MSTDecisionMakerSingle decision) { 
+	Vector out = getClusterVector(decision);
+	return ((int) out.size()); 
     }
     
+    /**
+       Access to the clusters in the list.
+    */
     public Enumeration getClusters(){ 
-	return ((Enumeration) getClusters(0) ); 
+	return ((Enumeration) vList.elements() ); 
     }
+
     /**
-       Get enumeration for ClusterCores with a minimum of <tt> minHits </tt>
-       hits.
-     */
-    public Enumeration getClusters(int minHits){ 
-	return ((Enumeration) getVMin(minHits).elements()); 
+       Access to the clusters in the list which pass the decision maker.
+    */
+    public Enumeration getClusters(MSTDecisionMakerSingle decision){ 
+	return ((Enumeration) getClusterVector(decision).elements()); 
     }
+
     /**
-       Get number of ClusterCores with a minimum of <tt> minHits </tt> 
-       and a maximum of <tt> maxHits</tt> hits.
-     */
-    public Enumeration getClusters(int minHits, int maxHits){ 
-	return ((Enumeration) getVMin(minHits,maxHits).elements()); 
+       Access to a copy of the cluster vector.
+    */
+    public Vector getClusterVector(){
+	Vector out = new Vector(vList);
+	return out;
     }
 
-    private Vector getVMin(int minHits, int maxHits){
-	return ((Vector) vMinProvider(minHits, maxHits)); 
+    /**
+       Access to a copy of the cluster vector, after filtering out hits
+       that don't pass the decision maker.
+    */
+    public Vector getClusterVector(MSTDecisionMakerSingle decision){
+	Vector out = new Vector();
+	Iterator iter = vList.listIterator();
+	while (iter.hasNext()){
+	    MSTCluster c = (MSTCluster) iter.next();
+	    if (decision.valid(c)) out.add(c);
+	}
+	return out;
     }
 
-    private Vector getVMin(int minHits){
-	return ((Vector) getVMin(minHits, 10000)); 
+    /**
+       Add a cluster to the list.
+       @param addMe Cluster to add
+    */
+    public void addCluster(MSTCluster addMe) {
+	vList.add(addMe);
     }
 
-    private Vector vMinProvider(int i, int j){
-	if ( i <= 1 && j >=10000 ) return ((Vector) vList); 
-	if ( i == iSave && j == jSave ) return ((Vector) vSave); 
-
-	Vector v = new Vector(); 
-	for ( int k=0; k<vList.size(); k++ ){
-	    MSTClusterCore core = (MSTClusterCore) vList.elementAt(k); 
-	    if ( core.getNHits() >= i &&
-		 core.getNHits() <= j    ) v.add(core); 
-	}
-	iSave = i; 
-	jSave = j; 
-	vSave = v; 
-	return ((Vector) v); 
+    /**
+       Remove a cluster from the list.
+       @return True if remove was done, false if it failed.
+       @param removeMe Cluster to remove
+    */
+    public boolean removeCluster(MSTCluster removeMe) {
+	boolean removedOK = vList.remove(removeMe);
+	return removedOK;
     }
 
     private Vector vList;
-    private Vector vSave; 
-    private int iSave; 
-    private int jSave; 
 };

lcd/hep/lcd/recon/cluster/MST
MSTClusterProcessor.java 1.1 -> 1.2
diff -u -r1.1 -r1.2
--- MSTClusterProcessor.java	12 Apr 2005 16:44:20 -0000	1.1
+++ MSTClusterProcessor.java	4 Aug 2005 22:58:28 -0000	1.2
@@ -11,77 +11,131 @@
 
 /**
  Main Processor for Minimum Spanning Tree Clustering.
- <p>
+<p>
+   This is a wrapper class for calling an MSTClusterBuilder extending AbstractProcessor. 
+   The constructor is called with a name-tag, which defines the input set of objects used
+   for clustering. Possible values are <tt>EMCal</tt> for all hits in the electromagnetic
+   calorimeter, <tt>HCal</tt> for all hits in the hadronic calorimeter and <tt>User</tt> for
+   a user-defined input list. The input list has to be of type CalorimeterHits or ClusterList
+   and must be stored in the LCDEvent object used by the processor. The name of the input
+   list has to be defined with <tt>setUserTag(String)</tt>.
+</p><p> 
+   Internally, all objects of the input list are transformed to MSTCluster. This format is also 
+   used for the output objects, which come in the form of an MSTClusterList written to the same
+   LCDEvent. The name of the output list can be defined with <tt>setClusterName(String)</tt>, 
+   the defaukt is "MSTCluster "+ the argument given to the constructor.
+</p><p>
+   The user also has to define a definition of 'distance' by providing a metrics of type
+   MSTAbstractMetrics using <tt>registerMetrics(MSTAbstractMetrics)</tt>. Also, a threshold has 
+   to be defined with <tt>setThreshold(double)</tt>. The package includes
+   the class GeometricalDistance as an example.
+</p><p>
+   Further, two filter interfaces are defined: MSTDecisionMakerSingle and MSTDecisionMakerPair.
+   The user can write his own classes implementing these interfaces for steering the
+   clustering algorithm. Four places for introducing filters are foreseen: <br>
+   <tt>setInputDecision(MSTDecisionMakerSingle)</tt> for filtering the list of input clusters, <br>
+   <tt>setSeedDecision(MSTDecisionMakerSingle)</tt> for a definition of valid seed clusters from the list of input clusters, <br>
+   <tt>setPairDecision(MSTDecisionMakerPair)</tt> for a definition of valid pairs (input clusters which are compared and eventually clustered to one output cluster), and <br>
+   <tt>setOutputDecision(MSTDecisionMakerSingle)</tt> for filtering the list of output clusters. <br>
+   There are a few MSTDecisionMaker provided with the package: DummyDecisionMaker, OmitEndcap, and SizeDecision. The user does not have to define any filters. As default, the DummyDecisionMaker is used at all places, which passes all non-empty clusters.
+</p><p>
+   Features of the Algorithm: <br>
+   - two input clusters end up in the same output cluster, if a) they individually pass the input filter, b) they together satisfy the pair filter, and c) their distance defined by the MSTAbstractMetrics is below the threshold defined by the user; <br>
+   - each output cluster contains only input clusters passed by the input filter and at least one input cluster satisfying the seed filter; <br>
+   - only those output clusters passing the output filter get saved.
+</p><p>
  Example of Use:
  <p><blockquote><pre>
-    MSTAbstractMetrics metrics = new GeometricalDistance();
-    
-    MSTClusterProcessor emProcessor = new MSTClusterProcessor("EMCal");
-    emProcessor.setThreshold(3.0); 
-    emProcessor.setClusterName("MSTCluster EMCal");
-    emProcessor.registerMetrics(metrics);
-    emProcessor.useEndcap(false); 
+    //  create the processor, use all EMCal hits as input objects
+    MSTClusterProcessor emProcessor = new MSTClusterProcessor("EMCal"); <br>
+    //  filter the input list, accept only hits in the barrel for clustering
+    emProcessor.setInputDecision(new OmitEndcap()); <br>
+    //  set the name of the output list
+    emProcessor.setClusterName("MSTCluster EMCal"); <br>
+    //  use 3D distance definition
+    emProcessor.registerMetrics(new GeometricalDistance()); <br>
+    //  use a threshold of 3cm
+    emProcessor.setThreshold(3.0);  <br>
+    //  filter the output list, save only clusters with at least 5 hits
+    emProcessor.setOutputDecision(new SizeDecision(5));
     add(emProcessor);
- </pre></blockquote><p>
- It generates a ClusterList of MSTClusterCores in the LCDEvent. Valid
-    argument for the constructor are <tt> EMCal</tt> and <tt> HCal </tt>
-    in order to read the standard EMCal and HCal CalorimeterHits
-    Objects. In addition, a tag  <tt> User</tt> is provided that
-    allows the user to read in his own CalorimeterHits objects.
-    In the latter case, the <tt> setUserTag(string)</tt> has to be set
-    by the user in order to read the corresponding CalorimeterHits
-    Object from <tt> event</tt>.  
+ </pre></blockquote>
+    
+   @version 1.01
+   @author [log in to unmask], [log in to unmask]
 
- @author [log in to unmask]
- @version 1.0
+   Package: MST
+   
+   Modification Log: 
+      -- 23 Mar 2005: Version 1.0   by  [log in to unmask]
+      -- 04 Aug 2005: Version 1.01  by  [log in to unmask]
+                      - generalize hits to cluster
+		      - introduce filters in form of MSTDecisionMakerSingle an
+		        MSTDecisionMakerPair
 */
 
 public class MSTClusterProcessor extends AbstractProcessor{
 
     /**
-       Constructor for MSTCluster processing -- valid Arguments are
-       "EMCal" and "HCal"
+       Constructor for MST processing.
+       Valid Arguments are "EMCal", "HCal", and "User".
+       "User" indicates that the user will supply a tag via setUserTag().
      */
     public MSTClusterProcessor(String inString){ 
 
 	calType = inString; 
 	if ( calType != "EMCal" && calType != "HCal" && calType != "User" )
-	    throw new AssertionError("Wrong Calorimter Type Specified"); 
+	    throw new AssertionError("Wrong Calorimter Type Specified. Options are: \n - EMCal \n - HCal \n - User"); 
 
 	printInitialization(); 
     }
 
-    private boolean lUseEndcap = true; 
     private double threshold = 3.0; 
     private String calType = "EMCal"; 
     private String userTag; 
     private String clusterName = "MSTCluster "+calType; 
     private MSTAbstractMetrics metrics = new GeometricalDistance(); 
+    private MSTDecisionMakerSingle inputDecision = new DummyDecisionMaker();
+    private MSTDecisionMakerSingle seedDecision = new DummyDecisionMaker();
+    private MSTDecisionMakerPair pairDecision = new DummyDecisionMaker();
+    private MSTDecisionMakerSingle outputDecision = new DummyDecisionMaker();
 
+    /**
+       Per-event loop.
+    */
     public void process(LCDEvent event){
 
-	CalorimeterHits hits; 
-	CalorimeterCell cell = event.getCalorimeterCell(); 
-
-	hits = event.getEMCalorimeterHits(); 
-	if ( calType == "HCal" ) hits = event.getHADCalorimeterHits(); 
-	if ( calType == "User" ) {
-	    if ( userTag == null )
-		throw new AssertionError("No userTag Defined"); 
-	    hits = (CalorimeterHits) event.get(userTag); 
+	// Find the name of the per-event object to use:
+	String listToGet = null;
+	if ( calType == "EMCal" ) {
+	    listToGet = "EMCalorimeterHits";
+	} else if ( calType == "HCal" ) {
+	    listToGet = "HADCalorimeterHits";
+	} else if ( calType == "User" ) {
+	    if ( userTag == null ) {
+		throw new AssertionError("User tag requested but not defined!");
+	    }
+	    listToGet = userTag;
+	} else {
+	    throw new AssertionError("Calorimeter type '"+calType+"' not recognized.");
 	}
-	MSTClusterBuilder clusterBuilder = new MSTClusterBuilder(hits, cell); 
+	
+	MSTClusterList inputList = new MSTClusterList(listToGet,event);
+	Vector vList = inputList.getClusterVector(inputDecision);
 
+	MSTClusterBuilder clusterBuilder = new MSTClusterBuilder( vList, event ); 
 	clusterBuilder.registerMetrics(metrics); 
 	clusterBuilder.setThreshold(threshold); 
-	clusterBuilder.useEndcap(lUseEndcap); 
-	clusterBuilder.initialize(); 
-	event.put(clusterName, 
-		  new MSTClusterList(clusterBuilder.doClustering()) );  
+	clusterBuilder.setSeedDecision(seedDecision);
+	clusterBuilder.setPairDecision(pairDecision);
+	clusterBuilder.setOutputDecision(outputDecision);
+	Vector outputVector = clusterBuilder.doClustering();
+	MSTClusterList outputList = new MSTClusterList(outputVector);
+	event.put(clusterName, outputList );  
     }
     
     /**
-       Set threshold for starting a new ClusterCore 
+       Set threshold for starting a new Cluster.
        (default threshold=3.0)
      */
     public void setThreshold(double value){
@@ -90,7 +144,7 @@
     }
 
     /**
-       Set name for ClusterList 
+       Set name for output ClusterList.
        (default "MSTCluster EMCal")
      */
     public void setClusterName(String string){
@@ -99,27 +153,61 @@
     }
 
     /**
-       Register metrics to be used in clustering
+       Register metrics to be used in clustering. The metric must be of a
+       class extending MSTAbstractMetrics and defines a distance between two
+       MSTCluster.
      */
     public void registerMetrics(MSTAbstractMetrics inMetrics){
 	metrics = inMetrics; 
     }
 
+    /**
+       Only objects passing the input filter are considered for clustering.
+       The input filter must be of a class implementing MSTDecisionMakerSingle.
+       Default is DummyDecisionMaker().
+    */
+    public void setInputDecision(MSTDecisionMakerSingle decision){
+	inputDecision = decision;}
+
+    /**
+       Only objects passing the seed filter are used to start a new cluster.
+       The seed filter must be of a class implementing MSTDecisionMakerSingle.
+       Default is DummyDecisionMaker().
+    */
+    public void setSeedDecision(MSTDecisionMakerSingle decision){
+	seedDecision = decision;}
+
+    /**
+       Two objects are only clustered together, if they pass the pair filter.
+       The pair filter must be of a class implementing MSTDecisionMakerPair.
+       Default is DummyDecisionMaker().
+    */
+    public void setPairDecision(MSTDecisionMakerPair decision){
+	pairDecision = decision;}
+
+    /**
+       Only objects passing the output filter get written to the ouput list.
+       The output filter must be of a class implementing MSTDecisionMakerSingle.
+       Default is DummyDecisionMaker().
+    */
+    public void setOutputDecision(MSTDecisionMakerSingle decision){
+	outputDecision = decision;}
+
+    /**
+       This tag is used to identify a user-defined input list. This tag may refer
+       to an object of type CalorimeterHits or ClusterList, which is store in the
+       event.
+    */
     public void setUserTag(String tag){
 	userTag = tag; 
     }
 
     private void printInitialization(){
 	System.out.println(""); 
-	System.out.println("MSTClusterBuilder v1.0 "+calType); 
+	System.out.println("MSTClusterBuilder v1.0 "+calType);
+	if ( calType == "User" )
+	    System.out.println("  input list: "+userTag);
 	System.out.println("----------------------"); 
     }
 
-    /**
-       Include Hits in the Endcaps according to the value of "bool"
-     */
-    public void useEndcap(boolean bool){
-	lUseEndcap = bool; 
-	System.out.println("Use Endcap: "+bool); 
-    }
 }

lcd/hep/lcd/recon/cluster/MST
MSTHit.java 1.1 -> 1.2
diff -u -r1.1 -r1.2
--- MSTHit.java	12 Apr 2005 16:44:20 -0000	1.1
+++ MSTHit.java	4 Aug 2005 22:58:28 -0000	1.2
@@ -3,66 +3,142 @@
 import java.io.*; 
 import java.util.*; 
 
+import hep.lcd.event.LCDEvent;
 import hep.lcd.event.MCParticle; 
 import hep.lcd.event.CalorimeterHit; 
+import hep.lcd.event.CalorimeterHits; 
 import hep.lcd.geometry.CalorimeterCell; 
 
 /**
- Extends definition of CalorimeterHits. In particular,
- coordinates and layer information are cached here for
- performance reasons.
-
- @author [log in to unmask]
- @version 1.0
+ <p>
+ Extends definition of CalorimeterHit. Additional features are <br>
+ - Cached information for faster processing <br>
+ - Number density of neighboring cells <br>
+ - Energy density of neighboring cells <br>
+ </p><p>
+ For calculating the density informations, a Hashtable with all EMCal and HADCal hits as objects and their TowerIDs as keys is used. If this Hashtable does not exist in <tt>event</tt>, it is created and written to <tt>event</tt> with the nametag <tt>Hashtable TowerID,CalorimeterHits</tt>.
+</p>
+
+ @author [log in to unmask], [log in to unmask]
+ @version 1.01
+
+ Modification log:
+
+  Aug 03, 2005: Version 1.01 by [log in to unmask]
+                - introduce number and energy densities; need to change
+	          constructor arguments to include an LCDEvent instead 
+	          of a CalorimeterCell
 */
 
 public class MSTHit implements CalorimeterHit{
-    public MSTHit(  CalorimeterHit hit
-		  , CalorimeterCell cell) {
-	coordinates = cell.getPosition(); 
-	layer = cell.getLayer(); 
-	radius = calculateRadius(); 
 
+    /**
+       Construct an MSTHit object given a CalorimeterHit.<br>
+    */
+    public MSTHit(  CalorimeterHit hit, LCDEvent event ) {
 	time = hit.getTime(); 
 	contributedEnergy = hit.getContributedEnergy(); 
 	energy = hit.getEnergy(); 
 	particles = hit.getMCParticles(); 
 	towerID = hit.getTowerID(); 
 	contributedTime = hit.getContributedTime(); 
+ 
+	thisEvent = event;
+	thisCell = event.getCalorimeterCell();
+	thisCell.setTowerID(towerID);
+	coordinates = thisCell.getPosition(); 
+	layer = thisCell.getLayer(); 
+	radius = calculateRadius(); 
+	
+	tableTowerID = new Hashtable();
+	boolean exists = false;
+	Enumeration k = event.keys();
+	while (k.hasMoreElements()){
+	    String s = (String)k.nextElement();
+	    if (s=="Hashtable TowerID,CalorimeterHits") exists = true;
+	}
+	if (exists && event.get("Hashtable TowerID,CalorimeterHits") instanceof Hashtable) {
+	    tableTowerID = (Hashtable) event.get("Hashtable TowerID,CalorimeterHits");
+	} else {
+	    CalorimeterHits hits = (CalorimeterHits)event.getEMCalorimeterHits();
+	    Enumeration iter = hits.getHits();
+	    while (iter.hasMoreElements()){
+		CalorimeterHit h = (CalorimeterHit) iter.nextElement();
+		tableTowerID.put(new Integer(h.getTowerID()),h);
+	    }
+	    event.put("Hashtable TowerID,CalorimeterHits",tableTowerID);
+	}
+	int[] neighbours = thisCell.getNeighbouringCells();
+	densityNumber = 1;
+	densityEnergy = energy;
+	for (int i=0; i<neighbours.length; i++){
+	    Integer n = new Integer(neighbours[i]);
+	    if (tableTowerID.containsKey(n)){
+		CalorimeterHit h = (CalorimeterHit)tableTowerID.get(n);
+		densityNumber++;
+		densityEnergy+=h.getEnergy();
+	    }
+	}
     }
 
+    /**
+       Get contributed time of MC particles in CalorimeterHit (cached information)
+     */
     public double[] getContributedTime(){
 	return ((double[]) contributedTime); 
     }
+
+    /**
+       Get time of CalorimeterHit (cached information)
+     */
     public double getTime(){
 	return ((double) time);
     }
 
+    /**
+       Get contributed energy of MC particles in CalorimeterHit (cached information)
+     */
     public double[] getContributedEnergy(){
 	return contributedEnergy; 
     }
+
+    /**
+       Get energy of CalorimeterHit (cached information)
+     */
     public double getEnergy(){
 	return energy; 
     }
+
+    /**
+       Get MC particles contributing to CalorimeterHit (cached information)
+     */
     public MCParticle[] getMCParticles(){
 	return particles; 
     }
+
+    /**
+       Get tower ID of CalorimeterHit (cached information)
+     */
     public int getTowerID(){
 	return towerID; 
     }
 
+    /**
+       Get coordinates, taken from the tower ID of CalorimeterHit (cached information)
+     */
     public double[] getCoordinates(){
 	return coordinates; 
     }
+
     /**
-       Get Layer of CalorimeterHit
+       Get layer, taken from the tower ID of CalorimeterHit (cached information)
      */
     public int getLayer(){
 	return ((int) layer); 
     }
 
     /**
-       Get Radius of CalorimeterHit in r-phi
+       Get radius in r-phi, calculated from the tower ID of CalorimeterHit (cached information)
      */
     public double getRadius(){
 	return((double) radius); 
@@ -73,6 +149,20 @@
 	return ((double) Math.sqrt(tmp)); 
     }
 
+    /** 
+	Get number density of CalorimeterHit, calculated from neighboring hits in LCDEvent
+     */
+    public double getNumberDensity(){
+	return densityNumber;
+    }
+
+    /** 
+	Get energy density of CalorimeterHit, calculated from neighboring hits in LCDEvent
+     */
+    public double getEnergyDensity(){
+	return densityEnergy;
+    }
+
     private double[] contributedEnergy; 
     private double time; 
     private double[] contributedTime; 
@@ -83,4 +173,11 @@
     private int layer; 
     private double radius; 
     private double[] coordinates = new double[3]; 
+
+    private Hashtable tableTowerID;
+    private double densityNumber;
+    private double densityEnergy;
+
+    private CalorimeterCell thisCell;
+    private LCDEvent thisEvent;
 }

lcd/hep/lcd/recon/cluster/MST
MSTClusterCore.java removed after 1.1
diff -N MSTClusterCore.java
--- MSTClusterCore.java	12 Apr 2005 16:44:20 -0000	1.1
+++ /dev/null	1 Jan 1970 00:00:00 -0000
@@ -1,74 +0,0 @@
-package hep.lcd.recon.cluster.MST; 
-
-import java.io.*; 
-import java.util.*; 
-
-import hep.lcd.event.MCParticle; 
-import hep.lcd.geometry.CalorimeterCell; 
-import hep.lcd.recon.cluster.util.AbstractCluster; 
-
-/**
-   Class for ClusterCore Candidates
-
-   @version 1.0
-   @author [log in to unmask]
-*/
-
-public class MSTClusterCore extends AbstractCluster {
-
-    public MSTClusterCore(CalorimeterCell cell){
-	super(cell);
-    }
-
-    private void calculateExtendedQuantities(){
-	calculatePurity(); 
-	lCalculated = true; 
-    }
-    
-    /**
-       Fraction of enery deposited by AlphaParticle realative 
-       to total energy in MSTClusterCore
-     */
-    public double getPurity(){
-	if ( !lCalculated ) calculateExtendedQuantities(); 
-	return ((double) purity); 
-    }
-
-    private void calculatePurity(){
-
-	int pMax = -1; 
-	double eMax = 0.; 
-	double eSum = 0.; 
-
-	MCParticle[] particles = getMCParticles(); 
-	double[] energies = getContributedEnergy(); 
-
-	for ( int i=0; i<particles.length; i++ ){
-	    eSum += energies[i]; 
-
-	    if ( energies[i] > eMax ){
-		eMax = energies[i]; 
-		pMax = i; 
-	    }
-	}
-	purity = energies[pMax]/eSum; 
-	setAlphaParticle(particles[pMax]); 
-    }
-
-    private void setAlphaParticle(MCParticle particle){
-	alphaParticle = particle; 
-    }
-    
-    /**
-       Get MCParticle contributing the most energy to the MSTClusterCore
-     */
-    public MCParticle getAlphaParticle(){
-	if ( !lCalculated ) calculateExtendedQuantities(); 
-	return ((MCParticle) alphaParticle); 
-    }
-    
-    private double purity = -1.; 
-    private boolean lCalculated = false; 
-    private MCParticle alphaParticle; 
-}
-
CVSspam 0.2.8