Commit in lcsim/src/org/lcsim/recon/cluster/structural on MAIN
CheatFragmentMerger.java+228added 1.1
FragmentHandler.java+114added 1.1
FragmentMerger.java+39added 1.1
SimpleFragmentMerger.java+140added 1.1
+521
4 added files
Fragment-handling routines for structural algorithm

lcsim/src/org/lcsim/recon/cluster/structural
CheatFragmentMerger.java added at 1.1
diff -N CheatFragmentMerger.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ CheatFragmentMerger.java	4 Jul 2006 23:59:53 -0000	1.1
@@ -0,0 +1,228 @@
+package org.lcsim.recon.cluster.structural;
+
+import java.util.List;
+import java.util.Vector;
+import java.util.Map;
+import java.util.HashMap;
+import org.lcsim.util.Driver;
+import org.lcsim.event.Cluster;
+import org.lcsim.event.EventHeader;
+import org.lcsim.event.CalorimeterHit;
+import org.lcsim.recon.cluster.util.BasicCluster;
+import org.lcsim.recon.cluster.analysis.*;
+
+/**
+ * Merge fragments into other clusters (cheating).
+ * The rules are that we cannot do primary fragment ID.
+ * So here is how the algorithm looks:
+ *
+ * For each fragment:
+ *   1) Find the dominant MC particle
+ *   2) Find the dominant cluster of that MC particle
+ *   3) If that cluster is in the "nonFragments" list, merge into it
+ *   4) If not, put the fragment to one side
+ * 
+ * Then for fragments left over:
+ *   5) If the dominant cluster was a fragment that got merged into a
+ *      nonFragment, also merge this fragment into the same nonFragment
+ *   6) If the dominant cluster was a fragment that did not get merged,
+ *      promote it to nonFragment and merge into it.
+ */
+
+public class CheatFragmentMerger extends Driver implements FragmentMerger
+{
+    public CheatFragmentMerger() {
+	super();
+    }
+    public void process(EventHeader event) {
+	super.process(event);
+	m_event = event;
+    }
+
+    // Support for association:
+    transient protected EventHeader m_event = null;
+    protected CreateClusterAnalysisLists m_clusterAssociator = null;
+    protected String m_clusterAssociatorOutputListMCParticleToCluster;
+    protected String m_clusterAssociatorOutputListClusterToMCParticle;
+    protected String[] m_clusterAssociatorHitListNames;
+    protected String[] m_clusterAssociatorClusterListNames;
+    protected String m_clusterAssociatorMCListName;
+    public void initializeAssociator(String hitListNameToCreate, String clusterListNameToCreate, String mcListName, String outputListMCParticleToCluster, String outputListClusterToMCParticle) {
+	m_clusterAssociatorHitListNames = new String[1];
+	m_clusterAssociatorClusterListNames = new String[1];
+	m_clusterAssociatorHitListNames[0] = hitListNameToCreate;
+	m_clusterAssociatorClusterListNames[0] = clusterListNameToCreate;
+	m_clusterAssociatorMCListName = mcListName;
+	m_clusterAssociatorOutputListMCParticleToCluster = outputListMCParticleToCluster;
+	m_clusterAssociatorOutputListClusterToMCParticle = outputListClusterToMCParticle;
+	m_clusterAssociator = new CreateClusterAnalysisLists(m_clusterAssociatorHitListNames, m_clusterAssociatorClusterListNames, m_clusterAssociatorMCListName, m_clusterAssociatorOutputListMCParticleToCluster, m_clusterAssociatorOutputListClusterToMCParticle);
+    }
+
+    /**
+     * Merge or otherwise handle the lists of fragments and non-fragments.
+     * 
+     * @param fragments    Input list of fragment Clusters
+     * @param nonFragments Input list of primary Clusters
+     * @return             List of merged/manipulated Clusters
+     */
+    public List<Cluster> mergeFragments(List<Cluster> fragments, List<Cluster> nonFragments)
+    {
+	// Association:
+	doAssoc(fragments, nonFragments);
+
+	// Our output will be a new set of clusters that contain the old ones.
+	// So we'll need wrapped output, starting with the nonFragments.
+	List<Cluster> outputClusters = new Vector<Cluster>();
+	Map<Cluster,BasicCluster> mapToWrappedClusters = new HashMap<Cluster,BasicCluster> ();
+	for (Cluster nonfrag : nonFragments) {
+	    BasicCluster newCluster = new BasicCluster();
+	    newCluster.addCluster(nonfrag);
+	    mapToWrappedClusters.put(nonfrag,newCluster);
+	    outputClusters.add(newCluster);
+	}
+	
+	// OK. For each fragment, find the immediate parent.
+	Map<Cluster,Cluster> mapFragmentsToParentNonFragments = new HashMap<Cluster,Cluster> ();
+	Map<Cluster,Cluster> mapFragmentsToParentFragments = new HashMap<Cluster,Cluster> ();
+        for (Cluster fragment : fragments) {
+            Cluster target = findBestMerge(fragment, nonFragments, fragments);	
+	    if (nonFragments.contains(target)) {
+		mapFragmentsToParentNonFragments.put(fragment, target);		
+	    } else {
+		assert(fragments.contains(target)); // sanity check
+		mapFragmentsToParentFragments.put(fragment, target);		
+	    }
+	}
+
+	// Handle the fragment merging carefully.
+	// We use a map from fragments to any associated daughters.
+	// Initially, each fragment goes in as a parent with no daughters.
+	Map<Cluster,List<Cluster>> mapFragmentsToDaughters = new HashMap<Cluster,List<Cluster>>();
+	for (Cluster fragment : mapFragmentsToParentNonFragments.keySet()) {
+	    mapFragmentsToDaughters.put(fragment, new Vector<Cluster>());
+	}
+
+	// We will loop around until the keySet of the map holds only fragments
+	// which do not have fragment parents. This can happen because:
+	//   a) Their parent is a nonFragment
+	//   b) They have no parent
+	//   c) They have a fragment parent, but it is ultimately one of their
+	//      own daughters (i.e. a loop exists)
+	boolean noChangeThisPass = false;
+	while (!noChangeThisPass) {
+	    noChangeThisPass = true;
+	    for (Cluster fragment : mapFragmentsToDaughters.keySet()) {
+		List<Cluster> fragmentDaughters = mapFragmentsToDaughters.get(fragment);
+		Cluster fragmentParent = mapFragmentsToParentFragments.get(fragment);
+		if (fragmentParent != null) {
+		    // Find it
+		    for (Cluster testFragment : mapFragmentsToDaughters.keySet()) {
+			if (fragment == testFragment) { break; }
+			List<Cluster> testDaughters = mapFragmentsToDaughters.get(testFragment);
+			if (testFragment == fragmentParent || testDaughters.contains(fragmentParent)) {
+			    // Match
+			    testDaughters.add(fragment);
+			    testDaughters.addAll(fragmentDaughters);
+			    mapFragmentsToDaughters.remove(fragment);
+			    noChangeThisPass = false;
+			    break;
+			}
+		    }
+		}
+		if (!noChangeThisPass) { break; }
+	    }
+	}
+	
+	// Finished looping
+	for (Cluster fragment : mapFragmentsToDaughters.keySet()) {
+	    List<Cluster> fragmentDaughters = mapFragmentsToDaughters.get(fragment);
+	    Cluster nonFragmentParent = mapFragmentsToParentNonFragments.get(fragment);
+	    if (nonFragmentParent != null) {
+		BasicCluster wrappedParent = mapToWrappedClusters.get(nonFragmentParent);
+		wrappedParent.addCluster(fragment);
+		for (Cluster daughter : fragmentDaughters) {
+		    wrappedParent.addCluster(daughter);
+		}
+	    } else {
+		// Promote
+		BasicCluster wrappedFragment = new BasicCluster();
+		wrappedFragment.addCluster(fragment);
+		for (Cluster daughter : fragmentDaughters) {
+		    wrappedFragment.addCluster(daughter);
+		}
+		// Add to output
+		outputClusters.add(wrappedFragment);
+	    }
+	}
+
+	{
+	    int nFragmentClusters = fragments.size();
+	    int nNonFragmentClusters = nonFragments.size();
+	    int nFragmentHits = 0;
+	    int nNonFragmentHits = 0;
+	    for (Cluster clus :    fragments) {    nFragmentHits += clus.getCalorimeterHits().size(); }
+	    for (Cluster clus : nonFragments) { nNonFragmentHits += clus.getCalorimeterHits().size(); }
+	    System.out.println(this.getClass()+": Input was "+nFragmentClusters+" fragments with "+nFragmentHits+" hits plus "+nNonFragmentClusters
+			       +" non-fragments with "+nNonFragmentHits+" hits ("+(nFragmentHits+nNonFragmentHits)+" total)");
+	    int nOutputClusters = outputClusters.size();
+	    int nOutputHits = 0;
+	    for (Cluster clus : outputClusters) { nOutputHits += clus.getCalorimeterHits().size(); }
+	    System.out.println(this.getClass()+": Output was "+nOutputClusters+" clusters with "+nOutputHits+" hits");
+	}
+	     
+	// Return output, which is the wrapped clusters with fragments merged in.
+	return outputClusters;
+    }
+
+    protected Cluster findBestMerge(Cluster fragment, List<Cluster> nonFragments, List<Cluster> fragments)
+    {
+	// Here we need to cheat.
+
+	// Look for the list of cluster info objects
+	List<ClusterMCPInfo> clusterInfoList = m_event.get(ClusterMCPInfo.class, m_clusterAssociatorOutputListClusterToMCParticle);
+	
+	// Find the info:
+	ClusterMCPInfo info = null;
+	for (ClusterMCPInfo currentInfo : clusterInfoList) {
+	    Cluster currentCluster = currentInfo.getCluster();
+	    if (currentCluster == fragment) { 
+		info = currentInfo; 
+	    }
+	}
+
+	if (info == null) {
+	    throw new java.lang.NullPointerException("ERROR: Info not found");
+	} else {
+	    // Found it OK.
+	    // We look up which the dominant contributing MCParticle is.
+	    // Then we look at that MCParticle and see which cluster it
+	    // puts the most energy in (the dominant cluster of that particle).
+	    
+	    MCPClusterInfo maxParticleInfo = info.getMaxMCPC();
+	    Cluster maxClusterOfMaxParticle = maxParticleInfo.getMaxCluster();
+	    if (maxClusterOfMaxParticle == fragment) {
+		// It happens to be a primary already somehow.
+		return null;
+	    } else {
+		// Found a parent. Check it's in one of the lists:
+		assert(nonFragments.contains(maxClusterOfMaxParticle) || fragments.contains(maxClusterOfMaxParticle));
+		return maxClusterOfMaxParticle;
+	    }
+	}
+    }
+
+    private void doAssoc(List<Cluster> fragments, List<Cluster> nonFragments) {
+	List<CalorimeterHit> hitList = new Vector<CalorimeterHit>();
+	List<Cluster> clusterList = new Vector<Cluster>();
+	clusterList.addAll(fragments);
+	clusterList.addAll(nonFragments);
+	for (Cluster clus : clusterList) {
+	    hitList.addAll(clus.getCalorimeterHits());
+	}
+	m_event.put(m_clusterAssociatorHitListNames[0], hitList);
+	m_event.put(m_clusterAssociatorClusterListNames[0], clusterList);
+	// Go!
+	m_clusterAssociator.CreateLists(m_event);
+    }
+
+}

