Print

Print


Commit in lcsim/src/org/lcsim/contrib/uiowa on MAIN
ReclusterDriver.java+830added 1.1
MJC: Unfinished code to iteratively redo clustering

lcsim/src/org/lcsim/contrib/uiowa
ReclusterDriver.java added at 1.1
diff -N ReclusterDriver.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ReclusterDriver.java	14 Oct 2007 22:08:10 -0000	1.1
@@ -0,0 +1,830 @@
+package org.lcsim.contrib.uiowa;
+
+import java.util.*; 
+import hep.physics.vec.*;
+import org.lcsim.util.*;
+import org.lcsim.util.hitmap.*;
+import org.lcsim.event.*;
+import org.lcsim.event.util.*;
+import org.lcsim.recon.cluster.mipfinder.*;
+import org.lcsim.recon.cluster.util.*;
+import org.lcsim.recon.cluster.clumpfinder.*;
+import org.lcsim.recon.pfa.identifier.*;
+import org.lcsim.mc.fast.tracking.ReconTrack;
+import org.lcsim.event.base.*;
+import hep.physics.particle.Particle;
+
+public class ReclusterDriver extends Driver {
+
+    String m_mcList;
+    String m_inputSmallClusters;
+    String m_inputUnusedHits;
+    String m_inputSkeletonClusters;
+    String m_inputPhotonClusters;
+    String m_inputMuonParticles;
+    String m_inputTrackList;
+    String m_inputLargeClusters;
+    String m_inputMIPs;
+    String m_inputClumps;
+    String m_inputSplitSkeletonClusters;
+    EventHeader m_event;
+
+    public ReclusterDriver(String mcList, String trackList, String muonParticles, String photonClusters, String skeletonClusters, String smallClusters, String unusedHits, String largeClusters, String mips, String clumps, String splitSkeletonClusters) {
+	m_mcList = mcList;
+	m_inputTrackList = trackList;
+	m_inputMuonParticles = muonParticles;
+	m_inputPhotonClusters = photonClusters;
+	m_inputSkeletonClusters = skeletonClusters;
+	m_inputSmallClusters = smallClusters;
+	m_inputUnusedHits = unusedHits;
+	m_inputLargeClusters = largeClusters;
+	m_inputMIPs = mips;
+	m_inputClumps = clumps;
+	m_inputSplitSkeletonClusters = splitSkeletonClusters;
+
+	// For now, hard-coded
+	LocalHelixExtrapolationTrackClusterMatcher genMatch = new LocalHelixExtrapolationTrackClusterMatcher();
+	LocalHelixExtrapolationTrackMIPClusterMatcher mipMatch = new LocalHelixExtrapolationTrackMIPClusterMatcher();
+	add(mipMatch);
+	add(genMatch);
+	m_trackClusterMatcher = new DualActionTrackClusterMatcher(mipMatch, genMatch);
+    }
+
+    public void process(EventHeader event) {
+	super.process(event); // hmm...
+	m_event = event;
+
+	// Read in
+	List<Track> trackList = event.get(Track.class, m_inputTrackList);
+	List<ReconstructedParticle> muonParticles = event.get(ReconstructedParticle.class, m_inputMuonParticles);
+	List<Cluster> photonClusters = event.get(Cluster.class, m_inputPhotonClusters);
+	List<Cluster> skeletonClusters = event.get(Cluster.class, m_inputSkeletonClusters);
+	List<Cluster> smallClusters = event.get(Cluster.class, m_inputSmallClusters);
+	List<Cluster> largeClusters = event.get(Cluster.class, m_inputLargeClusters);
+	List<Cluster> mips = event.get(Cluster.class, m_inputMIPs);
+	List<Cluster> clumps = event.get(Cluster.class, m_inputClumps);
+	HitMap unusedHits = ((HitMap)(event.get(m_inputUnusedHits)));
+	List<Cluster> splitSkeletonClusters = event.get(Cluster.class, m_inputSplitSkeletonClusters);
+	
+	System.out.println("DEBUG: ReclusterDriver read in "+trackList.size()+" tracks");
+	System.out.println("DEBUG: ReclusterDriver read in "+muonParticles.size()+" muons");
+	System.out.println("DEBUG: ReclusterDriver read in "+photonClusters.size()+" photons");
+	System.out.println("DEBUG: ReclusterDriver read in "+skeletonClusters.size()+" skeletons");
+	System.out.println("DEBUG: ReclusterDriver read in "+largeClusters.size()+" large clusters");
+	System.out.println("DEBUG: ReclusterDriver read in "+mips.size()+" MIP clusters");
+	System.out.println("DEBUG: ReclusterDriver read in "+clumps.size()+" clump clusters");
+	System.out.println("DEBUG: ReclusterDriver read in "+unusedHits.values().size()+" unused hits");
+	System.out.println("DEBUG: ReclusterDriver read in "+splitSkeletonClusters.size()+" pre-split skeletons");
+
+	Map<Cluster,Cluster> mapMIPToSkeleton = new HashMap<Cluster,Cluster>();
+	Map<Cluster,Cluster> mapClumpToSkeleton = new HashMap<Cluster,Cluster>();
+	for (Cluster skel : skeletonClusters) {
+	    for (Cluster clus : skel.getClusters()) {
+		if (mips.contains(clus)) {
+		    mapMIPToSkeleton.put(clus, skel);
+		} else if (clumps.contains(clus)) {
+		    mapClumpToSkeleton.put(clus, skel);
+		} else {
+		    throw new AssertionError("Internal consistency failure");
+		}
+	    }
+	}
+	Map<Cluster,Cluster> mapMIPToSplitSkeleton = new HashMap<Cluster,Cluster>();
+	Map<Cluster,Cluster> mapClumpToSplitSkeleton = new HashMap<Cluster,Cluster>();
+	for (Cluster skel : splitSkeletonClusters) {
+	    for (Cluster clus : skel.getClusters()) {
+		if (mips.contains(clus)) {
+		    mapMIPToSplitSkeleton.put(clus, skel);
+		} else if (clumps.contains(clus)) {
+		    mapClumpToSplitSkeleton.put(clus, skel);
+		} else {
+		    throw new AssertionError("Internal consistency failure");
+		}
+	    }
+	}
+	    
+
+	{
+	    int countSmallClusterHits = 0;
+	    for (Cluster clus : smallClusters) {
+		countSmallClusterHits += clus.getCalorimeterHits().size();
+	    }
+	    System.out.println("DEBUG: ReclusterDriver read in "+smallClusters.size()+" small clusters comprising "+countSmallClusterHits+" hits.");
+	}
+	
+	// Many of those LargeClusters contain skeleton(s), but a few may not. Identify them.
+	List<Cluster> largeClustersWithoutSkeletons = new Vector<Cluster>();
+	for (Cluster largeCluster : largeClusters) {
+	    List<CalorimeterHit> largeClusterHits = largeCluster.getCalorimeterHits();
+	    boolean largeClusterContainsSkeleton = false;
+	    for (Cluster skeletonCluster : skeletonClusters) {
+		for (CalorimeterHit hit : skeletonCluster.getCalorimeterHits()) {
+		    if (largeClusterHits.contains(hit)) {
+			largeClusterContainsSkeleton = true;
+			break;
+		    }
+		}
+		if (largeClusterContainsSkeleton) { break; }
+	    }
+	    if (!largeClusterContainsSkeleton) {
+		largeClustersWithoutSkeletons.add(largeCluster);
+	    }
+	}
+
+	{
+	    int countLargeHitsUnused = 0;
+	    for (Cluster clus : largeClustersWithoutSkeletons) {
+		countLargeHitsUnused += clus.getCalorimeterHits().size();
+	    }
+	    System.out.println("DEBUG: ReclusterDriver found "+largeClustersWithoutSkeletons.size()+" large clusters without skeletons, comprising "+countLargeHitsUnused+" hits.");
+	}
+
+	// Pull those unused hits:
+	List<CalorimeterHit> unusedHitsIgnoringLargeClustersWithoutSkeletons = new Vector<CalorimeterHit>();
+	unusedHitsIgnoringLargeClustersWithoutSkeletons.addAll(unusedHits.values());
+	for (Cluster clus : largeClustersWithoutSkeletons) {
+	    for (CalorimeterHit hit : clus.getCalorimeterHits()) {
+		unusedHitsIgnoringLargeClustersWithoutSkeletons.remove(hit);
+	    }
+	}
+	List<Cluster> wrappedUnusedHitsIgnoringLargeClustersWithoutSkeletons = new Vector<Cluster>();
+	for (CalorimeterHit hit : unusedHitsIgnoringLargeClustersWithoutSkeletons) {
+	    BasicCluster clus = new BasicCluster();
+	    clus.addHit(hit);
+	    wrappedUnusedHitsIgnoringLargeClustersWithoutSkeletons.add(clus);
+	}
+
+	// Match tracks to clusters without regard for E/p.
+	// We allow them to match the following categories of cluster:
+	//   mips & clumps (from skeletonClusters)
+	//   largeClustersWithoutSkeletons
+	//   photonClusters (could be electrons)
+	//   smallClusters
+	List<Cluster> mixedClusters = new Vector<Cluster>();
+	mixedClusters.addAll(mips);
+	mixedClusters.addAll(clumps);
+	mixedClusters.addAll(largeClustersWithoutSkeletons);
+	mixedClusters.addAll(photonClusters); // could match [electrons]
+	mixedClusters.addAll(smallClusters);
+	Map<Track,Cluster> tracksMatchedToClusters = new HashMap<Track,Cluster>();
+	Map<Cluster, List<Track>> clustersMatchedToTracks = new HashMap<Cluster, List<Track>>();
+	for (Track tr : trackList) {
+	    Cluster matchedCluster = m_trackClusterMatcher.matchTrackToCluster(tr, mixedClusters);
+	    if (matchedCluster != null) {
+		tracksMatchedToClusters.put(tr, matchedCluster);
+		List<Track> clusTrList = clustersMatchedToTracks.get(matchedCluster);
+		if (clusTrList == null) { clusTrList = new Vector<Track>(); clustersMatchedToTracks.put(matchedCluster, clusTrList); }
+		clusTrList.add(tr);
+	    }
+	}
+
+	// It can happen that some tracks don't match because they point to unused hits.
+	List<Track> unmatchedTracks = new Vector<Track>();
+	unmatchedTracks.addAll(trackList);
+	unmatchedTracks.removeAll(tracksMatchedToClusters.keySet());
+
+	// Some debug printout
+	System.out.println("DEBUG: "+unmatchedTracks.size()+" unmatched tracks remaining.");
+	System.out.println("DEBUG: Here are the "+tracksMatchedToClusters.keySet().size()+" matched tracks");
+	for (Track tr : tracksMatchedToClusters.keySet()) {
+	    Cluster clus = tracksMatchedToClusters.get(tr);
+	    double[] p = tr.getMomentum();
+	    Particle mcTruth = null;
+	    if (tr instanceof ReconTrack) {
+		mcTruth = ((ReconTrack)(tr)).getMCParticle();
+	    }
+	    if (tr instanceof BaseTrackMC) {
+		mcTruth = ((BaseTrackMC)(tr)).getMCParticle();
+	    }
+	    double mom = Math.sqrt(p[0]*p[0] + p[1]*p[1] + p[2]*p[2]);
+	    // Which list is that from?
+	    String printme = new String("Matched track with p=");
+	    printme += mom;
+	    if (mcTruth != null) {
+		printme += " (truth type "+mcTruth.getType()+")";
+	    }
+	    printme += " to a cluster with ";
+	    printme += clus.getCalorimeterHits().size();
+	    printme += " hits and energy of ";
+	    printme += clus.getEnergy();
+	    if (mips.contains(clus)) { printme += " [from MIPS]"; }
+	    if (clumps.contains(clus)) { printme += " [from CLUMPS]"; }
+	    if (largeClustersWithoutSkeletons.contains(clus)) { printme += " [from LARGE CLUSTERS W/O SKELETONS]"; }
+	    if (photonClusters.contains(clus)) { printme += " [from PHOTONS]"; }
+	    if (smallClusters.contains(clus)) { printme += " [from SMALL CLUSTERS]"; }
+	    Map<MCParticle, List<CalorimeterHit>> clusterTruth = new HashMap<MCParticle, List<CalorimeterHit>>();
+	    for (CalorimeterHit hit : clus.getCalorimeterHits()) {
+		SimCalorimeterHit simHit = (SimCalorimeterHit)(hit);
+		MCParticle mcTruthPart = simHit.getMCParticle(0);
+		List<CalorimeterHit> hitList = clusterTruth.get(mcTruthPart);
+		if (hitList == null) {
+		    hitList = new Vector<CalorimeterHit>();
+		    clusterTruth.put(mcTruthPart, hitList);
+		}
+		hitList.add(hit);
+	    }
+	    MCParticle maxParticle = null;
+	    for (MCParticle part : clusterTruth.keySet()) {
+		if (maxParticle == null || clusterTruth.get(maxParticle).size() < clusterTruth.get(part).size()) {
+		    maxParticle = part;
+		}
+	    }
+	    printme += " (dominant particle: "+maxParticle.getType().getPDGID()+" with "+clusterTruth.get(maxParticle).size()+" hits)";
+	    System.out.println(printme);
+	}
+
+	// OK. Now pick out those clusters that have something connected to them...
+	Collection<Cluster> seeds = clustersMatchedToTracks.keySet();
+	Map<Cluster, Double> seedClusterTrackEnergy = new HashMap<Cluster,Double>();
+	for (Cluster seed : seeds) {
+	    List<Track> seedTracks = clustersMatchedToTracks.get(seed);
+	    double energySum = 0.0;
+	    for (Track tr : seedTracks) {
+		double massSq = 0.140 * 0.140;
+		double[] threeMomentum = tr.getMomentum();
+		double momentumSq = threeMomentum[0]*threeMomentum[0] + threeMomentum[1]*threeMomentum[1] + threeMomentum[2]*threeMomentum[2];
+		double energySq = momentumSq + massSq;
+		energySum += Math.sqrt(energySq);
+	    }
+	    seedClusterTrackEnergy.put(seed, new Double(energySum));
+	}
+
+	// Set up initial thresholds
+	Map<Cluster,Double> mapSeedsToThresholds = new HashMap<Cluster,Double>();
+	for (Cluster seed : seeds) {
+	    mapSeedsToThresholds.put(seed, new Double(0.9));
+	}
+
+	// Need to add other clusters to them somewhat intelligently...
+
+	Map<Cluster,Cluster> mapPhotonClustersToSeeds = new HashMap<Cluster,Cluster>();
+	Map<Cluster,Cluster> mapSmallClustersToSeeds = new HashMap<Cluster,Cluster>();
+	Map<Cluster,Cluster> mapLargeClustersToSeeds = new HashMap<Cluster,Cluster>();
+	Map<Cluster,Cluster> mapMipsToSeeds = new HashMap<Cluster,Cluster>();
+	Map<Cluster,Cluster> mapClumpsToSeeds = new HashMap<Cluster,Cluster>();
+	Map<Cluster,Cluster> mapHitsToSeeds = new HashMap<Cluster,Cluster>();
+	
+	for (Cluster clus : photonClusters) {
+	    if (seeds.contains(clus)) {
+		mapPhotonClustersToSeeds.put(clus,clus);
+	    } else {
+		mapPhotonClustersToSeeds.put(clus,null);
+	    }
+	}
+	for (Cluster clus : smallClusters) { 
+	    if (seeds.contains(clus)) {
+		mapSmallClustersToSeeds.put(clus,clus);
+	    } else {
+		mapSmallClustersToSeeds.put(clus,null);
+	    }
+	}
+	for (Cluster clus : largeClusters) { 
+	    if (seeds.contains(clus)) {
+		mapLargeClustersToSeeds.put(clus,clus);
+	    } else {
+		mapLargeClustersToSeeds.put(clus,null);
+	    }
+	}
+	for (Cluster clus : mips) { 
+	    if (seeds.contains(clus)) {
+		mapMipsToSeeds.put(clus,clus);
+	    } else {
+		mapMipsToSeeds.put(clus,null);
+	    }
+	}
+	for (Cluster clus : clumps) { 
+	    if (seeds.contains(clus)) {
+		mapClumpsToSeeds.put(clus,clus);
+	    } else {
+		mapClumpsToSeeds.put(clus,null);
+	    }
+	}
+	for (Cluster clus : wrappedUnusedHitsIgnoringLargeClustersWithoutSkeletons) { 
+	    if (seeds.contains(clus)) {
+		mapHitsToSeeds.put(clus,clus);
+	    } else {
+		mapHitsToSeeds.put(clus,null);
+	    }
+	}
+
+
+
+
+
+	printStatus("Initial state:", seeds, seedClusterTrackEnergy, mapPhotonClustersToSeeds, mapSmallClustersToSeeds, mapLargeClustersToSeeds, mapMipsToSeeds, mapClumpsToSeeds, mapHitsToSeeds, clustersMatchedToTracks, mapSeedsToThresholds);
+
+	// First pass: assign unambiguous split skeletons
+	for (Cluster skel : splitSkeletonClusters) {
+	    List<Cluster> seedsInSkeleton = new Vector<Cluster>();
+	    for (Cluster clus : skel.getClusters()) {
+		if (seeds.contains(clus)) {
+		    seedsInSkeleton.add(clus);
+		}
+	    }
+	    if (seedsInSkeleton.size() == 1) {
+		// unique
+		Cluster seed = seedsInSkeleton.get(0);
+		for (Cluster clus : skel.getClusters()) {
+		    if (mips.contains(clus)) {
+			mapMipsToSeeds.put(clus, seed);
+		    } else if (clumps.contains(clus)) {
+			mapClumpsToSeeds.put(clus, seed);
+		    } else { 
+			throw new AssertionError("Internal consistency failure");
+		    }
+		}
+	    }
+	}
+
+	printStatus("After first pass:", seeds, seedClusterTrackEnergy, mapPhotonClustersToSeeds, mapSmallClustersToSeeds, mapLargeClustersToSeeds, mapMipsToSeeds, mapClumpsToSeeds, mapHitsToSeeds, clustersMatchedToTracks, mapSeedsToThresholds);
+
+	// Second pass: attach small clusters and hits to nearby seeds OR skeleton components OR large clusters OR photon clusters
+	// Create maps from small thing to big thing
+	double threshold = 50.0;
+	Map<Cluster, Cluster> mapAttachSmallClusters = new HashMap<Cluster,Cluster>();
+	Map<Cluster, Cluster> mapAttachHits = new HashMap<Cluster,Cluster>();
+	Map<List<Cluster>, Map<Cluster,Cluster>> targetMap = new HashMap<List<Cluster>, Map<Cluster,Cluster>>(); // e.g. from "mips" to "mapMipsToSeeds"
+	targetMap.put(mips, mapMipsToSeeds);
+	targetMap.put(clumps, mapClumpsToSeeds);
+	targetMap.put(largeClusters, mapLargeClustersToSeeds);
+	targetMap.put(photonClusters, mapPhotonClustersToSeeds);
+	List<Cluster> targets = new Vector<Cluster>();
+	for (List<Cluster> targetList : targetMap.keySet()) {
+	    targets.addAll(targetList);
+	}
+
+	for (Cluster clus : smallClusters) {
+	    double minDist = -1.0;
+	    Cluster nearest = null;
+	    for (Cluster target : targets) {
+		double dist = proximity(clus, target);
+		if (nearest == null || dist < minDist) {
+		    minDist = dist;
+		    nearest = target;
+		}
+	    }
+	    if (minDist < threshold) {
+		mapAttachSmallClusters.put(clus, nearest);
+	    }
+	}
+	for (Cluster clus : wrappedUnusedHitsIgnoringLargeClustersWithoutSkeletons) {
+	    double minDist = -1.0;
+	    Cluster nearest = null;
+	    for (Cluster target : targets) {
+		double dist = proximity(clus, target);
+		if (nearest == null || dist < minDist) {
+		    minDist = dist;
+		    nearest = target;
+		}
+	    }
+	    if (minDist < threshold) {
+		mapAttachHits.put(clus, nearest);
+	    }
+	}
+
+	for (Cluster clus : mapAttachSmallClusters.keySet()) {
+	    Cluster target = mapAttachSmallClusters.get(clus);
+	    for (List<Cluster> targetList : targetMap.keySet()) {
+		if (targetList.contains(target)) {
+		    mapSmallClustersToSeeds.put(clus, targetMap.get(targetList).get(target)); // maps clus to [seed of [target of [clus] ] ]
+		}
+	    }
+	}
+
+	for (Cluster clus : mapAttachHits.keySet()) {
+	    Cluster target = mapAttachHits.get(clus);
+	    for (List<Cluster> targetList : targetMap.keySet()) {
+		if (targetList.contains(target)) {
+		    mapHitsToSeeds.put(clus, targetMap.get(targetList).get(target)); // maps clus to [seed of [target of [clus] ] ]
+		}
+	    }
+	}
+
+	printStatus("After second pass:", seeds, seedClusterTrackEnergy, mapPhotonClustersToSeeds, mapSmallClustersToSeeds, mapLargeClustersToSeeds, mapMipsToSeeds, mapClumpsToSeeds, mapHitsToSeeds, clustersMatchedToTracks, mapSeedsToThresholds);	
+
+	// SCORING?
+	//
+	// Isolated hit to anything: Distance (1/r2), possibly relative position
+	// Small cluster to anything: Distance (1/r2), possibly relative position
+	// Skeleton component to skeleton component: tricky...
+	// Skeleton component to large cluster: tricky...
+	// Skeleton component to photon cluster: tricky...
+	// Large cluster to photon cluster or vice-versa: ?
+	//
+	// For small things, indirect proximity (small thing -> big thing -> seed) is OK.
+	// For big things, indirect proximity (big thing -> other big thing -> seed) is explosive.
+	//
+	// For skeleton components:
+	//   * Assign a score to each pair (e.g. MIP <--> CLUMP)
+	//   * For each valid route from seed, take product of scores,
+	//        e.g. route 1: SEED <--> CLUMP = 0.4
+	//        e.g. route 2: SEED <--> MIP <--> CLUMP = 0.9 * 1.0
+	//   * Take maximum of scores.
+	//   * For each pair, score caps at 1 -- e.g. likelihood in (1.0, 0.9) => score = 1, likelihood in (0.9, 0.0) => score = likelihood/0.9
+	//   * This approach ensures that score for each indirectly linked cluster is
+	//     always <= score for best path to get there. So if the distant cluster
+	//     is above threshold, we are guaranteed a path to it is also above threshold.
+	//
+	// To keep things somewhat bounded, need to impose distance cut-offs.
+	// Scoring normalization: 1.0 indicates a link that you'd definitely want to make. Cap at 1.0
+
+	iterateOnce(mapSeedsToThresholds, mapHitsToSeeds, mapSmallClustersToSeeds, mapLargeClustersToSeeds, mapPhotonClustersToSeeds, mapMipsToSeeds, mapClumpsToSeeds, targetMap, seeds);
+	printStatus("After third pass:", seeds, seedClusterTrackEnergy, mapPhotonClustersToSeeds, mapSmallClustersToSeeds, mapLargeClustersToSeeds, mapMipsToSeeds, mapClumpsToSeeds, mapHitsToSeeds, clustersMatchedToTracks, mapSeedsToThresholds);		
+
+	adjustThresholds(mapSeedsToThresholds, mapHitsToSeeds, mapSmallClustersToSeeds, mapLargeClustersToSeeds, mapPhotonClustersToSeeds, mapMipsToSeeds, mapClumpsToSeeds, seeds, seedClusterTrackEnergy);
+	iterateOnce(mapSeedsToThresholds, mapHitsToSeeds, mapSmallClustersToSeeds, mapLargeClustersToSeeds, mapPhotonClustersToSeeds, mapMipsToSeeds, mapClumpsToSeeds, targetMap, seeds);
+	printStatus("After fourth pass:", seeds, seedClusterTrackEnergy, mapPhotonClustersToSeeds, mapSmallClustersToSeeds, mapLargeClustersToSeeds, mapMipsToSeeds, mapClumpsToSeeds, mapHitsToSeeds, clustersMatchedToTracks, mapSeedsToThresholds);		
+
+	adjustThresholds(mapSeedsToThresholds, mapHitsToSeeds, mapSmallClustersToSeeds, mapLargeClustersToSeeds, mapPhotonClustersToSeeds, mapMipsToSeeds, mapClumpsToSeeds, seeds, seedClusterTrackEnergy);
+	iterateOnce(mapSeedsToThresholds, mapHitsToSeeds, mapSmallClustersToSeeds, mapLargeClustersToSeeds, mapPhotonClustersToSeeds, mapMipsToSeeds, mapClumpsToSeeds, targetMap, seeds);
+	printStatus("After fifth pass:", seeds, seedClusterTrackEnergy, mapPhotonClustersToSeeds, mapSmallClustersToSeeds, mapLargeClustersToSeeds, mapMipsToSeeds, mapClumpsToSeeds, mapHitsToSeeds, clustersMatchedToTracks, mapSeedsToThresholds);		
+
+	adjustThresholds(mapSeedsToThresholds, mapHitsToSeeds, mapSmallClustersToSeeds, mapLargeClustersToSeeds, mapPhotonClustersToSeeds, mapMipsToSeeds, mapClumpsToSeeds, seeds, seedClusterTrackEnergy);
+	iterateOnce(mapSeedsToThresholds, mapHitsToSeeds, mapSmallClustersToSeeds, mapLargeClustersToSeeds, mapPhotonClustersToSeeds, mapMipsToSeeds, mapClumpsToSeeds, targetMap, seeds);
+	printStatus("After sixth pass:", seeds, seedClusterTrackEnergy, mapPhotonClustersToSeeds, mapSmallClustersToSeeds, mapLargeClustersToSeeds, mapMipsToSeeds, mapClumpsToSeeds, mapHitsToSeeds, clustersMatchedToTracks, mapSeedsToThresholds);		
+
+	/*
+	for (Cluster seed : seeds) {
+	    List<Cluster> wholeCluster = new Vector<Cluster>();
+	    wholeCluster.add(seed);
+
+	    double trackEnergy = seedClusterTrackEnergy.get(seed);
+	    seedMap.put(seed, wholeClsuter);
+	}
+	*/	
+    }
+
+    void adjustThresholds(Map<Cluster,Double> mapSeedsToThresholds,
+			  Map<Cluster,Cluster> mapHitsToSeeds,
+			  Map<Cluster,Cluster> mapSmallClustersToSeeds,
+			  Map<Cluster,Cluster> mapLargeClustersToSeeds,
+			  Map<Cluster,Cluster> mapPhotonClustersToSeeds,
+			  Map<Cluster,Cluster> mapMipsToSeeds,
+			  Map<Cluster,Cluster> mapClumpsToSeeds,
+			  Collection<Cluster> seeds,
+			  Map<Cluster,Double> seedClusterTrackEnergy)
+    {
+	for (Cluster seed : seeds) {
+	    List<Cluster> matchedPhotons = reverseMap(seed, mapPhotonClustersToSeeds);
+	    List<Cluster> matchedSmallClusters = reverseMap(seed, mapSmallClustersToSeeds);
+	    List<Cluster> matchedLargeClusters = reverseMap(seed, mapLargeClustersToSeeds);
+	    List<Cluster> matchedMips = reverseMap(seed, mapMipsToSeeds);
+	    List<Cluster> matchedClumps = reverseMap(seed, mapClumpsToSeeds);
+	    List<Cluster> matchedHits = reverseMap(seed, mapHitsToSeeds);
+
+	    List<Cluster> combinedClusters = new Vector<Cluster>();
+	    combinedClusters.addAll(matchedPhotons);
+	    combinedClusters.addAll(matchedSmallClusters);
+	    combinedClusters.addAll(matchedLargeClusters);
+	    combinedClusters.addAll(matchedMips);
+	    combinedClusters.addAll(matchedClumps);
+	    combinedClusters.addAll(matchedHits);
+
+	    double clusterEnergy = energy(combinedClusters);	   
+	    double trackEnergy = seedClusterTrackEnergy.get(seed);
+	    double trackEnergySigma = 0.7 * Math.sqrt(trackEnergy);
+	    double normalisedResidual = (clusterEnergy - trackEnergy)/trackEnergySigma;
+
+	    Double currentThreshold = mapSeedsToThresholds.get(seed);
+	    double newThreshold = currentThreshold;
+
+	    if ( normalisedResidual > 1.0) {
+		// Cluster is too big => increase theshold
+		double baseScale = 1.0 - currentThreshold;
+		if (baseScale > 0.1) { baseScale = 0.1; }
+		double fractionToMove = (normalisedResidual / 10.0);
+		if (fractionToMove > 0.5) { fractionToMove = 0.5; }
+		newThreshold += (baseScale * fractionToMove);
+	    } else if (normalisedResidual < -1.0) {
+		// Too small => decrease threshold
+		double baseScale = 1.0 - currentThreshold;
+		if (baseScale > 0.1) { baseScale = 0.1; }
+		double fractionToMove = (-normalisedResidual / 10.0);
+		if (fractionToMove > 0.5) { fractionToMove = 0.5; }
+		newThreshold -= (baseScale * fractionToMove);
+	    }
+
+	    mapSeedsToThresholds.put(seed, newThreshold);
+	}    
+    }
+
+    void iterateOnce(Map<Cluster,Double> mapSeedsToThresholds,
+		     Map<Cluster,Cluster> mapHitsToSeeds,
+		     Map<Cluster,Cluster> mapSmallClustersToSeeds,
+		     Map<Cluster,Cluster> mapLargeClustersToSeeds,
+		     Map<Cluster,Cluster> mapPhotonClustersToSeeds,
+		     Map<Cluster,Cluster> mapMipsToSeeds,
+		     Map<Cluster,Cluster> mapClumpsToSeeds,
+		     Map<List<Cluster>, Map<Cluster,Cluster>> targetMap,
+		     Collection<Cluster> seeds
+		     ) {
+	// Compute scores
+
+	double const_hitScoreThreshold = 20.0;
+	double const_hitScoreScale = 40.0;
+	double const_hitMaxAllowedDistance = 200.0;
+
+	Map<Cluster, Map<Cluster,Double>> isolatedHitToSeedScoreMap = scoreOnProximity(const_hitScoreThreshold, const_hitScoreScale, const_hitMaxAllowedDistance, mapHitsToSeeds.keySet(), targetMap, seeds);
+	Map<Cluster, Map<Cluster,Double>> smallClusterToSeedScoreMap = scoreOnProximity(const_hitScoreThreshold, const_hitScoreScale, const_hitMaxAllowedDistance, mapSmallClustersToSeeds.keySet(), targetMap, seeds);
+	Map<Cluster, Map<Cluster,Double>> largeClusterToSeedScoreMap = scoreOnProximity(const_hitScoreThreshold, const_hitScoreScale, const_hitMaxAllowedDistance, mapLargeClustersToSeeds.keySet(), targetMap, seeds);
+	Map<Cluster, Map<Cluster,Double>> photonClusterToSeedScoreMap = scoreOnProximity(const_hitScoreThreshold, const_hitScoreScale, const_hitMaxAllowedDistance, mapPhotonClustersToSeeds.keySet(), targetMap, seeds);
+	Map<Cluster, Map<Cluster,Double>> mipToSeedScoreMap = scoreOnProximity(const_hitScoreThreshold, const_hitScoreScale, const_hitMaxAllowedDistance, mapMipsToSeeds.keySet(), targetMap, seeds);
+	Map<Cluster, Map<Cluster,Double>> clumpToSeedScoreMap = scoreOnProximity(const_hitScoreThreshold, const_hitScoreScale, const_hitMaxAllowedDistance, mapClumpsToSeeds.keySet(), targetMap, seeds);
+	
+	// Update mappings based on scores
+	updateMappings(isolatedHitToSeedScoreMap, mapHitsToSeeds, mapSeedsToThresholds);
+	updateMappings(smallClusterToSeedScoreMap, mapSmallClustersToSeeds, mapSeedsToThresholds);
+	updateMappings(largeClusterToSeedScoreMap, mapLargeClustersToSeeds, mapSeedsToThresholds);
+	updateMappings(photonClusterToSeedScoreMap, mapPhotonClustersToSeeds, mapSeedsToThresholds);
+	updateMappings(mipToSeedScoreMap, mapMipsToSeeds, mapSeedsToThresholds);
+	updateMappings(clumpToSeedScoreMap, mapClumpsToSeeds, mapSeedsToThresholds);
+    }
+
+    protected void updateMappings(Map<Cluster, Map<Cluster,Double>> clusterToSeedScoreMap, Map<Cluster,Cluster> clusterToSeedMap, Map<Cluster,Double> mapSeedsToThresholds) {
+	Collection<Cluster> seeds = mapSeedsToThresholds.keySet();
+	for (Cluster clus : clusterToSeedMap.keySet()) {
+	    if (seeds.contains(clus)) { continue; } // don't do anything silly
+	    double maxReducedScore = 0.0;
+	    Cluster bestSeedMatch = null;
+	    Map<Cluster,Double> scoreMap = clusterToSeedScoreMap.get(clus);
+	    for (Cluster seed : scoreMap.keySet()) {
+		Double seedScore = scoreMap.get(seed);
+		Double seedThreshold = mapSeedsToThresholds.get(seed);
+		double reducedScore = seedScore / seedThreshold;
+		if (reducedScore > 1.0) {
+		    if (bestSeedMatch==null || reducedScore > maxReducedScore) {
+			maxReducedScore = reducedScore;
+			bestSeedMatch = seed;
+		    }
+		}
+	    }
+	    clusterToSeedMap .put(clus, bestSeedMatch);
+	}		
+    }
+
+    protected Map<Cluster, Map<Cluster,Double>> scoreOnProximity(double const_hitScoreThreshold, double const_hitScoreScale, double const_hitMaxAllowedDistance, Collection<Cluster> inputClusters, Map<List<Cluster>, Map<Cluster,Cluster>> targetMap, Collection<Cluster> seeds)
+    {
+	// Crummy inverse-square drop-off formula:
+	//    f(x) = a / (x+b)^2
+	//   f(x0) = 1
+	//   f(x1) = 0.5
+	//    => b = (sqrt(2)*x0 + x1) / (-1 -sqrt(2))   // taking negative solution
+	//       a = (x0 + b)^2
+	double x0 = const_hitScoreThreshold;
+	double x1 = x0 + const_hitScoreScale;
+	double b = (1.41421*x0 - x1) / (-2.41421);
+	double a = (x0+b)*(x0+b); 
+	Map<Cluster, Map<Cluster,Double>> scoreMap = new HashMap<Cluster, Map<Cluster,Double>>();
+	for (Cluster clus : inputClusters) {
+	    if (seeds.contains(clus)) { continue; } // don't do anything silly
+	    Map<Cluster, Double> scoresForThisCluster = new HashMap<Cluster,Double>();
+	    for (List<Cluster> targetList : targetMap.keySet()) {
+		for (Cluster target : targetList) {
+		    double dist = proximity(clus, target);
+		    if (dist >  const_hitMaxAllowedDistance) { continue; }
+		    double score = a / ((dist + b)*(dist + b));
+		    if (score > 1.0) {
+			if (dist > const_hitScoreThreshold*1.01) { 
+			    System.out.println("DEBUG: Dist="+dist+", a="+a+", b="+b+", so score="+score);
+			    double score_x0 = a / ((x0+b)*(x0+b));
+			    double score_x1 = a / ((x1+b)*(x1+b));
+			    System.out.println("DEBUG: Score("+x0+") = "+score_x0+", Score("+x1+") = "+score_x1);					       
+			    throw new AssertionError("Internal consistency failure"); 
+			}
+			score = 1.0;
+		    }
+		    Cluster seedOfTarget = targetMap.get(targetList).get(target);
+		    if (seedOfTarget != null) {
+			Double previousScore = scoresForThisCluster.get(seedOfTarget);
+			if (previousScore == null || previousScore < score) {
+			    scoresForThisCluster.put(seedOfTarget, new Double(score));
+			}
+		    }
+		}
+	    }
+	    scoreMap.put(clus, scoresForThisCluster);
+	}
+	return scoreMap;
+    }
+
+    protected void printStatus(String desc, Collection<Cluster> seeds, Map<Cluster,Double> seedClusterTrackEnergy, Map<Cluster,Cluster> mapPhotonClustersToSeeds, Map<Cluster,Cluster> mapSmallClustersToSeeds, Map<Cluster,Cluster> mapLargeClustersToSeeds, Map<Cluster,Cluster> mapMipsToSeeds, Map<Cluster,Cluster> mapClumpsToSeeds, Map<Cluster,Cluster> mapHitsToSeeds, Map<Cluster,List<Track>> clustersMatchedToTracks, Map<Cluster,Double> mapSeedsToThresholds) {
+	System.out.println(desc);
+	for (Cluster seed : seeds) {
+	    String printme = new String();
+	    printme += "  Seed cluster (track E="+seedClusterTrackEnergy.get(seed)+", threshold="+mapSeedsToThresholds.get(seed)+") --> ";
+	    List<Cluster> matchedPhotons = reverseMap(seed, mapPhotonClustersToSeeds);
+	    List<Cluster> matchedSmallClusters = reverseMap(seed, mapSmallClustersToSeeds);
+	    List<Cluster> matchedLargeClusters = reverseMap(seed, mapLargeClustersToSeeds);
+	    List<Cluster> matchedMips = reverseMap(seed, mapMipsToSeeds);
+	    List<Cluster> matchedClumps = reverseMap(seed, mapClumpsToSeeds);
+	    List<Cluster> matchedHits = reverseMap(seed, mapHitsToSeeds);
+	    if (matchedPhotons.size() > 0) { printme += matchedPhotons.size()+" photons; "; }
+	    if (matchedSmallClusters.size() > 0) { printme += matchedSmallClusters.size()+" smallClus; "; }
+	    if (matchedLargeClusters.size() > 0) { printme += matchedLargeClusters.size()+" largeClus; "; }
+	    if (matchedMips.size() > 0) { printme += matchedMips.size()+" mips; "; }
+	    if (matchedClumps.size() > 0) { printme += matchedClumps.size()+" clumps; "; }
+	    if (matchedHits.size() > 0) { printme += matchedHits.size()+" hits; "; }
+
+	    List<Cluster> combinedClusters = new Vector<Cluster>();
+	    combinedClusters.addAll(matchedPhotons);
+	    combinedClusters.addAll(matchedSmallClusters);
+	    combinedClusters.addAll(matchedLargeClusters);
+	    combinedClusters.addAll(matchedMips);
+	    combinedClusters.addAll(matchedClumps);
+	    combinedClusters.addAll(matchedHits);
+	    printme += " --> energy = "+energy(combinedClusters);	   
+	    List<Track> trackList = clustersMatchedToTracks.get(seed);
+	    if (trackList.size()>0) {
+		printme += " (effic="+quoteEfficiency(trackList, combinedClusters)+", purity="+quotePurity(trackList, combinedClusters)+")";
+	    }
+
+	    System.out.println(printme);
+	}    
+
+	List<Cluster> unassignedPhotonClusters = new Vector<Cluster>();
+	List<Cluster> unassignedSmallClusters = new Vector<Cluster>();
+	List<Cluster> unassignedLargeClusters = new Vector<Cluster>();
+	List<Cluster> unassignedMips = new Vector<Cluster>();
+	List<Cluster> unassignedClumps = new Vector<Cluster>();
+	List<Cluster> unassignedHits = new Vector<Cluster>();
+	for (Cluster clus : mapPhotonClustersToSeeds.keySet()) {
+	    if (mapPhotonClustersToSeeds.get(clus) == null) {
+		unassignedPhotonClusters.add(clus);
+	    }
+	}
+	for (Cluster clus : mapSmallClustersToSeeds.keySet()) {
+	    if (mapSmallClustersToSeeds.get(clus) == null) {
+		unassignedSmallClusters.add(clus);
+	    }
+	}
+	for (Cluster clus : mapLargeClustersToSeeds.keySet()) {
+	    if (mapLargeClustersToSeeds.get(clus) == null) {
+		unassignedLargeClusters.add(clus);
+	    }
+	}
+	for (Cluster clus : mapMipsToSeeds.keySet()) {
+	    if (mapMipsToSeeds.get(clus) == null) {
+		unassignedMips.add(clus);
+	    }
+	}
+	for (Cluster clus : mapClumpsToSeeds.keySet()) {
+	    if (mapClumpsToSeeds.get(clus) == null) {
+		unassignedClumps.add(clus);
+	    }
+	}
+	for (Cluster clus : mapHitsToSeeds.keySet()) {
+	    if (mapHitsToSeeds.get(clus) == null) {
+		unassignedHits.add(clus);
+	    }
+	}
+	System.out.println("Unassigned: "+unassignedPhotonClusters.size()+" photons, "+unassignedSmallClusters.size()+" small clusters, "+unassignedLargeClusters.size()+" large clusters, "+unassignedMips.size()+" mips, "+unassignedClumps.size()+" clumps, "+unassignedHits.size()+" hits.");
+    }
+	
+    protected List<Cluster> reverseMap(Cluster inputClus, Map<Cluster,Cluster> map) {
+	List<Cluster> output = new Vector<Cluster>();
+	for (Cluster clus : map.keySet()) {
+	    if (map.get(clus) == inputClus) {
+		output.add(clus);
+	    }
+	}
+	return output;
+    }
+
+    protected double energy(List<Cluster> inputClusters) {
+	Cluster magicCombinedCluster = makeCombinedCluster(inputClusters);
+	return m_calib.getEnergy(magicCombinedCluster);
+    }
+    protected Cluster makeCombinedCluster(List<Cluster> inputClusters) {
+	BasicCluster magicCombinedCluster = new BasicCluster();
+	for (Cluster clus : inputClusters) {
+	    magicCombinedCluster.addCluster(clus);
+	}
+	return magicCombinedCluster;
+    }
+
+    protected double proximity(Cluster clus1, Cluster clus2) {
+	if (clus1.getCalorimeterHits().size()<1) { throw new AssertionError("Empty cluster"); }
+	if (clus2.getCalorimeterHits().size()<1) { throw new AssertionError("Empty cluster"); }
+	double minDist = 0;
+	boolean found = false;
+	for (CalorimeterHit hit1 : clus1.getCalorimeterHits()) {
+	    Hep3Vector hitPosition1 = new BasicHep3Vector(hit1.getPosition());
+	    for (CalorimeterHit hit2 : clus2.getCalorimeterHits()) {
+		Hep3Vector hitPosition2 = new BasicHep3Vector(hit2.getPosition());
+		double distance = VecOp.sub(hitPosition1,hitPosition2).magnitude();
+		if (distance<minDist || found==false) {
+		    found = true;
+		    minDist = distance;
+		}
+	    }
+	}
+	return minDist;
+    }
+
+    protected double quoteEfficiency(List<Track> trackList, List<Cluster> clusterList) {
+	Cluster combined = makeCombinedCluster(clusterList);
+	return quoteEfficiency(trackList, combined);
+    }
+    protected double quotePurity(List<Track> trackList, List<Cluster> clusterList) {
+	Cluster combined = makeCombinedCluster(clusterList);
+	return quotePurity(trackList, combined);
+    }
+    protected double quoteEfficiency(List<Track> trackList, Cluster clus) {
+	HitMap hitsEcal = ((HitMap)(m_event.get("inputHitMapEcal")));
+	HitMap hitsHcal = ((HitMap)(m_event.get("inputHitMapHcal")));
+	List<CalorimeterHit> allHits = new Vector<CalorimeterHit>();
+	allHits.addAll(hitsEcal.values());
+	allHits.addAll(hitsHcal.values());
+	Set<CalorimeterHit> truthHitsInEvent = findHitsFromTruth(trackList, allHits);
+	Set<CalorimeterHit> truthHitsInCluster = findHitsFromTruth(trackList, clus.getCalorimeterHits());
+	double denom = truthHitsInEvent.size();
+	double num = truthHitsInCluster.size();
+	return num/denom;
+    }
+    protected double quotePurity(List<Track> trackList, Cluster clus) {
+	Set<CalorimeterHit> truthHitsInCluster = findHitsFromTruth(trackList, clus.getCalorimeterHits());
+	double num = truthHitsInCluster.size();
+	double denom = clus.getCalorimeterHits().size();
+	return num/denom;
+    }
+
+    Set<CalorimeterHit> findHitsFromTruth(List<Track> trackList, List<CalorimeterHit> hits) {
+	Set<CalorimeterHit> outputHits = new HashSet<CalorimeterHit>();
+	for (Track tr : trackList) {
+	    List<CalorimeterHit> foundHits = findHitsFromTruth(tr, hits);
+	    outputHits.addAll(foundHits);
+	}
+	return outputHits;
+    }
+
+    List<CalorimeterHit> findHitsFromTruth(Track tr, List<CalorimeterHit> hits) {
+	MCParticle truthPart = null;
+	if (tr instanceof ReconTrack) {
+	    truthPart = (MCParticle)(((ReconTrack)(tr)).getMCParticle());
+	} else if (tr instanceof BaseTrackMC) {
+	    truthPart = ((BaseTrackMC)(tr)).getMCParticle();
+	}
+	return findHitsFromTruth(truthPart, hits);
+    }
+    List<CalorimeterHit> findHitsFromTruth(MCParticle part, List<CalorimeterHit> hitsToSearch) {
+	List<MCParticle> mcList = m_event.get(MCParticle.class, m_mcList);
+	List<MCParticle> truthParents = findParentsInList(part, mcList);
+	List<CalorimeterHit> truthHits = new Vector<CalorimeterHit>();
+	for (CalorimeterHit hit : hitsToSearch) {
+	    Set<MCParticle> contributingParticles = findMCParticles(hit, mcList);
+	    boolean match = false;
+	    for (MCParticle particleInHit : contributingParticles) {
+		if (truthParents.contains(particleInHit)) {
+		    match = true;
+		}
+	    }
+	    if (match) {
+		truthHits.add(hit);
+	    }
+	}
+	return truthHits;
+    }
+
+    protected Set<MCParticle> findMCParticles(CalorimeterHit hit, List<MCParticle> mcList)
+    {
+        if ( ! (hit instanceof SimCalorimeterHit) ) {
+            throw new AssertionError("Non-simulated hit!");
+        } else {
+            SimCalorimeterHit simHit = (SimCalorimeterHit) (hit);
+            Set<MCParticle> contributingParticlesFromList = new HashSet<MCParticle>();
+            int nContributingParticles = simHit.getMCParticleCount();
+            for (int i=0; i<nContributingParticles; i++) {
+                MCParticle part = simHit.getMCParticle(i);
+                List<MCParticle> parentsInList = findParentsInList(part, mcList);
+                contributingParticlesFromList.addAll(parentsInList);
+            }
+            return contributingParticlesFromList;
+        }
+    }
+    protected List<MCParticle> findParentsInList(MCParticle part, List<MCParticle> mcList) 
+    {
+        List<MCParticle> outputList = new Vector<MCParticle>();
+        if (mcList.contains(part)) {
+            // Already in there
+            outputList.add(part);
+        } else {
+            // Not in there -- recurse up through parents
+            List<MCParticle> parents = part.getParents();
+            if (parents.size()==0) {
+                // Ran out of options -- add nothing and return below
+            } else {
+                for (MCParticle parent : parents) {
+                    List<MCParticle> ancestorsInList = findParentsInList(parent, mcList);
+                    outputList.addAll(ancestorsInList);
+                }
+            }
+        }
+        return outputList;
+    }   
+
+
+    TrackClusterMatcher m_trackClusterMatcher;
+    
+    // Ugly...
+    DetailedNeutralHadronClusterEnergyCalculator m_calib = new DetailedNeutralHadronClusterEnergyCalculator();   
+}
CVSspam 0.2.8