All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
MetricProducer_module.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 // Class: MetricProducer
3 // Plugin Type: producer (Unknown Unknown)
4 // File: MetricProducer_module.cc
5 //
6 // Michelle Stancari
7 // August 2022
8 ////////////////////////////////////////////////////////////////////////
9 
10 #include "art/Framework/Core/EDProducer.h"
11 #include "art/Framework/Core/ModuleMacros.h"
12 #include "art/Framework/Principal/Event.h"
13 #include "art/Framework/Principal/Handle.h"
14 #include "art/Framework/Principal/Run.h"
15 #include "art/Framework/Principal/SubRun.h"
16 #include "canvas/Utilities/InputTag.h"
17 #include "fhiclcpp/ParameterSet.h"
18 #include "messagefacility/MessageLogger/MessageLogger.h"
19 
20 #include "sbndaq-artdaq-core/Overlays/Common/BernCRTFragmentV2.hh"
21 #include "sbndaq-artdaq-core/Overlays/Common/CAENV1730Fragment.hh"
22 #include "sbndaq-artdaq-core/Overlays/FragmentType.hh"
23 #include "artdaq-core/Data/Fragment.hh"
24 #include "artdaq-core/Data/ContainerFragment.hh"
25 
28 //#include "sbndaq-artdaq-core/Obj/SBND/pmtSoftwareTrigger.hh"
29 //#include "sbndaq-artdaq-core/Obj/SBND/CRTmetric.hh"
31 
32 #include <memory>
33 #include <iostream>
34 
35 namespace sbndaq {
36  class MetricProducer;
37 }
38 
39 class sbndaq::MetricProducer : public art::EDProducer {
40 public:
41  explicit MetricProducer(fhicl::ParameterSet const& p);
42  // The compiler-generated destructor is fine for non-base
43  // classes without bare pointers or other resource use.
44 
45  // Plugins should not be copied or assigned.
46  MetricProducer(MetricProducer const&) = delete;
47  MetricProducer(MetricProducer&&) = delete;
48  MetricProducer& operator=(MetricProducer const&) = delete;
50 
51  // Required functions.
52  void produce(art::Event& evt) override;
53 
54 private:
55 
56  //CRT Metric variables
57 
58  // fhicl parameters
59  art::Persistable is_persistable_;
62  bool fVerbose;
64 
65  //metric variables
66  int hitsperplane[7];
67 
68  //PMT Metric variables
69 
70  //fhicl parameters
72  double fTriggerTimeOffset; // offset of trigger time, default 0.5 sec
73  double fBeamWindowLength; // beam window length after trigger time, default 1.6us
74  uint32_t fWvfmLength;
75 
76  std::string fBaselineAlgo;
81  double fPEArea; // conversion factor from ADCxns area to PE count
82 
83  // event information
84  int fRun, fSubrun;
85  art::EventNumber_t fEvent;
86 
87  // PD information
88  opdet::sbndPDMapAlg pdMap; // photon detector map
89  std::vector<unsigned int> channelList;
90 
91  // beam window
92  // set in artdaqFragment producer, in reality would be provided by event builder
94  uint32_t beamWindowStart;
95  uint32_t beamWindowEnd;
96 
97  // waveforms
98  uint32_t fTriggerTime;
100  std::vector<std::vector<uint16_t>> fWvfmsVec;
101 
102  // pmt information
103  std::vector<sbnd::trigger::pmtInfo> fpmtInfoVec;
104 
105  //both info
108 
109 
110  void analyze_crt_fragment(artdaq::Fragment & frag);
111  void checkCAEN1730FragmentTimeStamp(const artdaq::Fragment &frag);
112  void analyzeCAEN1730Fragment(const artdaq::Fragment &frag);
113  void estimateBaseline(int i_ch);
114  void SimpleThreshAlgo(int i_ch);
115 
116 };
117 
118 
119 sbndaq::MetricProducer::MetricProducer(fhicl::ParameterSet const& p)
120  : EDProducer{p},
121  is_persistable_(p.get<bool>("is_persistable", true) ? art::Persistable::Yes : art::Persistable::No),
122  fBeamWindowStart(p.get<int>("BeamWindowStart",320000)),
123  fBeamWindowEnd(p.get<int>("BeamWindowEnd",350000)),
124  fVerbose(p.get<bool>("Verbose",false)),
125  fcrt_metrics(p.get<bool>("crt_metrics",true)),
126  fpmt_metrics(p.get<bool>("pmt_metrics",true)),
127  fTriggerTimeOffset(p.get<double>("TriggerTimeOffset", 0.5)),
128  fBeamWindowLength(p.get<double>("BeamWindowLength", 1.6)),
129  fWvfmLength(p.get<uint32_t>("WvfmLength", 5120)),
130  fBaselineAlgo(p.get<std::string>("BaselineAlgo", "estimate")),
131  fInputBaseline(p.get<double>("InputBaseline", 8000)),
132  fInputBaselineSigma(p.get<double>("InputBaselineSigma", 2)),
133  fADCThreshold(p.get<double>("ADCThreshold", 7960)),
134  fFindPulses(p.get<bool>("FindPulses", false)),
135  fPEArea(p.get<double>("PEArea", 66.33))
136  {
137  // Call appropriate produces<>() functions here.
138  produces< sbndaq::CRTmetric >("", is_persistable_);
139 
140  produces< sbnd::trigger::pmtSoftwareTrigger >("", is_persistable_);
141 
142  beamWindowStart = fTriggerTimeOffset*1e9;
143  beamWindowEnd = beamWindowStart + fBeamWindowLength*1000;
144 
145  // build PD map and channel list
146  auto subsetCondition = [](auto const& i)->bool { return i["pd_type"] == "pmt_coated" || i["pd_type"] == "pmt_uncoated"; };
147  auto pmtMap = pdMap.getCollectionFromCondition(subsetCondition);
148  for(auto const& i:pmtMap){
149  channelList.push_back(i["channel"]);
150  }
151  // Call appropriate consumes<>() for any products to be retrieved by this module.
152 }
153 
154 
156 {
157 
158  // load event information
159  fRun = evt.run();
160  fSubrun = evt.subRun();
161  fEvent = evt.event();
162 
163  if (fVerbose) std::cout << "Processing: Run " << fRun << ", Subrun " << fSubrun << ", Event " << fEvent << std::endl;
164 
165  // object to store required trigger information in
166  std::unique_ptr<sbndaq::CRTmetric> CRTMetricInfo = std::make_unique<sbndaq::CRTmetric>();
167 
168  // object to store trigger metrics in
169  std::unique_ptr<sbnd::trigger::pmtSoftwareTrigger> pmtSoftwareTriggerMetrics = std::make_unique<sbnd::trigger::pmtSoftwareTrigger>();
170 
171  // clear variables at the beginning of the event
172  // move this to constructor??
173  for (int ip=0;ip<7;++ip) { CRTMetricInfo->hitsperplane[ip]=0; hitsperplane[ip]=0;}
174  foundBeamTrigger = false;
175  fWvfmsFound = false;
176  fWvfmsVec.clear(); fWvfmsVec.resize(15*8); // 15 pmt channels per fragment, 8 fragments per trigger
177  fpmtInfoVec.clear(); fpmtInfoVec.resize(15*8);
178 
179  // get fragment handles
180  std::vector<art::Handle<artdaq::Fragments>> fragmentHandles = evt.getMany<std::vector<artdaq::Fragment>>();
181 
182  // loop over fragment handles
183  for (auto handle : fragmentHandles) {
184  if (!handle.isValid() || handle->size() == 0) continue;
185 
186  num_crt_frags = 0;
187  num_pmt_frags = 0;
188 
189  if (handle->front().type() == artdaq::Fragment::ContainerFragmentType) {
190  // container fragment
191  for (auto cont : *handle) {
192  artdaq::ContainerFragment contf(cont);
193  if (contf.fragment_type() == sbndaq::detail::FragmentType::BERNCRTV2){
194  if (fVerbose) std::cout << " Found " << contf.block_count() << " CRT Fragments in container " << std::endl;
195  if (fcrt_metrics){
196  for (size_t ii = 0; ii < contf.block_count(); ++ii) analyze_crt_fragment(*contf[ii].get());
197  }
198  }
199  }
200  }
201  else {
202  // normal fragment
203  size_t beamFragmentIdx = -1;
204  for (auto frag : *handle){
205  beamFragmentIdx++;
206  if (frag.type()==sbndaq::detail::FragmentType::BERNCRTV2) {
207  num_crt_frags++;
208  if (fcrt_metrics){analyze_crt_fragment(frag);}
209  }
210 
211 
212 
213  if (frag.type()==sbndaq::detail::FragmentType::CAENV1730) {
214  num_pmt_frags++;
215  if (fpmt_metrics){
216  // identify whether any fragments correspond to the beam spill
217  // loop over fragments, in steps of 8
218  //size_t beamFragmentIdx = 9999;
219  //for (size_t fragmentIdx = 0; fragmentIdx < handle->size(); fragmentIdx += 8) {
220  if (!foundBeamTrigger){
221  checkCAEN1730FragmentTimeStamp(frag);//handle->at(fragmentIdx));
222  if (foundBeamTrigger) {
223  //beamFragmentIdx = fragmentIdx;
224  if (fVerbose) std::cout << "Found fragment in time with beam" << std::endl;// at index: " << beamFragmentIdx << std::endl;
225  //break;
226  }
227 
228  // if set of fragment in time with beam found, process waveforms
229  if (foundBeamTrigger && beamFragmentIdx != 9999) {
230  for (size_t fragmentIdx = beamFragmentIdx; fragmentIdx < beamFragmentIdx+8; fragmentIdx++) {
231  analyzeCAEN1730Fragment(handle->at(fragmentIdx));
232  }
233  fWvfmsFound = true;
234  }
235  }
236  }//if saving pmt metrics
237  }//if is pmt frag
238 
239  }//loop over frags
240  }
241 
242  if (fVerbose) std::cout << "Found " << num_crt_frags << " BERNCRTV2 fragments" << std::endl;
243  if (fVerbose) std::cout << "Found " << num_pmt_frags << " CAEN1730 fragments" << std::endl;
244 
245  } // loop over frag handles
246 
247  if (fcrt_metrics){
248 
249  for (int i=0;i<7;++i) {CRTMetricInfo->hitsperplane[i] = hitsperplane[i];}
250 
251  if (fVerbose) {
252  std::cout << "CRT hit count during beam spill ";
253  for (int i=0;i<7;++i) std::cout << i << ": " << CRTMetricInfo->hitsperplane[i] << " " ;
254  std::cout << std::endl;
255  }
256 
257 
258  }//if save crt metrics
259 
260  if (fpmt_metrics){
261 
262  if (foundBeamTrigger && fWvfmsFound) {
263 
264  pmtSoftwareTriggerMetrics->foundBeamTrigger = true;
265  // store timestamp of trigger, relative to beam window start
266  double triggerTimeStamp = fTriggerTime - beamWindowStart;
267  pmtSoftwareTriggerMetrics->triggerTimestamp = triggerTimeStamp;
268  if (fVerbose) std::cout << "Saving trigger timestamp: " << triggerTimeStamp << " ns" << std::endl;
269 
270  double promptPE = 0;
271  double prelimPE = 0;
272 
273  int nAboveThreshold = 0;
274  // find the waveform bins that correspond to the start and end of the extended spill window (0 -> 1.8 us) within the 10 us waveform
275  // if the triggerTimeStamp < 1000, the beginning of the beam spill is *not* contained within the waveform
276  int beamStartBin = (triggerTimeStamp >= 1000)? 0 : int(500 - abs((triggerTimeStamp-1000)/2));
277  int beamEndBin = (triggerTimeStamp >= 1000)? int(500 + (fBeamWindowLength*1e3 - triggerTimeStamp)/2) : (beamStartBin + (fBeamWindowLength*1e3)/2);
278 
279  // wvfm loop to calculate metrics
280  for (int i_ch = 0; i_ch < 120; ++i_ch){
281  auto &pmtInfo = fpmtInfoVec.at(i_ch);
282  auto wvfm = fWvfmsVec[i_ch];
283 
284  // assign channel
285  pmtInfo.channel = channelList.at(i_ch);
286 
287  // calculate baseline
288  if (fBaselineAlgo == "constant"){ pmtInfo.baseline=fInputBaseline; pmtInfo.baselineSigma = fInputBaselineSigma; }
289  else if (fBaselineAlgo == "estimate") estimateBaseline(i_ch);
290 
291  // count number of PMTs above threshold
292  for (int bin = beamStartBin; bin < beamEndBin; ++bin){
293  auto adc = wvfm[bin];
294  if (adc < fADCThreshold){ nAboveThreshold++; break; }
295  }
296 
297  // quick estimate prompt and preliminary light, assuming sampling rate of 500 MHz (2 ns per bin)
298  double baseline = pmtInfo.baseline;
299  auto prompt_window = std::vector<uint16_t>(wvfm.begin()+500, wvfm.begin()+1000);
300  auto prelim_window = std::vector<uint16_t>(wvfm.begin()+beamStartBin, wvfm.begin()+500);
301  if (fFindPulses == false){
302  double ch_promptPE = (baseline-(*std::min_element(prompt_window.begin(), prompt_window.end())))/8;
303  double ch_prelimPE = (baseline-(*std::min_element(prelim_window.begin(), prelim_window.end())))/8;
304  promptPE += ch_promptPE;
305  prelimPE += ch_prelimPE;
306  }
307 
308  // pulse finder + prompt and prelim calculation with pulses
309  if (fFindPulses == true){
310  SimpleThreshAlgo(i_ch);
311  for (auto pulse : pmtInfo.pulseVec){
312  if (pulse.t_start > 500 && pulse.t_end < 550) promptPE+=pulse.pe;
313  if ((triggerTimeStamp) >= 1000){ if (pulse.t_end < 500) prelimPE+=pulse.pe; }
314  else if (triggerTimeStamp < 1000){
315  if (pulse.t_start > (500 - abs((triggerTimeStamp-1000)/2)) && pulse.t_end < 500) prelimPE+=pulse.pe;
316  }
317  }
318  }
319  } // end of wvfm loop
320 
321  pmtSoftwareTriggerMetrics->nAboveThreshold = nAboveThreshold;
322  pmtSoftwareTriggerMetrics->promptPE = promptPE;
323  pmtSoftwareTriggerMetrics->prelimPE = prelimPE;
324  if (fVerbose) std::cout << "nPMTs Above Threshold: " << nAboveThreshold << std::endl;
325  if (fVerbose) std::cout << "prompt pe: " << promptPE << std::endl;
326  if (fVerbose) std::cout << "prelim pe: " << prelimPE << std::endl;
327  }
328  else{
329  if (fVerbose) std::cout << "Beam and wvfms not found" << std::endl;
330  pmtSoftwareTriggerMetrics->foundBeamTrigger = false;
331  pmtSoftwareTriggerMetrics->triggerTimestamp = -9999;
332  pmtSoftwareTriggerMetrics->nAboveThreshold = -9999;
333  pmtSoftwareTriggerMetrics->promptPE = -9999;
334  pmtSoftwareTriggerMetrics->prelimPE = -9999;
335  }
336 
337  }//if save pmt metrics
338 
339  // add to event
340  evt.put(std::move(CRTMetricInfo));
341  evt.put(std::move(pmtSoftwareTriggerMetrics));
342 
343 }//produce
344 
345 
346 
347 void sbndaq::MetricProducer::analyze_crt_fragment(artdaq::Fragment & frag)
348 {
349 
350 
351  sbndaq::BernCRTFragmentV2 bern_fragment(frag);
352 
353  // use fragment ID to get plane information
354  const sbndaq::BernCRTFragmentMetadataV2* md = bern_fragment.metadata();
355  //frag.sequenceID()
356  auto thisone = frag.fragmentID(); uint plane = (thisone & 0x0700) >> 8;
357  if (plane>7) {std::cout << "bad plane value " << plane << std::endl; plane=0;}
358 
359  for(unsigned int iHit = 0; iHit < md->hits_in_fragment(); iHit++) {
360  sbndaq::BernCRTHitV2 const* bevt = bern_fragment.eventdata(iHit);
361  // require that this is data and not clock reset (0xC), and that the ts1 time is valid (0x2)
362  auto thisflag = bevt->flags;
363  if (thisflag & 0x2 && !(thisflag & 0xC) ) {
364  // check ts1 for beam window
365  auto thistime=bevt->ts1;
366  if ((int)thistime>fBeamWindowStart && (int)thistime<fBeamWindowEnd) hitsperplane[plane]++;
367  //CRTMetricInfo->hitsperplane[plane]++;
368  }
369  }
370 
371 
372 
373 }//analyze crt fragments
374 
375 
376 void sbndaq::MetricProducer::checkCAEN1730FragmentTimeStamp(const artdaq::Fragment &frag) {
377 
378  // get fragment metadata
379  sbndaq::CAENV1730Fragment bb(frag);
380  auto const* md = bb.Metadata();
381 
382  // access timestamp
383  uint32_t timestamp = md->timeStampNSec;
384 
385  // access beam signal, in ch15 of first PMT of each fragment set
386  // check entry 500 (0us), at trigger time
387  const uint16_t* data_begin = reinterpret_cast<const uint16_t*>(frag.dataBeginBytes()
388  + sizeof(sbndaq::CAENV1730EventHeader));
389  const uint16_t* value_ptr = data_begin;
390  uint16_t value = 0;
391 
392  size_t ch_offset = (size_t)(15*fWvfmLength);
393  size_t tr_offset = fTriggerTimeOffset*1e3;
394 
395  value_ptr = data_begin + ch_offset + tr_offset; // pointer arithmetic
396  value = *(value_ptr);
397 
398  if (value == 1 && timestamp >= beamWindowStart && timestamp <= beamWindowEnd) {
399  foundBeamTrigger = true;
400  fTriggerTime = timestamp;
401  }
402 }//check caen 1730 timestamp
403 
404 void sbndaq::MetricProducer::analyzeCAEN1730Fragment(const artdaq::Fragment &frag) {
405 
406  // access fragment ID; index of fragment out of set of 8 fragments
407  int fragId = static_cast<int>(frag.fragmentID());
408 
409  // access waveforms in fragment and save
410  const uint16_t* data_begin = reinterpret_cast<const uint16_t*>(frag.dataBeginBytes()
411  + sizeof(sbndaq::CAENV1730EventHeader));
412  const uint16_t* value_ptr = data_begin;
413  uint16_t value = 0;
414 
415  // channel offset
416  size_t nChannels = 15; // 15 pmts per fragment
417  size_t ch_offset = 0;
418 
419  // loop over channels
420  for (size_t i_ch = 0; i_ch < nChannels; ++i_ch){
421  fWvfmsVec[i_ch + nChannels*fragId].resize(fWvfmLength);
422  ch_offset = (size_t)(i_ch * fWvfmLength);
423  //--loop over waveform samples
424  for(size_t i_t = 0; i_t < fWvfmLength; ++i_t){
425  value_ptr = data_begin + ch_offset + i_t; // pointer arithmetic
426  value = *(value_ptr);
427  fWvfmsVec[i_ch + nChannels*fragId][i_t] = value;
428  } //--end loop samples
429  } //--end loop channels
430 }//analyze caen 1730 fragment
431 
433  auto wvfm = fWvfmsVec[i_ch];
434  auto &pmtInfo = fpmtInfoVec[i_ch];
435  // assuming that the first 500 ns doesn't include peaks, find the mean of the ADC count as the baseline
436  std::vector<uint16_t> subset = std::vector<uint16_t>(wvfm.begin(), wvfm.begin()+250);
437  double subset_mean = (std::accumulate(subset.begin(), subset.end(), 0))/(subset.size());
438  double val = 0;
439  for (size_t i=0; i<subset.size();i++){ val += (subset[i] - subset_mean)*(subset[i] - subset_mean);}
440  double subset_stddev = sqrt(val/subset.size());
441 
442  // if the first 500 ns seem to be messy, use the last 500
443  if (subset_stddev > 3){ // make this fcl parameter?
444  val = 0; subset.clear(); subset_stddev = 0;
445  subset = std::vector<uint16_t>(wvfm.end()-500, wvfm.end());
446  subset_mean = (std::accumulate(subset.begin(), subset.end(), 0))/(subset.size());
447  for (size_t i=0; i<subset.size();i++){ val += (subset[i] - subset_mean)*(subset[i] - subset_mean);}
448  subset_stddev = sqrt(val/subset.size());
449  }
450  pmtInfo.baseline = subset_mean;
451  pmtInfo.baselineSigma = subset_stddev;
452 }//estimateBaseline
453 
455  auto wvfm = fWvfmsVec[i_ch];
456  auto &pmtInfo = fpmtInfoVec[i_ch];
457  double baseline = pmtInfo.baseline;
458  double baseline_sigma = pmtInfo.baselineSigma;
459 
460  bool fire = false; // bool for if pulse has been detected
461  int counter = 0; // counts the bin of the waveform
462 
463  // these should be fcl parameters
464  double start_adc_thres = 5, end_adc_thres = 2;
465  double nsigma_start = 5, nsigma_end = 3;
466 
467  auto start_threshold = ( start_adc_thres > (nsigma_start * baseline_sigma) ? (baseline-start_adc_thres) : (baseline-(nsigma_start * baseline_sigma)));
468  auto end_threshold = ( end_adc_thres > (nsigma_end * baseline_sigma) ? (baseline - end_adc_thres) : (baseline - (nsigma_end * baseline_sigma)));
469 
470  std::vector<sbnd::trigger::pmtPulse> pulse_vec;
472  pulse.area = 0; pulse.peak = 0; pulse.t_start = 0; pulse.t_end = 0; pulse.t_peak = 0;
473  for (auto const &adc : wvfm){
474  if ( !fire && ((double)adc) <= start_threshold ){ // if its a new pulse
475  fire = true;
476  //vic: i move t_start back one, this helps with porch
477  pulse.t_start = counter - 1 > 0 ? counter - 1 : counter;
478  }
479 
480  if( fire && ((double)adc) > end_threshold ){ // found end of a pulse
481  fire = false;
482  //vic: i move t_start forward one, this helps with tail
483  pulse.t_end = counter < ((int)wvfm.size()) ? counter : counter - 1;
484  pulse_vec.push_back(pulse);
485  pulse.area = 0; pulse.peak = 0; pulse.t_start = 0; pulse.t_end = 0; pulse.t_peak = 0;
486  }
487 
488  if(fire){ // if we're in a pulse
489  pulse.area += (baseline-(double)adc);
490  if (pulse.peak > (baseline-(double)adc)) { // Found a new maximum
491  pulse.peak = (baseline-(double)adc);
492  pulse.t_peak = counter;
493  }
494  }
495  counter++;
496  }
497 
498  if(fire){ // Take care of a pulse that did not finish within the readout window.
499  fire = false;
500  pulse.t_end = counter - 1;
501  pulse_vec.push_back(pulse);
502  pulse.area = 0; pulse.peak = 0; pulse.t_start = 0; pulse.t_end = 0; pulse.t_peak = 0;
503  }
504 
505  pmtInfo.pulseVec = pulse_vec;
506  // calculate PE from area
507  for (auto &pulse : pmtInfo.pulseVec){pulse.pe = pulse.area/fPEArea;}
508 }//SimpleThreshAlgo
509 
510 
511 // -------------------------------------------------
512 
513 // -------------------------------------------------
514 
515 DEFINE_ART_MODULE(sbndaq::MetricProducer)
std::vector< unsigned int > channelList
pdgs p
Definition: selectors.fcl:22
void analyze_crt_fragment(artdaq::Fragment &frag)
MetricProducer(fhicl::ParameterSet const &p)
MetricProducer & operator=(MetricProducer const &)=delete
std::vector< sbnd::trigger::pmtInfo > fpmtInfoVec
void produce(art::Event &evt) override
constexpr details::BinObj< T > bin(T value)
Returns a wrapper to print the specified data in binary format.
int hitsperplane[7]
Definition: CRTmetric.hh:13
art::Persistable is_persistable_
T abs(T value)
auto counter(T begin, T end)
Returns an object to iterate values from begin to end in a range-for loop.
Definition: counter.h:285
void analyzeCAEN1730Fragment(const artdaq::Fragment &frag)
BEGIN_PROLOG baseline
void checkCAEN1730FragmentTimeStamp(const artdaq::Fragment &frag)
temporary value
TCEvent evt
Definition: DataStructs.cxx:8
std::vector< std::vector< uint16_t > > fWvfmsVec
BEGIN_PROLOG could also be cout