lcsim/src/org/lcsim/recon/cluster/structural
FragmentHandler.java added at 1.1
diff -N FragmentHandler.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ FragmentHandler.java	4 Jul 2006 23:59:53 -0000	1.1
@@ -0,0 +1,114 @@
+package org.lcsim.recon.cluster.structural;
+
+import java.util.*; 
+import org.lcsim.util.*;
+import org.lcsim.event.*;
+import org.lcsim.recon.cluster.util.BasicCluster;
+import org.lcsim.util.hitmap.HitMap;
+
+/**
+ * A Driver to handle fragments. Each event, clusters and individual
+ * hits are read in. A <code>FragmentIdentifier</code> is applied to
+ * the clusters to determine which are fragments. The individual hits
+ * are also treated as fragments. The lists of fragments and
+ * non-fragments are then passed to a <code>FragmentMerger</code>
+ * for further processing. Afterwards, the output cluster list is
+ * written to the event, as is a HitMap with any unused hits.
+ *
+ * The input lists and clusters are left unchanged.
+ *
+ * @see FragmentIdentifier
+ * @see FragmentMerger
+ * @version $Id: FragmentHandler.java,v 1.1 2006/07/04 23:59:53 mcharles Exp $
+ */
+
+public class FragmentHandler extends Driver
+{
+    /**
+     * Constructor.
+     */
+    public FragmentHandler() {}
+
+    /**
+     * Process one event.
+     */
+    public void process(EventHeader event) 
+    {
+	// Input Clusters
+	List<Cluster> inputClusters = new Vector<Cluster>();
+	for (String name : m_inputClusterListNames) {
+	    List<Cluster> clist = event.get(Cluster.class, name);
+	    inputClusters.addAll(clist);
+	}
+
+	// Initially, fill the output hitmap with all hits
+	HitMap leftoverHits = new HitMap();
+	for (Cluster clus : inputClusters) {
+	    for (CalorimeterHit hit : clus.getCalorimeterHits()) {
+		Long id = new Long(hit.getCellID());
+		leftoverHits.put(id,hit);
+	    }
+	}
+
+	// Sort the input clusters into fragments and non-fragments
+	List<Cluster> fragments = new Vector<Cluster>();
+	List<Cluster> nonFragments = new Vector<Cluster>();
+	for (Cluster clus : inputClusters) {
+	    boolean isFragment = m_fragmentID.isFragment(clus, event);
+	    if (isFragment) {
+		fragments.add(clus);
+	    } else {
+		nonFragments.add(clus);
+	    }
+	}
+
+	// Add in any loose hits (treat as one-hit clusters)
+	for (String name : m_inputHitMapNames) {
+	    Map<Long,CalorimeterHit> map = (Map<Long,CalorimeterHit>)(event.get(name));
+	    leftoverHits.putAll(map);
+	    for (CalorimeterHit hit : map.values()) {
+		BasicCluster cl = new BasicCluster();
+		cl.addHit(hit);
+		fragments.add(cl);
+	    }
+	}
+	
+	// Merge:
+	List<Cluster> mergedClusters = m_fragmentMerger.mergeFragments(fragments, nonFragments);
+	    
+	// Take used hits out of the "leftoverHits" hitmap. Whatever is
+	// left is the set of unused hits.
+	for (Cluster clus : mergedClusters) {
+	    for (CalorimeterHit hit : clus.getCalorimeterHits()) {
+		Long id = new Long(hit.getCellID());		
+		leftoverHits.remove(id);
+	    }
+	}
+
+	// Output
+	event.put(m_outputClusterListName, mergedClusters);
+	event.put(m_outputHitMapName, leftoverHits);
+    }
+
+    /** Add another named list of clusters. */
+    public void addInputClusterList(String name) {m_inputClusterListNames.add(name);}
+    /** Add another named HitMap. */
+    public void addInputHitMap(String name) {m_inputHitMapNames.add(name);}
+    /** Set the name to write the modified clusters to. */
+    public void setOutputClusterList(String name) {m_outputClusterListName=name;}
+    /** Set the name to write the HitMap of unused hits to. */
+    public void setOutputHitMap(String name) {m_outputHitMapName=name;}
+    /** Specify the FragmentIdentifier to use. */
+    public void setFragmentIdentifier(FragmentIdentifier id) {m_fragmentID=id;}
+    /** Specify the FragmentMerger to use. */
+    public void setFragmentMerger(FragmentMerger merger) {m_fragmentMerger=merger;}
+
+    List<String> m_inputClusterListNames = new Vector<String>();
+    List<String> m_inputHitMapNames = new Vector<String>();
+    
+    String m_outputClusterListName;
+    String m_outputHitMapName;
+    FragmentIdentifier m_fragmentID;
+    FragmentMerger m_fragmentMerger;
+				
+}

