Author: [log in to unmask] Date: Tue Nov 25 20:26:14 2014 New Revision: 1596 Log: Add optional output file for generated calibration data. Make fit optional and if disabled use the histogram's mean and sigma instead. Minor refactoring of code into separate methods. Modified: java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/EcalCalibrationsDriver.java Modified: java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/EcalCalibrationsDriver.java ============================================================================= --- java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/EcalCalibrationsDriver.java (original) +++ java/trunk/ecal-recon/src/main/java/org/hps/recon/ecal/EcalCalibrationsDriver.java Tue Nov 25 20:26:14 2014 @@ -7,10 +7,12 @@ import hep.aida.IFunctionFactory; import hep.aida.IHistogram1D; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collections; -import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -34,6 +36,10 @@ * This Driver will generate a {@link org.hps.conditions.EcalCalibration} collection * from the ADC value distributions of raw ECAL data. It may optionally insert this * information into the conditions database using the file's run number. + * + * Currently, it uses every ADC value for the distribution, but filtering should probably + * be added to exclude hits above a certain threshold. + * * @author Jeremy McCormick <[log in to unmask]> */ public class EcalCalibrationsDriver extends Driver { @@ -41,21 +47,58 @@ EcalConditions ecalConditions = null; DatabaseConditionsManager conditionsManager = null; AIDA aida = AIDA.defaultInstance(); + IFunctionFactory functionFactory = aida.analysisFactory().createFunctionFactory(null); + IFitFactory fitFactory = aida.analysisFactory().createFitFactory(); boolean loadCalibrations = false; + boolean performFit = true; + File outputFile = null; Set<Integer> runs = new HashSet<Integer>(); + /** + * Set whether to automatically load the conditions into the database. + * @param loadCalibrations True to load conditions into the database. + */ public void setLoadCalibrations(boolean loadCalibrations) { this.loadCalibrations = loadCalibrations; } - public void detectorChanged(Detector detector) { + /** + * Set whether to perform a function fit to get the mean and sigma values. + * @param performFit True to perform a function fit of the histogram. + */ + public void setPerformFit(boolean performFit) { + this.performFit = performFit; + } + + /** + * Set an output file path for writing the calibration data. + * @param outputFileName The path to the output file. + */ + public void setOutputFileName(String outputFileName) { + if (outputFileName == null) { + throw new IllegalArgumentException("The outputFileName argument is null."); + } + outputFile = new File(outputFileName); + } + + /** + * Initialize this Driver when conditions change is triggered. + */ + @Override + public void detectorChanged(Detector detector) { conditionsManager = DatabaseConditionsManager.getInstance(); ecalConditions = conditionsManager.getCachedConditions(EcalConditions.class, TableConstants.ECAL_CONDITIONS).getCachedData(); + + // Create a histogram for every ECAL channel. for (EcalChannel channel : ecalConditions.getChannelCollection().getObjects()) { aida.histogram1D("ECAL Channel " + channel.getChannelId(), 300, 0, 300.); } } + /** + * Process the event data, filling the channel histograms from the ADC values. + */ + @Override public void process(EventHeader event) { runs.add(event.getRunNumber()); if (event.hasCollection(RawTrackerHit.class, "EcalReadoutHits")) { @@ -72,6 +115,11 @@ } } + /** + * End of data hook, which will get the mean and sigma for the ADC distributions. + * It may optionally write these to a file and push the values into the database. + */ + @Override public void endOfData() { if (runs.size() == 0) { @@ -79,29 +127,35 @@ } List<Integer> runList = new ArrayList<Integer>(runs); Collections.sort(runList); - - IFunctionFactory functionFactory = aida.analysisFactory().createFunctionFactory(null); - IFitFactory fitFactory = aida.analysisFactory().createFitFactory(); - + EcalCalibrationCollection calibrations = new EcalCalibrationCollection(); TableMetaData tableMetaData = conditionsManager.findTableMetaData(TableConstants.ECAL_CALIBRATIONS); calibrations.setTableMetaData(tableMetaData); + // Loop over all ECAL channels. for (EcalChannel channel : ecalConditions.getChannelCollection().getObjects()) { + + // Get the histogram with ADC distribution for this channel. IHistogram1D histogram = aida.histogram1D("ECAL Channel " + channel.getChannelId()); - IFunction function = functionFactory.createFunctionByName("Gaussian", "G"); - IFitter fitter = fitFactory.createFitter("chi2", "jminuit"); - double[] parameters = new double[3]; - parameters[0] = histogram.maxBinHeight(); - parameters[1] = histogram.mean(); - parameters[2] = histogram.rms(); - function.setParameters(parameters); - IFitResult fitResult = fitter.fit(histogram, function); int channelId = channel.getChannelId(); - - double mean = fitResult.fittedParameter("mean"); - double sigma = fitResult.fittedParameter("sigma"); + + double mean = 0; + double sigma = 0; + + if (performFit) { + // Perform a Gaussian fit and use its mean and sigma. + IFitResult fitResult = doGaussianFit(histogram); + mean = fitResult.fittedParameter("mean"); + sigma = fitResult.fittedParameter("sigma"); + } else { + // Use the histogram's statistics for mean and sigma. + mean = histogram.mean(); + sigma = histogram.rms(); + } + + // Create a new calibration object and add it to the collection, using mean for pedestal + // and sigma for noise. EcalCalibration calibration = new EcalCalibration(channelId, mean, sigma); try { calibrations.add(calibration); @@ -109,27 +163,96 @@ throw new RuntimeException(e); } } - System.out.println(calibrations.toString()); + + // Get the list of field names for the header. + StringBuffer buffer = new StringBuffer(); + for (String fieldName : tableMetaData.getFieldNames()) { + buffer.append(fieldName + " "); + } + buffer.setLength(buffer.length() - 1); + String fieldNames = buffer.toString(); + + // Is there an output file for the calibration data? + if (outputFile != null) { + // Write the calibration data to an output text file. + writeToFile(calibrations, fieldNames); + } else { + // Just print out the information to the console. + System.out.println(fieldNames); + System.out.println(calibrations.toString()); + } + + // Load the calibrations into the conditions database? if (loadCalibrations) { - int collectionId = conditionsManager.getNextCollectionID(TableConstants.ECAL_CALIBRATIONS); - try { - calibrations.setCollectionId(collectionId); - calibrations.insert(); - int runStart = runList.get(0); - int runEnd = runList.get(runList.size() - 1); - ConditionsRecord conditionsRecord = new ConditionsRecord( - calibrations.getCollectionId(), - runStart, - runEnd, - TableConstants.ECAL_CALIBRATIONS, - TableConstants.ECAL_CALIBRATIONS, - "Auto generated by EcalCalibrationsDriver.", - "eng_run"); - conditionsRecord.setTableMetaData(conditionsManager.findTableMetaData(TableConstants.CONDITIONS_RECORD)); - conditionsRecord.insert(); - } catch (ConditionsObjectException | SQLException e) { - throw new RuntimeException(e); - } - } + loadCalibrations(runList, calibrations); + } + } + + /** + * Write the calibration data to an output file. + * @param calibrations The calibration data which is record delimited by new lines. + * @param fieldNames The list of field names as a single string. + */ + private void writeToFile(EcalCalibrationCollection calibrations, String fieldNames) { + FileWriter writer = null; + try { + writer = new FileWriter(outputFile); + writer.write(fieldNames); + writer.write(calibrations.toString()); + } catch (IOException e) { + throw new RuntimeException("There was a problem writing to the output file.", e); + } finally { + if (writer != null) { + try { + writer.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + /** + * Load the calibration data into the conditions database. + * @param runList The list of runs processed in the job. + * @param calibrations The collection of calibration objects. + */ + private void loadCalibrations(List<Integer> runList, EcalCalibrationCollection calibrations) { + int collectionId = conditionsManager.getNextCollectionID(TableConstants.ECAL_CALIBRATIONS); + try { + calibrations.setCollectionId(collectionId); + calibrations.insert(); + int runStart = runList.get(0); + int runEnd = runList.get(runList.size() - 1); + ConditionsRecord conditionsRecord = new ConditionsRecord( + calibrations.getCollectionId(), + runStart, + runEnd, + TableConstants.ECAL_CALIBRATIONS, + TableConstants.ECAL_CALIBRATIONS, + "Auto generated by EcalCalibrationsDriver.", + "eng_run"); + conditionsRecord.setTableMetaData(conditionsManager.findTableMetaData(TableConstants.CONDITIONS_RECORD)); + conditionsRecord.insert(); + } catch (ConditionsObjectException | SQLException e) { + throw new RuntimeException(e); + } + } + + /** + * Do a Gaussian fit to calculate the mean and sigma of a histogram. + * @param histogram The histogram for the ECAL channel. + * @return The fit result. + */ + private IFitResult doGaussianFit(IHistogram1D histogram) { + IFunction function = functionFactory.createFunctionByName("Gaussian", "G"); + IFitter fitter = fitFactory.createFitter("chi2", "jminuit"); + double[] parameters = new double[3]; + parameters[0] = histogram.maxBinHeight(); + parameters[1] = histogram.mean(); + parameters[2] = histogram.rms(); + function.setParameters(parameters); + IFitResult fitResult = fitter.fit(histogram, function); + return fitResult; } }