Author: [log in to unmask] Date: Sat May 23 13:16:58 2015 New Revision: 3016 Log: improving ecal pulse fitting Added: java/trunk/steering-files/src/main/resources/org/hps/steering/users/baltzell/EngineeringRun2014EcalRecon_FitPulses.lcsim java/trunk/users/src/main/java/org/hps/users/baltzell/EcalRawConverterDriver.java Modified: java/trunk/users/src/main/java/org/hps/users/baltzell/Ecal3PoleFunction.java java/trunk/users/src/main/java/org/hps/users/baltzell/EcalRawConverter.java Added: java/trunk/steering-files/src/main/resources/org/hps/steering/users/baltzell/EngineeringRun2014EcalRecon_FitPulses.lcsim ============================================================================= --- java/trunk/steering-files/src/main/resources/org/hps/steering/users/baltzell/EngineeringRun2014EcalRecon_FitPulses.lcsim (added) +++ java/trunk/steering-files/src/main/resources/org/hps/steering/users/baltzell/EngineeringRun2014EcalRecon_FitPulses.lcsim Sat May 23 13:16:58 2015 @@ -0,0 +1,77 @@ +<!-- + Offline reconstruction for 2014 engineering run (ECal only) data. + + Changes made by JM: + + -Replaced clustering Drivers with new recon.ecal.cluster classes. + -Commented out the legacy clusterer. + -Configured ReconClusterDriver to not write the rejected hit collection. + -Changed output cluster collection names. + + NAB: (Feb 11, 2015) Added EcalRunningPedestalDriver + + @author Matt Graham <[log in to unmask]> + @author Jeremy McCormick<[log in to unmask]> +--> +<lcsim xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" + xs:noNamespaceSchemaLocation="http://www.lcsim.org/schemas/lcsim/1.0/lcsim.xsd"> + <execute> + <!--<driver name="EventMarkerDriver" />--> + <driver name="EcalRunningPedestal"/> + <driver name="EcalRawConverter" /> + <driver name="ReconClusterer" /> + <driver name="GTPOnlineClusterer" /> + <driver name="LCIOWriter" /> + <!--<driver name="AidaSaveDriver" />--> + <driver name="CleanupDriver" /> + </execute> + <drivers> + <driver name="EventMarkerDriver" type="org.lcsim.job.EventMarkerDriver"> + <eventInterval>1</eventInterval> + </driver> + <driver name="EcalRunningPedestal" type="org.hps.recon.ecal.EcalRunningPedestalDriver"> + <minLookbackEvents>10</minLookbackEvents> + <maxLookbackEvents>50</maxLookbackEvents> + </driver> + <!--<driver name="EcalRawConverter" type="org.hps.recon.ecal.EcalRawConverterDriver">--> + <driver name="EcalRawConverter" type="org.hps.users.baltzell.EcalRawConverterDriver"> + <ecalCollectionName>EcalCalHits</ecalCollectionName> + <use2014Gain>false</use2014Gain> + <useTimestamps>false</useTimestamps> + <useTruthTime>false</useTruthTime> + <useRunningPedestal>true</useRunningPedestal> + <useTimeWalkCorrection>false</useTimeWalkCorrection> + <emulateFirmware>true</emulateFirmware> + <emulateMode7>true</emulateMode7> + <useFit>true</useFit> + <fixShapeParameter>true</fixShapeParameter> + <debug>0</debug> + <fitFileName>ecalPulseFits-SHAPEFIX.txt-Minus10</fitFileName> + </driver> + <driver name="ReconClusterer" type="org.hps.recon.ecal.cluster.ReconClusterDriver"> + <logLevel>WARNING</logLevel> + <outputClusterCollectionName>EcalClusters</outputClusterCollectionName> + <hitEnergyThreshold>0.01</hitEnergyThreshold> + <seedEnergyThreshold>0.100</seedEnergyThreshold> + <clusterEnergyThreshold>0.200</clusterEnergyThreshold> + <minTime>0.0</minTime> + <timeWindow>25.0</timeWindow> + <useTimeCut>true</useTimeCut> + <writeRejectedHitCollection>false</writeRejectedHitCollection> + </driver> + <driver name="GTPOnlineClusterer" type="org.hps.recon.ecal.cluster.ClusterDriver"> + <logLevel>WARNING</logLevel> + <clustererName>GTPOnlineClusterer</clustererName> + <outputClusterCollectionName>EcalClustersGTP</outputClusterCollectionName> + <!-- seedMinEnergy --> + <cuts>0.100</cuts> + </driver> + <driver name="LCIOWriter" type="org.lcsim.util.loop.LCIODriver"> + <outputFilePath>${outputFile}.slcio</outputFilePath> + </driver> + <driver name="AidaSaveDriver" type="org.lcsim.job.AidaSaveDriver"> + <outputFileName>ecalPulseFits.root</outputFileName> + </driver> + <driver name="CleanupDriver" type="org.lcsim.recon.tracking.digitization.sisim.config.ReadoutCleanupDriver" /> + </drivers> +</lcsim> Modified: java/trunk/users/src/main/java/org/hps/users/baltzell/Ecal3PoleFunction.java ============================================================================= --- java/trunk/users/src/main/java/org/hps/users/baltzell/Ecal3PoleFunction.java (original) +++ java/trunk/users/src/main/java/org/hps/users/baltzell/Ecal3PoleFunction.java Sat May 23 13:16:58 2015 @@ -7,8 +7,9 @@ protected double pedestal=0; protected double time0=0; - protected double amplitude=0; - protected double shape=0; + protected double integral=0; + protected double width=0; + public boolean debug=false; public Ecal3PoleFunction() { this(""); @@ -16,19 +17,20 @@ public Ecal3PoleFunction(String title) { super(); this.variableNames = new String[] { "time" }; - this.parameterNames = new String[] { "pedestal","time0","amplitude","shape" }; + this.parameterNames = new String[] { "pedestal","time0","integral","width" }; init(title); } + + void setDebug(boolean debug) { this.debug=debug; } @Override public double value(double[] v) { final double time = v[0]; if (time <= time0) return pedestal; return pedestal - + amplitude + + integral / Math.pow(width,3) / 2 * Math.pow(time-time0,2) - / (2*shape) - * Math.exp(-(time-time0)/shape); + * Math.exp(-(time-time0)/width); } @Override @@ -36,9 +38,9 @@ super.setParameters(pars); pedestal=pars[0]; time0=pars[1]; - amplitude=pars[2]; - shape=pars[3]; - System.err.println(String.format("%8.2f %8.2f %8.2f %8.2f",pars[0],pars[1],pars[2],pars[3])); + integral=pars[2]; + width=pars[3]; + if (debug) System.err.println(String.format("%8.2f %8.2f %8.2f %8.2f",pars[0],pars[1],pars[2],pars[3])); } @Override @@ -46,7 +48,8 @@ super.setParameter(key,value); if (key.equals("pedestal")) pedestal=value; else if (key.equals("time0")) time0=value; - else if (key.equals("amplitude")) amplitude=value; - else if (key.equals("shape")) shape=value; + else if (key.equals("integral")) integral=value; + else if (key.equals("width")) width=value; } + } Modified: java/trunk/users/src/main/java/org/hps/users/baltzell/EcalRawConverter.java ============================================================================= --- java/trunk/users/src/main/java/org/hps/users/baltzell/EcalRawConverter.java (original) +++ java/trunk/users/src/main/java/org/hps/users/baltzell/EcalRawConverter.java Sat May 23 13:16:58 2015 @@ -1,4 +1,6 @@ package org.hps.users.baltzell; + +import org.hps.users.baltzell.Ecal3PoleFunction; import hep.aida.IAnalysisFactory; import hep.aida.IDataPointSet; @@ -11,6 +13,8 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.io.FileWriter; +import java.io.IOException; import java.util.ArrayList; import java.util.Map; import java.util.logging.Level; @@ -73,7 +77,8 @@ IFunction fitFunction=new Ecal3PoleFunction(); IDataPointSet data=aida.analysisFactory().createDataPointSetFactory(null).create("ADC DataPointSet", 2); - IHistogram1D hShape = aida.histogram1D("shape",500,0,5); + IHistogram1D hWidth = aida.histogram1D("hShape",500,0,5); + IHistogram1D hQuality = aida.histogram1D("hChiSquare",500,0,5); private boolean useTimeWalkCorrection = false; private boolean useRunningPedestal = false; @@ -83,6 +88,12 @@ private boolean useDAQConfig = false; private FADCConfig config = null; + public int debug = 0; + public boolean useFit = false; + public String fitFileName = null; + public FileWriter fitFileWriter = null; + public boolean fixShapeParameter = false; + /* * The time for one FADC sample (units = ns). */ @@ -132,10 +143,8 @@ private EcalConditions ecalConditions = null; public EcalRawConverter() { - - Logger minuitLogger = Logger.getLogger("org.freehep.math.minuit"); - minuitLogger.setLevel(Level.OFF); - + if (debug<1) Logger.getLogger("org.freehep.math.minuit").setLevel(Level.OFF); + // Track changes in the DAQ configuration. ConfigurationManager.addActionListener(new ActionListener() { @Override @@ -316,8 +325,8 @@ public IFitResult fitPulse(short samples[],int thresholdCrossing,double maxADC,double noise) { if (thresholdCrossing < 9 || thresholdCrossing > 15) return null; - - System.err.println("FITTING....................................................."); + + if (debug>0) System.err.println("FITTING....................................................."); data.clear(); int nped=0; @@ -330,39 +339,70 @@ ped /= nped; if (nped==0) return null; int npts=0; - for (int ii=thresholdCrossing-5; ii<thresholdCrossing+20; ii++) { - if (ii<0 || ii>=samples.length) break; - System.err.print(ii+":"+samples[ii]+" "); + int sumADC=0; + for (int ii=thresholdCrossing-10; ii<thresholdCrossing+15; ii++) { + if (ii<0) continue; + if (ii>=samples.length) break; + if (debug>0) System.err.print(ii+":"+samples[ii]+" "); data.addPoint(); data.point(npts).coordinate(0).setValue(ii); data.point(npts).coordinate(1).setValue(samples[ii]); data.point(npts).coordinate(1).setErrorMinus(noise); data.point(npts).coordinate(1).setErrorPlus(noise); + sumADC += samples[ii]; npts++; } - System.err.print("\n"); + if (debug>0) System.err.print("\n"); if (npts>=10) { -// System.err.println("------- "+ped+" "+thresholdCrossing+" "+maxADC); + if (debug>0) System.err.println("------- "+ped+" "+thresholdCrossing+" "+maxADC); if (maxADC-ped < 0) return null; + final double pulseIntegral = sumADC-ped*npts; fitFunction.setParameter("pedestal",ped); fitFunction.setParameter("time0",(double)thresholdCrossing-2); - fitFunction.setParameter("amplitude",(maxADC-ped)*37.2/24); - fitFunction.setParameter("shape",2.5); - - System.err.println(String.format("A= %8.2f",(maxADC-ped)*37.2/24)); - System.err.println(String.format("T= %8.2f",4*((double)thresholdCrossing-2))); - System.err.println(String.format("P= %8.2f",ped)); - System.err.println(String.format("S= %8.2f",2.5)); - - fitter.fitParameterSettings("amplitude").setBounds(0,999999); + fitFunction.setParameter("integral",pulseIntegral>0?pulseIntegral:10); + fitFunction.setParameter("width",2.478); + ((Ecal3PoleFunction)fitFunction).setDebug(debug>1); + + if (debug>0) { + System.err.println(String.format("A= %8.2f",fitFunction.parameter("integral"))); + System.err.println(String.format("T= %8.2f",fitFunction.parameter("time0")*4)); + System.err.println(String.format("P= %8.2f",fitFunction.parameter("pedestal"))); + System.err.println(String.format("S= %8.2f",fitFunction.parameter("width"))); + } + + fitter.fitParameterSettings("integral").setBounds(0,999999); fitter.fitParameterSettings("time0").setBounds(5,40); - fitter.fitParameterSettings("shape").setBounds(0.1,5); - - Logger.getLogger("org.freehep.math.minuit").setLevel(Level.OFF); - return fitter.fit(data,fitFunction); + fitter.fitParameterSettings("width").setBounds(0.1,5); + if (fixShapeParameter) fitter.fitParameterSettings("width").setFixed(true); + + if (debug<1) Logger.getLogger("org.freehep.math.minuit").setLevel(Level.OFF); + IFitResult fitResult = fitter.fit(data,fitFunction); + writeFit(samples,fitResult); + return fitResult; } return null; } + + public void writeFit(short samples[],IFitResult fit) { + if (fitFileName == null) return; + if (fitFileWriter == null) { + try { fitFileWriter=new FileWriter(fitFileName); } + catch (IOException ee) { throw new RuntimeException("Error opening file "+fitFileName,ee); } + } + + try { + for (final short ss : samples) fitFileWriter.write(String.format("%6d ",ss)); + fitFileWriter.write(String.format("%8.3f",fit.fittedParameter("integral"))); + fitFileWriter.write(String.format("%8.3f",fit.fittedParameter("time0"))); + fitFileWriter.write(String.format("%8.3f",fit.fittedParameter("pedestal"))); + fitFileWriter.write(String.format("%8.3f",fit.fittedParameter("width"))); + fitFileWriter.write(String.format("%8.3f",fit.quality())); + fitFileWriter.write("\n"); + } catch (IOException ee) { + throw new RuntimeException("Error writing file "+fitFileName,ee); + } + } + /* @@ -457,36 +497,48 @@ } } - - double noise=findChannel(hit.getCellID()).getCalibration().getNoise(); - IFitResult fitResult=fitPulse(samples,thresholdCrossing,maxADC,noise); - if (fitResult != null) { - double A=fitResult.fittedParameter("amplitude"); - double T=fitResult.fittedParameter("time0"); - double P=fitResult.fittedParameter("pedestal"); - double S=fitResult.fittedParameter("shape"); - - // analytic integral from T to T+NSAMP - final int NSAMP=30; - double I = (A/2/S) * S*S*S* ( 2 - Math.exp(-NSAMP/S) * (Math.pow((S+NSAMP)/S,2) + 1)); - /* - System.err.println(";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"); - System.err.println(String.format("A= %8.2f %8.2f",I,sumADC-P*30)); - System.err.println(String.format("T= %8.2f %8.2f",T*4,pulseTime)); - System.err.println(String.format("P= %8.2f %8.2f",P,minADC)); - System.err.println(String.format("S= %8.2f",S)); - System.err.println(String.format("M= %8.2f %8.2f",A,(maxADC-P))); - */ + + if (useFit) + { + final double noise=findChannel(hit.getCellID()).getCalibration().getNoise(); + IFitResult fitResult=fitPulse(samples,thresholdCrossing,maxADC,noise); + + if (fitResult != null) { + + final double P=fitResult.fittedParameter("pedestal"); + final double A=fitResult.fittedParameter("integral"); + final double T=fitResult.fittedParameter("time0"); + final double S=fitResult.fittedParameter("width"); + final double Q=fitResult.quality(); + + // finite integral from T to T+NSAMP: + //final int NSAMP=30; + //final double I = (A/2/S) * S*S*S* ( 2 - Math.exp(-NSAMP/S) * (Math.pow((S+NSAMP)/S,2) + 1)); + + // infinite integral: + //final double I = A*S*S; + + // after normalizing the function: + final double I = A; + + if (debug>0) { + System.err.println(";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"); + System.err.println(String.format("A= %8.2f %8.2f",I,sumADC-P*30)); + System.err.println(String.format("T= %8.2f %8.2f",T*4,pulseTime)); + System.err.println(String.format("P= %8.2f %8.2f",P,minADC)); + System.err.println(String.format("S= %8.2f",S)); + System.err.println(String.format("M= %8.2f %8.2f",A,(maxADC-P))); + } + + hWidth.fill(S); + hQuality.fill(Q); pulseTime = T*4; sumADC = I; - maxADC = -1; - - hShape.fill(S); + } } return new double []{pulseTime,sumADC,minADC,maxADC}; } - - + /* * This HitDtoA is for emulating the conversion of Mode-1 readout (RawTrackerHit) * into what EcalRawConverter would have created from a Mode-3 or Mode-7 readout. @@ -556,7 +608,7 @@ final double min = data[2]; // TODO: stick min and max in a GenericObject with an final double max = data[3]; // LCRelation to finish mode-7 emulation - if (max > 0) { + if (!useFit) { // do pedestal subtraction: sum -= getPulsePedestal(event, cellID, samples.length, thresholdCrossing); } Added: java/trunk/users/src/main/java/org/hps/users/baltzell/EcalRawConverterDriver.java ============================================================================= --- java/trunk/users/src/main/java/org/hps/users/baltzell/EcalRawConverterDriver.java (added) +++ java/trunk/users/src/main/java/org/hps/users/baltzell/EcalRawConverterDriver.java Sat May 23 13:16:58 2015 @@ -0,0 +1,534 @@ +package org.hps.users.baltzell; + +import java.util.ArrayList; +import java.util.List; + +import org.hps.conditions.database.DatabaseConditionsManager; +import org.hps.conditions.ecal.EcalChannelConstants; +import org.hps.conditions.ecal.EcalConditions; +import org.hps.recon.ecal.daqconfig.ConfigurationManager; +import org.lcsim.event.CalorimeterHit; +import org.lcsim.event.EventHeader; +import org.lcsim.event.GenericObject; +import org.lcsim.event.LCRelation; +import org.lcsim.event.RawCalorimeterHit; +import org.lcsim.event.RawTrackerHit; +import org.lcsim.geometry.Detector; +import org.lcsim.lcio.LCIOConstants; +import org.lcsim.util.Driver; + +/** + * + * + * baltzell New in 2015: (default behavior is unchanged) + * Added firmware emulation for converting from Mode-1 readout (RawTrackerHit) + * to Mode-3 pulse (CalorimeterHit). Turn it on with "emulateFirmware", else + * defaults to previous behavior. + * + * Removed integralWindow in favor of NSA/NSB to allow treating all Modes uniformly. + * (New) NSA+NSB == (Old) integralWindow*4(ns) + * + * Implemented finding multiple peaks for Mode-1. + */ +public class EcalRawConverterDriver extends Driver { + + // To import database conditions + private EcalConditions ecalConditions = null; + + private EcalRawConverter converter = null; + private String rawCollectionName = "EcalReadoutHits"; + private final String ecalReadoutName = "EcalHits"; + private String ecalCollectionName = "EcalCalHits"; + + private static final String extraDataRelationsName = "EcalReadoutExtraDataRelations"; + + private int debug = 0; + private double threshold = Double.NEGATIVE_INFINITY; + private boolean applyBadCrystalMap = true; + private boolean dropBadFADC = false; + private boolean runBackwards = false; + private boolean useTimestamps = false; + private boolean useTruthTime = false; + private boolean useDAQConfig = false; + + private boolean emulateFirmware = false; + + public EcalRawConverterDriver() { + converter = new EcalRawConverter(); + } + + public void setUseFit(boolean useFit) { converter.useFit=useFit; } + public void setFitFileName(String name) { converter.fitFileName=name; } + public void setFixShapeParameter(boolean fix) { converter.fixShapeParameter=fix; } + + + /** + * Set to <code>true</code> to use the "2014" gain formula:<br/> + * <pre>channelGain * adcSum * gainFactor * readoutPeriod</pre> + * <p> + * Set to <code>false</code> to use the gain formula for the Test Run: + * <pre>gain * adcSum * ECalUtils.MeV</pre> + * + * @param use2014Gain True to use 2014 gain formulation. + */ + public void setUse2014Gain(boolean use2014Gain) { + converter.setUse2014Gain(use2014Gain); + } + + /** + * Set to <code>true</code> to apply time walk correction from {@link EcalTimeWalk#correctTimeWalk(double, double)}. + * <p> + * This is only applicable to Mode-3 data. + * + * @param useTimeWalkCorrection True to apply time walk correction. + */ + public void setUseTimeWalkCorrection(boolean useTimeWalkCorrection) { + converter.setUseTimeWalkCorrection(useTimeWalkCorrection); + } + + /** + * Set to <code>true</code> to use a running pedestal calibration from mode 7 data. + * <p> + * The running pedestal values are retrieved from the event collection "EcalRunningPedestals" + * which is a <code>Map</code> between {@link org.hps.conditions.ecal.EcalChannel} objects + * are their average pedestal. + * + * @param useRunningPedestal True to use a running pedestal value. + */ + public void setUseRunningPedestal(boolean useRunningPedestal) { + converter.setUseRunningPedestal(useRunningPedestal); + } + + /** + * Set to <code>true</code> to generate a {@link org.lcsim.event.CalorimeterHit} + * collection which is a conversion from energy to raw signals. + * + * @param runBackwards True to run the procedure backwards. + */ + public void setRunBackwards(boolean runBackwards) { + this.runBackwards = runBackwards; + } + + /** + * Set to <code>true</code> to drop hits that are mapped to a hard-coded + * bad FADC configuration from the Test Run. + * + * @param dropBadFADC True to drop hits mapped to a bad FADC. + */ + public void setDropBadFADC(boolean dropBadFADC) { + this.dropBadFADC = dropBadFADC; + } + + /** + * Set a minimum energy threshold in GeV for created {@link org.lcsim.event.CalorimeterHit} + * objects to be written into the output collection. + * @param threshold The minimum energy threshold in GeV. + */ + public void setThreshold(double threshold) { + this.threshold = threshold; + } + + /** + * Set to <code>true</code> to use Mode-7 emulation in calculations. + * False is Mode-3. + * + * @param mode7 True to use Mode-7 emulation in calculations. + */ + public void setEmulateMode7(boolean mode7) { + converter.setMode7(mode7); + } + + /** + * Set to <code>true</code> to emulate firmware conversion of Mode-1 to Mode-3/7 data. + * + * @param emulateFirmware True to use firmware emulation. + */ + public void setEmulateFirmware(boolean emulateFirmware) { + this.emulateFirmware = emulateFirmware; + } + + /** + * Set the leading-edge threshold in ADC counts, relative to pedestal, for pulse-finding + * and time determination. + * <p> + * Used to convert Mode-1 readout into Mode-3 or Mode-7 data that is usable by clustering. + * + * @param threshold The leading edge threshold in ADC counts. + */ + public void setLeadingEdgeThreshold(double threshold) { + converter.setLeadingEdgeThreshold(threshold); + } + + /** + * Set the number of samples in the FADC readout window. + * <p> + * This is needed in order to properly pedestal-correct clipped pulses for mode-3 and mode-7. + * It is ignored for mode-1 input, since this data already includes the number of samples. + * <p> + * A non-positive number disables pulse-clipped pedestals and reverts to the old behavior which + * assumed that the integration range was constant. + * + * @param windowSamples The number of samples in the FADC readout window. + */ + public void setWindowSamples(int windowSamples) { + converter.setWindowSamples(windowSamples); + } + + /** + * Set the integration range in nanoseconds after the threshold crossing. + * <p> + * These numbers must be multiples of 4 nanoseconds. + * <p> + * This value is used for pulse integration in Mode-1, and pedestal subtraction in all modes. + * + * @param nsa The number of nanoseconds after the threshold crossing. + * @see #setNsb(int) + */ + public void setNsa(int nsa) { + converter.setNSA(nsa); + } + + /** + * Set the integration range in nanoseconds before the threshold crossing. + * <p> + * These numbers must be multiples of 4 nanoseconds. + * <p> + * This value is used for pulse integration in Mode-1, and pedestal subtraction in all modes. + * + * @param nsb The number of nanoseconds after the threshold crossing. + * @see #setNsa(int) + */ + public void setNsb(int nsb) { + converter.setNSB(nsb); + } + + /** + * Set the maximum number of peaks to search for in the signal, + * which must be between 1 and 3, inclusive. + * @param nPeak The maximum number of peaks to search for in the signal. + */ + public void setNPeak(int nPeak) { + converter.setNPeak(nPeak); + } + + /** + * Set a constant gain factor in the converter for all channels. + * @param gain The constant gain value. + */ + public void setGain(double gain) { + converter.setGain(gain); + } + + /** + * Set the {@link org.lcsim.event.CalorimeterHit} collection name, + * which is used as input in "normal" mode and output when running + * "backwards". + * + * @param ecalCollectionName The <code>CalorimeterHit</code> collection name. + * @see #runBackwards + */ + public void setEcalCollectionName(String ecalCollectionName) { + this.ecalCollectionName = ecalCollectionName; + } + + /** + * Set the raw collection name which is used as output in "normal" mode + * and input when running "backwards". + * <p> + * Depending on the Driver configuration, this could be a collection + * of {@link org.lcsim.event.RawTrackerHit} objects for Mode-1 + * or {@link org.lcsim.event.RawCalorimeterHit} objects for Mode-3 + * or Mode-7. + * + * @param rawCollectionName The raw collection name. + */ + public void setRawCollectionName(String rawCollectionName) { + this.rawCollectionName = rawCollectionName; + } + + /** + * Set to <code>true</code> to ignore data from channels that + * are flagged as "bad" in the conditions system. + * @param apply True to ignore bad channels. + */ + public void setApplyBadCrystalMap(boolean apply) { + this.applyBadCrystalMap = apply; + } + + /** + * Set to <code>true</code> to turn on debug output. + * @param debug True to turn on debug output. + */ + public void setDebug(int debug) { + this.debug = debug; + converter.debug = debug; + } + + /** + * Set to <code>true</code> to use timestamp information from the ECal or trigger. + * @param useTimestamps True to use timestamp information. + */ + // FIXME: What does this actually do? What calculations does it affect? + public void setUseTimestamps(boolean useTimestamps) { + this.useTimestamps = useTimestamps; + } + + /** + * Set to <code>true</code> to use MC truth information. + * @param useTruthTime True to use MC truth information. + */ + // FIXME: What does this actually do? What calculations does it affect? + public void setUseTruthTime(boolean useTruthTime) { + this.useTruthTime = useTruthTime; + } + + /** + * Sets whether the driver should use the DAQ configuration from + * EvIO file for its parameters. If activated, the converter will + * obtain gains, thresholds, pedestals, the window size, and the + * pulse integration window from the EvIO file. This will replace + * and overwrite any manually defined settings.<br/> + * <br/> + * Note that if this setting is active, the driver will not output + * any data until a DAQ configuration has been read from the data + * stream. + * @param state - <code>true</code> indicates that the configuration + * should be read from the DAQ data in an EvIO file. Setting this + * to <code>false</code> will cause the driver to use its regular + * manually-defined settings and pull gains and pedestals from the + * conditions database. + */ + public void setUseDAQConfig(boolean state) { + useDAQConfig = state; + converter.setUseDAQConfig(state); + } + + @Override + public void startOfData() { + if (ecalCollectionName == null) { + throw new RuntimeException("The parameter ecalCollectionName was not set!"); + } + } + + @Override + public void detectorChanged(Detector detector) { + + // set the detector for the converter + // FIXME: This method doesn't even need the detector object and does not use it. + converter.setDetector(detector); + + // ECAL combined conditions object. + ecalConditions = DatabaseConditionsManager.getInstance().getEcalConditions(); + } + + /** + * @return false if the channel is a good one, true if it is a bad one + * @param CalorimeterHit + */ + public boolean isBadCrystal(CalorimeterHit hit) { + // Get the channel data. + EcalChannelConstants channelData = findChannel(hit.getCellID()); + + return channelData.isBadChannel(); + } + + /** + * @return false if the ADC is a good one, true if it is a bad one + * @param CalorimeterHit + */ + public boolean isBadFADC(CalorimeterHit hit) { + return (getCrate(hit.getCellID()) == 1 && getSlot(hit.getCellID()) == 3); + } + + private static double getTimestamp(int system, EventHeader event) { //FIXME: copied from org.hps.readout.ecal.ReadoutTimestamp + if (event.hasCollection(GenericObject.class, "ReadoutTimestamps")) { + List<GenericObject> timestamps = event.get(GenericObject.class, "ReadoutTimestamps"); + for (GenericObject timestamp : timestamps) { + if (timestamp.getIntVal(0) == system) { + return timestamp.getDoubleVal(0); + } + } + return 0; + } else { + return 0; + } + } + + @Override + public void process(EventHeader event) { + // Do not process the event if the DAQ configuration should be + // used for value, but is not initialized. + if(useDAQConfig && !ConfigurationManager.isInitialized()) { + return; + } + + final int SYSTEM_TRIGGER = 0; + final int SYSTEM_TRACKER = 1; + final int SYSTEM_ECAL = 2; + + double timeOffset = 0.0; + if (useTimestamps) { + double t0ECal = getTimestamp(SYSTEM_ECAL, event); + double t0Trig = getTimestamp(SYSTEM_TRIGGER, event); + timeOffset += (t0ECal - t0Trig) + 200.0; + } + if (useTruthTime) { + double t0ECal = getTimestamp(SYSTEM_ECAL, event); + timeOffset += ((t0ECal + 250.0) % 500.0) - 250.0; + } + + int flags = 0; + flags += 1 << LCIOConstants.RCHBIT_TIME; //store hit time + flags += 1 << LCIOConstants.RCHBIT_LONG; //store hit position; this flag has no effect for RawCalorimeterHits + + if (!runBackwards) { + ArrayList<CalorimeterHit> newHits = new ArrayList<CalorimeterHit>(); + + /* + * This is for FADC Mode-1 data: + */ + if (event.hasCollection(RawTrackerHit.class, rawCollectionName)) { + List<RawTrackerHit> hits = event.get(RawTrackerHit.class, rawCollectionName); + + for (RawTrackerHit hit : hits) { + + ArrayList<CalorimeterHit> newHits2 = new ArrayList<CalorimeterHit>(); + if (emulateFirmware) { + newHits2.addAll(converter.HitDtoA(event,hit)); + } else { + newHits2.add(converter.HitDtoA(hit)); + } + + for (CalorimeterHit newHit : newHits2) { + + // Get the channel data. + EcalChannelConstants channelData = findChannel(newHit.getCellID()); + + if (applyBadCrystalMap && channelData.isBadChannel()) { + continue; + } + if (dropBadFADC && isBadFADC(newHit)) { + continue; + } + if (newHit.getRawEnergy() > threshold) { + newHits.add(newHit); + } + } + } + event.put(ecalCollectionName, newHits, CalorimeterHit.class, flags, ecalReadoutName); + } + + /* + * This is for FADC pulse mode data (Mode-3 or Mode-7): + */ + if (event.hasCollection(RawCalorimeterHit.class, rawCollectionName)) { + + /* + * This is for FADC Mode-7 data: + */ + if (event.hasCollection(LCRelation.class, extraDataRelationsName)) { // extra information available from mode 7 readout + List<LCRelation> extraDataRelations = event.get(LCRelation.class, extraDataRelationsName); + for (LCRelation rel : extraDataRelations) { + RawCalorimeterHit hit = (RawCalorimeterHit) rel.getFrom(); + if (debug>0) { + System.out.format("old hit energy %d\n", hit.getAmplitude()); + } + GenericObject extraData = (GenericObject) rel.getTo(); + CalorimeterHit newHit; + newHit = converter.HitDtoA(event,hit, extraData, timeOffset); + if (newHit.getRawEnergy() > threshold) { + if (applyBadCrystalMap && isBadCrystal(newHit)) { + continue; + } + if (dropBadFADC && isBadFADC(newHit)) { + continue; + } + if (debug>0) { + System.out.format("new hit energy %f\n", newHit.getRawEnergy()); + } + newHits.add(newHit); + } + + } + } else { + /* + * This is for FADC Mode-3 data: + */ + List<RawCalorimeterHit> hits = event.get(RawCalorimeterHit.class, rawCollectionName); + for (RawCalorimeterHit hit : hits) { + if (debug>0) { + System.out.format("old hit energy %d\n", hit.getAmplitude()); + } + CalorimeterHit newHit; + newHit = converter.HitDtoA(event, hit, timeOffset); + if (newHit.getRawEnergy() > threshold) { + if (applyBadCrystalMap && isBadCrystal(newHit)) { + continue; + } + if (dropBadFADC && isBadFADC(newHit)) { + continue; + } + if (debug>0) { + System.out.format("new hit energy %f\n", newHit.getRawEnergy()); + } + newHits.add(newHit); + } + } + } + event.put(ecalCollectionName, newHits, CalorimeterHit.class, flags, ecalReadoutName); + } + } else { + ArrayList<RawCalorimeterHit> newHits = new ArrayList<RawCalorimeterHit>(); + if (event.hasCollection(CalorimeterHit.class, ecalCollectionName)) { + List<CalorimeterHit> hits = event.get(CalorimeterHit.class, ecalCollectionName); + + for (CalorimeterHit hit : hits) { + if (debug>0) { + System.out.format("old hit energy %f\n", hit.getRawEnergy()); + } + RawCalorimeterHit newHit = converter.HitAtoD(hit); + if (newHit.getAmplitude() > 0) { + if (debug>0) { + System.out.format("new hit energy %d\n", newHit.getAmplitude()); + } + newHits.add(newHit); + } + } + event.put(rawCollectionName, newHits, RawCalorimeterHit.class, flags, ecalReadoutName); + } + } + + } + + /** + * Convert physical ID to gain value. + * + * @param cellID (long) + * @return channel constants (EcalChannelConstants) + */ + private EcalChannelConstants findChannel(long cellID) { + return ecalConditions.getChannelConstants(ecalConditions.getChannelCollection().findGeometric(cellID)); + } + + /** + * Return crate number from cellID + * + * @param cellID (long) + * @return Crate number (int) + */ + private int getCrate(long cellID) { + // Find the ECAL channel and return the crate number. + return ecalConditions.getChannelCollection().findGeometric(cellID).getCrate(); + } + + /** + * Return slot number from cellID + * + * @param cellID (long) + * @return Slot number (int) + */ + private int getSlot(long cellID) { + // Find the ECAL channel and return the slot number. + return ecalConditions.getChannelCollection().findGeometric(cellID).getSlot(); + } +}