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()); - } - } }