Print

Print


Author: [log in to unmask]
Date: Fri May 22 12:22:19 2015
New Revision: 3009

Log:
modify cluster driver to duplicate output collections

Added:
    java/trunk/users/src/main/java/org/hps/users/holly/ClusterDriver.java

Added: java/trunk/users/src/main/java/org/hps/users/holly/ClusterDriver.java
 =============================================================================
--- java/trunk/users/src/main/java/org/hps/users/holly/ClusterDriver.java	(added)
+++ java/trunk/users/src/main/java/org/hps/users/holly/ClusterDriver.java	Fri May 22 12:22:19 2015
@@ -0,0 +1,349 @@
+package org.hps.users.holly;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.hps.recon.ecal.cluster.ClusterUtilities;
+import org.hps.recon.ecal.cluster.Clusterer;
+import org.hps.recon.ecal.cluster.ClustererFactory;
+import org.hps.recon.ecal.cluster.NumericalCuts;
+import org.lcsim.event.CalorimeterHit;
+import org.lcsim.event.Cluster;
+import org.lcsim.event.EventHeader;
+import org.lcsim.geometry.Detector;
+import org.lcsim.geometry.Subdetector;
+import org.lcsim.geometry.subdetector.HPSEcal3;
+import org.lcsim.geometry.subdetector.HPSEcal3.NeighborMap;
+import org.lcsim.lcio.LCIOConstants;
+import org.lcsim.util.Driver;
+
+/**
+ * <p>
+ * This is a {@link org.lcsim.util.Driver} that creates ECAL {@link org.lcsim.event.Cluster} 
+ * collections using the {@link Clusterer} interface.
+ * <p>
+ * A specific clustering engine can be created with the {@link #setClustererName(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.  
+ * 
+ * @see Clusterer
+ * @see NumericalCuts
+ * @see org.lcsim.event.Cluster
+ * @see org.lcsim.util.Driver 
+ * 
+ * @author Jeremy McCormick <[log in to unmask]>
+ * @author Holly Szumila <[log in to unmask]>
+ */
+public class ClusterDriver extends Driver {
+        
+    protected String ecalName = "Ecal";
+    protected HPSEcal3 ecal;
+    protected NeighborMap neighborMap;
+    protected String outputClusterCollectionName = "EcalClusters";
+    protected String inputHitCollectionName = "EcalCalHits";
+    protected Clusterer clusterer;
+    protected double[] cuts;
+    protected String correctionClusterCollectionName = "CorrEcalClusters";
+    
+    protected boolean createEmptyClusterCollection = true;
+    protected boolean raiseErrorNoHitCollection = false;
+    protected boolean skipNoClusterEvents = false;
+    protected boolean writeClusterCollection = true;
+    protected boolean storeHits = true;
+    protected boolean sortHits = false;
+    protected boolean validateClusters = false;
+    protected boolean copyClusterCollection = false;
+    
+    /**
+     * No argument constructor.
+     */
+    public ClusterDriver() {        
+    }
+    
+    /**
+     * Set the name of the ECAL in the detector framework.
+     * This is kind of dangerous, so set this argument at your own peril!
+     * @param ecalName The name of the ECAL.
+     */
+    public void setEcalName(String ecalName) {
+        this.ecalName = ecalName;
+    }
+    
+    /**
+     * Set the name of the output Cluster collection.
+     * @param outputClusterCollectionName The name of the output Cluster collection.
+     */
+    public void setOutputClusterCollectionName(String outputClusterCollectionName) {        
+        this.outputClusterCollectionName = outputClusterCollectionName;
+        getLogger().config("outputClusterCollectionName = " + this.outputClusterCollectionName);
+    }
+    
+    /**
+     * Set the name of the copied output Cluster collection to have energy and position
+     * information later overwritten when cluster is matched to track.
+     * @param correctionClusterCollectionName
+     */
+    public void setCorrectionClusterCollectionName(String correctionClusterCollectionName){
+    	this.correctionClusterCollectionName = correctionClusterCollectionName;
+    	getLogger().config("correctionClusterCollectionName = " + this.correctionClusterCollectionName);
+    }
+    
+    /**
+     * Set the name of the input CalorimeterHit collection to use for clustering.
+     * @param inputHitcollectionName The name of the input hit collection.
+     */
+    public void setInputHitCollectionName(String inputHitCollectionName) {
+        this.inputHitCollectionName = inputHitCollectionName;
+        getLogger().config("inputClusterCollectionName = " + this.inputHitCollectionName);
+    }
+    
+    /**
+     * True to raise a <code>NextEventException</code> if no Clusters are created by the Clusterer.
+     * @param skipNoClusterEvents True to skip events with no clusters.
+     */
+    public void setSkipNoClusterEvents(boolean skipNoClusterEvents) {
+        this.skipNoClusterEvents = skipNoClusterEvents;       
+        getLogger().config("skipNoClusterEvents = " + this.skipNoClusterEvents);
+    }
+    
+    /**
+     * True to write the Cluster collection to the output LCIO file.
+     * @param writeClusterCollection True to write the Cluster to the event; false to mark as transient.
+     */
+    public void setWriteClusterCollection(boolean writeClusterCollection) {
+        this.writeClusterCollection = writeClusterCollection;
+        getLogger().config("writeClusterCollection = " + this.writeClusterCollection);
+    }
+    
+    /**
+     * True to raise an exception if the input hit collection is not found in the event.
+     * @param raiseErrorNoHitCollection True to raise an exception if hit collection is not in event.
+     */
+    public void setRaiseErrorNoHitCollection(boolean raiseErrorNoHitCollection) {
+        this.raiseErrorNoHitCollection = raiseErrorNoHitCollection;
+    }
+    
+    /**
+     * True to store hit references into the output clusters.
+     * This will set <code>LCIOConstants.CLBIT_HITS</code> on the collection flags.
+     * @param storeHits True to store hits.
+     */
+    public void setStoreHits(boolean storeHits) {
+        this.storeHits = storeHits;
+    }
+    
+    /**
+     * True to sort the clusters' hits before writing to event.
+     * @param sortHits True to sort hits.
+     */
+    public void setSortHits(boolean sortHits) {
+        this.sortHits = sortHits;
+    }
+    
+    /**
+     * True to copy cluster collection for a second time to the event.
+     * This is meant to allow for overwriting of cluster energy and positions
+     * with corrections when the cluster is matched to a track and pid. 
+     * @param copyClusterCollection
+     */
+    public void setCopyClusterCollection(boolean copyClusterCollection) {
+    	this.copyClusterCollection = copyClusterCollection;
+    }
+           
+    /**
+     * Set the Clusterer by name.  
+     * This will use a factory method which first tries to use some hard-coded names from 
+     * the cluster package.  As a last resort, it will interpret the name as a canonical 
+     * class name and try to instantiate it using the Class API.
+     * @param The name or canonical class name of the Clusterer.
+     */
+    public void setClustererName(String name) {
+        clusterer = ClustererFactory.create(name);
+        getLogger().config("Clusterer was set to " + this.clusterer.getClass().getSimpleName());
+    }
+    
+    /**
+     * Set the Clusterer which implements the clustering algorithm.
+     * @param clusterer The Clusterer.
+     */
+    public void setClusterer(Clusterer clusterer) {
+        this.clusterer = clusterer;
+        getLogger().config("Clusterer was set to " + this.clusterer.getClass().getSimpleName());
+    }
+    
+    /**
+     * Set whether an empty collection should be created if there are no clusters made by the Clusterer.
+     * @param createEmptyClusterCollection True to write an empty collection to the event.
+     */
+    public void setCreateEmptyClusterCollection(boolean createEmptyClusterCollection) {
+        this.createEmptyClusterCollection = createEmptyClusterCollection;
+    }
+    
+    /**
+     * Set the numerical cuts of the Clusterer.
+     * @param cuts The numerical cuts.
+     */
+    public void setCuts(double[] cuts) {
+        this.cuts = cuts;
+    }
+    
+    /**
+     * Set whether to validate the output.
+     * @param validateClusters True to validate output.
+     */
+    public void setValidateClusters(boolean validateClusters) {
+        this.validateClusters = validateClusters;
+    }
+    
+    /**
+     * Setup conditions specific configuration.
+     */
+    public void detectorChanged(Detector detector) {
+        Subdetector subdetector = detector.getSubdetector("Ecal");
+        if (subdetector == null) {
+            throw new RuntimeException("There is no subdetector called " + ecalName + " in the detector.");
+        }
+        if (!(subdetector instanceof HPSEcal3)) {
+            throw new RuntimeException("Ther subdetector " + ecalName + " does not have the right type.");
+        }
+        ecal = (HPSEcal3) subdetector;
+        neighborMap = ecal.getNeighborMap();
+    }
+    
+    /**
+     * Perform start of job initialization.
+     */
+    public void startOfData() {
+        if (this.clusterer == null) {
+            throw new RuntimeException("The clusterer was never initialized.");
+        }
+        if (this.cuts != null) {
+            this.clusterer.getCuts().setValues(cuts);
+        } 
+        StringBuffer sb = new StringBuffer();
+        sb.append("Clusterer has the following cuts ...");
+        sb.append('\n');
+        for (int cutIndex = 0; cutIndex < clusterer.getCuts().getValues().length; cutIndex++) {
+            sb.append(this.clusterer.getCuts().getNames()[cutIndex] + " = " + this.clusterer.getCuts().getValue(cutIndex));
+            sb.append('\n');
+        } 
+        getLogger().config(sb.toString());
+        this.clusterer.initialize();
+    }
+    
+    /**
+     * This method implements the default clustering procedure based on input parameters.
+     */
+    public void process(EventHeader event) {    
+                       
+        if (event.hasCollection(CalorimeterHit.class, inputHitCollectionName)) {       
+            List<CalorimeterHit> hits = event.get(CalorimeterHit.class, inputHitCollectionName);
+            getLogger().fine("input hit collection " + inputHitCollectionName + " has " + hits.size() + " hits");
+            
+            // Cluster the hits, copying the list from the event in case the clustering algorithm modifies it.
+            List<Cluster> clusters = clusterer.createClusters(event, new ArrayList<CalorimeterHit>(hits));
+            
+            if (clusters == null) {
+                throw new RuntimeException("The clusterer returned a null list from its createClusters method.");
+            }
+            if (clusters.isEmpty() && this.skipNoClusterEvents) {
+                getLogger().finer("skipping event with no clusters");
+                throw new NextEventException();
+            }
+            if (event.hasCollection(Cluster.class, 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) {
+                if (sortHits) {
+                    // Sort the hits.
+                    ClusterUtilities.sortReconClusterHits(clusters);
+                }                                                           
+                getLogger().finer("writing " + clusters.size() + " clusters to collection " + outputClusterCollectionName);
+                event.put(outputClusterCollectionName, clusters, Cluster.class, flags);
+                        
+                if (copyClusterCollection 
+                		&& event.hasCollection(Cluster.class, outputClusterCollectionName)){
+                	List<Cluster> clusterCopy = event.get(Cluster.class, outputClusterCollectionName);	
+                	event.put(correctionClusterCollectionName,clusterCopy,Cluster.class,flags);
+                }
+                               
+                if (!this.writeClusterCollection) {
+                    getLogger().finer("Collection is set to transient and will not be persisted.");
+                    event.getMetaData(clusters).setTransient(true);
+                }
+                
+                if (validateClusters) {
+                    // Perform basic validation checks.
+                    this.validateClusters(event);
+                }
+            }
+        } else {
+            getLogger().info("The input hit collection " + this.inputHitCollectionName + " is missing from the event.");
+            if (this.raiseErrorNoHitCollection) {
+                throw new RuntimeException("The expected input hit collection is missing from the event.");
+            }
+        }
+    }
+  
+    /**
+     * Get a {@link Clusterer} using type inference for the concrete type.
+     * @return The Clusterer object.
+     */
+    @SuppressWarnings("unchecked")
+    <ClustererType extends Clusterer> ClustererType getClusterer() {
+        // Return the Clusterer and cast it to the type provided by the caller.
+        return (ClustererType) clusterer;
+    }
+
+    /**
+     * Perform basic validation of the cluster output collection, including checking
+     * that the cluster collection was created, clusters are not null, 
+     * none of the clustered hits are null, and each hit exists in the input 
+     * hit collection.
+     * @param event The LCSim event.
+     */
+    void validateClusters(EventHeader event) {
+        if (!event.hasCollection(Cluster.class, outputClusterCollectionName)) {
+            throw new RuntimeException("Cluster collection " + outputClusterCollectionName + " is missing.");
+        }
+        List<Cluster> clusters = event.get(Cluster.class, outputClusterCollectionName);
+        List<CalorimeterHit> inputHitCollection = event.get(CalorimeterHit.class, inputHitCollectionName);
+        for (int clusterIndex = 0; clusterIndex < clusters.size(); clusterIndex++) {
+            getLogger().finest("checking cluster " + clusterIndex);
+            Cluster cluster = clusters.get(clusterIndex);
+            if (clusters.get(clusterIndex) == null) {
+                throw new RuntimeException("The Cluster at index " + clusterIndex + " is null.");
+            }
+            List<CalorimeterHit> clusterHits = cluster.getCalorimeterHits();
+            getLogger().finest("cluster has " + clusterHits.size() + " hits");
+            for (int hitIndex = 0; hitIndex < clusterHits.size(); hitIndex++) {
+                getLogger().finest("checking cluster hit " + hitIndex);                              
+                CalorimeterHit clusterHit = clusterHits.get(hitIndex);
+                if (clusterHit == null) {
+                    throw new RuntimeException("The CalorimeterHit at index " + hitIndex + " in the cluster is null.");
+                }
+                if (!inputHitCollection.contains(clusterHit)) {
+                    getLogger().severe("The CalorimeterHit at index " + hitIndex + " with ID " + clusterHit.getIdentifier().toHexString() + " is missing from the input hit collection.");
+                    printHitIDs(inputHitCollection);
+                    throw new RuntimeException("The CalorimeterHit at index " + hitIndex + " in the cluster is missing from the input hit collection.");
+                }
+            }
+        }
+    }
+    
+    void printHitIDs(List<CalorimeterHit> hits) {        
+        StringBuffer buffer = new StringBuffer();
+        buffer.append("hit IDs");
+        buffer.append('\n');
+        for (CalorimeterHit hit : hits) {            
+            buffer.append(hit.getIdentifier().toHexString());
+            buffer.append('\n');
+        }
+        getLogger().finest(buffer.toString());
+    }
+}