Author: [log in to unmask]
Date: Mon May 23 15:11:53 2016
New Revision: 4368
Log:
new converter drivers to test with no time correction
Added:
java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/EcalRawConverter2Driver.java
Added: java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/EcalRawConverter2Driver.java
=============================================================================
--- java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/EcalRawConverter2Driver.java (added)
+++ java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/EcalRawConverter2Driver.java Mon May 23 15:11:53 2016
@@ -0,0 +1,625 @@
+package org.hps.recon.ecal;
+
+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.record.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;
+
+/**
+ * This <code>Driver</code> converts raw ECal data collections to {@link org.lcsim.event.CalorimeterHit} collections
+ * with energy and time information. The {@link EcalRawConverter} does most of the low-level work.
+ * <p>
+ * The following input collections are used:
+ * <ul>
+ * <li>EcalReadoutHits
+ * <li>
+ * <li>EcalReadoutExtraDataRelations</li>
+ * <li>EcalRunningPedestals</li>
+ * </ul>
+ * <p>
+ * The results are by default written to the <b>EcalCalHits</b> output collection.
+ */
+public class EcalRawConverter2Driver extends Driver {
+
+ // To import database conditions
+ private EcalConditions ecalConditions = null;
+
+ private EcalRawConverter2 converter = null;
+ /**
+ * Input collection name (unless runBackwards=true, then it's output). Can be a
+ * {@link org.lcsim.event.RawTrackerHit} or {@link org.lcsim.event.RawCalorimeterHit} These have ADC and sample time
+ * information.
+ */
+ private String rawCollectionName = "EcalReadoutHits";
+
+ /**
+ * Output collection name (unless runBackwards=true, then it's input). Always a
+ * {@link org.lcsim.event.CalorimeterHit} This has energy (GeV) and ns time information.
+ */
+ private String ecalCollectionName = "EcalCalHits";
+
+ /**
+ * ecalCollectionName "type" (must match detector-data)
+ */
+ private final String ecalReadoutName = "EcalHits";
+
+ /*
+ * Output relation between ecalCollectionName and Mode-7 pedestals
+ */
+ private static final String extraDataRelationsName = "EcalReadoutExtraDataRelations";
+
+ private boolean debug = false;
+
+ /**
+ * Hit threshold in GeV. Anything less will not be put into LCIO.
+ */
+ private double threshold = Double.NEGATIVE_INFINITY;
+
+ /**
+ * Whether to reject bad crystals.
+ */
+ private boolean applyBadCrystalMap = true;
+
+ /**
+ * Whether to reject bad FADC channels.
+ */
+ private boolean dropBadFADC = false;
+
+ /**
+ * If true, convert ecalCollectionName to rawCollectionName (GeV to ADC). Else, convert rawCollectionName to
+ * ecalCollectionName (ADC to GeV).
+ */
+ private boolean runBackwards = false;
+
+ /**
+ *
+ */
+ private boolean useTimestamps = false;
+
+ /**
+ *
+ */
+ private boolean useTruthTime = false;
+
+ /**
+ * Whether to use DAQ config read from EVIO for EcalRawConverter parameters. Should be completely removed to a
+ * standalone class soilely for trigger emulation.
+ */
+ private boolean useDAQConfig = false;
+
+ /**
+ * Whether to perform "firmware algorithm" on Mode-1 data. If so, this includes finding a threshold crossing,
+ * extracting a pulse time, and integrating over some configurable sample range inside the window to extract pulse
+ * integral. If not, it simply integrates the entire window and makes no attempt at extracting pulse time. This is
+ * poorly named.
+ */
+ private boolean emulateFirmware = true;
+
+ public EcalRawConverter2Driver() {
+ converter = new EcalRawConverter2();
+ }
+
+ /**
+ * Set to <code>true</code> to use pulse fitting instead of arithmetic integration:<br/>
+ */
+ public void setUseFit(boolean useFit) {
+ converter.setUseFit(useFit);
+ }
+
+ /**
+ * Fix 3-pole function width to be the same for all 442 ECal channels. Units=samples.
+ */
+ public void setGlobalFixedPulseWidth(double width) {
+ converter.setGlobalFixedPulseWidth(width);
+ }
+
+ /**
+ * Set to <code>true</code> to fix fitted pulse widths to their channel's mean value:<br/>
+ */
+ public void setFixShapeParameter(boolean fix) {
+ converter.setFixShapeParameter(fix);
+ }
+
+ /**
+ * Limit threshold crossing range that is candidate for pulse-fitting. Units=samples.
+ */
+ public void setFitThresholdTimeLo(int sample) {
+ converter.setFitThresholdTimeLo(sample);
+ }
+
+ public void setFitThresholdTimeHi(int sample) {
+ converter.setFitThresholdTimeHi(sample);
+ }
+
+ /**
+ * Constrain pulse fit time0 parameter. Units=samples.
+ */
+ public void setFitLimitTimeLo(int sample) {
+ converter.setFitLimitTimeLo(sample);
+ }
+
+ public void setFitLimitTimeHi(int sample) {
+ converter.setFitLimitTimeHi(sample);
+ }
+
+ /**
+ * 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(boolean debug) {
+ this.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 hit the <code>CalorimeterHit</code> pointing to the channel
+ */
+ 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 hit the <code>CalorimeterHit</code> pointing to the FADC
+ */
+ 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) {
+ 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) {
+ 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) {
+ 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) {
+ 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) {
+ System.out.format("old hit energy %f\n", hit.getRawEnergy());
+ }
+ RawCalorimeterHit newHit = converter.HitAtoD(hit);
+ if (newHit.getAmplitude() > 0) {
+ if (debug) {
+ 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();
+ }
+}
|