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