NumpyFrameSaver.cxx
Go to the documentation of this file.
2 
5 #include "WireCellUtil/cnpy.h"
6 
7 #include <string>
8 #include <vector>
9 #include <algorithm>
10 #include <tuple>
11 #include <sstream>
12 
15 
16 using namespace WireCell;
17 
19  : m_save_count(0)
20  , l(Log::logger("io"))
21 {
22 }
23 
24 Sio::NumpyFrameSaver::~NumpyFrameSaver()
25 {
26 }
27 
28 
29 WireCell::Configuration Sio::NumpyFrameSaver::default_configuration() const
30 {
32 
33  // If digitize is true, then samples as 16 bit ints. Otherwise
34  // save as 32 bit floats.
35  cfg["digitize"] = false;
36 
37  // This number is set to the waveform sample array before any
38  // charge is added.
39  cfg["baseline"] = 0.0;
40 
41  // This number will be multiplied to each waveform sample before
42  // casting to dtype.
43  cfg["scale"] = 1.0;
44 
45  // This number will be added to each scaled waveform sample before
46  // casting to dtype.
47  cfg["offset"] = 0.0;
48 
49  // The frame tags to consider for saving. If null or empty then all traces are used.
50  cfg["frame_tags"] = Json::arrayValue;
51  // The summary tags to consider for saving
52  //cfg["summary_tags"] = Json::arrayValue;
53  // The channel mask maps to consider for saving
54  //cfg["chanmaskmaps"] = Json::arrayValue;
55 
56  // The output file name to write. Only compressed (zipped) Numpy
57  // files are supported. Writing is always in "append" mode. It's
58  // up to the user to delete a previous instance of the file if
59  // it's old contents are not wanted.
60  cfg["filename"] = "wct-frame.npz";
61 
62  return cfg;
63 }
64 
66 {
67  m_cfg = config;
68 }
69 
70 
71 bool Sio::NumpyFrameSaver::operator()(const IFrame::pointer& inframe,
72  IFrame::pointer& outframe)
73 {
74  if (!inframe) {
75  l->debug("NumpyFrameSaver: EOS");
76  outframe = nullptr;
77  return true;
78  }
79 
80  outframe = inframe; // pass through actual frame
81 
82  const std::string mode = "a";
83 
84  const float baseline = m_cfg["baseline"].asFloat();
85  const float scale = m_cfg["scale"].asFloat();
86  const float offset = m_cfg["offset"].asFloat();
87  const bool digitize = m_cfg["digitize"].asBool();
88 
89  const std::string fname = m_cfg["filename"].asString();
90 
91  // Eigen3 array is indexed as (irow, icol) or (ichan, itick)
92  // one row is one channel, one column is a tick.
93  // Numpy saves reversed dimensions: {ncols, nrows} aka {ntick, nchan} dimensions.
94 
95 
96  if (m_cfg["frame_tags"].isNull() or m_cfg["frame_tags"].empty()) {
97  m_cfg["frame_tags"][0] = "";
98  }
99 
100 
101  std::stringstream ss;
102  ss << "NumpyFrameSaver: see frame #" << inframe->ident()
103  << " with " << inframe->traces()->size() << " traces with frame tags:";
104  for (auto t : inframe->frame_tags()) {
105  ss << " \"" << t << "\"";
106  }
107  ss << " and trace tags:";
108  for (auto t : inframe->trace_tags()) {
109  ss << " \"" << t << "\"";
110  }
111  ss << " looking for tags:";
112  for (auto jt: m_cfg["frame_tags"]) {
113  ss << " \"" << jt.asString() << "\"";
114  }
115  l->debug(ss.str());
116 
117 
118  for (auto jtag : m_cfg["frame_tags"]) {
119  const std::string tag = jtag.asString();
120  auto traces = FrameTools::tagged_traces(inframe, tag);
121  l->debug("NumpyFrameSaver: save {} tagged as {}", traces.size(), tag);
122  if (traces.empty()) {
123  l->warn("NumpyFrameSaver: no traces for tag: \"{}\"", tag);
124  continue;
125  }
126  auto channels = FrameTools::channels(traces);
127  std::sort(channels.begin(), channels.end());
128  auto chbeg = channels.begin();
129  auto chend = std::unique(chbeg, channels.end());
130  auto tbinmm = FrameTools::tbin_range(traces);
131 
132  // fixme: may want to give user some config over tbin range to save.
133  const size_t ncols = tbinmm.second-tbinmm.first;
134  const size_t nrows = std::distance(chbeg, chend);
135  l->debug("NumpyFrameSaver: saving ncols={} nrows={}", ncols, nrows);
136 
137  Array::array_xxf arr = Array::array_xxf::Zero(nrows, ncols) + baseline;
138  FrameTools::fill(arr, traces, channels.begin(), chend, tbinmm.first);
139  arr = arr * scale + offset;
140 
141  { // the 2D frame array
142  const std::string aname = String::format("frame_%s_%d", tag.c_str(), m_save_count);
143  if (digitize) {
144  Array::array_xxs sarr = arr.cast<short>();
145  const short* sdata = sarr.data();
146  cnpy::npz_save(fname, aname, sdata, {ncols, nrows}, mode);
147  }
148  else {
149  cnpy::npz_save(fname, aname, arr.data(), {ncols, nrows}, mode);
150  }
151  l->debug("NumpyFrameSaver: saved {} with {} channels {} ticks @t={} ms qtot={}",
152  aname, nrows, ncols, inframe->time() / units::ms, arr.sum());
153  }
154 
155  { // the channel array
156  const std::string aname = String::format("channels_%s_%d", tag.c_str(), m_save_count);
157  cnpy::npz_save(fname, aname, channels.data(), {nrows}, mode);
158  }
159 
160  { // the tick array
161  const std::string aname = String::format("tickinfo_%s_%d", tag.c_str(), m_save_count);
162  const std::vector<double> tickinfo{inframe->time(), inframe->tick(), (double)tbinmm.first};
163  cnpy::npz_save(fname, aname, tickinfo.data(), {3}, mode);
164  }
165  }
166 
167  ++m_save_count;
168  return true;
169 }
std::shared_ptr< const IFrame > pointer
Definition: IData.h:19
std::string string
Definition: nybbler.cc:12
void npz_save(std::string zipname, std::string fname, const T *data, const std::vector< size_t > &shape, std::string mode="w")
Definition: cnpy.h:138
WIRECELL_FACTORY(NumpyFrameSaver, WireCell::Sio::NumpyFrameSaver, WireCell::IFrameFilter, WireCell::IConfigurable) using namespace WireCell
cfg
Definition: dbjson.py:29
static QStrList * l
Definition: config.cpp:1044
static ITrace::vector tagged_traces(IFrame::pointer frame, IFrame::tag_t tag)
def configure(cfg)
Definition: cuda.py:34
static Config * config
Definition: config.cpp:1054
double distance(double x1, double y1, double z1, double x2, double y2, double z2)
std::pair< int, int > tbin_range(const ITrace::vector &traces)
Definition: FrameTools.cxx:143
logptr_t logger(std::string name)
Definition: Logging.cxx:71
Eigen::Array< short, Eigen::Dynamic, Eigen::Dynamic > array_xxs
A 16 bit short integer 2D array.
Definition: Array.h:45
Definition: Main.h:22
void scale(Sequence< Val > &seq, Val scalar)
Scale (multiply) sequence values by scalar.
Definition: Waveform.h:146
def fill(s)
Definition: translator.py:93
static const double ms
Definition: Units.h:100
Json::Value Configuration
Definition: Configuration.h:50
unsigned nrows(sqlite3 *db, std::string const &tablename)
Definition: helpers.cc:84
decltype(auto) constexpr empty(T &&obj)
ADL-aware version of std::empty.
Definition: StdUtils.h:92
Eigen::ArrayXXf array_xxf
A real, 2D array.
Definition: Array.h:54