lcsim/src/org/lcsim/recon/tracking/digitization/sisim/config
diff -N CaloDigiSetupDriver.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ CaloDigiSetupDriver.java 21 Aug 2012 12:04:12 -0000 1.1
@@ -0,0 +1,328 @@
+package org.lcsim.recon.tracking.digitization.sisim.config;
+
+import hep.physics.matrix.SymmetricMatrix;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.lcsim.conditions.ConditionsManager;
+import org.lcsim.conditions.ConditionsSet;
+import org.lcsim.detector.IDetectorElement;
+import org.lcsim.event.CalorimeterHit;
+import org.lcsim.event.Cluster;
+import org.lcsim.event.EventHeader;
+import org.lcsim.event.MCParticle;
+import org.lcsim.event.RawTrackerHit;
+import org.lcsim.event.SimCalorimeterHit;
+import org.lcsim.event.SimTrackerHit;
+import org.lcsim.event.TrackerHit;
+import org.lcsim.event.EventHeader.LCMetaData;
+import org.lcsim.event.base.BaseRawTrackerHit;
+import org.lcsim.event.base.BaseSimTrackerHit;
+import org.lcsim.event.base.BaseTrackerHit;
+import org.lcsim.geometry.Calorimeter;
+import org.lcsim.geometry.Detector;
+import org.lcsim.geometry.IDDecoder;
+import org.lcsim.geometry.compact.Segmentation;
+import org.lcsim.geometry.compact.converter.pandora.CalorimeterConditions;
+import org.lcsim.geometry.segmentation.CartesianGridXY;
+import org.lcsim.geometry.segmentation.GlobalGridXY;
+import org.lcsim.recon.cluster.nn.NearestNeighborClusterer;
+import org.lcsim.spacegeom.CartesianPoint;
+import org.lcsim.spacegeom.SpacePoint;
+import org.lcsim.util.Driver;
+
+public class CaloDigiSetupDriver extends Driver {
+
+ protected String inputCollection;
+ protected String simCollection;
+ protected String rawCollection;
+ protected String digiCollection;
+ protected double mipCut = 0.0;
+ protected double timeCut = 0.0;
+ protected List<Integer> layers;
+ protected int clusteringRange = 1;
+
+ protected double oneCLusterError = 1./Math.sqrt(12);
+ protected double twoCLusterError = 1./5.;
+ protected double threeCLusterError = 1./3.;
+ protected double fourCLusterError = 1./2.;
+ protected double fiveCLusterError = 1.;
+
+ /**
+ * Driver to digitize SimCalorimeterHits as TrackerHits. Intended to be used for the first ECal layers which
+ * have no absorber plates in front of them and thus are in fact tracking layers.
+ *
+ * First applies threshold cuts from the calorimeter calibration and then performs a nearest neighbor clustering
+ * on the SimCalorimeterHits. The clusters are converted into RawTrackerHits, maintaining a list of SimTrackerHits
+ * that correspond to all energy deposits in the calorimeter cluster. There is no time or ADC values set for the
+ * RawTrackerHits as there is no specific readout foreseen here. At the moment these raw hits only maintain the
+ * link to the MCParticles. Finally, TrackerHits are created from the RawTrackerHits. The energy is taken directly
+ * from the cluster energy. Hit covariances are calculated from the cluster size.
+ *
+ * TODO: Allow to use digitized calorimeter hits as input.
+ *
+ * @author <a href="mailto:[log in to unmask]">Christian Grefe</a>
+ */
+ public CaloDigiSetupDriver() {
+ // Intended to only use hits from the first layer
+ layers = new ArrayList<Integer>();
+ layers.add(0);
+ }
+
+ /**
+ * Sets the name of the collection of SimCalorimeterHits that are used as the input.
+ * The output names are set automatically, e.g. setting the input collection to EcalBarrelHits
+ * would produce collections: EcalBarrelTrackerHits, Raw_EcalBarrelTrackerHits and
+ * Digi_EcalBarrelHits.
+ * @param inputCollection
+ */
+ public void setInputCollection(String inputCollection) {
+ this.inputCollection = inputCollection;
+ // FIXME: maybe not hard code these names?!
+ this.simCollection = inputCollection.replace("Hits", "TrackerHits");
+ this.rawCollection = "Raw_" + inputCollection.replace("Hits", "TrackerHits");
+ this.digiCollection = "Digi_" + inputCollection.replace("Hits", "TrackerHits");
+ }
+
+ public void setSimHitsCollectionName(String collectionName) {
+ this.simCollection = collectionName;
+ }
+
+ public void setRawHitsCollectionName(String collectionName) {
+ this.rawCollection = collectionName;
+ }
+
+ public void setTrackerHitsCollectionName(String collectionName) {
+ this.digiCollection = collectionName;
+ }
+
+ public void setOneClusterErr(double error) {
+ this.oneCLusterError = error;
+ }
+
+ public void setTwoClusterErr(double error) {
+ this.oneCLusterError = error;
+ }
+
+ public void setThreeClusterErr(double error) {
+ this.oneCLusterError = error;
+ }
+
+ public void setFourClusterErr(double error) {
+ this.oneCLusterError = error;
+ }
+
+ public void setFiveClusterErr(double error) {
+ this.oneCLusterError = error;
+ }
+
+ /**
+ * Sets the calorimeter layers from which hits are converted into TrackerHits.
+ * This is only intended for the first ECal layer, i.e. layer 0.
+ * @param layers
+ */
+ public void setLayers(int[] layers) {
+ this.layers = new ArrayList<Integer>();
+ for (int layer : layers) {
+ this.layers.add(layer);
+ }
+ }
+
+ /**
+ * Sets the search range for the nearest neighbor clustering in units of cells
+ * @param maxClusterExtend
+ */
+ public void setClusteringRange(int clusteringRange) {
+ this.clusteringRange = clusteringRange;
+ }
+
+ @Override
+ protected void detectorChanged(Detector detector) {
+ Calorimeter calorimeter = (Calorimeter) detector.getReadout(inputCollection).getIDDecoder().getSubdetector();
+ ConditionsSet caloCalibration = ConditionsManager.defaultInstance().getConditions("CalorimeterCalibration");
+ CalorimeterConditions caloConditions = new CalorimeterConditions(calorimeter, caloCalibration);
+ mipCut = caloConditions.getMipCut();
+ timeCut = caloConditions.getTimeCut();
+ }
+
+ @Override
+ protected void startOfData() {
+ if (inputCollection == null) {
+ throw new RuntimeException(getName() + ": missing inputCollectionName!");
+ }
+ if (simCollection == null) {
+ throw new RuntimeException(getName() + ": missing simCollectionName!");
+ }
+ if (rawCollection == null) {
+ throw new RuntimeException(getName() + ": missing rawCollectionName!");
+ }
+ if (digiCollection == null) {
+ throw new RuntimeException(getName() + ": missing digiCollectionName!");
+ }
+ }
+
+ @Override
+ protected void process(EventHeader event) {
+
+ // Find all calorimeter hits in the relevant layers
+ // Also apply mip threshold and timing cut
+ List<SimCalorimeterHit> inputCaloHits = event.get(SimCalorimeterHit.class, inputCollection);
+ List<CalorimeterHit> caloHits = new ArrayList<CalorimeterHit>();
+ for (SimCalorimeterHit caloHit : inputCaloHits) {
+ if (layers.contains(caloHit.getLayerNumber()) && caloHit.getRawEnergy() > mipCut && (timeCut == 0 || caloHit.getTime() < timeCut)) {
+ caloHits.add(caloHit);
+ }
+ }
+
+ // Get the ID decoder and cell sizes. Needed to determine hit resolution
+ LCMetaData meta = event.getMetaData(inputCaloHits);
+ String readoutName = inputCollection;
+ IDDecoder idDecoder = meta.getIDDecoder();
+ Segmentation segmentation = (Segmentation) idDecoder;
+ double cellSizeU = segmentation.getCellSizeU();
+ double cellSizeV = segmentation.getCellSizeV();
+
+ // Clustering in a single calorimeter layer
+ NearestNeighborClusterer clusterer = new NearestNeighborClusterer( clusteringRange, clusteringRange, 0, 0, 0.);
+ List<Cluster> clusters = clusterer.createClusters(caloHits);
+
+ // Lists to hold the hits for the output collections
+ List<SimTrackerHit> simHits = new ArrayList<SimTrackerHit>();
+ List<RawTrackerHit> rawHits = new ArrayList<RawTrackerHit>();
+ List<TrackerHit> digiHits = new ArrayList<TrackerHit>();
+
+ for (Cluster cluster : clusters) {
+ IDetectorElement detectorElement = null;
+ double energy = 0.;
+ double energyTimeSum = 0.;
+ double xTimeSum = 0.;
+ double yTimeSum = 0.;
+ double zTimeSum = 0.;
+
+ int minU = Integer.MAX_VALUE;
+ int maxU = -Integer.MAX_VALUE;
+ int minV = Integer.MAX_VALUE;
+ int maxV = -Integer.MAX_VALUE;
+
+ List<RawTrackerHit> clusterRawHits = new ArrayList<RawTrackerHit>();
+ for (CalorimeterHit caloHit : cluster.getCalorimeterHits()) {
+ // Determine the detector element. Note that this is the layer and we expect only hits from a single layer in the cluster
+ if (detectorElement == null) detectorElement = caloHit.getDetectorElement();
+ long cellID = caloHit.getCellID();
+
+ // FIXME: this should be some encoding based on the readout chip, for now we don't use the raw hits
+ int iTime = 0;
+ short[] adcValues = new short[] {0};
+
+ List<SimTrackerHit> simTrackerHits = convertCalorimeterHit((SimCalorimeterHit)caloHit, meta);
+
+ double hitTime = caloHit.getTime();
+ double hitEnergy = caloHit.getRawEnergy();
+
+ // Calculations for energy weighted time and position
+ energy += hitEnergy;
+ energyTimeSum += hitTime*hitEnergy;
+ SpacePoint p = new SpacePoint(caloHit.getPositionVec());
+ xTimeSum += p.x()*hitEnergy;
+ yTimeSum += p.y()*hitEnergy;
+ zTimeSum += p.z()*hitEnergy;
+
+ // Determine the cluster extent in local coordinate
+ int u = 0;
+ int v = 0;
+ if (segmentation instanceof CartesianGridXY) {
+ CartesianGridXY grid = (CartesianGridXY) segmentation;
+ u = grid.getXBin(p.x());
+ v = grid.getYBin(p.y());
+ } else if (segmentation instanceof GlobalGridXY) {
+ GlobalGridXY grid = (GlobalGridXY) segmentation;
+ u = grid.getXBin(p.x());
+ v = grid.getYBin(p.y());
+ } else {
+ throw new RuntimeException("Unknown IDDecoder - expected CartesianGridXY or GlobalGridXY");
+ }
+ if (u < minU) minU = u;
+ if (u > maxU) maxU = u;
+ if (v < minV) minV = v;
+ if (v > maxV) maxV = v;
+
+ // Create the raw hit and fill keep the sim hits
+ clusterRawHits.add(new BaseRawTrackerHit(iTime, cellID, adcValues, simTrackerHits, detectorElement));
+ simHits.addAll(simTrackerHits);
+ }
+ // Calculate energy weighted hit time and position
+ double time = energyTimeSum / energy;
+ SpacePoint p = new CartesianPoint(xTimeSum / energy, yTimeSum / energy, zTimeSum / energy );
+
+ // Calculate cluster dimensions in units of cells
+ int dU = maxU - minU + 1;
+ int dV = maxV - minV + 1;
+
+ // Set the covariance matrix in local coordinates using the cluster size
+ SymmetricMatrix localCovariance = new SymmetricMatrix(3);
+ localCovariance.setElement(0, 0, Math.pow(cellSizeU*getPositionErrorFactor(dU), 2));
+ localCovariance.setElement(1, 1, Math.pow(cellSizeV*getPositionErrorFactor(dV), 2));
+ localCovariance.setElement(2, 2, 0.0); // layer thickness is negligible
+
+ // Transform into global coordinates for track fit
+ SymmetricMatrix globalCovariance = detectorElement.getGeometry().getLocalToGlobal().transformed(localCovariance);
+
+ // Create the final tracker hit and fill all relevant lists
+ BaseTrackerHit digiHit = new BaseTrackerHit(p.v(), globalCovariance.asPackedArray(true), time, energy, 0);
+ digiHit.addRawTrackerHits(clusterRawHits);
+ rawHits.addAll(clusterRawHits);
+ digiHits.add(digiHit);
+ }
+
+ // Save the collections in the event
+ event.put(simCollection, simHits, SimTrackerHit.class, 0, readoutName);
+ event.put(rawCollection, rawHits, RawTrackerHit.class, 0, readoutName);
+ event.put(digiCollection, digiHits, TrackerHit.class, 0, readoutName);
+ }
+
+
+ // Convenience method to set the appropriate cluster error
+ protected double getPositionErrorFactor(int size) {
+ double errorFactor = 0.0;
+ if (size == 1) {
+ errorFactor = oneCLusterError;
+ } else if (size == 2) {
+ errorFactor = twoCLusterError;
+ } else if (size == 3) {
+ errorFactor = threeCLusterError;
+ } else if (size == 4) {
+ errorFactor = fourCLusterError;
+ } else {
+ errorFactor = fiveCLusterError;
+ }
+ return errorFactor;
+ }
+
+
+ // Generic method to convert all contributions of a SimCalorimeterHit into SimTrackerHits
+ static public List<SimTrackerHit> convertCalorimeterHit(SimCalorimeterHit caloHit, LCMetaData meta) {
+ List<SimTrackerHit> trackerHits = new ArrayList<SimTrackerHit>();
+ SpacePoint position = new SpacePoint(caloHit.getPositionVec());
+ IDetectorElement de = caloHit.getDetectorElement();
+ int cellID = (int) caloHit.getCellID();
+ for (int iMCP = 0; iMCP < caloHit.getMCParticleCount(); iMCP++) {
+ MCParticle mcp = caloHit.getMCParticle(iMCP);
+ double energy = caloHit.getContributedEnergy(iMCP);
+ double time = caloHit.getContributedTime(iMCP);
+ SpacePoint momentum = new SpacePoint(mcp.getMomentum()); // this should be a SpacePointVector!
+ // FIXME: use the step position in new LCIO version
+ //float[] stepPosition = caloHit.getStepPosition(iMCP);
+ //SpacePoint mcpPosition = new CartesianPoint(stepPosition[0], stepPosition[1], stepPosition[2]);
+ double pathLength = 0;
+ BaseSimTrackerHit trackerHit = new BaseSimTrackerHit(position.v(), energy, momentum.v(), pathLength, time, cellID, mcp, meta, de);
+ // FIXME: manually set the long id, should be done by the constructor above
+ trackerHit.setCellID64(caloHit.getCellID());
+
+ trackerHits.add(trackerHit);
+ }
+ return trackerHits;
+ }
+
+}