Author: [log in to unmask]
Date: Fri Jan 9 16:24:39 2015
New Revision: 1912
Log:
Add several more clustering algorithm implementations to the ecal.cluster package.
Added:
java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/CTPClusterer.java
java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/GTPClusterDriver.java
java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/GTPClusterer.java
Modified:
java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/CTPEcalClusterer.java
java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/GTPEcalClusterer.java
java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/ClustererFactory.java
java/trunk/ecal-recon/src/test/java/org/hps/recon/ecal/cluster/ClustererTest.java
Modified: java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/CTPEcalClusterer.java
=============================================================================
--- java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/CTPEcalClusterer.java (original)
+++ java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/CTPEcalClusterer.java Fri Jan 9 16:24:39 2015
@@ -28,8 +28,10 @@
* @author Jeremy McCormick <[log in to unmask]>
* @author Tim Nelson <[log in to unmask]>
* @author Sho Uemura <[log in to unmask]>
- * @version $Id: CTPEcalClusterer.java,v 1.1 2013/02/25 22:39:24 meeg Exp $
+ *
+ * @deprecated Use the {@link org.hps.recon.ecal.cluster.CTPClusterer} with the {@link org.hps.recon.ecal.cluster.ClusterDriver}.
*/
+@Deprecated
public class CTPEcalClusterer extends Driver {
Detector detector = null;
Modified: java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/GTPEcalClusterer.java
=============================================================================
--- java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/GTPEcalClusterer.java (original)
+++ java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/GTPEcalClusterer.java Fri Jan 9 16:24:39 2015
@@ -30,7 +30,10 @@
* either at the same location as the seed hit or is a neighbor to the seed hit.
* @author Kyle McCarty
* @author Sho Uemura
+ *
+ * @deprecated Use the {@link org.hps.recon.ecal.cluster.GTPClusterDriver} instead.
*/
+@Deprecated
public class GTPEcalClusterer extends Driver {
Detector detector = null;
Added: java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/CTPClusterer.java
=============================================================================
--- java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/CTPClusterer.java (added)
+++ java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/CTPClusterer.java Fri Jan 9 16:24:39 2015
@@ -0,0 +1,332 @@
+package org.hps.recon.ecal.cluster;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.PriorityQueue;
+import java.util.Set;
+
+import org.hps.recon.ecal.CalorimeterHitUtilities;
+import org.lcsim.conditions.ConditionsEvent;
+import org.lcsim.event.CalorimeterHit;
+import org.lcsim.event.Cluster;
+import org.lcsim.event.EventHeader;
+import org.lcsim.event.base.BaseCalorimeterHit;
+import org.lcsim.event.base.BaseCluster;
+import org.lcsim.geometry.IDDecoder;
+
+/**
+ * Creates clusters from CalorimeterHits in the HPSEcal detector.
+ *
+ * The clustering algorithm is from JLab Hall B 6 GeV DVCS Trigger Design doc.
+ *
+ * @author Kyle McCarty
+ * @author Sho Uemura <[log in to unmask]>
+ * @author Jeremy McCormick <[log in to unmask]>
+ */
+public class CTPClusterer extends AbstractClusterer {
+
+ IDDecoder dec;
+
+ Set<Long> clusterCenters = null;
+ Map<Long, Double> hitSums = null;
+ Map<Long, CalorimeterHit> hitMap = null;
+
+ // The time period in which clusters may be formed. A negative value means that all hits
+ // will always be used in cluster finding, regardless of the time difference between them.
+ double clusterWindow = -1;
+
+ // The minimum energy needed for a hit to be considered.
+ double addEMin = 0;
+
+ CTPClusterer() {
+ super(new String[] { "addEMin", "clusterWindow"}, new double[] { 0., -1. });
+ }
+
+ public void initialize() {
+ addEMin = getCuts().getValue("addEMin");
+ clusterWindow = getCuts().getValue("clusterWindow");
+ }
+
+ @Override
+ public void conditionsChanged(ConditionsEvent event) {
+ super.conditionsChanged(event);
+
+ // Get the decoder for the ECal IDs.
+ dec = ecal.getIDDecoder();
+
+ // Make set of valid cluster centers.
+ // Exclude edge crystals as good cluster centers.
+ clusterCenters = new HashSet<Long>();
+ for (Long cellID : neighborMap.keySet()) {
+ boolean isValidCenter = true;
+ Set<Long> neighbors = neighborMap.get(cellID);
+ for (Long neighborID : neighbors) {
+ Set<Long> neighborneighbors = new HashSet<Long>();
+ neighborneighbors.addAll(neighborMap.get(neighborID));
+ neighborneighbors.add(neighborID);
+
+ if (neighborneighbors.containsAll(neighbors)) {
+ isValidCenter = false;
+ break;
+ }
+ }
+ if (isValidCenter) {
+ clusterCenters.add(cellID);
+ }
+ }
+ }
+
+ @Override
+ public List<Cluster> createClusters(EventHeader event, List<CalorimeterHit> hits) {
+
+ // Define a list of clusters to be filled.
+ List<Cluster> clusters;
+
+ // If there is a cluster window, run the cluster window code. A cluster window is a time
+ // period in nanoseconds during which hits can be applied to the same cluster.
+ if (clusterWindow >= 0) {
+ // Create priority queues. These are sorted by the time variable associated with each hit.
+ PriorityQueue<CalorimeterHit> futureHits = new PriorityQueue<CalorimeterHit>(10, new TimeComparator());
+ PriorityQueue<CalorimeterHit> pastHits = new PriorityQueue<CalorimeterHit>(10, new TimeComparator());
+
+ // Initialize the cluster list variable.
+ clusters = new ArrayList<Cluster>();
+
+ // Populate the list of unprocessed hits with the calorimeter hits. These will then be sorted
+ // by time, from first to last, automatically by the priority queue.
+ for (CalorimeterHit hit : hits) {
+ if (hit.getRawEnergy() > addEMin) {
+ futureHits.add(hit);
+ }
+ }
+
+ // We process the unprocessed hits...
+ while (!futureHits.isEmpty()) {
+ // Move the first occurring hit from the unprocessed list to the processed list.
+ CalorimeterHit nextHit = futureHits.poll();
+ pastHits.add(nextHit);
+
+ // Add any hits that occurred at the same time as the hit we just added to the processed list.
+ while (!futureHits.isEmpty() && futureHits.peek().getTime() == nextHit.getTime()) {
+ pastHits.add(futureHits.poll());
+ }
+
+ // Remove hits that happened earlier than the cluster window period.
+ while (pastHits.peek().getTime() < nextHit.getTime() - clusterWindow) {
+ pastHits.poll();
+ }
+
+ // Calculate the cluster energy for each crystal. This should be the
+ // total energy for the 3x3 crystal collection sorrounding the center
+ // crystal.
+ sumHits(pastHits);
+
+ // Choose which crystal is the appropriate cluster crystal.
+ clusters.addAll(createClusters());
+ }
+ // If there is no cluster window, then all the hits in the event are visible simultaneously.
+ } else {
+ // Calculate the cluster energy of each crystal.
+ sumHits(hits);
+ // Generate the clusters.
+ clusters = createClusters();
+ }
+
+ return clusters;
+ }
+
+ private void sumHits(Collection<CalorimeterHit> hits) {
+ // Store the latest hit on each crystal in a map for later reference in
+ // the clustering algorithm.
+ hitMap = new HashMap<Long, CalorimeterHit>();
+ // Store the cluster energy for each crystal. Cluster energy represents
+ // the total energy of the 3x3 crystal set.
+ hitSums = new HashMap<Long, Double>();
+
+ // Loop over the active calorimeter hits to compute the cluster energies.
+ for (CalorimeterHit hit : hits) {
+ // Make a hit map for quick lookup by ID.
+ hitMap.put(hit.getCellID(), hit);
+
+ // Get the cell ID for the current crystal's neighbors.
+ Set<Long> neighbors = neighborMap.get(hit.getCellID());
+
+ // If there are no neighbors, something is rather wrong.
+ if (neighbors == null) {
+ throw new RuntimeException("Oops! Set of neighbors is null!");
+ }
+
+ // Store the energy of the current calorimeter hit.
+ Double hitSum;
+
+ // We are only interested in this crystal's cluster energy if it is
+ // a valid cluster crystal. Edge crystals are not allowed to be clusters,
+ // so these are ignored.
+ if (clusterCenters.contains(hit.getCellID())) {
+ // Check if an energy has been assigned to the crystal.
+ hitSum = hitSums.get(hit.getCellID());
+
+ // If not, then the crystal's cluster energy is equal to this hit's energy.
+ if (hitSum == null) {
+ hitSums.put(hit.getCellID(), hit.getRawEnergy());
+ }
+ // Otherwise, add the energy of this hit to the total crystal cluster energy.
+ else {
+ hitSums.put(hit.getCellID(), hitSum + hit.getRawEnergy());
+ }
+ }
+
+ // Loop over neighbors to add the current hit's energy to the neighbor's
+ // cluster energy.
+ for (Long neighborId : neighbors) {
+ // If the crystal is not an edge crystal, ignore its hit energy.
+ if (!clusterCenters.contains(neighborId)) {
+ continue;
+ }
+
+ // Get the cluster energy of the neighboring crystals.
+ hitSum = hitSums.get(neighborId);
+
+ // If the neighbor crystal has no cluster energy, then set the
+ // cluster energy to the current hit's energy.
+ if (hitSum == null) {
+ hitSums.put(neighborId, hit.getRawEnergy());
+ }
+ // Otherwise, add the hit's energy to the neighbor's cluster energy.
+ else {
+ hitSums.put(neighborId, hitSum + hit.getRawEnergy());
+ }
+ }
+ }
+ }
+
+ private List<Cluster> createClusters() {
+ // Create a list of clusters to be added to the event,
+ List<Cluster> clusters = new ArrayList<Cluster>();
+
+ // We examine each crystal with a non-zero cluster energy.
+ for(Long possibleCluster : hitSums.keySet()) {
+ // Get the luster energy for the crystal this hit is assocaite with.
+ Double thisSum = hitSums.get(possibleCluster);
+
+ // Get neighboring crystals' IDs.
+ Set<Long> neighbors = neighborMap.get(possibleCluster);
+
+ // If there are no neighbors, throw an error.
+ if (neighbors == null) {
+ throw new RuntimeException("Oops! Set of neighbors is null!");
+ }
+
+ // Get the x/y position of the hit's associated crystal.
+ dec.setID(possibleCluster);
+ int x1 = dec.getValue("ix");
+ int y1 = dec.getValue("iy");
+
+ // Store whether it is a valid cluster or not.
+ boolean isCluster = true;
+
+ // Check to see if any of the crystal's neighbors preclude the current crystal
+ // from being a proper cluster. The cluster crystal should have the highest
+ // energy among its neighbors
+ for (Long neighborId : neighbors) {
+ // Get the x/y position of the neighbor's associated crystal.
+ dec.setID(neighborId);
+ int x2 = dec.getValue("ix");
+ int y2 = dec.getValue("iy");
+
+ // If the neighbor's energy value does not exist, we don't need to perform
+ // any additional checks for this neighbor. A crystal with no energy can
+ // not be the center of a cluster.
+ Double neighborSum = hitSums.get(neighborId);
+ if (neighborSum == null) {
+ continue;
+ }
+
+ // If the neighbor's energy value is greater than this crystal's value,
+ // then this crystal is not the cluster and we may terminate the check.
+ if (neighborSum > thisSum) {
+ isCluster = false;
+ break;
+ }
+ // If the crystals each have the same energy, we choose the crystal that
+ // is closest to the electron side of the detector. If both crystals are
+ // equally close, we choose the crystal closest to the beam gap. If the
+ // neighbor fits these parameters better, this is not a crystal and we
+ // may skip any further checks.
+ else if (neighborSum.equals(thisSum) && (x1 > x2 || (x1 == x2 && Math.abs(y1) < Math.abs(y2)))) {
+ isCluster = false;
+ break;
+ }
+ }
+
+ // If the crystal was not invalidated by the any of the neighboring crystals,
+ // then it is a cluster crystal and should be processed.
+ if (isCluster) {
+ // Make a list to store the hits that are part of this cluster.
+ List<CalorimeterHit> hits = new ArrayList<CalorimeterHit>();
+
+ // Store the time at which the cluster occurred.
+ double clusterTime = Double.NEGATIVE_INFINITY;
+
+ // Get the last hit on this crystal.
+ CalorimeterHit hit = hitMap.get(possibleCluster);
+
+ // If the hit exists, add it to the list of associated hits.
+ if (hit != null) {
+ hits.add(hit);
+
+ // If the latest hit's time is later than the current cluster time,
+ // set the cluster time to the latest hit's time.
+ if (hit.getTime() > clusterTime) {
+ clusterTime = hit.getTime();
+ }
+ }
+
+ // Add all of the neighboring crystals to the cluster, if they have a
+ // hit associated with them. Crystals with no hits are not actually part
+ // of a cluster.
+ for (Long neighborId : neighbors) {
+ hit = hitMap.get(neighborId);
+ if (hit != null) {
+ hits.add(hit);
+ if (hit.getTime() > clusterTime) {
+ clusterTime = hit.getTime();
+ }
+ }
+ }
+
+ // Generate a new cluster seed hit from the above results.
+ CalorimeterHit seedHit = (BaseCalorimeterHit)CalorimeterHitUtilities.create(0.0, clusterTime, possibleCluster, hits.get(0).getMetaData());
+
+ // Generate a new cluster from the seed hit.
+ BaseCluster cluster = new BaseCluster();
+ cluster.addHit(seedHit);
+ // Populate the cluster with each of the chosen neighbors.
+ for (CalorimeterHit clusterHit : hits) {
+ cluster.addHit(clusterHit);
+ }
+ // Add the cluster to the cluster list.
+ clusters.add(cluster);
+ }
+ }
+
+ // Return the list of clusters.
+ return clusters;
+ }
+
+ static class TimeComparator implements Comparator<CalorimeterHit> {
+ // Compare by time with the earlier coming before the later.
+ public int compare(CalorimeterHit o1, CalorimeterHit o2) {
+ if (o1.getTime() == o2.getTime()) {
+ return 0;
+ } else {
+ return (o1.getTime() > o2.getTime()) ? 1 : -1;
+ }
+ }
+ }
+}
Modified: java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/ClustererFactory.java
=============================================================================
--- java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/ClustererFactory.java (original)
+++ java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/ClustererFactory.java Fri Jan 9 16:24:39 2015
@@ -13,13 +13,15 @@
* this class implements the {@link Clusterer} interface and will throw an error if it does not.
*
* @see Clusterer
+ * @see CTPClusterer
* @see DualThresholdCosmicClusterer
+ * @see GTPClusterer
* @see GTPOnlineClusterer
* @see LegacyClusterer
* @see NearestNeighborClusterer
* @see ReconClusterer
* @see SimpleReconClusterer
- * @see SimpleCosmicClusterer
+ * @see SimpleCosmicClusterer
*
* @author Jeremy McCormick <[log in to unmask]>
*/
@@ -54,6 +56,10 @@
clusterer = new SimpleCosmicClusterer();
} else if (GTPOnlineClusterer.class.getSimpleName().equals(name)) {
clusterer = new GTPOnlineClusterer();
+ } else if (GTPClusterer.class.getSimpleName().equals(name)) {
+ clusterer = new GTPClusterer();
+ } else if (CTPClusterer.class.getSimpleName().equals(name)) {
+ clusterer = new CTPClusterer();
} else {
// Try to instantiate a Clusterer object from the name argument, assuming it is a canonical class name.
try {
Added: java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/GTPClusterDriver.java
=============================================================================
--- java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/GTPClusterDriver.java (added)
+++ java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/GTPClusterDriver.java Fri Jan 9 16:24:39 2015
@@ -0,0 +1,31 @@
+package org.hps.recon.ecal.cluster;
+
+/**
+ * This is a Driver to wrap the GTPClusterer algorithm,
+ * allowing the <code>limitClusterRange</code> to be
+ * set publicly.
+ *
+ * @see GTPClusterer
+ *
+ * @author Jeremy McCormick <[log in to unmask]>
+ */
+public class GTPClusterDriver extends ClusterDriver {
+
+ public GTPClusterDriver() {
+ clusterer = ClustererFactory.create("GTPClusterer");
+ }
+
+ /**
+ * Sets whether hits should be added to a cluster from the entire
+ * cluster window or just the "future" hits, plus one clock-cycle
+ * of "past" hits as a safety buffer to account for time uncertainty.
+ *
+ * @param limitClusterRange - <code>true</code> indicates that
+ * the asymmetric clustering window should be used and <code>
+ * false</code> that the symmetric window should be used.
+ */
+ void setLimitClusterRange(boolean limitClusterRange) {
+ GTPClusterer gtpClusterer = getClusterer();
+ gtpClusterer.setLimitClusterRange(limitClusterRange);
+ }
+}
Added: java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/GTPClusterer.java
=============================================================================
--- java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/GTPClusterer.java (added)
+++ java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/GTPClusterer.java Fri Jan 9 16:24:39 2015
@@ -0,0 +1,271 @@
+package org.hps.recon.ecal.cluster;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.lcsim.event.CalorimeterHit;
+import org.lcsim.event.Cluster;
+import org.lcsim.event.EventHeader;
+import org.lcsim.event.base.BaseCluster;
+
+/**
+ * Class <code>GTPCalorimeterClusterer</code> processes events and converts hits
+ * into clusters, where appropriate. It uses the modified 2014 clustering algorithm.<br/>
+ * <br/>
+ * For a hit to be a cluster center, it is required to have an energy above some
+ * tunable minimum threshold. Additionally, the hit must be a local maximum with
+ * respect to its neighbors and itself over a tunable (default 2) clock cycles.
+ * Hits that pass these checks are then required to additional have a total
+ * cluster energy that exceeds another tunable minimum threshold.<br/>
+ * <br/>
+ * A hit is added to a cluster as a component if it has a non-zero energy and
+ * within the aforementioned tunable time buffer used for clustering and is
+ * either at the same location as the seed hit or is a neighbor to the seed hit.
+ * @author Kyle McCarty
+ * @author Sho Uemura
+ */
+public class GTPClusterer extends AbstractClusterer {
+
+ /**
+ * <b>seedEnergyThreshold</b><br/><br/>
+ * <code>private double <b>seedEnergyThreshold</b></code><br/><br/>
+ * The minimum energy required for a hit to be considered as a cluster
+ * center. Hits with energy less than this value will be ignored.
+ */
+ private double seedEnergyThreshold;
+
+ /**
+ * <b>clusterWindow</b><br/><br/>
+ * <code>private int <b>clusterWindow</b></code><br/><br/>
+ * Indicates the number of FADC clock cycles (each cycle is 4 ns) before and
+ * after a given cycle that should be considered when checking if a cluster
+ * is a local maximum in space-time.
+ */
+ private int clusterWindow;
+
+ /**
+ * <b>hitBuffer</b><br/><br/>
+ * <code>private LinkedList<List<CalorimeterHit>> <b>hitBuffer</b></code><br/><br/>
+ * Stores a set of all the hits occurring in each clock cycle for the number
+ * of clock cycles that should be considered for clustering.
+ */
+ private LinkedList<Map<Long, CalorimeterHit>> hitBuffer;
+
+ /**
+ * <b>limitClusterRange</b><br/><br/>
+ * <code>private boolean <b>limitClusterRange</b></code><br/><br/>
+ * Whether an asymmetric or symmetric window should be used for
+ * adding hits to a cluster.
+ */
+ private boolean limitClusterRange = false;
+
+ GTPClusterer() {
+ super(new String[] { "seedEnergyThreshold", "clusterWindow" }, new double[] { 0.05, 2.});
+ }
+
+ public void initialize() {
+
+ // Set cuts.
+ setSeedEnergyThreshold(getCuts().getValue("seedEnergyThreshold"));
+ setClusterWindow((int) getCuts().getValue("clusterWindow"));
+
+ // Initiate the hit buffer.
+ hitBuffer = new LinkedList<Map<Long, CalorimeterHit>>();
+
+ // Populate the event buffer with (2 * clusterWindow + 1)
+ // empty events. These empty events represent the fact that
+ // the first few events will not have any events in the past
+ // portion of the buffer.
+ int bufferSize = (2 * clusterWindow) + 1;
+ for (int i = 0; i < bufferSize; i++) {
+ hitBuffer.add(new HashMap<Long, CalorimeterHit>(0));
+ }
+ }
+
+ /**
+ * Generates a list of clusters from the current hit buffer. The "present"
+ * event is taken to be the list of hits occurring at index
+ * <code>clusterWindow</code>, which is the middle of the buffer.
+ *
+ * @return Returns a <code>List</code> of <code>HPSEcalCluster
+ * </code> objects generated from the current event.
+ */
+ private List<Cluster> getClusters() {
+ // Generate a list for storing clusters.
+ List<Cluster> clusters = new ArrayList<Cluster>();
+
+ // Get the list of hits at the current time in the event buffer.
+ Map<Long, CalorimeterHit> currentHits = hitBuffer.get(clusterWindow);
+
+ // For a hit to be a cluster center, it must be a local maximum
+ // both with respect to its neighbors and itself both in the
+ // present time and at all times within the event buffer.
+ seedLoop:
+ for (Long currentID : currentHits.keySet()) {
+ // Get the actual hit object.
+ CalorimeterHit currentHit = currentHits.get(currentID);
+
+ // Store the energy of the current hit.
+ double currentEnergy = currentHit.getRawEnergy();
+
+ // If the hit energy is lower than the minimum threshold,
+ // then we immediately reject this hit as a possible cluster.
+ if (currentEnergy < seedEnergyThreshold) {
+ continue seedLoop;
+ }
+
+ // Store the crystals that are part of this potential cluster,
+ // starting with the cluster seed candidate.
+ BaseCluster cluster = new BaseCluster();
+ cluster.addHit(currentHit);
+
+ // Get the set of neighbors for this hit.
+ Set<Long> neighbors = neighborMap.get(currentHit.getCellID());
+
+ // Sort through each event stored in the buffer.
+ int bufferIndex = 0;
+ for (Map<Long, CalorimeterHit> bufferHits : hitBuffer) {
+ // Get the hit energy at the current hit's position in
+ // the buffer, if it exists. Ignore the current seed candidate.
+ CalorimeterHit bufferHit = bufferHits.get(currentID);
+ if (bufferHit != null && bufferHit != currentHit) {
+ double bufferHitEnergy = bufferHit.getRawEnergy();
+
+ // Check to see if the hit at this point in the buffer
+ // is larger than then original hit. If it is, we may
+ // stop the comparison because this is not a cluster.
+ if (bufferHitEnergy > currentEnergy) {
+ continue seedLoop;
+ }
+
+ // If the buffer hit is smaller, then add its energy
+ // to the cluster total energy.
+ else {
+ if(limitClusterRange && bufferIndex <= clusterWindow + 1) { cluster.addHit(bufferHit); }
+ else if(!limitClusterRange) { cluster.addHit(bufferHit); }
+ }
+ }
+
+ // We must also make sure that the original hit is
+ // larger than all of the neighboring hits at this
+ // point in the buffer as well.
+ for (Long neighborID : neighbors) {
+ // Get the neighbor hit energy if it exists.
+ CalorimeterHit neighborHit = bufferHits.get(neighborID);
+ if (neighborHit != null) {
+ double neighborHitEnergy = neighborHit.getRawEnergy();
+
+ // Check to see if the neighbor hit at this point
+ // in the buffer is larger than then original hit.
+ // If it is, we may stop the comparison because this
+ // is not a cluster.
+ if (neighborHitEnergy > currentEnergy) {
+ continue seedLoop;
+ }
+
+ // If the buffer neighbor hit is smaller, then
+ // add its energy to the cluster total energy.
+ else {
+ if(limitClusterRange && bufferIndex <= clusterWindow + 1) { cluster.addHit(neighborHit); }
+ else if(!limitClusterRange) { cluster.addHit(neighborHit); }
+ }
+ }
+ }
+
+ // Increment the buffer index.
+ bufferIndex++;
+ }
+
+ // Add the cluster to the list of clusters.
+ clusters.add(cluster);
+ }
+
+ // Return the generated list of clusters.
+ return clusters;
+ }
+
+ /**
+ * Places hits from the current event into the event hit buffer and
+ * processes the buffer to extract clusters. Clusters are then stored in the
+ * event object.
+ *
+ * @param event - The event to process.
+ */
+ public List<Cluster> createClusters(EventHeader event, List<CalorimeterHit> hits) {
+
+ // Store each hit in a set by its cell ID so that it may be
+ // easily acquired later.
+ HashMap<Long, CalorimeterHit> hitMap = new HashMap<Long, CalorimeterHit>();
+ for (CalorimeterHit hit : hits) {
+ hitMap.put(hit.getCellID(), hit);
+ }
+
+ // Remove the last event from the hit buffer and add the new one.
+ hitBuffer.removeLast();
+ hitBuffer.addFirst(hitMap);
+
+ // Run the clustering algorithm on the buffer.
+ List<Cluster> clusterList = getClusters();
+
+ return clusterList;
+ }
+
+ /**
+ * Sets the number of clock cycles before and after a given cycle that will
+ * be used when checking whether a given hit is a local maximum in both time
+ * and space. Note that a value of
+ * <code>N
+ * </code> indicates that
+ * <code>N</code> clock cycles before and
+ * <code>N</code> clock cycles after will be considered. Thusly, a total of
+ * <code>2N + 1</code> clock cycles will be used total.
+ *
+ * @param clusterWindow - The number of additional clock cycles to include
+ * in the clustering checks. A negative value will be treated as zero.
+ */
+ void setClusterWindow(int clusterWindow) {
+ // The cluster window of must always be at least zero.
+ if (clusterWindow < 0) {
+ this.clusterWindow = 0;
+ }
+
+ // If the cluster window is non-zero, then store it.
+ else {
+ this.clusterWindow = clusterWindow;
+ }
+ }
+
+ /**
+ * Sets whether hits should be added to a cluster from the entire
+ * cluster window or just the "future" hits, plus one clock-cycle
+ * of "past" hits as a safety buffer to account for time uncertainty.
+ *
+ * @param limitClusterRange - <code>true</code> indicates that
+ * the asymmetric clustering window should be used and <code>
+ * false</code> that the symmetric window should be used.
+ */
+ void setLimitClusterRange(boolean limitClusterRange) {
+ this.limitClusterRange = limitClusterRange;
+ }
+
+ /**
+ * Sets the minimum energy threshold below which hits will not be considered
+ * as cluster centers.
+ *
+ * @param seedEnergyThreshold - The minimum energy for a cluster center.
+ */
+ void setSeedEnergyThreshold(double seedEnergyThreshold) {
+ // A negative energy threshold is non-physical. All thresholds
+ // be at least zero.
+ if (seedEnergyThreshold < 0.0) {
+ this.seedEnergyThreshold = 0.0;
+ } // If the energy threshold is valid, then use it.
+ else {
+ this.seedEnergyThreshold = seedEnergyThreshold;
+ }
+ }
+}
Modified: java/trunk/ecal-recon/src/test/java/org/hps/recon/ecal/cluster/ClustererTest.java
=============================================================================
--- java/trunk/ecal-recon/src/test/java/org/hps/recon/ecal/cluster/ClustererTest.java (original)
+++ java/trunk/ecal-recon/src/test/java/org/hps/recon/ecal/cluster/ClustererTest.java Fri Jan 9 16:24:39 2015
@@ -35,7 +35,7 @@
*/
public class ClustererTest extends TestCase {
- static int nEvents = 1000;
+ static int nEvents = 100;
static final String fileLocation = "http://www.lcsim.org/test/hps-java/MockDataReconTest.slcio";
File inputFile;
File testOutputDir;
@@ -48,11 +48,11 @@
throw new RuntimeException(e);
}
- // Create test output dir.
+ // Create test output directory.
testOutputDir = new TestOutputFile(getClass().getSimpleName());
testOutputDir.mkdir();
- // Initialize conditions system.
+ // Initialize the conditions system.
new DatabaseConditionsManager();
DatabaseConditionsManager.getInstance().setLogLevel(Level.WARNING);
}
@@ -79,6 +79,14 @@
public void testGTPOnlineClusterer() {
runClustererTest("GTPOnlineClusterer", null, true, true);
+ }
+
+ public void testCTPClusterer() {
+ runClustererTest("CTPClusterer", null, true, false);
+ }
+
+ public void testGTPClusterer() {
+ runClustererTest("GTPClusterer", null, true, false);
}
/**
|