diff --git a/DataModel/ADCPulse.cpp b/DataModel/ADCPulse.cpp index 84a3e3247..03d5a0d21 100755 --- a/DataModel/ADCPulse.cpp +++ b/DataModel/ADCPulse.cpp @@ -7,9 +7,11 @@ ADCPulse::ADCPulse(int TubeId, double start_time, double peak_time, double baseline, double sigma_baseline, unsigned long area, unsigned short raw_amplitude, double calibrated_amplitude, - double charge) : Hit(TubeId, start_time, charge), + double charge, const std::vector& trace_x, const std::vector& trace_y) : + Hit(TubeId, start_time, charge), start_time_(start_time), peak_time_(peak_time), baseline_(baseline), sigma_baseline_(sigma_baseline), raw_area_(area), - raw_amplitude_(raw_amplitude), calibrated_amplitude_(calibrated_amplitude) + raw_amplitude_(raw_amplitude), calibrated_amplitude_(calibrated_amplitude), + trace_x_(trace_x), trace_y_(trace_y) { } diff --git a/DataModel/ADCPulse.h b/DataModel/ADCPulse.h index 891e840d0..422a7cd81 100755 --- a/DataModel/ADCPulse.h +++ b/DataModel/ADCPulse.h @@ -9,6 +9,7 @@ // ToolAnalysis includes #include "ChannelKey.h" #include "Hit.h" +#include class ADCPulse : public Hit { @@ -22,8 +23,9 @@ class ADCPulse : public Hit { // int TubeId member ADCPulse(int TubeId, double start_time, double peak_time, double baseline, double sigma_baseline, unsigned long raw_area, - unsigned short raw_amplitude, double calibrated_amplitude, - double charge); + unsigned short raw_amplitude, double calibrated_amplitude, double charge, + const std::vector& trace_x = std::vector(), + const std::vector& trace_y = std::vector()); // @brief Returns the start time (ns) of the pulse relative to the // start of its minibuffer @@ -59,6 +61,10 @@ class ADCPulse : public Hit { // (baseline-subtracted) pulse inline double amplitude() const { return calibrated_amplitude_; } + // @brief Returns the x [ns] and y [ADC] points of the "found" pulse (baseline-subtracted and relative to pulse start point) + inline const std::vector& GetTraceXPoints() const { return trace_x_; } + inline const std::vector& GetTraceYPoints() const { return trace_y_; } + template void serialize(Archive& ar, const unsigned int version) { @@ -71,6 +77,10 @@ class ADCPulse : public Hit { ar & raw_area_; ar & raw_amplitude_; ar & calibrated_amplitude_; + if (version > 0) { + ar & trace_x_; + ar & trace_y_; + } } protected: @@ -83,4 +93,11 @@ class ADCPulse : public Hit { unsigned short raw_amplitude_; // ADC double calibrated_amplitude_; // V + + std::vector trace_x_ = {}; // x points of the pulse (start at 0, relative to pulse start) [ns] + std::vector trace_y_ = {}; // y points of the pulse (baseline-subtracted) [ADC] }; + +// (From Andrew Sutton) Need to increment the class version since we added time as a new variable +// the version number ensures backward compatibility when serializing +BOOST_CLASS_VERSION(ADCPulse, 1) diff --git a/UserTools/PhaseIIADCHitFinder/PhaseIIADCHitFinder.cpp b/UserTools/PhaseIIADCHitFinder/PhaseIIADCHitFinder.cpp index da2cfcb8a..761e22ddf 100755 --- a/UserTools/PhaseIIADCHitFinder/PhaseIIADCHitFinder.cpp +++ b/UserTools/PhaseIIADCHitFinder/PhaseIIADCHitFinder.cpp @@ -12,17 +12,18 @@ bool PhaseIIADCHitFinder::Initialise(std::string config_filename, DataModel& dat m_data = &data; // Load the default threshold settings for finding pulses - verbosity = 3; + verbosity = 0; use_led_waveforms = false; pulse_finding_approach = "threshold"; adc_threshold_db = "none"; - default_adc_threshold = 5; + default_adc_threshold = 7; threshold_type = "relative"; - pulse_window_type = "fixed"; + pulse_window_type = "Fixed_2023_Gains"; pulse_window_start_shift = -3; pulse_window_end_shift = 25; adc_window_db = "none"; //Used when pulse_finding_approach="fixed_windows" eventbuilding_mode = false; + mc_waveforms = false; //Load any configurables set in the config file m_variables.Get("verbosity",verbosity); @@ -781,13 +782,28 @@ std::vector PhaseIIADCHitFinder::find_pulses_bywindow( } } + // extract the x and y points of the pulse (subtract off baseline and "zero" the pulse to the pulse start) + std::vector trace_x; + std::vector trace_y; + + double pulse_start_time = wmin * NS_PER_ADC_SAMPLE; + double pulse_baseline = calibrated_minibuffer_data.GetBaseline(); + + for (size_t p = wmin; p <= wmax; ++p) { + double ns_time = p * NS_PER_ADC_SAMPLE; + double val_adc = raw_minibuffer_data.GetSample(p); + trace_x.push_back(ns_time - pulse_start_time); + trace_y.push_back(val_adc - pulse_baseline); + } + // Store the freshly made pulse in the vector of found pulses pulses.emplace_back(channel_key, - ( wmin * NS_PER_SAMPLE )-timing_offset, - (peak_sample * NS_PER_SAMPLE)-timing_offset, + ( wmin * NS_PER_ADC_SAMPLE )-timing_offset, + (peak_sample * NS_PER_ADC_SAMPLE)-timing_offset, calibrated_minibuffer_data.GetBaseline(), calibrated_minibuffer_data.GetSigmaBaseline(), - raw_area, max_ADC, calibrated_amplitude, charge); + raw_area, max_ADC, calibrated_amplitude, charge, + trace_x, trace_y); } return pulses; } @@ -895,13 +911,28 @@ std::vector PhaseIIADCHitFinder::find_pulses_bythreshold( } } + // extract the x and y points of the pulse (subtract off baseline and "zero" the pulse to the pulse start) + std::vector trace_x; + std::vector trace_y; + + double pulse_start_time = pulse_start_sample * NS_PER_ADC_SAMPLE; + double pulse_baseline = calibrated_minibuffer_data.GetBaseline(); + + for (size_t p = pulse_start_sample; p <= pulse_end_sample; ++p) { + double ns_time = p * NS_PER_ADC_SAMPLE; + double val_adc = raw_minibuffer_data.GetSample(p); + trace_x.push_back(ns_time - pulse_start_time); + trace_y.push_back(val_adc - pulse_baseline); + } + // Store the freshly made pulse in the vector of found pulses pulses.emplace_back(channel_key, - ( pulse_start_sample * NS_PER_SAMPLE )-timing_offset, - (peak_sample * NS_PER_SAMPLE)-timing_offset, + ( pulse_start_sample * NS_PER_ADC_SAMPLE )-timing_offset, + (peak_sample * NS_PER_ADC_SAMPLE)-timing_offset, calibrated_minibuffer_data.GetBaseline(), calibrated_minibuffer_data.GetSigmaBaseline(), - raw_area, max_ADC, calibrated_amplitude, charge); + raw_area, max_ADC, calibrated_amplitude, charge, + trace_x, trace_y); } @@ -1052,6 +1083,19 @@ std::vector PhaseIIADCHitFinder::find_pulses_bythreshold( } } + // extract the x and y points of the pulse (subtract off baseline and "zero" the pulse to the pulse start) + std::vector trace_x; + std::vector trace_y; + double pulse_start_time = pulse_start_sample * NS_PER_ADC_SAMPLE; + double pulse_baseline = calibrated_minibuffer_data.GetBaseline(); + + for (size_t p = pulse_start_sample; p <= pulse_end_sample; ++p) { + double ns_time = p * NS_PER_ADC_SAMPLE; + double val_adc = raw_minibuffer_data.GetSample(p); + trace_x.push_back(ns_time - pulse_start_time); + trace_y.push_back(val_adc - pulse_baseline); + } + if(verbosity>v_debug) std::cout << "PhaseIIADCHitFinder: Hit time [ns] " << hit_time * NS_PER_ADC_SAMPLE << std::endl; if (hit_time < 0.0) { @@ -1074,7 +1118,8 @@ std::vector PhaseIIADCHitFinder::find_pulses_bythreshold( ( hit_time * NS_PER_ADC_SAMPLE )-timing_offset, // interpolated hit time calibrated_minibuffer_data.GetBaseline(), calibrated_minibuffer_data.GetSigmaBaseline(), - raw_area, max_ADC, calibrated_amplitude, charge); + raw_area, max_ADC, calibrated_amplitude, charge, + trace_x, trace_y); } } @@ -1174,6 +1219,19 @@ std::vector PhaseIIADCHitFinder::find_pulses_bythreshold( } } + // extract the x and y points of the pulse (subtract off baseline and "zero" the pulse to the pulse start) + std::vector trace_x; + std::vector trace_y; + double pulse_start_time = pulse_start_sample * NS_PER_ADC_SAMPLE; + double pulse_baseline = calibrated_minibuffer_data.GetBaseline(); + + for (size_t p = pulse_start_sample; p <= pulse_end_sample; ++p) { + double ns_time = p * NS_PER_ADC_SAMPLE; + double val_adc = raw_minibuffer_data.GetSample(p); + trace_x.push_back(ns_time - pulse_start_time); + trace_y.push_back(val_adc - pulse_baseline); + } + if (hit_time < 0.0) { // If for some reason the interpolation finds a negative time value (if the pulse is extremely early in the buffer), // default to the peak time (maximum ADC value of the pulse) @@ -1202,7 +1260,8 @@ std::vector PhaseIIADCHitFinder::find_pulses_bythreshold( (hit_time * NS_PER_ADC_SAMPLE)-timing_offset, // interpolated hit time calibrated_minibuffer_data.GetBaseline(), calibrated_minibuffer_data.GetSigmaBaseline(), - raw_area, max_ADC, calibrated_amplitude, charge); + raw_area, max_ADC, calibrated_amplitude, charge, + trace_x, trace_y); } } } else { diff --git a/UserTools/PhaseIIADCHitFinder/README.md b/UserTools/PhaseIIADCHitFinder/README.md index 0b3f413f4..0e8de25b2 100755 --- a/UserTools/PhaseIIADCHitFinder/README.md +++ b/UserTools/PhaseIIADCHitFinder/README.md @@ -1,10 +1,36 @@ # PhaseIIADCHitFinder -PhaseIIADCHitFinder +Hit finding tool for the tank PMTs. The configurable settings and types of pulse integration are listed below, but the main feature of the tool is to identify pulses via: +- With a threshold-style approach (default - used in the EventBuilder and in the MC waveform workflow) +- Search for the maximum peak within the entire buffer (used for Gains calibration with LED data) +- Within a fixed, pre-defined window (used for assessing the pedestal / (dark) noise rates or other relevant analyses) ## Data -Describe any data formats PhaseIIADCHitFinder creates, destroys, changes, or analyzes. E.G. +After identifying a pulse and obtaining the integrated charge [nQ], the pulse timing [ns], and other features of the identified "hit", the tool will populate the ADCPulse class (DataModel/ADCPulse), storing: +- `int` TubeID (PMT channel ID) +- `double` start_time (Returns the start time (ns) of the pulse relative to the start of its minibuffer) +- `double` peak_time (Returns the peak time (ns) of the pulse relative to the start of its minibuffer) +- `double` baseline (Returns the approximate baseline (ADC) used to calibrate the pulse) +- `double` sigma_baseline (Returns the approximate error on the baseline (ADC) used to calibrate the pulse) +- `unsigned long` area (Returns the area (ADC * samples) of the uncalibrated pulse) +- `unsigned short` raw_amplitude (Returns the amplitude (ADC) of the uncalibrated pulse) +- `double` calibrated_amplitude (eturns the amplitude (V) of the calibrated (baseline-subtracted) pulse) +- `double` charge (Returns the charge (nC) of the calibrated (baseline-subtracted) pulse) +- `const std::vector&` trace_x (Returns the x [ns] points of the "found" pulse (baseline-subtracted and relative to pulse start point)) +- `const std::vector&` trace_y (Returns the y [ADC] points of the "found" pulse (baseline-subtracted and relative to pulse start point)) + +into: `std::map>` RecoADCHits + +It will then store the hit charge [nQ] and the hit time [ns] to the store via: `std::map>*` Hits + +The tool will set additional objects to the store: +- ADCThreshold (Threshold used to identify pulses) +- InProgressHits, InProgressRecoADCHits, InProgressHitsAux, InProgressRecoADCHitsAux, NewHitsData (for book-keeping in the event building mode) + +Lastly, for the auxillary channels, it will store the ADCPulse and Hit instead to: +- `std::map>` RecoADCAuxHits +- `std::map>*` AuxHits ## Configuration @@ -16,56 +42,61 @@ UseLEDWaveforms [int]: Specifies whether hits and pulses are found using the 1=Use LED window waveforms, 0 = Use full waveforms. +MCWaveforms [int]: Whether the `PMTWaveformSim` MC waveform generator is being used for MC Hits. + +EventBuilding [int]: Whether this tool is being used in the event building toolchain. + ###### PULSE FINDING TECHNIQUES ######### PulseFindingApproach [string]: String that defines what algorithm is used to find pulses. Possible options: - "threshold": Search for an ADC sample to cross some defined threshold. Threshold + - "threshold": (DEFAULT for event building and MC waveforms) Search for an ADC sample to cross some defined threshold. Threshold is manipulable using DefaultADCThreshold and DefaultThresholdType config variables. - "fixed_window": Fixed windows defined in the WindowIntegrationDB text file are + - "fixed_window": Fixed windows defined in the WindowIntegrationDB text file are treated entirely as pulses. - "full_window": Every waveform is integrated completely and background-subtracted + - "full_window": Every waveform is integrated completely and background-subtracted to form a single pulse object. - "full_window_maxpeak": The maximum peak anywhere in the window is taken as the pulse. + - "full_window_maxpeak": The maximum peak anywhere in the window is taken as the pulse. the pulse is integrated to either side of the max until dropping to - < 10% of the max peak amplitude, then background-subtracted. + < 10% of the max peak amplitude, then background-subtracted. This is + used in the LED analysis (use with find_pulses_bywindow) - "signal_window_maxpeak": The maximum peak anywhere beyond the baseline estimation window + - "signal_window_maxpeak": The maximum peak anywhere beyond the baseline estimation window is taken as the pulse. the pulse is integrated to either side of the max until dropping to < 10% of the max peak amplitude, then background-subtracted. - "NNLS": Uses the NNLS algorithm that will be applied to LAPPD hit reconstruction. + - "NNLS": Uses the NNLS algorithm that will be applied to LAPPD hit reconstruction. Not yet implemented. ###### "threshold" setting configurables ######## -DefaultADCThreshold [int]: Defines the default threshold to be used for any PMT +- DefaultADCThreshold [int]: Defines the default threshold to be used for any PMT that does not have a channel_key, threshold value defined in the ADCThresholdDB file. -DefaultThresholdType [string]: Marks whether the given threshold values in the DB value are +- DefaultThresholdType [string]: Marks whether the given threshold values in the DB value are relative to the calibrated baseline ("relative"), or absolute ADC counts ("absolute"). -PulseWindowType [string]: If using "threshold" on pulse finding approach, this toggle defines +- PulseWindowType [string]: If using "threshold" on pulse finding approach, this toggle defines how the pulse windows in a waveform are found. There are three options: fixed window ("fixed"), dynamic window where the pulse windows are defined by crossing and un-crossing threshold ("dynamic"), and ("Fixed_2023_Gains") which implements the same integration window used in the 2023 Gains calibration where the pulse windows are defined by crossing and un-crossing the baseline. -PulseWindowStart [int]: Start of pulse window relative to when adc trigger threshold +- PulseWindowStart [int]: Start of pulse window relative to when adc trigger threshold was crossed. Only used when PulseFindingApproach==threshold and PulseWindowType==fixed. Unit is ADC samples. -PulseWindowEnd [int]: End of pulse window relative to when adc trigger threshold +- PulseWindowEnd [int]: End of pulse window relative to when adc trigger threshold was crossed. Only used when PulseFindingApproach==threshold and PulseWindowType==fixed. Unit is ADC samples. -ADCThresholdDB [string]: Absolute path to a CSV file where each line is the pair +- ADCThresholdDB [string]: Absolute path to a CSV file where each line is the pair channel_key (int), threshold (int). For any channel_key,threshold pair defined in the config file, these thresholds will be used in place of the default ADC threshold. Thresholds define the ADC threshold for each PMT used when pulse-finding. @@ -77,5 +108,103 @@ WindowIntegrationDB [string]: Absolute path to a CSV file where each line has th A channel can be given multiple integration windows. Windows are in ADC samples. A single pulse will be calculated for each integration window defined. + +## Example of working configurations +##### EventBuilding ##### ``` +verbosity 0 + +UseLEDWaveforms 0 + +PulseFindingApproach threshold +PulseWindowType Fixed_2023_Gains +DefaultADCThreshold 7 +DefaultThresholdType relative + +EventBuilding 1 +MCWaveforms 0 ``` + +##### PrintADCData (obtaining raw traces for pulse analysis) ##### +``` +verbosity 0 + +UseLEDWaveforms 0 + +PulseFindingApproach threshold +PulseWindowType Fixed_2023_Gains +DefaultADCThreshold 7 +DefaultThresholdType relative + +EventBuilding 0 +MCWaveforms 0 +``` + +##### MC ##### +``` +verbosity 0 + +UseLEDWaveforms 0 + +PulseFindingApproach threshold +PulseWindowType Fixed_2023_Gains +DefaultADCThreshold 7 +DefaultThresholdType relative + +EventBuilding 0 +MCWaveforms 1 +``` + +##### Gains ##### +``` +verbosity 0 + +UseLEDWaveforms 1 + +PulseFindingApproach full_window_maxpeak + +EventBuilding 0 +MCWaveforms 0 +``` + +## Tools needed to run this tool successfully +All working configurations must include at minimum the following tools: +``` +LoadGeometry +PhaseIIADCCalibrator +PhaseIIADCHitFinder +``` + +This is executed either: +1. Over RAWData as part of the event building procedure +2. Over ProcessedData that contains the raw waveforms instead of the extracted hits information (no hit finding was ran during the event building to purposely give you the raw waveforms) +3. On MC waveforms generated by the `PMTWaveformSim` tool to give you data-like MC hits + +In all three cases additional tools are needed. + +For 1. (RAWData for event building): +``` +LoadGeometry +LoadRawData (EventBuilder or DataDecoder) or EBLoadRaw (EventBuilderV2) +PMTDataDecoder +TriggerDataDecoder +PhaseIIADCCalibrator +PhaseIIADCHitFinder +``` + +For 2. (ProcessedData with raw waveforms): +``` +LoadGeometry +LoadANNIEEvent +PhaseIIADCCalibrator +PhaseIIADCHitFinder +``` + +For 3. (MC waveforms): +``` +LoadGeometry +LoadWCSim +PMTWaveformSim +PhaseIIADCHitFinder +``` +