Author: [log in to unmask] Date: Sat Mar 7 14:18:26 2015 New Revision: 2346 Log: I create this driver as a copy of the EcalEventDisplay, including - as it was - the raw waveform, if available. I hope this will not be modified again Added: java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/plots/EcalEventDisplayWithRawWaveform.java Added: java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/plots/EcalEventDisplayWithRawWaveform.java ============================================================================= --- java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/plots/EcalEventDisplayWithRawWaveform.java (added) +++ java/trunk/monitoring-drivers/src/main/java/org/hps/monitoring/ecal/plots/EcalEventDisplayWithRawWaveform.java Sat Mar 7 14:18:26 2015 @@ -0,0 +1,514 @@ +package org.hps.monitoring.ecal.plots; + +import hep.aida.IHistogram1D; +import hep.aida.IHistogram2D; +import hep.aida.IPlotter; +import hep.aida.IPlotterFactory; +import hep.aida.IPlotterStyle; + +import java.awt.Point; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.hps.monitoring.ecal.eventdisplay.ui.PDataEventViewer; +import org.hps.monitoring.ecal.eventdisplay.ui.PEventViewer; +import org.hps.monitoring.ecal.eventdisplay.ui.Viewer; +import org.hps.monitoring.ecal.eventdisplay.util.CrystalEvent; +import org.hps.monitoring.ecal.eventdisplay.util.CrystalListener; +import org.hps.recon.ecal.ECalUtils; +import org.lcsim.event.CalorimeterHit; +import org.lcsim.event.Cluster; +import org.lcsim.event.EventHeader; +import org.lcsim.geometry.Detector; +import org.lcsim.util.Driver; +import org.lcsim.util.aida.AIDA; + +/** + * Driver <code>EcalEventDisplay</code> generates the histograms shown + * to the user in the fifth tab of the monitoring application. An + * instance of the single event display is employed to allow for the + * selection of specific crystal channels.<br/> + * <br/> + * The implementation is as follows: + * <ul><li>The event display is opened in a separate window</li> + * <li>It is updated regularly, according to the event refresh rate</li> + * <li>If the user clicks on a crystal, the corresponding energy and time + * distributions (both of type <code>IHistogram1D</code>) are shown in + * the last panel of the monitoring application, as well as a 2D histogram + * (hit time vs. hit energy). Finally, if available, the raw waveshape (in + * mV) is displayed.</li> + * + * @author Andrea Celentano + */ +public class EcalEventDisplayWithRawWaveform extends Driver implements CrystalListener, ActionListener { + // Class variables. + private static final int NUM_CHANNELS = 11 * 47; + + // Plotter objects and variables. + private IPlotter plotter; + private IPlotterFactory plotterFactory; + private AIDA aida = AIDA.defaultInstance(); + + // LCIO Collection names. + private String inputCollection = "EcalCalHits"; + private String clusterCollection = "EcalClusters"; + private String inputCollectionRaw = "EcalReadoutHits"; + + // Channel plot lists. + private ArrayList<IHistogram1D> channelEnergyPlot; + private ArrayList<IHistogram1D> clusterEnergyPlot; + private ArrayList<IHistogram1D> channelTimePlot; + //private ArrayList<IHistogram1D> channelRawWaveform; + private ArrayList<IHistogram2D> channelTimeVsEnergyPlot; + + // Internal variables. + private PEventViewer viewer; // Single event display. + private int pedSamples = 10; // + private IPlotterStyle pstyle; // The plotter style for all plots. + private long lastEventTime = 0; // Tracks the time at which the last event occurred. + private int eventRefreshRate = 1; // The number of seconds before an update occurs. + private boolean resetOnUpdate = true; // Clears the event display on each update. + private double minEch = 10 * ECalUtils.MeV; // The energy scale minimum. + private double maxEch = 3500 * ECalUtils.MeV; // The energy scale maximum. + private int[] windowRaw = new int[NUM_CHANNELS]; // The number of samples in a waveform for each channel. + private boolean[] isFirstRaw = new boolean[NUM_CHANNELS]; // Whether a waveform plot was initiated for each channel. + + // Plot style and title variables. + private static final String NO_TITLE = ""; + private static final String SIGNAL_TIME_TITLE = "Time (ns)"; + private static final String HIT_TIME_TITLE = "Hit Time (ns)"; + private static final String SIGNAL_DATA_STYLE_COLOR = "orange"; + private static final String RAW_WAVEFORM_TITLE = "Raw Waveform"; + private static final String HIT_ENERGY_TITLE = "Hit Energy (GeV)"; + private static final String CLUSTER_ENERGY_TITLE = "Cluster Energy (GeV)"; + private static final String SIGNAL_AMPLITUDE_TITLE = "Signal Amplitude (mV)"; + + /** + * Sets the upper bound of the energy scales used by the driver. + * Energy units are in GeV. + * @param maxEch - The energy scale upper bound. + */ + public void setMaxEch(double maxEch) { + this.maxEch = maxEch; + } + + /** + * Sets the lower bound of the energy scales used by the driver. + * Energy units are in GeV. + * @param minEch - The lower energy scale bound. + */ + public void setMinEch(double minEch) { + this.minEch = minEch; + } + + public void setPedSamples(int pedSamples) { + this.pedSamples = pedSamples; + } + /** + * Sets the LCIO collection name for the processed calorimeter hits. + * @param inputCollection - The LCIO collection name. + */ + public void setInputCollection(String inputCollection) { + this.inputCollection = inputCollection; + } + + /** + * Sets the LCIO collection name for the raw waveform hits. + * @param inputCollectionRaw - The LCIO collection name. + */ + public void setInputCollectionRaw(String inputCollectionRaw) { + this.inputCollectionRaw = inputCollectionRaw; + } + + /** + * Sets the LCIO collection name for calorimeter clusters. + * @param inputClusterCollection - The LCIO collection name. + */ + public void setInputClusterCollection(String inputClusterCollection) { + this.clusterCollection = inputClusterCollection; + } + + /** + * Sets the rate at which the GUI updates its elements, + * @param eventRefreshRate - The rate at which the GUI should be + * updated, in seconds. + */ + public void setEventRefreshRate(int eventRefreshRate) { + this.eventRefreshRate = eventRefreshRate; + } + + /** + * Sets whether the event display should be cleared after event + * or whether it should retain the previously displayed results. + * @param resetOnUpdate - <code>true</code> means that the event + * display should be cleared on each update and <code>false</code> + * that it should not. + */ + public void setResetOnUpdate(boolean resetOnUpdate) { + this.resetOnUpdate = resetOnUpdate; + } + + /** + * Initializes the single channel monitoring plots for all crystal + * channels and defines the plotter region that contains them. + */ + @Override + public void detectorChanged(Detector detector) { + // Reset the AIDA tree directory. + aida.tree().cd("/"); + + // Store histograms for the crystals. + channelEnergyPlot = new ArrayList<IHistogram1D>(NUM_CHANNELS); + channelTimePlot = new ArrayList<IHistogram1D>(NUM_CHANNELS); + channelRawWaveform = new ArrayList<IHistogram1D>(NUM_CHANNELS); + clusterEnergyPlot = new ArrayList<IHistogram1D>(NUM_CHANNELS); + channelTimeVsEnergyPlot = new ArrayList<IHistogram2D>(NUM_CHANNELS); + + // Create the histograms for single channel energy and time + // distribution. + for(int ii = 0; ii < NUM_CHANNELS; ii++) { + // The above instruction is a terrible hack, just to fill + // the arrayList with all the elements. They'll be initialized + // properly during the event readout, Since we want to account + // for possibly different raw waveform dimensions! + + //Get the x and y indices for the current channel. + int row = EcalMonitoringUtilities.getRowFromHistoID(ii); + int column = EcalMonitoringUtilities.getColumnFromHistoID(ii); + + // Initialize the histograms for the current crystal channel. + channelEnergyPlot.add(aida.histogram1D(detector.getDetectorName() + " : " + + inputCollection + " : Hit Energy : " + column + " " + row + + ": " + ii, 100, -0.2, maxEch)); + channelTimePlot.add(aida.histogram1D(detector.getDetectorName() + " : " + + inputCollection + " : Hit Time : " + column + " " + row + ": " + + ii, 100, 0, 400)); + channelTimeVsEnergyPlot.add(aida.histogram2D(detector.getDetectorName() + + " : " + inputCollection + " : Hit Time Vs Energy : " + column + + " " + row + ": " + ii, 100, 0, 400, 100, -0.2, maxEch)); + channelRawWaveform.add(aida.histogram1D(detector.getDetectorName() + " : " + + inputCollection + " : Hit Energy : " + column + " " + row + ": " + ii)); + clusterEnergyPlot.add(aida.histogram1D(detector.getDetectorName() + " : " + + inputCollection + " : Cluster Energy : " + column + " " + row + + ": " + ii, 100, -0.2, maxEch)); + + // Note that no raw waveform has yet been read for this + // crystal/channel. + windowRaw[ii] = 1; + isFirstRaw[ii] = true; + } + + // Define the plot region that will display the single channel + // plots in the monitoring application. + plotterFactory = aida.analysisFactory().createPlotterFactory("Single Channel"); + plotter = plotterFactory.create("Single Channel"); + pstyle = this.createDefaultStyle(); + plotter.setTitle(""); + plotter.createRegions(2,2); + + // Define the first plot region. + pstyle.xAxisStyle().setLabel(HIT_ENERGY_TITLE); + pstyle.yAxisStyle().setLabel(NO_TITLE); + plotter.region(0).plot(channelEnergyPlot.get(0), pstyle); + + // Define the second plot region. + pstyle.xAxisStyle().setLabel(HIT_TIME_TITLE); + pstyle.yAxisStyle().setLabel(NO_TITLE); + plotter.region(1).plot(channelTimePlot.get(0), pstyle); + + // Define the third plot region; this encompasses the time vs. + // energy plots. + pstyle.xAxisStyle().setLabel(HIT_TIME_TITLE); + pstyle.yAxisStyle().setLabel(HIT_ENERGY_TITLE); + plotter.region(2).plot(channelTimeVsEnergyPlot.get(0), pstyle); + + + + + // Define the fourth plot region; this encompasses the raw + // wave form plots. + pstyle.xAxisStyle().setLabel(RAW_WAVEFORM_TITLE); + pstyle.yAxisStyle().setLabel(NO_TITLE); + pstyle.dataStyle().fillStyle().setColor(SIGNAL_DATA_STYLE_COLOR); + pstyle.dataStyle().markerStyle().setColor(SIGNAL_DATA_STYLE_COLOR); + pstyle.dataStyle().errorBarStyle().setVisible(false); + plotter.region(3).plot(channelRawWaveform.get(0), pstyle); + + + // Display the plot region. + plotter.show(); + + // Set the time tracker variables. + lastEventTime = 0; + } + + /** + * Initializes the <code>Viewer</code> for the single event display. + * If a configuration file is available, then it is used by the + * <code>Viewer</code> to display hardware configuration mappings. + * Otherwise, this is excluded. + */ + @Override + public void startOfData() { + // Check if the configuration mapping file exists. + File config = new File("ecal-mapping-config.csv"); + + // If the file exists, load the viewer that will display it. + if(config.exists() && config.canRead()) { + // Account for IO read errors. Only load this version if + // the data file can be read successfully. + try { viewer = new PDataEventViewer(config.getAbsolutePath()); } + + // Otherwise, open the regular version. + catch (IOException e) { viewer = new PEventViewer(); } + } + + // If the file is not present, then just load the normal version. + else { viewer = new PEventViewer(); } + + // Set the viewer properties. + viewer.setScaleMinimum(minEch); + viewer.setScaleMaximum(maxEch); + viewer.addCrystalListener(this); + + // Make the Viewer object visible. + viewer.setVisible(true); + } + + /** + * Hides the single event display and disposes it from memory. + */ + @Override + public void endOfData() { + viewer.setVisible(false); + viewer.dispose(); + } + + @Override + public void process(EventHeader event){ + // Check whether enough time has passed to perform an update + // on the event display. + boolean update = false; + long currentTime = System.currentTimeMillis() / 1000; + if((currentTime - lastEventTime) > eventRefreshRate){ + lastEventTime = currentTime; + update = true; + } + + // If an update should be made, perform the update. + if(update && resetOnUpdate) { viewer.resetDisplay(); } + + // If the event has calorimeter hit objects... + if(event.hasCollection(CalorimeterHit.class, inputCollection)) { + // Get the list of calorimeter hits. + List<CalorimeterHit> hits = event.get(CalorimeterHit.class, inputCollection); + + // For each of the calorimeter hits... + for (CalorimeterHit hit : hits) { + // Get the x and y indices for the current hit. + int ix = hit.getIdentifierFieldValue("ix"); + int iy = hit.getIdentifierFieldValue("iy"); + + if (iy != 0 && ix != 0) { + // Get the histogram index for the hit. + int id = EcalMonitoringUtilities.getHistoIDFromRowColumn(iy, ix); + + // If the hit has energy, populate the plots. + if(hit.getCorrectedEnergy() > 0) { + channelEnergyPlot.get(id).fill(hit.getCorrectedEnergy()); + channelTimePlot.get(id).fill(hit.getTime()); + channelTimeVsEnergyPlot.get(id).fill(hit.getTime(), hit.getCorrectedEnergy()); + } + + // If an update to the event display should be + // performed, give it the hits. + if(update) { viewer.addHit(hit); } + } + } + } + + // If there are clusters in the event... + if (event.hasCollection(Cluster.class, clusterCollection)) { + // Get the list of clusters. + List<Cluster> clusters = event.get(Cluster.class, clusterCollection); + + // Iterate over the clusters and add them to the event + // display if appropriate. + for (Cluster cluster : clusters) { + // Get the ix and iy indices for the seed. + int ix = cluster.getCalorimeterHits().get(0).getIdentifierFieldValue("ix"); + int iy = cluster.getCalorimeterHits().get(0).getIdentifierFieldValue("iy"); + + // Get the histogram index for the hit. + int id = EcalMonitoringUtilities.getHistoIDFromRowColumn(iy, ix); + + // Add the cluster energy to the plot. + if(cluster.getEnergy() > 0.0) { + clusterEnergyPlot.get(id).fill(cluster.getEnergy()); + } + + // If an update is needed, add the cluster to the viewer. + if(update) { viewer.addCluster(cluster); } + } + } + + + // Plot the raw waveform only if raw tracker hit exist in the + // event. + if (event.hasCollection(RawTrackerHit.class, inputCollectionRaw)){ + // Get the list of raw tracker hits. + List<RawTrackerHit> hits = event.get(RawTrackerHit.class, inputCollectionRaw); + + // Process each raw tracker hit. + for (RawTrackerHit hit : hits) { + // Get the x and y indices for the hit. + int ix = hit.getIdentifierFieldValue("ix"); + int iy = hit.getIdentifierFieldValue("iy"); + + if(iy != 0 && ix != 0) { + if(!ECalUtils.isInHole(iy, ix)) { + // Get the crystal ID for the current hit. + int id = ECalUtils.getHistoIDFromRowColumn(iy, ix); + + // The window is length is not known by default. + // If this is the first hit, read the window + // length and initialize the plot. + if(isFirstRaw[id]) { + // Note that this plot is initialized. + isFirstRaw[id] = false; + + // Set the waveform array. + windowRaw[id] = hit.getADCValues().length; + + // Initialize the waveform plot. + channelRawWaveform.set(id, aida.histogram1D(detector.getDetectorName() + + " : " + inputCollectionRaw + " : Raw Waveform : " + ix + " " + + iy + ": " + id, windowRaw[id], -0.5 * ECalUtils.ecalReadoutPeriod, + (-0.5 + windowRaw[id]) * ECalUtils.ecalReadoutPeriod)); + } + + // If the plot should be updated, do so. + if(update) { + channelRawWaveform.get(id).reset(); + for (int jj = 0; jj < windowRaw[id]; jj++) { + channelRawWaveform.get(id).fill(jj * ECalUtils.ecalReadoutPeriod, + hit.getADCValues()[jj] * ECalUtils.adcResolution * 1000); + } + double[] result = ECalUtils.computeAmplitude(hit.getADCValues(), windowRaw[id], pedSamples); + channelRawWaveform.get(id).setTitle("Ampl: " + String.format("%.2f", result[0]) + + " mV , ped : " + String.format("%.2f", result[1]) + " " + + String.format("%.2f", result[2]) + " ADC counts"); + plotter.region(3).refresh(); + } + } + } + } + } + + + // Update the single event display. + if(update) { viewer.updateDisplay(); } + } + + @Override + public void actionPerformed(ActionEvent ae) { } + + @Override + public void crystalActivated(CrystalEvent e) { } + + @Override + public void crystalDeactivated(CrystalEvent e) { } + + /** + * Updates the monitoring plots for the crystal that was clicked. + */ + @Override + public void crystalClicked(CrystalEvent e) { + // Get the crystal that was clicked in the LCSim coordinate system. + Point ecalPoint = Viewer.toEcalPoint(e.getCrystalID()); + + // Make sure that the clicked crystal is valid. Necessary?? + if((ecalPoint.x != 0) && (ecalPoint.y != 0)) + if (!EcalMonitoringUtilities.isInHole(ecalPoint.y, ecalPoint.x)) { + // Get the crystal ID. + int id = EcalMonitoringUtilities.getHistoIDFromRowColumn(ecalPoint.y, ecalPoint.x); + + // Clear and replot region 0 for the new crystal. + plotter.region(0).clear(); + pstyle.xAxisStyle().setLabel(HIT_ENERGY_TITLE); + pstyle.yAxisStyle().setLabel(NO_TITLE); + plotter.region(0).plot(channelEnergyPlot.get(id), pstyle); + + // Clear and replot region 1 for the new crystal. + plotter.region(1).clear(); + pstyle.xAxisStyle().setLabel(HIT_TIME_TITLE); + pstyle.yAxisStyle().setLabel(NO_TITLE); + plotter.region(1).plot(channelTimePlot.get(id), pstyle); + + // Clear and replot region 2 for the new crystal. + plotter.region(2).clear(); + pstyle.xAxisStyle().setLabel(HIT_TIME_TITLE); + pstyle.yAxisStyle().setLabel(HIT_ENERGY_TITLE); + plotter.region(2).plot(channelTimeVsEnergyPlot.get(id), pstyle); + + + // Process and plot the region 3 plot. + if(!isFirstRaw[id]) { + pstyle.yAxisStyle().setLabel(SIGNAL_AMPLITUDE_TITLE); + pstyle.xAxisStyle().setLabel(SIGNAL_TIME_TITLE); + pstyle.dataStyle().fillStyle().setColor(SIGNAL_DATA_STYLE_COLOR); + pstyle.dataStyle().markerStyle().setColor(SIGNAL_DATA_STYLE_COLOR); + pstyle.dataStyle().errorBarStyle().setVisible(false); + } + else { + pstyle.xAxisStyle().setLabel(HIT_ENERGY_TITLE); + pstyle.yAxisStyle().setLabel(""); + } + plotter.region(3).plot(channelRawWaveform.get(id), pstyle); + } + } + + /** + * Initializes the default style for plots. + * @return Returns an <code>IPlotterStyle</code> object that + * represents the default style for plots. + */ + public IPlotterStyle createDefaultStyle() { + IPlotterStyle pstyle = plotterFactory.createPlotterStyle(); + // Set the appearance of the axes. + pstyle.xAxisStyle().labelStyle().setBold(true); + pstyle.yAxisStyle().labelStyle().setBold(true); + pstyle.xAxisStyle().tickLabelStyle().setBold(true); + pstyle.yAxisStyle().tickLabelStyle().setBold(true); + pstyle.xAxisStyle().lineStyle().setColor("black"); + pstyle.yAxisStyle().lineStyle().setColor("black"); + pstyle.xAxisStyle().lineStyle().setThickness(2); + pstyle.yAxisStyle().lineStyle().setThickness(2); + + // Set color settings. + pstyle.dataStyle().fillStyle().setParameter("colorMapScheme", "rainbow"); + pstyle.dataStyle().fillStyle().setParameter("showZeroHeightBins", Boolean.FALSE.toString()); + pstyle.dataStyle().errorBarStyle().setVisible(false); + pstyle.setParameter("hist2DStyle", "colorMap"); + + // Force auto range to zero. + pstyle.yAxisStyle().setParameter("allowZeroSuppression", "false"); + pstyle.xAxisStyle().setParameter("allowZeroSuppression", "false"); + + // Set the title style. + pstyle.titleStyle().textStyle().setFontSize(20); + + // Draw caps on error bars. + pstyle.dataStyle().errorBarStyle().setParameter("errorBarDecoration", (new Float(1.0f)).toString()); + + // Turn off grid lines until explicitly enabled. + pstyle.gridStyle().setVisible(false); + + // Return the style. + return pstyle; + } +}