28 #include "art_root_io/TFileService.h"
29 #include "art_root_io/TFileDirectory.h"
30 #include "art/Framework/Core/ModuleMacros.h"
31 #include "art/Framework/Core/SharedProducer.h"
32 #include "art/Framework/Core/ProcessingFrame.h"
33 #include "art/Framework/Principal/Run.h"
34 #include "art/Framework/Principal/Event.h"
35 #include "art/Framework/Principal/Handle.h"
36 #include "art/Persistency/Common/PtrMaker.h"
37 #include "canvas/Persistency/Common/Assns.h"
38 #include "canvas/Persistency/Common/Ptr.h"
39 #include "canvas/Utilities/InputTag.h"
40 #include "canvas/Utilities/Exception.h"
41 #include "cetlib_except/exception.h"
42 #include "messagefacility/MessageLogger/MessageLogger.h"
43 #include "fhiclcpp/types/TableAs.h"
44 #include "fhiclcpp/types/OptionalAtom.h"
45 #include "fhiclcpp/types/Atom.h"
67 namespace icarus {
class PMTWaveformBaselinesFromChannelData; }
220 Name{
"AcceptedSampleRangeRMS" },
222 "The range of samples accepted for baseline, expressed in number of"
223 " RMS units from the first baseline estimation"
228 Name{
"ExcessSampleLimit" },
230 "If there is this number of samples outside the RMS limit,"
231 " the waveform is not used"
239 Name{
"OpticalWaveforms" },
240 Comment{
"label of input digitized optical waveform data product" }
244 Name{
"PretriggerBufferSize" },
245 Comment{
"the number of samples in the pre-trigger readout buffer" }
249 Name{
"PMTconfigurationTag" },
250 Comment{
"PMT readout configuration object" }
254 Name{
"PretriggerBufferFractionForBaseline" },
256 {
"fraction of the pretrigger buffer used for baseline determination" },
261 Name{
"ExcludeSpillTimeIfMoreThan" },
263 "do not include the waveform at spill time"
264 " if there are at least this number of off-spill PMT waveforms"
266 std::numeric_limits<unsigned int>::max()
269 fhicl::TableAs<opdet::SharedWaveformBaseline::Params_t, AlgoConfig>
272 Name{
"AlgoParams" },
273 Comment{
"baseline algorithm parameters" }
277 Name{
"PlotBaselines" },
278 Comment{
"produce plots on the extracted baseline" },
283 Name{
"BaselineTimeAverage" },
284 Comment{
"binning of the baseline profile vs. time [s]" },
290 Name{
"OutputCategory" },
291 Comment{
"tag of the module output to console via message facility" },
292 "PMTWaveformBaselinesFromChannelData"
305 (
Parameters const& config, art::ProcessingFrame
const&);
312 virtual void beginJob(art::ProcessingFrame
const& frame)
override;
315 virtual void beginRun(art::Run& run, art::ProcessingFrame
const&)
override;
319 (art::Event& event, art::ProcessingFrame
const&)
override;
322 virtual void endJob(art::ProcessingFrame
const& frame)
override;
384 (std::vector<raw::OpDetWaveform const*>& waveforms,
double time)
const;
391 void setupPlots(art::ProcessingFrame
const& frame);
398 static std::vector<std::vector<raw::OpDetWaveform const*>>
groupByChannel
399 (std::vector<raw::OpDetWaveform>
const& waveforms);
427 template <
typename Vect,
typename Index =
typename Vect::
size_type>
428 class VectorExpander {
432 using value_type =
typename Vector_t::value_type;
433 using reference =
typename Vector_t::reference;
436 VectorExpander(
Vector_t& v, value_type defaultValue = value_type{})
437 : fVector{ &v }, fDefValue{ std::move(defaultValue) } {}
440 reference operator() (
Index_t index)
const
442 auto const ix =
static_cast<typename Vector_t::size_type
>(index);
443 if (fVector->size() <= ix) fVector->resize(ix + 1, fDefValue);
444 return (*fVector)[ix];
450 value_type fDefValue;
462 (
Parameters const& config, art::ProcessingFrame
const& frame)
463 : art::SharedProducer(config)
466 , fExcludeSpillTimeIfMoreThan(config().ExcludeSpillTimeIfMoreThan())
467 , fPretriggerSamples(config().PretriggerBufferSize().value_or(0U))
468 , fPMTconfigTag(config().PMTconfigurationTag())
469 , fSampleFraction(config().PretriggerBufferFractionForBaseline())
470 , fAlgoParams(config().AlgoParams())
471 , fPlotBaselines(config().PlotBaselines())
472 , fBaselineTimeAverage(config().BaselineTimeAverage())
478 .OpticalClockPeriod()
485 serializeExternal<art::InEvent>(std::string{
"TFileService" });
487 async<art::InEvent>();
493 if (fPMTconfigTag.has_value() == (fPretriggerSamples != 0)) {
494 throw art::Exception(art::errors::Configuration)
495 <<
"Exactly one between parameters '"
496 << config().PMTconfigurationTag.name() <<
"' and '"
497 << config().PretriggerBufferFractionForBaseline.name()
498 <<
"' (with a positive value) must be specified!\n";
506 log <<
"Using the standard (median) algorithm, waveform by waveform, on '"
507 << fOpDetWaveformTag.encode() <<
"'";
513 consumes<std::vector<raw::OpDetWaveform>>(fOpDetWaveformTag);
514 if (fPMTconfigTag) consumes<sbn::PMTconfiguration>(*fPMTconfigTag);
519 produces<std::vector<icarus::WaveformBaseline>>();
520 produces<std::vector<icarus::WaveformRMS>>();
521 produces<art::Assns<icarus::WaveformBaseline, raw::OpDetWaveform>>();
522 produces<art::Assns<icarus::WaveformRMS, raw::OpDetWaveform>>();
529 (art::ProcessingFrame
const& frame)
535 if (fPlotBaselines) setupPlots(frame);
542 (art::Run& run, art::ProcessingFrame
const&)
546 fPretriggerSamples = getPretriggerBuffer
549 assert(fPretriggerSamples > 0U);
551 fAlgoParams.nSample = std::max(
553 static_cast<unsigned int>(std::round(fSampleFraction * fPretriggerSamples))
559 log << run.id() <<
": baseline algorithm configuration:\n";
560 fAlgoParams.dump(log,
" - ");
568 (art::Event& event, art::ProcessingFrame
const& frame)
579 <<
event.id() <<
" trigger time: " << triggerTime;
584 auto const& waveformHandle
585 =
event.getValidHandle<std::vector<raw::OpDetWaveform>>(fOpDetWaveformTag);
586 auto const& waveforms = *waveformHandle;
594 double const eventTime =
static_cast<double>(
event.time().timeHigh())
595 + static_cast<double>(event.time().timeHigh()) * 1
e-9;
597 std::vector<BaselineInfo_t> channelBaselines;
598 VectorExpander baselineForChannel { channelBaselines };
600 auto waveformsByChannel = groupByChannel(waveforms);
602 for (
auto const& [ channel, waveforms ]:
util::enumerate(waveformsByChannel))
604 if (waveforms.empty())
continue;
607 <<
"Processing " << waveforms.size() <<
" waveforms for channel "
613 if (waveforms.size() >= fExcludeSpillTimeIfMoreThan) {
615 unsigned int const nExcluded
616 = removeWaveformsAround(waveforms, triggerTime.value());
617 if (nExcluded > 0U) {
619 <<
"Removed " << nExcluded <<
"/" << (waveforms.size() + nExcluded)
620 <<
" waveforms at trigger time " << triggerTime;
629 = sharedWaveformBaselineAlgo(waveforms);
632 <<
"Channel " << channel <<
": baseline " << baseline.
baseline
633 <<
" ADC# from " << baseline.
nSamples <<
" samples in "
634 << baseline.
nWaveforms <<
"/" << waveforms.size()
635 <<
" waveforms; found RMS=" << baseline.
RMS <<
" ADC#";
637 auto const chIndex =
static_cast<std::size_t
>(channel);
638 baselineForChannel(chIndex) = { baseline.
baseline, baseline.
RMS };
640 if (fHBaselines) fHBaselines->Fill(
double(channel), baseline.
baseline);
641 if (chIndex < fBaselinesVsTime.size())
642 fBaselinesVsTime[chIndex].emplace_back(eventTime, baseline.
baseline);
650 std::vector<icarus::WaveformBaseline> baselines;
651 baselines.reserve(waveforms.size());
652 art::Assns<icarus::WaveformBaseline, raw::OpDetWaveform> baselineToWaveforms;
653 art::PtrMaker<icarus::WaveformBaseline>
const makeBaselinePtr(event);
655 std::vector<icarus::WaveformRMS> RMSs;
656 RMSs.reserve(waveforms.size());
657 art::Assns<icarus::WaveformRMS, raw::OpDetWaveform> RMStoWaveforms;
658 art::PtrMaker<icarus::WaveformRMS>
const makeRMSPtr(event);
660 for (
auto const& [ iWaveform, waveform ]:
util::enumerate(waveforms)) {
663 = channelBaselines[
static_cast<std::size_t
>(waveform.ChannelNumber())];
665 art::Ptr<raw::OpDetWaveform>
const waveformPtr{ waveformHandle, iWaveform };
667 baselines.push_back(baselineInfo.
baseline);
668 RMSs.push_back(baselineInfo.
RMS);
669 baselineToWaveforms.addSingle(makeBaselinePtr(iWaveform), waveformPtr);
670 RMStoWaveforms.addSingle(makeRMSPtr(iWaveform), waveformPtr);
678 std::make_unique<std::vector<icarus::WaveformBaseline>>
679 (std::move(baselines))
682 (std::make_unique<std::vector<icarus::WaveformRMS>>(std::move(RMSs)));
684 std::make_unique<art::Assns<icarus::WaveformBaseline, raw::OpDetWaveform>>
685 (std::move(baselineToWaveforms))
688 std::make_unique<art::Assns<icarus::WaveformRMS, raw::OpDetWaveform>>
689 (std::move(RMStoWaveforms))
698 (art::ProcessingFrame
const& frame)
701 if (fPlotBaselines) buildBaselineGraphs(frame);
708 (art::ProcessingFrame
const& frame)
711 auto const&
tfs = *(frame.serviceHandle<art::TFileService>());
712 auto const& geom = *(frame.serviceHandle<
geo::Geometry>()->provider());
714 fNPlotChannels = geom.NOpChannels();
716 fHBaselines =
tfs.make<TH2F>(
718 "PMT baseline;channel;baseline per channel [ / 8 ADC ]",
719 fNPlotChannels, 0.0, double(fNPlotChannels),
720 256, 13312.0, 15360.0
727 fBaselinesVsTime.resize(fNPlotChannels);
740 throw art::Exception{ art::errors::Unknown }
741 <<
"No boards found in the PMT configuration!\n";
743 else if (prebuffer.
max() > prebuffer.
min()) {
744 throw art::Exception{ art::errors::Unknown }
745 <<
"Found different pre-trigger readout buffer sizes between "
746 << prebuffer.
min() <<
" and " << prebuffer.
max() <<
" ticks.\n";
748 return prebuffer.
min();
753 std::vector<std::vector<raw::OpDetWaveform const*>>
755 (std::vector<raw::OpDetWaveform>
const& waveforms)
758 std::vector<std::vector<raw::OpDetWaveform const*>> groups;
759 VectorExpander groupForChannel { groups };
762 groupForChannel(waveform.ChannelNumber()).push_back(&waveform);
771 (std::vector<raw::OpDetWaveform const*>& waveforms,
double time)
const
776 double const tickDuration
777 = detinfo::timescales::electronics_time::interval_t{
fOpticalTick }.value();
779 auto const hasTriggerTime
782 double const relTime = time - waveform->TimeStamp();
783 return (relTime >= 0.0) && (relTime < (waveform->size() * tickDuration));
787 = std::remove_if(waveforms.begin(), waveforms.end(), hasTriggerTime);
789 unsigned int const nRemoved =
std::distance(iLast, waveforms.end());
790 waveforms.erase(iLast, waveforms.end());
799 (art::ProcessingFrame
const& frame)
802 auto&
tfs = *(frame.serviceHandle<art::TFileService>());
804 art::TFileDirectory graphDir =
tfs.mkdir(
"graphs",
"Baseline vs. time");
805 art::TFileDirectory profileDir
806 =
tfs.mkdir(
"profiles",
"Baseline profiles vs. time");
808 for (
auto const channel:
util::counter(fBaselinesVsTime.size())) {
810 auto& timeAndBaselines = fBaselinesVsTime[channel];
811 if (timeAndBaselines.empty())
continue;
815 std::sort(timeAndBaselines.begin(), timeAndBaselines.end());
818 auto*
const graph = graphDir.makeAndRegister<TGraph>(
820 "PMT channel #" +
std::to_string(channel) +
": baseline vs. run time",
821 timeAndBaselines.size()
823 assert(graph->GetXaxis());
824 assert(graph->GetYaxis());
825 graph->GetXaxis()->SetTitle(
"event time");
826 graph->GetYaxis()->SetTitle(
"baseline [ ADC ]");
830 double const startTime = timeAndBaselines.front().first;
831 double const totalTime = timeAndBaselines.back().first - startTime;
832 auto const nBins = std::max(1U,
833 static_cast<unsigned int>(std::ceil(totalTime / fBaselineTimeAverage))
835 double const endTime = startTime + nBins * fBaselineTimeAverage;
837 auto*
const profile = profileDir.make<TProfile>(
839 (
"PMT channel #" +
std::to_string(channel) +
": baseline vs. run time")
841 nBins, startTime, endTime
843 assert(profile->GetXaxis());
844 assert(profile->GetYaxis());
845 profile->GetXaxis()->SetTitle(
846 (
"event time [ / " +
std::to_string(fBaselineTimeAverage) +
" s ]")
849 profile->GetYaxis()->SetTitle(
"average baseline [ ADC ]");
852 auto const [ time,
baseline ] = data;
Data_t max() const
Returns the accumulated maximum, or a very small number if no values.
bool has_data() const
Returns whether at least one datum has been added.
This_t & add(Data_t value)
Include a single value in the statistics.
Definition of util::enumerate().
unsigned int preTriggerTicks() const
Ticks in the waveform before the trigger.
Classes gathering simple statistics.
unsigned int Index_t
Type to denote the index of the flag.
auto enumerate(Iterables &&...iterables)
Range-for loop helper tracking the number of iteration.
Interface to detinfo::DetectorClocks.
Keeps track of the minimum and maximum value we observed.
Information from the configuration of a V1730 PMT readout board.
Access the description of detector geometry.
Data_t min() const
Returns the accumulated minimum, or a very large number if no values.
auto counter(T begin, T end)
Returns an object to iterate values from begin to end in a range-for loop.
double distance(geo::Point_t const &point, CathodeDesc_t const &cathode)
Returns the distance of a point from the cathode.
daq::details::BoardSetup_t convert(DaqDecoderICARUSPMT::BoardSetupConfig const &config)
Special function fhicl::TableAs uses to convert BoardSetupConfig.
BEGIN_PROLOG vertical distance to the surface Name
Information from the configuration of PMT readout.
Test of util::counter and support utilities.
The geometry of one entire detector, as served by art.
An interval (duration, length, distance) between two quantity points.
physics producers discrimopdaq OpticalWaveforms
std::string to_string(WindowPattern const &pattern)
Dimensioned variables representing space or time quantities.
A class exposing an upgraded interface of detinfo::DetectorClocksData.
Data types for detinfo::DetectorTimings.
art::ServiceHandle< art::TFileService > tfs
recob::tracking::Vector_t Vector_t
Class containing configuration for PMT readout.
timescale_traits< ElectronicsTimeCategory >::time_point_t electronics_time
A point in time on the electronics time scale.
art framework interface to geometry description
std::vector< sbn::V1730Configuration > boards
Configuration of all PMT readout boards.
Class containing configuration for a V1730 board.