All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
FrameSaver.cxx
Go to the documentation of this file.
1 /*
2  */
3 
4 #include "FrameSaver.h"
5 
8 
9 #include "art/Framework/Core/EDProducer.h"
10 #include "art/Framework/Principal/Event.h"
11 
12 #include "WireCellIface/IAnodePlane.h"
13 #include "WireCellIface/IFrame.h"
14 #include "WireCellIface/ITrace.h"
15 #include "WireCellUtil/NamedFactory.h"
16 
17 // it would be nice to remove this dependency since it is needed only
18 // to add bogus information to raw::RawDigit.
22 
23 #include <algorithm>
24 #include <map>
25 
27 
28 using namespace wcls;
29 using namespace WireCell;
30 
31 FrameSaver::FrameSaver() : m_frame(nullptr), m_nticks(0) {}
32 
33 FrameSaver::~FrameSaver() {}
34 
35 WireCell::Configuration
37 {
38  Configuration cfg;
39  cfg["anode"] = "AnodePlane";
40 
41  // If true, truncate frame waveforms and save as raw::RawDigit,
42  // else leave as floating point recob::Wire
43  cfg["digitize"] = false;
44 
45  // If true, save recob::Wires as sparse (true zero suppressed).
46  // Note, this sparsification is performed independently from if
47  // the input IFrame itself is sparse or not.
48  cfg["sparse"] = true;
49 
50  // Provide a configurable translation layer between WCT Plane View IDs
51  // and those from larsoft. Default is to assume they're the same,
52  // except for in certain cases (such as the vertical drift geometry)
53  // where the map is provided in the jsonnet/json
54  cfg["plane_map"][std::to_string((int)WireCell::kUlayer)] = (int)geo::kU;
55  cfg["plane_map"][std::to_string((int)WireCell::kVlayer)] = (int)geo::kV;
56  cfg["plane_map"][std::to_string((int)WireCell::kWlayer)] = (int)geo::kW;
57 
58  // If digitize, raw::RawDigit has slots for pedestal mean and
59  // sigma. Legacy/obsolete code stuff unrelated values into these
60  // slots. If pedestal_mean is a number, it will be stuffed. If
61  // it is the string "fiction" then the DetPedestalService will be
62  // used to set some unrelated pedestal value.
63  cfg["pedestal_mean"] = 0.0;
64  // Stuff some value into pedestal sigma. This value has nothing
65  // to do with the produced NF'ed waveforms.
66  cfg["pedestal_sigma"] = 0.0;
67 
68  // frames to output, if any
69  cfg["frame_tags"] = Json::arrayValue;
70  cfg["frame_scale"] = 1.0; // multiply this number to all
71  // waveform samples. If list, then
72  // one number per frame tags.
73  // If nonzero, force number of ticks in output waveforms.
74  // If zero, use whatever input data has.
75  // If -1, use value as per LS's detector properties service.
76  cfg["nticks"] = m_nticks;
77 
78  // Summaries to output, if any
79  cfg["summary_tags"] = Json::arrayValue;
80  cfg["summary_scale"] = 1.0;
81  // Sumaries are *per trace* quantities coming in but it is likely
82  // that some (most?) consumers of the output will expect *per
83  // channel* quantities. Aggregating by channel requires some
84  // operator to be applied to the sequence of summary values from a
85  // common channel. The operator is defined as an object keyed by
86  // the summary tag. Values may be "set" which will simply save a
87  // summary value to the output element (last one from a channel
88  // wins) or "sum" (the default) which will add up the values.
89  cfg["summary_operator"] = Json::objectValue;
90 
91  // Names of channel mask maps to save, if any.
92  cfg["chanmaskmaps"] = Json::arrayValue;
93 
94  return cfg;
95 }
96 
97 static float
98 summary_sum(const std::vector<float>& tsvals)
99 {
100  return std::accumulate(tsvals.begin(), tsvals.end(), 0);
101 }
102 static float
103 summary_set(const std::vector<float>& tsvals)
104 {
105  if (tsvals.empty()) { return 0.0; }
106  return tsvals.back();
107 }
108 
109 void
110 FrameSaver::configure(const WireCell::Configuration& cfg)
111 {
112  const std::string anode_tn = cfg["anode"].asString();
113  if (anode_tn.empty()) { THROW(ValueError() << errmsg{"FrameSaver requires an anode plane"}); }
114 
115  WireCell::IAnodePlane::pointer anode = Factory::find_tn<IAnodePlane>(anode_tn);
116  for (auto chid : anode->channels()) {
117 
118  auto wpid = anode->resolve(chid);
119  geo::View_t view;
120 
121  // Use configurable translation between WCT and larsoft
122  // plane view IDs. Relevant especially for VD 3 view
123  // since the 2nd induction plane is actually labelled
124  // kY in larsoft vs kV in WCT
125  // Unless otherwise specified, this map amounts to
126  // kU->kU, kV->kV, kW->kW
127  std::string wct_layer = std::to_string((int)wpid.layer());
128  view = (geo::View_t) (cfg["plane_map"][wct_layer].asInt());
129 
130  m_chview[chid] = view;
131  }
132 
133  m_digitize = get(cfg, "digitize", false);
134  m_sparse = get(cfg, "sparse", true);
135 
136  m_cmms = cfg["chanmaskmaps"];
137 
138  m_pedestal_mean = cfg["pedestal_mean"];
139  m_pedestal_sigma = get(cfg, "pedestal_sigma", 0.0);
140 
141  if (!cfg["frame_scale"].isNull()) {
142  m_frame_scale.clear();
143  auto jscale = cfg["frame_scale"];
144  m_frame_tags.clear();
145  auto jtags = cfg["frame_tags"];
146  for (auto jtag : jtags) {
147  std::string tag = jtag.asString();
148 
149  // get any waveform scaling for this frame
150  const int ind = m_frame_tags.size();
151  double scale = 1.0;
152  //std::cerr << "JSCALE:" << jscale << std::endl;
153  if (!jscale.isNull()) {
154  if (jscale.isArray()) { scale = jscale[ind].asDouble(); }
155  else {
156  scale = jscale.asDouble();
157  }
158  }
159 
160  m_frame_scale.push_back(scale);
161  m_frame_tags.push_back(tag);
162  }
163  m_nticks = get(cfg, "nticks", m_nticks);
164  }
165 
166  auto jso = cfg["summary_operator"];
167 
168  if (!cfg["summary_tags"].isNull()) {
169  m_summary_scale.clear();
170  auto jscale = cfg["summary_scale"];
171  m_summary_tags.clear();
172  auto jtags = cfg["summary_tags"];
173  for (auto jtag : jtags) {
174  std::string tag = jtag.asString();
175 
176  const int ind = m_summary_tags.size();
177  double scale = 1.0;
178  if (!jscale.isNull()) {
179  if (jscale.isArray()) { scale = jscale[ind].asDouble(); }
180  else {
181  scale = jscale.asDouble();
182  }
183  }
184 
185  m_summary_tags.push_back(tag);
186  m_summary_scale.push_back(scale);
187  auto so = get<std::string>(jso, tag, "sum");
188  if (so == "set") { m_summary_operators[tag] = summary_set; }
189  else {
191  }
192  }
193  }
194 }
195 
196 void
197 FrameSaver::produces(art::ProducesCollector& collector)
198 {
199  for (auto tag : m_frame_tags) {
200  if (!m_digitize) {
201  std::cerr << "wclsFrameSaver: promising to produce recob::Wires named \"" << tag << "\"\n";
202  collector.produces<std::vector<recob::Wire>>(tag);
203  }
204  else {
205  std::cerr << "wclsFrameSaver: promising to produce raw::RawDigits named \"" << tag << "\"\n";
206  collector.produces<std::vector<raw::RawDigit>>(tag);
207  }
208  }
209  for (auto tag : m_summary_tags) {
210  std::cerr << "wclsFrameSaver: promising to produce channel summary named \"" << tag << "\"\n";
211  collector.produces<std::vector<double>>(tag);
212  }
213  for (auto cmm : m_cmms) {
214  const std::string cmm_name = cmm.asString();
215  std::cerr << "wclsFrameSaver: promising to produce channel masks named \"" << cmm_name
216  << "\"\n";
217  collector.produces<channel_list>(cmm_name + "channels");
218  collector.produces<channel_masks>(cmm_name + "masks");
219  }
220 }
221 
222 static void
223 tagged_traces(IFrame::pointer frame, std::string tag, ITrace::vector& ret)
224 {
225  auto const& all_traces = frame->traces();
226  const auto& ttinds = frame->tagged_traces(tag);
227  if (ttinds.size()) {
228  for (size_t index : ttinds) {
229  ret.push_back(all_traces->at(index));
230  }
231  return;
232  }
233  auto ftags = frame->frame_tags();
234  if (std::find(ftags.begin(), ftags.end(), tag) == ftags.end()) { return; }
235  ret.insert(ret.begin(), all_traces->begin(), all_traces->end());
236 }
237 
238 typedef std::unordered_map<int, ITrace::vector> traces_bychan_t;
239 static void
241 {
242  for (auto trace : traces) {
243  int chid = trace->channel();
244  ret[chid].push_back(trace);
245  }
246 }
247 
248 // Issolate some silly legacy shenanigans to keep the rest of the code
249 // blissfully ignorant of the evilness this implies.
250 struct PU {
251  Json::Value pu;
252 
253  PU(Json::Value pu) : pu(pu) {}
254 
255  float
256  operator()(int chid)
257  {
258  if (pu.isNumeric()) { return pu.asFloat(); }
259  if (pu.asString() == "fiction") {
260  art::ServiceHandle<lariov::DetPedestalService const> dps;
261  const auto& pv = dps->GetPedestalProvider();
262  return pv.PedMean(chid);
263  }
264  return 0.0;
265  }
266 };
267 
268 void
269 FrameSaver::save_as_raw(art::Event& event)
270 {
271  int nticks_want = m_nticks;
272  if (nticks_want < 0) {
273  auto const detProp =
274  art::ServiceHandle<detinfo::DetectorPropertiesService const>()->DataFor(event);
275  nticks_want = detProp.NumberTimeSamples();
276  }
277 
278  size_t nftags = m_frame_tags.size();
279  for (size_t iftag = 0; iftag < nftags; ++iftag) {
280  std::string ftag = m_frame_tags[iftag];
281  std::cerr << "wclsFrameSaver: saving raw::RawDigits tagged \"" << ftag << "\"\n";
282 
283  double scale = m_frame_scale[iftag];
284 
285  ITrace::vector traces;
286  tagged_traces(m_frame, ftag, traces);
287  traces_bychan_t bychan;
288  traces_bychan(traces, bychan);
289 
290  std::unique_ptr<std::vector<raw::RawDigit>> out(new std::vector<raw::RawDigit>);
291 
292  for (auto chv : m_chview) {
293  const int chid = chv.first;
294  const auto& traces = bychan[chid];
295  const size_t ntraces = traces.size();
296 
297  int tbin = 0;
298  std::vector<float> charge;
299  if (ntraces) {
300  auto trace = traces[0];
301  tbin = trace->tbin();
302  charge = trace->charge();
303  }
304  // charge may be empty here
305 
306  // enforce number of ticks if we are so configured.
307  size_t ncharge = charge.size();
308  int nticks = tbin + ncharge;
309  if (nticks_want) { // force output waveform size
310  if (nticks_want < nticks) { ncharge = nticks_want - tbin; }
311  nticks = nticks_want;
312  }
313  raw::RawDigit::ADCvector_t adcv(nticks);
314  for (size_t ind = 0; ind < ncharge; ++ind) {
315  adcv[tbin + ind] = scale * charge[ind]; // scale + truncate/redigitize
316  }
317  out->emplace_back(raw::RawDigit(chid, nticks, adcv, raw::kNone));
318  if (m_pedestal_mean.asString() == "native") {
319  short baseline = Waveform::most_frequent(adcv);
320  out->back().SetPedestal(baseline, m_pedestal_sigma);
321  }
322  else {
323  PU pu(m_pedestal_mean);
324  out->back().SetPedestal(pu(chid), m_pedestal_sigma);
325  }
326  }
327  event.put(std::move(out), ftag);
328  }
329 }
330 
331 void
332 FrameSaver::save_as_cooked(art::Event& event)
333 {
334  int nticks_want = m_nticks;
335  if (nticks_want < 0) {
336  auto const detProp =
337  art::ServiceHandle<detinfo::DetectorPropertiesService const>()->DataFor(event);
338  nticks_want = detProp.NumberTimeSamples();
339  std::cerr << "wclsFrameSaver saving cooked to " << nticks_want << " ticks\n";
340  }
341 
342  size_t nftags = m_frame_tags.size();
343  for (size_t iftag = 0; iftag < nftags; ++iftag) {
344  std::string ftag = m_frame_tags[iftag];
345 
346  double scale = m_frame_scale[iftag];
347 
348  ITrace::vector traces;
349  tagged_traces(m_frame, ftag, traces);
350  if (traces.empty()) {
351  std::cerr << "wclsFrameSaver: no traces tagged \"" << ftag << "\"\n";
352  // fall through loop so we put (empty) outwires
353  }
354  else {
355  std::cerr << "wclsFrameSaver: saving " << traces.size() << " traces tagged \"" << ftag
356  << "\"\n";
357  }
358 
359  traces_bychan_t bychan;
360  traces_bychan(traces, bychan);
361 
362  std::unique_ptr<std::vector<recob::Wire>> outwires(new std::vector<recob::Wire>);
363 
364  double total_charge = 0.0;
365  int total_samples = 0;
366 
367  for (auto chv : m_chview) {
368  const int chid = chv.first;
369  const auto& traces = bychan[chid];
370 
371  recob::Wire::RegionsOfInterest_t rois(nticks_want);
372 
373  for (const auto& trace : traces) {
374  const int tbin = trace->tbin();
375  const auto& charge = trace->charge();
376 
377  auto beg = charge.begin();
378  const auto first = beg;
379  auto end = charge.end();
380  if (nticks_want) { // user set waveform size
381  if (tbin >= nticks_want) { beg = end; }
382  else {
383  int backup = tbin + charge.size() - nticks_want;
384  if (backup > 0) { end -= backup; }
385  }
386  }
387  if (beg >= end) {
388  std::cerr << "wclsFrameSaver: no samples within desired window for channel " << chid
389  << "\n";
390  continue;
391  }
392  if (!m_sparse) {
393  std::vector<float> scaled(beg, end);
394  for (size_t i = 0; i < scaled.size(); ++i)
395  scaled[i] *= scale;
396  // prefer combine_range() but it segfaults.
397  rois.add_range(tbin, scaled.begin(), scaled.end());
398  continue;
399  }
400  // sparsify trace whether or not it may itself already
401  // represents a sparse ROI
402  while (true) {
403  beg = std::find_if(beg, end, [](float v) { return v != 0.0; });
404  if (beg == end) { break; }
405  auto mid = std::find_if(beg, end, [](float v) { return v == 0.0; });
406  std::vector<float> scaled(beg, mid);
407  for (int ind = 0; ind < mid - beg; ++ind) {
408  scaled[ind] *= scale;
409  total_charge += scaled[ind];
410  ++total_samples;
411  }
412  rois.add_range(tbin + beg - first, scaled.begin(), scaled.end());
413  beg = mid;
414  }
415  }
416 
417  const geo::View_t view = chv.second;
418  outwires->emplace_back(recob::Wire(rois, chid, view));
419  }
420  std::cerr << "FrameSaver: q=" << total_charge << " n=" << total_samples << " tag=" << ftag
421  << "\n";
422  event.put(std::move(outwires), ftag);
423  } // loop over tags
424 }
425 
426 void
427 FrameSaver::save_summaries(art::Event& event)
428 {
429  const int ntags = m_summary_tags.size();
430  if (0 == ntags) {
431  return; // no tags
432  }
433 
434  const size_t nchans = m_chview.size();
435 
436  // for each summary
437  for (int tag_ind = 0; tag_ind < ntags; ++tag_ind) {
438  // The scale set for the tag.
439  const double scale = m_summary_scale[tag_ind];
440 
441  std::unique_ptr<std::vector<double>> outsum(new std::vector<double>(nchans, 0.0));
442 
443  // The "summary" and "traces" vectors of the same tag are
444  // synced, element-by-element. Each element corresponds to
445  // one trace (ROI). No particular order or correlation by
446  // channel exists, and that's what the rest of this code
447  // creates.
448  auto tag = m_summary_tags[tag_ind];
449  const auto& summary = m_frame->trace_summary(tag);
450  ITrace::vector traces;
451  tagged_traces(m_frame, tag, traces);
452  const size_t ntraces = traces.size();
453 
454  std::unordered_map<int, std::vector<float>> bychan;
455  for (size_t itrace = 0; itrace < ntraces; ++itrace) {
456  const int chid = traces[itrace]->channel();
457  const double summary_value = summary[itrace];
458  bychan[chid].push_back(summary_value);
459  }
460  auto oper = m_summary_operators[tag];
461 
462  size_t chanind = 0;
463  for (auto chv : m_chview) {
464  const int chid = chv.first;
465  const float val = oper(bychan[chid]);
466  outsum->at(chanind) = val * scale;
467  ++chanind;
468  }
469  event.put(std::move(outsum), tag);
470  }
471 }
472 
473 void
474 FrameSaver::save_cmms(art::Event& event)
475 {
476  if (m_cmms.isNull()) { return; }
477  if (!m_cmms.isArray()) {
478  std::cerr
479  << "wclsFrameSaver: wrong type for configuration array of channel mask maps to save\n";
480  return;
481  }
482  for (auto jcmm : m_cmms) {
483  std::string name = jcmm.asString();
484  std::unique_ptr<channel_list> out_list(new channel_list);
485  std::unique_ptr<channel_masks> out_masks(new channel_masks);
486 
487  auto cmm = m_frame->masks();
488  auto it = cmm.find(name);
489  if (it == cmm.end()) {
490  std::cerr << "wclsFrameSaver: failed to find requested channel masks \"" << name << "\"\n";
491  continue;
492  }
493  for (auto cmit : it->second) { // int->vec<pair<int,int>>
494  out_list->push_back(cmit.first);
495  for (auto be : cmit.second) {
496  out_masks->push_back(cmit.first);
497  out_masks->push_back(be.first);
498  out_masks->push_back(be.second);
499  }
500  }
501 
502  if (out_list->empty()) {
503  std::cerr << "wclsFrameSaver: found empty channel masks for \"" << name << "\"\n";
504  }
505  event.put(std::move(out_list), name + "channels");
506  event.put(std::move(out_masks), name + "masks");
507  }
508 }
509 
510 void
511 FrameSaver::save_empty(art::Event& event)
512 {
513  // art (apparently?) requires something to be saved if a produces() is promised.
514  std::cerr << "wclsFrameSaver: saving empty frame to art::Event\n";
515 
516  for (auto ftag : m_frame_tags) {
517  if (m_digitize) {
518  std::unique_ptr<std::vector<raw::RawDigit>> out(new std::vector<raw::RawDigit>);
519  event.put(std::move(out), ftag);
520  }
521  else {
522  std::unique_ptr<std::vector<recob::Wire>> outwires(new std::vector<recob::Wire>);
523  event.put(std::move(outwires), ftag);
524  }
525  }
526 
527  for (auto stag : m_summary_tags) {
528  std::unique_ptr<std::vector<double>> outsum(new std::vector<double>);
529  event.put(std::move(outsum), stag);
530  }
531 
532  for (auto jcmm : m_cmms) {
533  std::string name = jcmm.asString();
534  std::unique_ptr<channel_list> out_list(new channel_list);
535  std::unique_ptr<channel_masks> out_masks(new channel_masks);
536  event.put(std::move(out_list), name + "channels");
537  event.put(std::move(out_masks), name + "masks");
538  }
539 }
540 
541 void
542 FrameSaver::visit(art::Event& event)
543 {
544  if (!m_frame) {
545  save_empty(event);
546  return;
547  }
548 
549  if (m_digitize) { save_as_raw(event); }
550  else {
551  save_as_cooked(event);
552  }
553 
554  save_summaries(event);
555 
556  save_cmms(event);
557 
558  m_frame = nullptr; // done with stashed frame
559 }
560 
561 bool
562 FrameSaver::operator()(const WireCell::IFrame::pointer& inframe,
563  WireCell::IFrame::pointer& outframe)
564 {
565  // set an IFrame based on last visited event.
566  outframe = inframe;
567  if (inframe) {
568  if (m_frame) {
569  std::cerr
570  << "wclsFrameSaver: warning: dropping prior frame. Fixme to handle queue of frames.\n";
571  }
572  // else {
573  // std::cerr << "wclsFrameSaver got frame\n";
574  // }
575  m_frame = inframe;
576  }
577  // else {
578  // std::cerr << "wclsFrameSaver sees EOS\n";
579  // }
580  return true;
581 }
582 
583 // Local Variables:
584 // mode: c++
585 // c-basic-offset: 4
586 // End:
std::map< int, geo::View_t > m_chview
Definition: FrameSaver.h:74
std::vector< int > channel_masks
Definition: FrameSaver.h:68
Collection of charge vs time digitized from a single readout channel.
Definition: RawDigit.h:69
BEGIN_PROLOG could also be cerr
float operator()(int chid)
Definition: FrameSaver.cxx:256
enum geo::_plane_proj View_t
Enumerate the possible plane projections.
Planes which measure V.
Definition: geo_types.h:130
virtual WireCell::Configuration default_configuration() const
IConfigurable.
Definition: FrameSaver.cxx:36
void save_cmms(art::Event &event)
Definition: FrameSaver.cxx:474
virtual bool operator()(const WireCell::IFrame::pointer &inframe, WireCell::IFrame::pointer &outframe)
IFrameFilter.
Definition: FrameSaver.cxx:562
std::vector< short > ADCvector_t
Type representing a (compressed) vector of ADC counts.
Definition: RawDigit.h:73
PU(Json::Value pu)
Definition: FrameSaver.cxx:253
const datarange_t & add_range(size_type offset, ITER first, ITER last)
Adds a sequence of elements as a range with specified offset.
void save_empty(art::Event &event)
Definition: FrameSaver.cxx:511
Definition of basic raw digits.
virtual void visit(art::Event &event)
Implement to visit an Art event.
Definition: FrameSaver.cxx:542
no compression
Definition: RawTypes.h:9
static void tagged_traces(IFrame::pointer frame, std::string tag, ITrace::vector &ret)
Definition: FrameSaver.cxx:223
std::vector< std::string > m_summary_tags
Definition: FrameSaver.h:78
static float summary_set(const std::vector< float > &tsvals)
Definition: FrameSaver.cxx:103
M::value_type trace(const M &m)
auto vector(Vector const &v)
Returns a manipulator which will print the specified array.
Definition: DumpUtils.h:265
void save_as_cooked(art::Event &event)
Definition: FrameSaver.cxx:332
static float summary_sum(const std::vector< float > &tsvals)
Definition: FrameSaver.cxx:98
Planes which measure U.
Definition: geo_types.h:129
virtual void configure(const WireCell::Configuration &config)
Definition: FrameSaver.cxx:110
std::vector< std::string > m_frame_tags
Definition: FrameSaver.h:78
process_name WireCellLarsoft wclsFrameSaver
auto end(FixedBins< T, C > const &) noexcept
Definition: FixedBins.h:585
BEGIN_PROLOG baseline
Json::Value m_pedestal_mean
Definition: FrameSaver.h:86
std::unordered_map< int, ITrace::vector > traces_bychan_t
Definition: FrameSaver.cxx:238
WIRECELL_FACTORY(wclsChannelNoiseDB, wcls::ChannelNoiseDB, wcls::IArtEventVisitor, WireCell::IChannelNoiseDatabase) using namespace WireCell
void save_as_raw(art::Event &event)
Definition: FrameSaver.cxx:269
std::vector< double > m_summary_scale
Definition: FrameSaver.h:79
WireCell::IFrame::pointer m_frame
Definition: FrameSaver.h:77
static void traces_bychan(ITrace::vector &traces, traces_bychan_t &ret)
Definition: FrameSaver.cxx:240
std::unordered_map< std::string, summarizer_function > m_summary_operators
Definition: FrameSaver.h:82
Json::Value m_cmms
Definition: FrameSaver.h:86
std::vector< double > m_frame_scale
Definition: FrameSaver.h:79
std::string to_string(WindowPattern const &pattern)
std::vector< int > channel_list
Definition: FrameSaver.h:67
Class holding the regions of interest of signal from a channel.
Definition: Wire.h:118
Declaration of basic channel signal object.
then echo fcl name
virtual void produces(art::ProducesCollector &collector)
IArtEventVisitor.
Definition: FrameSaver.cxx:197
Planes which measure W (third view for Bo, MicroBooNE, etc).
Definition: geo_types.h:131
void save_summaries(art::Event &event)
Definition: FrameSaver.cxx:427
Json::Value pu
Definition: FrameSaver.cxx:251
auto const detProp
double m_pedestal_sigma
Definition: FrameSaver.h:87