Author: [log in to unmask] Date: Mon Jun 8 19:17:47 2015 New Revision: 3115 Log: change default pulse width to 9.6 ns Modified: java/trunk/ecal-readout-sim/src/main/java/org/hps/readout/ecal/FADCEcalReadoutDriver.java Modified: java/trunk/ecal-readout-sim/src/main/java/org/hps/readout/ecal/FADCEcalReadoutDriver.java ============================================================================= --- java/trunk/ecal-readout-sim/src/main/java/org/hps/readout/ecal/FADCEcalReadoutDriver.java (original) +++ java/trunk/ecal-readout-sim/src/main/java/org/hps/readout/ecal/FADCEcalReadoutDriver.java Mon Jun 8 19:17:47 2015 @@ -49,41 +49,26 @@ private Subdetector ecal; private EcalConditions ecalConditions = null; //buffer for preamp signals (units of volts, no pedestal) - private Map<Long, RingBuffer> signalMap = null; + private Map<Long, RingBuffer> analogPipelines = null; //ADC pipeline for readout (units of ADC counts) - private Map<Long, FADCPipeline> pipelineMap = null; + private Map<Long, FADCPipeline> digitalPipelines = null; //buffer for window sums - private Map<Long, Integer> sumMap = null; + private Map<Long, Integer> triggerPathHitSums = null; //buffer for timestamps - private Map<Long, Integer> timeMap = null; + private Map<Long, Integer> triggerPathHitTimes = null; //queue for hits to be output to clusterer - private PriorityQueue<RawCalorimeterHit> outputQueue = null; - //length of ring buffer (in readout cycles) + private PriorityQueue<RawCalorimeterHit> triggerPathDelayQueue = null; + //output buffer for hits + private LinkedList<RawCalorimeterHit> triggerPathCoincidenceQueue = new LinkedList<RawCalorimeterHit>(); private int bufferLength = 100; - //length of readout pipeline (in readout cycles) private int pipelineLength = 2000; - //shaper time constant in ns - private double tp = 6.95; - //delay (number of readout periods) between start of summing window and output of hit to clusterer + private double tp = 9.6; private int delay0 = 32; - //start of readout window relative to trigger time (in readout cycles) - //in FADC documentation, "Programmable Latency" or PL private int readoutLatency = 100; - //number of ADC samples to read out - //in FADC documentation, "Programmable Trigger Window" or PTW private int readoutWindow = 100; - //number of ADC samples to read out before each rising threshold crossing - //in FADC documentation, "number of samples before" or NSB private int numSamplesBefore = 5; - //number of ADC samples to read out after each rising threshold crossing - //in FADC documentation, "number of samples before" or NSA private int numSamplesAfter = 30; -// private HPSEcalConverter converter = null; - //output buffer for hits - private LinkedList<RawCalorimeterHit> buffer = new LinkedList<RawCalorimeterHit>(); - //number of readout periods for which a given hit stays in the buffer private int coincidenceWindow = 2; - //output collection name for hits read out from trigger private String ecalReadoutCollectionName = "EcalReadoutHits"; private int mode = ECAL_PULSE_INTEGRAL_MODE; private int readoutThreshold = 10; @@ -92,17 +77,8 @@ private double fixedGain = -1; private boolean constantTriggerWindow = true; private boolean addNoise = false; - - // 32.8 p.e./MeV = New detector in 2014 - // 2 p.e./MeV = Test Run detector - private double pePerMeV = 32.8; //photoelectrons per MeV, used to calculate noise - - //switch between test run and 2014 definitions of gain constants - // true = ONLY simulation studies in 2014 - // false = Test Run data/simulations and 2014+ Detector's real data + private double pePerMeV = 32.8; private boolean use2014Gain = false; - - //switch between three pulse shape functions private PulseShape pulseShape = PulseShape.ThreePole; public enum PulseShape { @@ -118,14 +94,34 @@ // converter = new HPSEcalConverter(null); } + /** + * Add noise (photoelectron statistics and readout/preamp noise) to hits + * before adding them to the analog pipeline. + * + * @param addNoise True to add noise, default of false. + */ public void setAddNoise(boolean addNoise) { this.addNoise = addNoise; } + /** + * Sets the trigger-path hit processing algorithm. + * + * @param constantTriggerWindow True for 2014+ FADC behavior, false for test + * run behavior. True by default. + */ public void setConstantTriggerWindow(boolean constantTriggerWindow) { this.constantTriggerWindow = constantTriggerWindow; } + /** + * Override the ECal gains set in the conditions system with a single + * uniform value. + * + * @param fixedGain Units of MeV/(ADC counts in pulse integral). Negative + * value causes the conditions system to be used for gains. Default is + * negative. + */ public void setFixedGain(double fixedGain) { this.fixedGain = fixedGain; } @@ -134,78 +130,176 @@ this.ecalName = ecalName; } + /** + * Threshold for readout-path hits. For 2014+ running this should always + * equal the trigger threshold. + * + * @param readoutThreshold Units of ADC counts, default of 10. + */ public void setReadoutThreshold(int readoutThreshold) { this.readoutThreshold = readoutThreshold; } + /** + * Scale factor for trigger-path hit amplitudes. Only used for test run. + * + * @param scaleFactor Default of 1. + */ public void setScaleFactor(double scaleFactor) { this.scaleFactor = scaleFactor; } + /** + * Threshold for trigger-path hits. For 2014+ running this should always + * equal the readout threshold. + * + * @param triggerThreshold Units of ADC counts, default of 10. + */ public void setTriggerThreshold(int triggerThreshold) { this.triggerThreshold = triggerThreshold; } + /** + * Output collection name for readout-path hits. + * + * @param ecalReadoutCollectionName + */ public void setEcalReadoutCollectionName(String ecalReadoutCollectionName) { this.ecalReadoutCollectionName = ecalReadoutCollectionName; } + /** + * Number of ADC samples to process after each rising threshold crossing. In + * FADC documentation, "number of samples after" or NSA. + * + * @param numSamplesAfter Units of 4 ns FADC clock cycles, default of 30. + */ public void setNumSamplesAfter(int numSamplesAfter) { this.numSamplesAfter = numSamplesAfter; } + /** + * Number of ADC samples to process before each rising threshold crossing. + * In FADC documentation, "number of samples before" or NSB. + * + * @param numSamplesBefore Units of 4 ns FADC clock cycles, default of 5. + */ public void setNumSamplesBefore(int numSamplesBefore) { this.numSamplesBefore = numSamplesBefore; } + /** + * Start of readout window relative to trigger time (in readout cycles). In + * FADC documentation, "Programmable Latency" or PL. + * + * @param readoutLatency Units of 4 ns FADC clock cycles, default of 100. + */ public void setReadoutLatency(int readoutLatency) { this.readoutLatency = readoutLatency; } + /** + * Number of ADC samples to read out. In FADC documentation, "Programmable + * Trigger Window" or PTW. + * + * @param readoutWindow Units of 4 ns FADC clock cycles, default of 100. + */ public void setReadoutWindow(int readoutWindow) { this.readoutWindow = readoutWindow; } + /** + * Number of clock cycles for which the same trigger-path hit is sent to the + * clusterer. Only used for old clusterer simulations (CTPClusterDriver). + * Otherwise this should be set to 1. + * + * @param coincidenceWindow Units of 4 ns FADC clock cycles, default of 1. + */ public void setCoincidenceWindow(int coincidenceWindow) { this.coincidenceWindow = coincidenceWindow; } + /** + * Switch between test run and 2014 definitions of gain constants. True for + * MC studies and mock data in 2014. For all real data (test run and 2014+), + * test run MC, and 2015+ production MC, this should be false. + * + * + * @param use2014Gain True ONLY for simulation studies in 2014. Default of + * false. + */ public void setUse2014Gain(boolean use2014Gain) { this.use2014Gain = use2014Gain; } + /** + * Model used for the preamp pulse shape. + * + * @param pulseShape ThreePole, DoubleGaussian, or CRRC. Default is + * ThreePole. + */ public void setPulseShape(String pulseShape) { this.pulseShape = PulseShape.valueOf(pulseShape); } + /** + * Shaper time constant. Definition depends on the pulse shape. For the + * three-pole function, this is equal to RC, or half the peaking time. + * + * @param tp Units of ns, default of 9.6. + */ public void setTp(double tp) { this.tp = tp; } -// public void setFallTime(double fallTime) { -// this.fallTime = fallTime; -// } + /** + * Photoelectrons per MeV, used to calculate noise due to photoelectron + * statistics. Test run detector had a value of 2 photoelectrons/MeV; new + * 2014 detector has a value of 32.8 photoelectrons/MeV. + * + * @param pePerMeV Units of photoelectrons/MeV, default of 32.8. + */ public void setPePerMeV(double pePerMeV) { this.pePerMeV = pePerMeV; } -// public void setRiseTime(double riseTime) { -// this.riseTime = riseTime; -// } + /** + * Latency between threshold crossing and output of trigger-path hit to + * clusterer. + * + * @param delay0 Units of 4 ns FADC clock cycles, default of 32. + */ public void setDelay0(int delay0) { this.delay0 = delay0; } + /** + * Length of analog pipeline. + * + * @param bufferLength Units of 4 ns FADC clock cycles, default of 100. + */ public void setBufferLength(int bufferLength) { this.bufferLength = bufferLength; resetFADCBuffers(); } + /** + * Length of digital pipeline. The digital pipeline in the FADC is 2000 + * cells long. + * + * @param pipelineLength Units of 4 ns FADC clock cycles, default of 2000. + */ public void setPipelineLength(int pipelineLength) { this.pipelineLength = pipelineLength; resetFADCBuffers(); } + /** + * Mode for readout-path hits. + * + * @param mode 1, 2 or 3. Values correspond to the standard FADC mode + * numbers (1=raw, 2=pulse, 3=pulse integral). + */ public void setMode(int mode) { this.mode = mode; if (mode != ECAL_RAW_MODE && mode != ECAL_PULSE_MODE && mode != ECAL_PULSE_INTEGRAL_MODE) { @@ -219,7 +313,7 @@ * @return */ public Map<Long, RingBuffer> getSignalMap() { - return signalMap; + return analogPipelines; } /** @@ -228,16 +322,23 @@ * @return */ public Map<Long, FADCPipeline> getPipelineMap() { - return pipelineMap; - } - + return digitalPipelines; + } + + /** + * Digitize values in the analog pipelines and append them to the digital + * pipelines. Integrate trigger-path hits and add them to the trigger path + * queues. Read out trigger-path hits to the list sent to the clusterer. + * + * @param hits List to be filled by this method. + */ @Override protected void readHits(List<RawCalorimeterHit> hits) { - for (Long cellID : signalMap.keySet()) { - RingBuffer signalBuffer = signalMap.get(cellID); - - FADCPipeline pipeline = pipelineMap.get(cellID); + for (Long cellID : analogPipelines.keySet()) { + RingBuffer signalBuffer = analogPipelines.get(cellID); + + FADCPipeline pipeline = digitalPipelines.get(cellID); pipeline.step(); // Get the channel data. @@ -250,9 +351,9 @@ int pedestalSubtractedValue = digitizedValue - pedestal; //System.out.println(signalBuffer.currentValue() + " " + currentValue + " " + pipeline.currentValue()); - Integer sum = sumMap.get(cellID); + Integer sum = triggerPathHitSums.get(cellID); if (sum == null && pedestalSubtractedValue > triggerThreshold) { - timeMap.put(cellID, readoutCounter); + triggerPathHitTimes.put(cellID, readoutCounter); if (constantTriggerWindow) { int sumBefore = 0; for (int i = 0; i < numSamplesBefore; i++) { @@ -261,57 +362,57 @@ } sumBefore += pipeline.getValue(numSamplesBefore - i - 1); } - sumMap.put(cellID, sumBefore); + triggerPathHitSums.put(cellID, sumBefore); } else { - sumMap.put(cellID, pedestalSubtractedValue); + triggerPathHitSums.put(cellID, pedestalSubtractedValue); } } if (sum != null) { if (constantTriggerWindow) { - if (timeMap.get(cellID) + numSamplesAfter >= readoutCounter) { + if (triggerPathHitTimes.get(cellID) + numSamplesAfter >= readoutCounter) { if (debug) { - System.out.format("trigger %d, %d: %d\n", cellID, readoutCounter - timeMap.get(cellID) + numSamplesBefore - 1, pipeline.getValue(0)); + System.out.format("trigger %d, %d: %d\n", cellID, readoutCounter - triggerPathHitTimes.get(cellID) + numSamplesBefore - 1, pipeline.getValue(0)); } - sumMap.put(cellID, sum + pipeline.getValue(0)); - } else if (timeMap.get(cellID) + delay0 <= readoutCounter) { + triggerPathHitSums.put(cellID, sum + pipeline.getValue(0)); + } else if (triggerPathHitTimes.get(cellID) + delay0 <= readoutCounter) { // System.out.printf("sum = %f\n", sum); - outputQueue.add(new BaseRawCalorimeterHit(cellID, + triggerPathDelayQueue.add(new BaseRawCalorimeterHit(cellID, (int) Math.round(sum / scaleFactor), - 64 * timeMap.get(cellID))); - sumMap.remove(cellID); + 64 * triggerPathHitTimes.get(cellID))); + triggerPathHitSums.remove(cellID); } } else { - if (pedestalSubtractedValue < triggerThreshold || timeMap.get(cellID) + delay0 == readoutCounter) { + if (pedestalSubtractedValue < triggerThreshold || triggerPathHitTimes.get(cellID) + delay0 == readoutCounter) { // System.out.printf("sum = %f\n",sum); - outputQueue.add(new BaseRawCalorimeterHit(cellID, + triggerPathDelayQueue.add(new BaseRawCalorimeterHit(cellID, (int) Math.round((sum + pedestalSubtractedValue) / scaleFactor), - 64 * timeMap.get(cellID))); - sumMap.remove(cellID); + 64 * triggerPathHitTimes.get(cellID))); + triggerPathHitSums.remove(cellID); } else { - sumMap.put(cellID, sum + pedestalSubtractedValue); + triggerPathHitSums.put(cellID, sum + pedestalSubtractedValue); } } } signalBuffer.step(); } - while (outputQueue.peek() != null && outputQueue.peek().getTimeStamp() / 64 <= readoutCounter - delay0) { - if (outputQueue.peek().getTimeStamp() / 64 < readoutCounter - delay0) { + while (triggerPathDelayQueue.peek() != null && triggerPathDelayQueue.peek().getTimeStamp() / 64 <= readoutCounter - delay0) { + if (triggerPathDelayQueue.peek().getTimeStamp() / 64 < readoutCounter - delay0) { System.out.println(this.getName() + ": Stale hit in output queue"); - outputQueue.poll(); + triggerPathDelayQueue.poll(); } else { - buffer.add(outputQueue.poll()); - } - } - while (!buffer.isEmpty() && buffer.peek().getTimeStamp() / 64 <= readoutCounter - delay0 - coincidenceWindow) { - buffer.remove(); + triggerPathCoincidenceQueue.add(triggerPathDelayQueue.poll()); + } + } + while (!triggerPathCoincidenceQueue.isEmpty() && triggerPathCoincidenceQueue.peek().getTimeStamp() / 64 <= readoutCounter - delay0 - coincidenceWindow) { + triggerPathCoincidenceQueue.remove(); } if (debug) { - for (RawCalorimeterHit hit : buffer) { + for (RawCalorimeterHit hit : triggerPathCoincidenceQueue) { System.out.format("new hit: energy %d\n", hit.getAmplitude()); } } - hits.addAll(buffer); + hits.addAll(triggerPathCoincidenceQueue); } @Override @@ -355,7 +456,7 @@ } protected short[] getWindow(long cellID) { - FADCPipeline pipeline = pipelineMap.get(cellID); + FADCPipeline pipeline = digitalPipelines.get(cellID); short[] adcValues = new short[readoutWindow]; for (int i = 0; i < readoutWindow; i++) { adcValues[i] = (short) pipeline.getValue(readoutLatency - i - 1); @@ -369,7 +470,7 @@ protected List<RawTrackerHit> readWindow() { // System.out.println("Reading FADC data"); List<RawTrackerHit> hits = new ArrayList<RawTrackerHit>(); - for (Long cellID : pipelineMap.keySet()) { + for (Long cellID : digitalPipelines.keySet()) { short[] adcValues = getWindow(cellID); EcalChannelConstants channelData = findChannel(cellID); boolean isAboveThreshold = false; @@ -389,7 +490,7 @@ protected List<RawTrackerHit> readPulses() { // System.out.println("Reading FADC data"); List<RawTrackerHit> hits = new ArrayList<RawTrackerHit>(); - for (Long cellID : pipelineMap.keySet()) { + for (Long cellID : digitalPipelines.keySet()) { short[] window = getWindow(cellID); short[] adcValues = null; int pointerOffset = 0; @@ -423,7 +524,7 @@ protected List<RawCalorimeterHit> readIntegrals() { // System.out.println("Reading FADC data"); List<RawCalorimeterHit> hits = new ArrayList<RawCalorimeterHit>(); - for (Long cellID : pipelineMap.keySet()) { + for (Long cellID : digitalPipelines.keySet()) { short[] window = getWindow(cellID); int adcSum = 0; int pointerOffset = 0; @@ -456,11 +557,17 @@ return hits; } + /** + * Fill the analog pipelines with the preamp pulses generated by hits in the + * ECal. + * + * @param hits ECal hits from SLIC/Geant4. + */ @Override protected void putHits(List<CalorimeterHit> hits) { //fill the readout buffers for (CalorimeterHit hit : hits) { - RingBuffer eDepBuffer = signalMap.get(hit.getCellID()); + RingBuffer eDepBuffer = analogPipelines.get(hit.getCellID()); double energyAmplitude = hit.getRawEnergy(); // Get the channel data. EcalChannelConstants channelData = findChannel(hit.getCellID()); @@ -476,6 +583,9 @@ } energyAmplitude += RandomGaussian.getGaussian(0, noise); } + if ((1) * readoutPeriod + readoutTime() - (ClockSingleton.getTime() + hit.getTime()) >= readoutPeriod) { + throw new RuntimeException("trying to add a hit to the analog pipeline, but time seems incorrect"); + } for (int i = 0; i < bufferLength; i++) { eDepBuffer.addToCell(i, energyAmplitude * pulseAmplitude((i + 1) * readoutPeriod + readoutTime() - (ClockSingleton.getTime() + hit.getTime()), hit.getCellID())); } @@ -485,9 +595,9 @@ @Override protected void initReadout() { //initialize buffers - sumMap = new HashMap<Long, Integer>(); - timeMap = new HashMap<Long, Integer>(); - outputQueue = new PriorityQueue(20, new TimeComparator()); + triggerPathHitSums = new HashMap<Long, Integer>(); + triggerPathHitTimes = new HashMap<Long, Integer>(); + triggerPathDelayQueue = new PriorityQueue(20, new TimeComparator()); resetFADCBuffers(); } @@ -506,17 +616,26 @@ if (ecal == null) { return false; } - signalMap = new HashMap<Long, RingBuffer>(); - pipelineMap = new HashMap<Long, FADCPipeline>(); + analogPipelines = new HashMap<Long, RingBuffer>(); + digitalPipelines = new HashMap<Long, FADCPipeline>(); Set<Long> cells = ((HPSEcal3) ecal).getNeighborMap().keySet(); for (Long cellID : cells) { EcalChannelConstants channelData = findChannel(cellID); - signalMap.put(cellID, new RingBuffer(bufferLength)); - pipelineMap.put(cellID, new FADCPipeline(pipelineLength, (int) Math.round(channelData.getCalibration().getPedestal()))); + analogPipelines.put(cellID, new RingBuffer(bufferLength)); + digitalPipelines.put(cellID, new FADCPipeline(pipelineLength, (int) Math.round(channelData.getCalibration().getPedestal()))); } return true; } + /** + * Returns pulse amplitude at the given time (relative to hit time). Gain is + * applied. + * + * @param time Units of ns. Relative to hit time (negative=before the start + * of the pulse). + * @param cellID Crystal ID as returned by hit.getCellID(). + * @return Amplitude, units of volts/GeV. + */ private double pulseAmplitude(double time, long cellID) { // Get the channel data. EcalChannelConstants channelData = findChannel(cellID); @@ -546,10 +665,11 @@ /** * Returns pulse amplitude at the given time (relative to hit time). - * Amplitude is normalized so the pulse integral is 1. - * - * @param time - * @return + * + * @param time Units of ns. Relative to hit time (negative=before the start + * of the pulse). + * @return Amplitude, units of inverse ns. Normalized so the pulse integral + * is 1. */ public static double pulseAmplitude(double time, PulseShape shape, double shapingTime) { if (time <= 0.0) {