lcsim/src/org/lcsim/recon/cluster/structural
FragmentMerger.java added at 1.1
diff -N FragmentMerger.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ FragmentMerger.java	4 Jul 2006 23:59:53 -0000	1.1
@@ -0,0 +1,39 @@
+package org.lcsim.recon.cluster.structural;
+
+import java.util.List;
+import org.lcsim.event.Cluster;
+import org.lcsim.event.EventHeader;
+
+/**
+ * Interface for a class that takes in lists of fragments
+ * and non-fragments, then manipulates them. The conventional
+ * behaviour is to try to merge the fragments in with their
+ * parent non-fragments, but other forms of manipulation 
+ * are possible (e.g. purging all fragments).
+ *
+ * Implementing classes should leave the input clusters unchanged.
+ * Do <B>not</B> merge like this:
+ * <BLOCKQUOTE>
+ *   nonFragment.addCluster(fragment);
+ * <BLOCKQUOTE>
+ * Instead, create a new cluster and add to it like this:
+ * <BLOCKQUOTE>
+ *      BasicCluster newCluster = new BasicCluster();
+ * <BR> newCluster.addCluster(nonFragment);
+ * <BR> newCluster.addCluster(fragment); // repeated for each fragment to be merged into this cluster
+ * </BLOCKQUOTE>
+ *
+ * @version $Id: FragmentMerger.java,v 1.1 2006/07/04 23:59:53 mcharles Exp $
+ */
+
+public interface FragmentMerger
+{
+    /**
+     * Merge or otherwise handle the lists of fragments and non-fragments.
+     * 
+     * @param fragments    Input list of fragment Clusters
+     * @param nonFragments Input list of primary Clusters
+     * @return             List of merged/manipulated Clusters
+     */
+    public List<Cluster> mergeFragments(List<Cluster> fragments, List<Cluster> nonFragments);
+}

