Author: [log in to unmask]
Date: Tue Dec 16 15:15:39 2014
New Revision: 1761
Log:
Snapshot of work on new clustering package including better doc and API additions. Several simple Clusterer algorithms are implemented now and seem to basically work.
Added:
java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/SimpleClasInnerCalClusterer.java
- copied, changed from r1759, java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/SimpleInnerCalClusterer.java
Removed:
java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/SimpleInnerCalClusterer.java
Modified:
java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/AbstractClusterer.java
java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/ClusterDriver.java
java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/ClusterUtilities.java
java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/Clusterer.java
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/LegacyClusterer.java
Modified: java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/AbstractClusterer.java
=============================================================================
--- java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/AbstractClusterer.java (original)
+++ java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/AbstractClusterer.java Tue Dec 16 15:15:39 2014
@@ -2,20 +2,131 @@
import java.util.List;
+import org.hps.conditions.database.DatabaseConditionsManager;
+import org.lcsim.conditions.ConditionsEvent;
import org.lcsim.event.CalorimeterHit;
import org.lcsim.event.Cluster;
+import org.lcsim.event.EventHeader;
import org.lcsim.geometry.subdetector.HPSEcal3;
import org.lcsim.geometry.subdetector.HPSEcal3.NeighborMap;
+/**
+ * This is an abstract class that {@link Clusterer} classes should implement
+ * to perform a clustering algorithm on a <code>CalorimeterHit</code> collection.
+ * The sub-class should implement {@link #createClusters(List)} which is
+ * the method that should perform the clustering.
+ *
+ * @author Jeremy McCormick <[log in to unmask]>
+ */
public abstract class AbstractClusterer implements Clusterer {
- HPSEcal3 ecal;
- NeighborMap neighborMap;
+ protected HPSEcal3 ecal;
+ protected NeighborMap neighborMap;
+ protected double[] cuts;
+ protected double[] defaultCuts;
+ protected String[] cutNames;
- public void setEcalSubdetector(HPSEcal3 ecal) {
- this.ecal = ecal;
+ /**
+ * This is the primary method for sub-classes to implement their clustering algorithm.
+ * @param hits
+ * @return
+ */
+ public abstract List<Cluster> createClusters(EventHeader event, List<CalorimeterHit> hits);
+
+ /**
+ * Detector setup performed here to get reference to ECAL subdetector and neighbor mapping.
+ */
+ @Override
+ public void conditionsChanged(ConditionsEvent event) {
+ // Default setup of ECAL subdetector.
+ this.ecal = (HPSEcal3) DatabaseConditionsManager.getInstance().getDetectorObject().getSubdetector("Ecal");
this.neighborMap = ecal.getNeighborMap();
}
- public abstract List<Cluster> createClusters(List<CalorimeterHit> hits);
+ /**
+ * By default nothing is done in this method, but start of job initialization can happen here like reading
+ * cut settings into instance variables for convenience. This is called in the <code>startOfData</code>
+ * method of {@link ClusterDriver}.
+ */
+ public void initialize() {
+ }
+
+ /**
+ * Default constructor which takes names of cuts and their default values.
+ * Even if there are no cuts, these should be arrays of length 0 instead of null.
+ * @param cutNames The names of the cuts for this clustering algorithm.
+ * @param defaultCuts The default cut values for the algorithm matching the cutNames ordering.
+ * @throw IllegalArgumentException if the arguments are null or the arrays are different lengths.
+ */
+ protected AbstractClusterer(String cutNames[], double[] defaultCuts) {
+ if (cutNames == null) {
+ throw new IllegalArgumentException("The cutNames is set to null.");
+ }
+ if (defaultCuts == null) {
+ throw new IllegalArgumentException("The defaultCuts is set to null.");
+ }
+ if (cutNames.length != defaultCuts.length) {
+ throw new IllegalArgumentException("The cutNames and defaultCuts are not the same length.");
+ }
+ this.cutNames = cutNames;
+ this.defaultCuts = defaultCuts;
+ this.cuts = defaultCuts;
+ }
+
+ public void setCuts(double[] cuts) {
+ if (cuts.length != this.cutNames.length) {
+ throw new IllegalArgumentException("The cuts array has the wrong length: " + cuts.length);
+ }
+ this.cuts = cuts;
+ }
+
+ public double[] getCuts() {
+ return cuts;
+ }
+
+ public double getCut(String name) {
+ int index = indexFromName(name);
+ if (index == -1) {
+ throw new IllegalArgumentException("There is no cut called " + name + " defined by this clusterer.");
+ }
+ return getCut(index);
+ }
+
+ public double getCut(int index) {
+ if (index > cuts.length || index < 0) {
+ throw new IndexOutOfBoundsException("The index " + index + " is out of bounds for cuts array.");
+ }
+ return cuts[index];
+ }
+
+ public String[] getCutNames() {
+ return cutNames;
+ }
+
+ @Override
+ public void setCut(int index, double value) {
+ cuts[index] = value;
+ }
+
+ public boolean isDefaultCuts() {
+ return cuts == defaultCuts;
+ }
+
+ public double[] getDefaultCuts() {
+ return defaultCuts;
+ }
+
+ public void setCut(String name, double value) {
+ int index = indexFromName(name);
+ cuts[index] = value;
+ }
+
+ protected int indexFromName(String name) {
+ for (int index = 0; index < cuts.length; index++) {
+ if (getCutNames()[index] == name) {
+ return index;
+ }
+ }
+ return -1;
+ }
}
Modified: java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/ClusterDriver.java
=============================================================================
--- java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/ClusterDriver.java (original)
+++ java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/ClusterDriver.java Tue Dec 16 15:15:39 2014
@@ -1,6 +1,7 @@
package org.hps.recon.ecal.cluster;
import java.util.List;
+import java.util.logging.Logger;
import org.lcsim.event.CalorimeterHit;
import org.lcsim.event.Cluster;
@@ -10,10 +11,18 @@
import org.lcsim.geometry.subdetector.HPSEcal3;
import org.lcsim.lcio.LCIOConstants;
import org.lcsim.util.Driver;
+import org.lcsim.util.log.LogUtil;
+import org.lcsim.util.log.BasicFormatter;
/**
- * This is a basic Driver that creates Cluster collections through the
- * Clusterer interface.
+ * <p>
+ * This is a basic Driver that creates ECAL <code>Cluster</code> collections
+ * through the {@link Clusterer} interface.
+ * <p>
+ * A specific clustering engine can be created with the {@link #setClusterer(String)} method
+ * which will use a factory to create it by name. The cuts of the {@link Clusterer}
+ * can be set generically with the {@link #setCuts(double[])} method.
+ *
* @author Jeremy McCormick <[log in to unmask]>
*/
public class ClusterDriver extends Driver {
@@ -28,28 +37,35 @@
protected boolean skipNoClusterEvents = false;
protected boolean writeClusterCollection = true;
protected boolean storeHits = true;
+ protected double[] cuts;
+ protected Logger logger = LogUtil.create(ClusterDriver.class, new BasicFormatter(ClusterDriver.class.getSimpleName()));
protected ClusterDriver() {
+ logger.config("initializing");
}
public void setEcalName(String ecalName) {
this.ecalName = ecalName;
}
- public void setOutputClusterCollectionName(String outputClusterCollectionName) {
+ public void setOutputClusterCollectionName(String outputClusterCollectionName) {
this.outputClusterCollectionName = outputClusterCollectionName;
+ this.getLogger().config("outputClusterCollectionName = " + this.outputClusterCollectionName);
}
public void setInputHitCollectionName(String inputHitCollectionName) {
this.inputHitCollectionName = inputHitCollectionName;
+ this.getLogger().config("inputClusterCollectionName = " + this.inputHitCollectionName);
}
public void setSkipNoClusterEvents(boolean skipNoClusterEvents) {
- this.skipNoClusterEvents = skipNoClusterEvents;
+ this.skipNoClusterEvents = skipNoClusterEvents;
+ this.getLogger().config("skipNoClusterEvents = " + this.skipNoClusterEvents);
}
public void setWriteClusterCollection(boolean writeClusterCollection) {
this.writeClusterCollection = writeClusterCollection;
+ this.getLogger().config("writeClusterCollection = " + this.writeClusterCollection);
}
public void setRaiseErrorNoHitCollection(boolean raiseErrorNoHitCollection) {
@@ -72,7 +88,12 @@
this.createEmptyClusterCollection = createEmptyClusterCollection;
}
+ public void setCuts(double[] cuts) {
+ this.cuts = cuts;
+ }
+
public void detectorChanged(Detector detector) {
+ logger.fine("detectorChanged");
Subdetector subdetector = detector.getSubdetector(ecalName);
if (subdetector == null) {
throw new RuntimeException("There is no subdetector called " + ecalName + " in the detector.");
@@ -84,45 +105,62 @@
}
public void startOfData() {
+ logger.fine("startOfData");
if (this.clusterer == null) {
throw new RuntimeException("The clusterer was never initialized.");
}
- if (this.clusterer instanceof AbstractClusterer) {
- ((AbstractClusterer)clusterer).setEcalSubdetector(ecal);
- }
+ if (this.cuts != null) {
+ logger.config("setting cuts on clusterer");
+ this.clusterer.setCuts(cuts);
+ for (int cutIndex = 0; cutIndex < clusterer.getCuts().length; cutIndex++) {
+ logger.config(" " + this.clusterer.getCutNames()[cutIndex] + " = " + this.clusterer.getCut(cutIndex));
+ }
+ }
+ logger.config("initializing clusterer");
+ this.clusterer.initialize();
}
/**
* This method implements the default clustering procedure based on input parameters.
*/
public void process(EventHeader event) {
+ this.getLogger().fine("processing event #" + event.getEventNumber());
if (event.hasCollection(CalorimeterHit.class, inputHitCollectionName)) {
List<CalorimeterHit> hits = event.get(CalorimeterHit.class, inputHitCollectionName);
- List<Cluster> clusters = clusterer.createClusters(hits);
+ this.getLogger().fine("Input hit collection " + inputHitCollectionName + " has " + hits.size() + " hits.");
+ List<Cluster> clusters = clusterer.createClusters(event, hits);
if (clusters == null) {
- throw new RuntimeException("The clusterer returned a null pointer.");
+ throw new RuntimeException("The clusterer returned null from its createClusters method.");
}
if (clusters.isEmpty() && this.skipNoClusterEvents) {
+ logger.finer("Skipping event with no clusters.");
throw new NextEventException();
}
if (event.hasCollection(Cluster.class, this.outputClusterCollectionName)) {
- throw new RuntimeException("There is already a cluster collection called " + this.outputClusterCollectionName);
+ this.getLogger().severe("There is already a cluster collection called " + this.outputClusterCollectionName);
+ throw new RuntimeException("Cluster collection already exists in event.");
}
int flags = 0;
if (this.storeHits) {
flags = 1 << LCIOConstants.CLBIT_HITS;
}
if (!clusters.isEmpty() || this.createEmptyClusterCollection) {
+ logger.finer("writing " + clusters.size() + " clusters to collection " + outputClusterCollectionName);
event.put(outputClusterCollectionName, clusters, Cluster.class, flags);
if (!this.writeClusterCollection) {
+ logger.finer("Collection is set to transient and will not be persisted.");
event.getMetaData(clusters).setTransient(true);
}
}
} else {
- this.getLogger().warning("The input hit collection " + this.inputHitCollectionName + " is missing from the event.");
+ this.getLogger().severe("The input hit collection " + this.inputHitCollectionName + " is missing from the event.");
if (this.raiseErrorNoHitCollection) {
- throw new RuntimeException("The expected hit collection " + this.inputHitCollectionName + " is missing from the event.");
+ throw new RuntimeException("The expected input hit collection is missing from the event.");
}
}
}
+
+ public Logger getLogger() {
+ return logger;
+ }
}
Modified: java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/ClusterUtilities.java
=============================================================================
--- java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/ClusterUtilities.java (original)
+++ java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/ClusterUtilities.java Tue Dec 16 15:15:39 2014
@@ -1,5 +1,6 @@
package org.hps.recon.ecal.cluster;
+import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
@@ -7,13 +8,21 @@
import java.util.Set;
import org.lcsim.event.CalorimeterHit;
+import org.lcsim.event.Cluster;
+import org.lcsim.event.base.BaseCluster;
import org.lcsim.geometry.subdetector.HPSEcal3;
+/**
+ * This is a set of simple clustering utility methods.
+ */
public final class ClusterUtilities {
private ClusterUtilities() {
}
+ /**
+ * Create a map of IDs to their hits.
+ */
public static Map<Long, CalorimeterHit> createHitMap(List<CalorimeterHit> hits) {
Map<Long, CalorimeterHit> hitMap = new LinkedHashMap<Long, CalorimeterHit>();
for (CalorimeterHit hit : hits) {
@@ -23,7 +32,7 @@
}
/**
- * Given a hit, find its list of neighboring crystals that have hits and return their IDs.
+ * Given a hit, find the list of neighboring crystal IDs that also have hits.
* @param hit The input hit.
* @param hitMap The hit map with all the collection's hits.
* @return The set of neighboring hit IDs.
@@ -38,4 +47,30 @@
}
return neighborHitIDs;
}
+
+ /**
+ * Create a basic cluster from a list of hits.
+ * @param clusterHits The list of hits.
+ * @return The basic cluster.
+ */
+ protected static Cluster createBasicCluster(List<CalorimeterHit> clusterHits) {
+ BaseCluster cluster = new BaseCluster();
+ double totalEnergy = 0;
+ for (CalorimeterHit clusterHit : clusterHits) {
+ cluster.addHit(clusterHit);
+ totalEnergy += clusterHit.getCorrectedEnergy();
+ }
+ cluster.setEnergy(totalEnergy);
+ return cluster;
+ }
+
+ /**
+ * Compare CalorimeterHit objects by their energy.
+ */
+ public static class HitEnergyComparator implements Comparator<CalorimeterHit> {
+ @Override
+ public int compare(CalorimeterHit o1, CalorimeterHit o2) {
+ return Double.compare(o1.getCorrectedEnergy(), o2.getCorrectedEnergy());
+ }
+ }
}
Modified: java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/Clusterer.java
=============================================================================
--- java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/Clusterer.java (original)
+++ java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/Clusterer.java Tue Dec 16 15:15:39 2014
@@ -2,11 +2,85 @@
import java.util.List;
+import org.lcsim.conditions.ConditionsListener;
import org.lcsim.event.CalorimeterHit;
import org.lcsim.event.Cluster;
-import org.lcsim.geometry.subdetector.HPSEcal3;
+import org.lcsim.event.EventHeader;
-public interface Clusterer {
+/**
+ * This is an interface for creating clusters and providing cut values
+ * to the clustering algorithms in a generic fashion.
+ */
+public interface Clusterer extends ConditionsListener {
- List<Cluster> createClusters(List<CalorimeterHit> hits);
+ /**
+ * Create a list of output clusters from input hits.
+ * @param event The current LCSim event.
+ * @param hits The list of hits.
+ * @return The output clusters.
+ */
+ List<Cluster> createClusters(EventHeader event, List<CalorimeterHit> hits);
+
+ /**
+ * Perform start of job intialization on this object.
+ */
+ void initialize();
+
+ /**
+ * Get the list of numerical cuts.
+ * @return The list of numerical cuts.
+ */
+ double[] getCuts();
+
+ /**
+ * Get the default cut values.
+ * @return The default cut values.
+ */
+ double[] getDefaultCuts();
+
+ /**
+ * True if algorithm is using its default cuts.
+ * @return True if using the default cuts.
+ */
+ boolean isDefaultCuts();
+
+ /**
+ * Set numerical cuts array.
+ * @param cuts The numerical cuts.
+ */
+ void setCuts(double[] cuts);
+
+ /**
+ * Get a cut value by its index.
+ * @param index The index of the cut.
+ * @return The cut value at index.
+ */
+ double getCut(int index);
+
+ /**
+ * Get a cut value by name.
+ * @param name The name of the cut.
+ * @return The named cut.
+ */
+ double getCut(String name);
+
+ /**
+ * Set a cut value by name.
+ * @param name The name of the cut.
+ * @param value The value of the cut.
+ */
+ void setCut(String name, double value);
+
+ /**
+ * Set a cut value by index.
+ * @param index The index of the cut.
+ * @param value The value of the cut.
+ */
+ void setCut(int index, double value);
+
+ /**
+ * Get the names of the cuts.
+ * @return The names of the cuts.
+ */
+ String[] getCutNames();
}
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 Tue Dec 16 15:15:39 2014
@@ -1,18 +1,49 @@
package org.hps.recon.ecal.cluster;
+import org.lcsim.conditions.ConditionsListener;
+import org.lcsim.conditions.ConditionsManager;
+
+/**
+ * This is a convenience class for creating different kinds of clustering algorithms.
+ * @author Jeremy McCormick <[log in to unmask]>
+ */
public final class ClustererFactory {
private ClustererFactory() {
}
+ /**
+ * Create a clustering algorithm with a set of cuts.
+ * @param name The name of the clustering algorithm.
+ * @param cuts The set of cuts (can be null).
+ * @return The clustering algorithm.
+ * @throw IllegalArgumentException if there is no Clusterer found with name.
+ */
+ public static Clusterer create(String name, double[] cuts) {
+ Clusterer clusterer;
+ System.out.println("simple name:" + LegacyClusterer.class.getSimpleName());
+ if (LegacyClusterer.class.getSimpleName().equals(name)) {
+ clusterer = new LegacyClusterer();
+ } else if (SimpleClasInnerCalClusterer.class.getSimpleName().equals(name)) {
+ clusterer = new SimpleClasInnerCalClusterer();
+ } else {
+ throw new IllegalArgumentException("Unknown clusterer type: " + name);
+ }
+ if (clusterer instanceof ConditionsListener) {
+ ConditionsManager.defaultInstance().addConditionsListener((ConditionsListener) clusterer);
+ }
+ if (cuts != null) {
+ clusterer.setCuts(cuts);
+ }
+ return clusterer;
+ }
+
+ /**
+ * Create a clustering algorithm with default cut values.
+ * @param name The name of the clustering algorithm.
+ * @return The clustering algorithm.
+ */
public static Clusterer create(String name) {
- if (LegacyClusterer.class.getSimpleName().equals(name)) {
- return new LegacyClusterer();
- } if (SimpleInnerCalClusterer.class.getSimpleName().equals(name)) {
- return new SimpleInnerCalClusterer();
- } else {
- throw new IllegalArgumentException("Unknown clusterer: " + name);
- }
+ return create(name, null);
}
-
}
Modified: java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/LegacyClusterer.java
=============================================================================
--- java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/LegacyClusterer.java (original)
+++ java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/LegacyClusterer.java Tue Dec 16 15:15:39 2014
@@ -9,36 +9,35 @@
import org.hps.recon.ecal.HPSEcalCluster;
import org.lcsim.event.CalorimeterHit;
import org.lcsim.event.Cluster;
+import org.lcsim.event.EventHeader;
/**
- * This Driver creates clusters from the CalorimeterHits of an
- * {@link org.lcsim.geometry.subdetectur.HPSEcal3} detector.
- *
- * The clustering algorithm is from pages 83 and 84 of the HPS Proposal.
+ * <p>
+ * This Driver creates clusters from a CalorimeterHit input collection.
+ * <p>
+ * The clustering algorithm is implemented according to the description in pages 83 and 84 of the
+ * <a href="https://confluence.slac.stanford.edu/download/attachments/86676777/HPSProposal-FINAL_Rev2.pdf">HPS Proposal document</a>.
+ * <p>
+ * This is a simple algorithm that is obsolete! The current IC or hardware algorithm clustering algorithms should generally be used instead.
*
* @author Jeremy McCormick <[log in to unmask]>
* @author Tim Nelson <[log in to unmask]>
*/
public class LegacyClusterer extends AbstractClusterer {
-
- // Minimum E for cluster seed.
- double minimumClusterSeedEnergy = 0.05 * ECalUtils.GeV;
-
- // Minimum E to add hit to cluster.
- double minimumHitEnergy = 0.03 * ECalUtils.GeV;
-
- void setMinimumClusterSeedEnergy(double minimumClusterSeedEnergy) {
- this.minimumClusterSeedEnergy = minimumClusterSeedEnergy;
- }
-
- void setMinimumHitEnergy(double minimumHitEnergy) {
- this.minimumHitEnergy = minimumHitEnergy;
- if (minimumClusterSeedEnergy < minimumHitEnergy) {
- minimumClusterSeedEnergy = minimumHitEnergy;
- }
+
+ double minClusterSeedEnergy;
+ double minHitEnergy;
+
+ LegacyClusterer() {
+ super(new String[] { "minClusterSeedEnergy", "minHitEnergy" }, new double[] { 0.05 * ECalUtils.GeV, 0.03 * ECalUtils.GeV });
}
- public List<Cluster> createClusters(List<CalorimeterHit> hits) {
+ public void initialize() {
+ minClusterSeedEnergy = this.getCut("minClusterSeedEnergy");
+ minHitEnergy = this.getCut("minHitEnergy");
+ }
+
+ public List<Cluster> createClusters(EventHeader event, List<CalorimeterHit> hits) {
Map<Long, CalorimeterHit> hitMap = ClusterUtilities.createHitMap(hits);
@@ -48,7 +47,7 @@
// Loop over ECal hits to find cluster seeds.
for (CalorimeterHit hit : hitMap.values()) {
// Cut on min seed E.
- if (hit.getRawEnergy() < minimumClusterSeedEnergy) {
+ if (hit.getRawEnergy() < minClusterSeedEnergy) {
continue;
}
@@ -74,7 +73,7 @@
}
// Add to cluster if above min E.
- if (neighborHit.getRawEnergy() >= minimumHitEnergy) {
+ if (neighborHit.getRawEnergy() >= minHitEnergy) {
neighborHits.add(neighborHit);
}
}
Copied: java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/SimpleClasInnerCalClusterer.java (from r1759, java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/SimpleInnerCalClusterer.java)
=============================================================================
--- java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/SimpleInnerCalClusterer.java (original)
+++ java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/cluster/SimpleClasInnerCalClusterer.java Tue Dec 16 15:15:39 2014
@@ -2,7 +2,6 @@
import java.util.ArrayList;
import java.util.Collections;
-import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -10,98 +9,83 @@
import org.hps.recon.ecal.HPSEcalCluster;
import org.lcsim.event.CalorimeterHit;
import org.lcsim.event.Cluster;
+import org.lcsim.event.EventHeader;
/**
- * This Driver creates clusters from the CalorimeterHits of an
- * {@link org.lcsim.geometry.subdetector.HPSEcal3} detector.
- *
- * Uses basic IC clustering algorithm as given in CLAS note 2004-040: no common
- * hits (hits are assigned to cluster with largest seed hit energy).
- *
- * Hit time information is not used, and multiple hits in the same crystal are
- * not handled correctly (a warning is printed); optional time cut is applied at
- * the beginning to discard hits too far from t0.
+ * <p>
+ * This clustering algorithm creates clusters from an input CalorimeterHit collection.
+ * <p>
+ * It uses the basic Inner Calorimeter (IC) clustering algorithm as described in
+ * <a href="https://misportal.jlab.org/ul/Physics/Hall-B/clas/viewFile.cfm/2005-001.pdf?documentId=6">CLAS Note 2004-040</a>.
+ * <p>
+ * Hits are assigned to a cluster with the largest seed hit energy. Time information is not used, and multiple hits in the same
+ * crystal are not handled correctly so an exception is throw if this occurs. An optional cut can be applied to discard hits
+ * with a time that is too far from t0.
*
* @author Holly Szumila-Vance <[log in to unmask]>
* @author Sho Uemura <[log in to unmask]>
- *
+ * @author Jeremy McCormick <[log in to unmask]>
*/
-public class SimpleInnerCalClusterer extends AbstractClusterer {
-
- //Minimum energy that counts as hit
- double Emin = 0.001;
- boolean timeCut = false;
- double minTime = 0.0;
- double timeWindow = 20.0;
+public class SimpleClasInnerCalClusterer extends AbstractClusterer {
+
+ double minEnergy;
+ double minTime;
+ double timeWindow;
+ boolean timeCut;
/**
- * Minimum energy for a hit to be used in a cluster. Default of 0.001 GeV..
- * @param Emin
+ * Initialize the algorithm with default cuts.
*/
- public void setEmin(double Emin) {
- this.Emin = Emin;
+ public SimpleClasInnerCalClusterer() {
+ super(new String[] { "minEnergy", "minTime", "timeWindow", "timeCut" }, new double[] { 0., 0.001, 0.0, 20.0 });
}
- /**
- * Apply time cuts to hits. Defaults to false.
- * @param timeCut
- */
- public void setTimeCut(boolean timeCut) {
- this.timeCut = timeCut;
+ public void initialize() {
+ // Setup class variables from cuts.
+ timeCut = (this.getCut("timeCut") == 1.0);
+ minEnergy = this.getCut("minEnergy");
+ minTime = this.getCut("minTime");
+ timeWindow = this.getCut("timeWindow");
}
- /**
- * Minimum hit time, if timeCut is true. Default of 0 ns.
- * @param minTime
- */
- public void setMinTime(double minTime) {
- this.minTime = minTime;
- }
-
- /**
- * Width of time window, if timeCut is true. Default of 20 ns.
- * @param timeWindow
- */
- public void setTimeWindow(double timeWindow) {
- this.timeWindow = timeWindow;
- }
-
- public List<Cluster> createClusters(List<CalorimeterHit> allHits) {
+ public List<Cluster> createClusters(EventHeader event, List<CalorimeterHit> hitCollection) {
// New Cluster list to be added to event.
List<Cluster> clusters = new ArrayList<Cluster>();
- //Create a Calorimeter hit list in each event, then sort with highest energy first
- ArrayList<CalorimeterHit> sortedHitList = new ArrayList<CalorimeterHit>(allHits.size());
- for (CalorimeterHit h : allHits) {
- //reject hits below the energy cut
- if (h.getCorrectedEnergy() < Emin) {
+ // Create a Calorimeter hit list in each event, then sort with highest energy first
+ ArrayList<CalorimeterHit> sortedHitList = new ArrayList<CalorimeterHit>(hitCollection.size());
+ for (CalorimeterHit h : hitCollection) {
+ // reject hits below the energy cut
+ if (h.getCorrectedEnergy() < this.minEnergy) {
continue;
}
- //if time cut is being used, reject hits outside the time window
+ // if time cut is being used, reject hits outside the time window
if (timeCut && (h.getTime() < minTime || h.getTime() > minTime + timeWindow)) {
continue;
}
sortedHitList.add(h);
}
-
- //sort the list, highest energy first
- Collections.sort(sortedHitList, Collections.reverseOrder(new EnergyComparator()));
- //map from seed hit to cluster
+ // sort the list, highest energy first
+ Collections.sort(sortedHitList, Collections.reverseOrder(new CalorimeterHit.CalorimeterHitEnergyComparator()));
+
+ // map from seed hit to cluster
Map<CalorimeterHit, HPSEcalCluster> seedToCluster = new HashMap<CalorimeterHit, HPSEcalCluster>();
- //Quick Map to access hits from cell IDs
+ // Quick Map to access hits from cell IDs
Map<Long, CalorimeterHit> idToHit = new HashMap<Long, CalorimeterHit>();
- //map from each hit to its cluster seed
+ // map from each hit to its cluster seed
Map<CalorimeterHit, CalorimeterHit> hitToSeed = new HashMap<CalorimeterHit, CalorimeterHit>();
- //Fill Map with cell ID and hit
+ // Fill Map with cell ID and hit
for (CalorimeterHit hit : sortedHitList) {
- //if (idToHit.containsKey(hit.getCellID())) {
- // System.out.println(this.getName() + ": multiple CalorimeterHits in same crystal");
- //}
+ if (idToHit.containsKey(hit.getCellID())) {
+ //System.out.println(this.getName() + ": multiple CalorimeterHits in same crystal");
+ // Make this an error for now.
+ throw new RuntimeException("Multiple CalorimeterHits found in same crystal.");
+ }
idToHit.put(hit.getCellID(), hit);
}
@@ -121,7 +105,7 @@
}
}
}
- if (biggestSeed == null) { //if no neighbors had more energy than this hit, this hit is a seed
+ if (biggestSeed == null) { // if no neighbors had more energy than this hit, this hit is a seed
hitToSeed.put(hit, hit);
HPSEcalCluster cluster = new HPSEcalCluster(hit.getCellID());
clusters.add(cluster);
@@ -131,7 +115,7 @@
}
}
- //add all hits to clusters
+ // add all hits to clusters
for (CalorimeterHit hit : sortedHitList) {
CalorimeterHit seed = hitToSeed.get(hit);
HPSEcalCluster cluster = seedToCluster.get(seed);
@@ -140,12 +124,4 @@
return clusters;
}
-
- private class EnergyComparator implements Comparator<CalorimeterHit> {
-
- @Override
- public int compare(CalorimeterHit o1, CalorimeterHit o2) {
- return Double.compare(o1.getCorrectedEnergy(), o2.getCorrectedEnergy());
- }
- }
}
|