lcsim/src/org/lcsim/recon/cluster/structural
SimpleFragmentMerger.java added at 1.1
diff -N SimpleFragmentMerger.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ SimpleFragmentMerger.java	4 Jul 2006 23:59:53 -0000	1.1
@@ -0,0 +1,140 @@
+package org.lcsim.recon.cluster.structural;
+
+import java.util.*;
+import hep.physics.vec.*;
+import org.lcsim.event.*;
+import org.lcsim.recon.cluster.util.BasicCluster;
+
+/**
+ * A simple implementation of <code>FragmentMerger</code>.
+ * For each fragment, find the best-match non-fragment and
+ * merge it. The best-match is determined using the
+ * protected method <code>findBestMerge</code>. The default
+ * is the nearest (hit-hit distance) non-fragment, but this
+ * can be changed by extending this class and over-riding
+ * that method.
+ *
+ * @version $Id: SimpleFragmentMerger.java,v 1.1 2006/07/04 23:59:53 mcharles Exp $
+ */
+
+public class SimpleFragmentMerger implements FragmentMerger
+{
+    /**
+     * Constructor.
+     */
+    public SimpleFragmentMerger() {}
+
+    /**
+     * Merge the fragments in with the nonFragments. The input lists
+     * are left unaltered.
+     *
+     * @return The list of merged Clusters
+     */
+    public List<Cluster> mergeFragments(List<Cluster> fragments, List<Cluster> nonFragments)
+    {
+	// Our output will be a new set of clusters that contain the old ones.
+	// So we'll need wrapped output:
+	Map<Cluster,BasicCluster> mapToWrappedClusters = new HashMap<Cluster,BasicCluster> ();
+	for (Cluster nonfrag : nonFragments) {
+	    BasicCluster newCluster = new BasicCluster();
+	    newCluster.addCluster(nonfrag);
+	    mapToWrappedClusters.put(nonfrag,newCluster);
+	}
+
+	// First pass: loop over fragments and find the best merge...
+	Map<Cluster,Cluster> mapFragmentsToParents = new HashMap<Cluster,Cluster> ();
+	List<Cluster> unmappedFragments = new Vector<Cluster>();
+        for (Cluster fragment : fragments) {
+            Cluster target = findBestMerge(fragment, nonFragments, fragments);
+            if (target != null) {
+		mapFragmentsToParents.put(fragment, target);
+	    } else {
+		unmappedFragments.add(fragment);
+	    }
+	}
+
+	// Second pass: Do the merges.
+	//   If this implementation can merge fragments into other fragments,
+	//   there is a danger that we will wind up with fragments that are
+	//   never merged into a non-fragment and hence never written out.
+	for (Cluster fragment : mapFragmentsToParents.keySet()) {
+	    Cluster target = mapFragmentsToParents.get(fragment);
+	    BasicCluster wrappedTarget = mapToWrappedClusters.get(target);
+	    wrappedTarget.addCluster(fragment);
+        }
+
+	// Return output, which is the wrapped non-fragments with fragments merged in.
+	List<Cluster> output = new Vector<Cluster>();
+	output.addAll(mapToWrappedClusters.values());
+	return output;
+    }
+
+    protected Cluster findBestMerge(Cluster fragment, List<Cluster> nonFragments, List<Cluster> fragments)
+    {
+        // This is kind of dumb.
+        // What's the nearest non-fragment using hit-hit distance?
+        Cluster nearest = null;
+        double minDistance = 0;
+        for (Cluster nonFragment : nonFragments) {
+            double dist = distance(fragment, nonFragment);
+            if (dist<minDistance || nearest==null) {
+                nearest = nonFragment;
+                minDistance = dist;
+            }
+        }
+        if (nearest == null) {
+            if (nonFragments.size() != 0) {
+                throw new AssertionError("BUG: There are non-fragments, but none is the nearest");
+            } else {
+                return null;
+            }
+        } else {
+            return nearest;
+        }
+    }
+
+
+    // This belongs outside this class.
+    private double distance(Cluster clus1, Cluster clus2)
+    {
+        // Loop over hits...
+        boolean firstCheck = true;
+        double minDistance = Double.NaN; // Will stay NaN if a cluster is empty
+        List<CalorimeterHit> hits = clus1.getCalorimeterHits();
+        for (CalorimeterHit hit : hits) {
+            double dist = distance(clus2, hit);
+            if (firstCheck || dist<minDistance) {
+                minDistance = dist;
+                firstCheck = false;
+            }
+        }
+
+        return minDistance;
+    }   
+    // This belongs outside this class.
+    private double distance(Cluster clus, CalorimeterHit hit)
+    {
+        // Loop over hits...
+        boolean firstCheck = true;
+        double minDistance = Double.NaN; // Will stay NaN if clus is empty
+        List<CalorimeterHit> hits = clus.getCalorimeterHits();
+        for (CalorimeterHit hitInCluster : hits) {
+            double dist = distance(hit, hitInCluster);
+            if (firstCheck || dist<minDistance) {
+                minDistance = dist;
+                firstCheck = false;
+            }
+        }
+
+        return minDistance;
+    }   
+    // This belongs outside this class.
+    private double distance(CalorimeterHit hit1, CalorimeterHit hit2)
+    {
+        Hep3Vector vect1 = new BasicHep3Vector(hit1.getPosition());
+        Hep3Vector vect2 = new BasicHep3Vector(hit2.getPosition());
+        Hep3Vector displacement = VecOp.sub(vect1, vect2);
+        return displacement.magnitude();
+    }
+
+}
CVSspam 0.2.8