MagnifySink.cxx
Go to the documentation of this file.
2 #include "WireCellIface/ITrace.h"
3 
4 #include "TH1F.h"
5 #include "TH2F.h"
6 #include "TH2I.h"
7 #include "TFile.h"
8 #include "TTree.h"
9 
12 
13 #include <vector>
14 #include <string>
15 
18 
19 using namespace WireCell;
20 
21 
23  : m_nrebin(1)
24  , log(Log::logger("magnify"))
25 {
26 }
27 
28 Root::MagnifySink::~MagnifySink()
29 {
30 }
31 
33 {
35 
36  fn = cfg["input_filename"].asString();
37  if (fn.empty() and !cfg["shunt"].empty()) {
38  log->error("MagnifySink: asked to shunt but not given input file name");
39  THROW(ValueError() << errmsg{"MagnifySink: must provide input filename to shunt objects to output"});
40  }
41 
42  fn = cfg["output_filename"].asString();
43  if (fn.empty()) {
44  THROW(ValueError() << errmsg{"Must provide output filename to MagnifySink"});
45  }
46 
47  auto anode_tn = get<std::string>(cfg, "anode", "AnodePlane");
48  m_anode = Factory::find_tn<IAnodePlane>(anode_tn);
49 
50  m_cfg = cfg;
51 
52  m_nrebin = get<int>(cfg, "nrebin", m_nrebin);
53 
54  create_file();
55 }
56 
57 
58 WireCell::Configuration Root::MagnifySink::default_configuration() const
59 {
61 
62  cfg["anode"] = "AnodePlane";
63 
64  // fixme: this TOTALLY violates the design of wire cell DFP.
65  cfg["input_filename"] = "";
66 
67  // List of TObjects to copy from input file to output file.
68  cfg["shunt"] = Json::arrayValue;
69 
70  // Name of ROOT file to write.
71  cfg["output_filename"] = "";
72 
73  // A list of trace tags defining which waveforms are saved to Magnify histograms.
74  cfg["frames"] = Json::arrayValue;
75 
76  // If no tags for traces, i.e. trace_has_tag=false in a frame,
77  // set desired tag to ""
78  // as FrameTool::tagged_traces(frame, "") calls untagged_traces(frame).
79  cfg["trace_has_tag"] = true;
80 
81  // A list of pairs mapping a cmm key name to a ttree name.
82  cfg["cmmtree"] = Json::arrayValue;
83 
84  // The ROOT file mode with which to open the file. Use "RECREATE"
85  // to overrite an existing file. This might be useful for the
86  // first MagnifySink in a chain. Use "UPDATE" for subsequent
87  // sinks that add to the file.
88  cfg["root_file_mode"] = "RECREATE";
89 
90  // If runinfo is given it should be a JSON object and its values
91  // will be copied into the Trun tree. If instead it is null AND
92  // an input file is given AND it contains a Trun tree, it will be
93  // copied to output.
94  cfg["runinfo"] = Json::nullValue;
95 
96  cfg["nrebin"] = 1;
97 
98  // List tagged traces from which to save the "trace summary"
99  // vector into a 1D histogram which will be named after the tag.
100  // See "summary_operator".
101  cfg["summaries"] = Json::arrayValue;
102 
103  // An object mapping tags to operators for aggregating trace
104  // summary values on the same channel. Operator may be "sum" to
105  // add up all values on the same channel or "set" to assign values
106  // to the channel bin (last one wins). If a tag is not found, the
107  // default operator is "sum".
108  cfg["summary_operator"] = Json::objectValue;
109 
110  return cfg;
111 }
112 
113 typedef std::unordered_set<std::string> string_set_t;
115 {
116  string_set_t ret;
117  for (auto jone : cfg) {
118  ret.insert(jone.asString());
119  }
120  return ret;
121 }
122 
123 // fixme: this little helper is also in FrameUtil
124 /*
125 ITrace::vector get_tagged_traces(IFrame::pointer frame, IFrame::tag_t tag)
126 {
127  ITrace::vector ret;
128  auto const& all_traces = frame->traces();
129  for (size_t index : frame->tagged_traces(tag)) {
130  ret.push_back(all_traces->at(index));
131  }
132  if (!ret.empty()) {
133  return ret;
134  }
135  auto ftags = frame->frame_tags();
136  if (std::find(ftags.begin(), ftags.end(), tag) == ftags.end()) {
137  return ret;
138  }
139  return *all_traces; // must make copy
140 }
141 */
142 
143 std::vector<WireCell::Binning> collate_byplane(const ITrace::vector& traces, const IAnodePlane::pointer anode,
144  ITrace::vector byplane[])
145 {
146  std::vector<int> uvwt[4];
147  for (auto trace : traces) {
148  const int chid = trace->channel();
149  auto wpid = anode->resolve(chid);
150  const int iplane = wpid.index();
151  if (iplane<0 || iplane>=3) {
152  THROW(RuntimeError() << errmsg{"Illegal wpid"});
153  }
154  uvwt[iplane].push_back(chid);
155  byplane[iplane].push_back(trace);
156  uvwt[3].push_back(trace->tbin());
157  uvwt[3].push_back(trace->tbin() + trace->charge().size());
158  }
159 
160  std::vector<Binning> binnings(4);
161  for (int ind=0; ind<4; ++ind) {
162  auto const& one = uvwt[ind];
163  // std::cerr << "[wgu] get ind: " << ind << " size: " << one.size() << std::endl;
164  if (one.empty()) {
165  // THROW(ValueError() << errmsg{"MagnifySink: bogus bounds"});
166  std::cerr << "[wgu] plane: " << ind << " has not traces. " << std::endl;
167  }
168  else{
169  auto mme = std::minmax_element(one.begin(), one.end());
170  const int vmin = *mme.first;
171  const int vmax = *mme.second;
172  if (ind == 3) {
173  const int n = vmax - vmin;
174  // binnings.push_back(Binning(n, vmin, vmax););
175  binnings.at(ind) = Binning(n, vmin, vmax);
176  }
177  else {
178  // Channel-centered binning
179  const double diff = vmax - vmin;
180  // binnings.push_back(Binning(diff+1, vmin-0.5, vmax+0.5));
181  binnings.at(ind) = Binning(diff+1, vmin-0.5, vmax+0.5);
182  }
183  }
184  }
185  return binnings;
186 }
187 
188 void Root::MagnifySink::create_file() {
189  const std::string ofname = m_cfg["output_filename"].asString();
190  const std::string mode = "RECREATE";
191  TFile* output_tf = TFile::Open(ofname.c_str(), mode.c_str());
192  output_tf->Close("R");
193  delete output_tf;
194  output_tf = nullptr;
195 }
196 
197 
198 void Root::MagnifySink::do_shunt(TFile* output_tf)
199 {
200  std::stringstream ss;
201  auto truncfg = m_cfg["runinfo"];
202  if (!truncfg.empty()) {
203  TTree *rtree = new TTree("Trun","Trun");
204  rtree->SetDirectory(output_tf);
205  ss << "MagnifySink: making Tree RunInfo:\n";
206 
207  std::vector<int> ints;
208  // issue to be fixed:
209  // the first element seems to be misconnected
210  // to a wrong address in the rtree->Branch when rtree->Fill
211  // So kind of initilized to the vector could solve this issues
212  ints.push_back(0);
213  std::vector<float> floats;
214  floats.push_back(0.0);
215 
216  int frame_number=0;
217  int celltree_input = 0;
218  for (auto name : truncfg.getMemberNames()) {
219  auto jval = truncfg[name];
220  ss << "\t" << name << " = " << jval << "\n";
221  if(name == "eventNo") frame_number = std::stoi(jval.asString());
222  if(name == "Celltree") {
223  celltree_input = jval.asInt();
224  continue;
225  }
226  if (jval.isInt()) {
227  ints.push_back(jval.asInt());
228  rtree->Branch(name.c_str(), &ints.back(), (name+"/I").c_str());
229  continue;
230  }
231  if (jval.isDouble()) {
232  floats.push_back(jval.asFloat());
233  rtree->Branch(name.c_str(), &floats.back(), (name+"/F").c_str());
234  continue;
235  }
236  if (jval.isString()) {
237  ints.push_back(std::stoi(jval.asString()));
238  rtree->Branch(name.c_str(), &ints.back(), (name+"/I").c_str());
239  continue;
240  }
241  log->warn("MagnifySink: warning: got unknown type for run info entry: \"{}\" = {}",
242  name, jval);
243  }
244  if (ss.str().size() > 0) {
245  log->debug(ss.str());
246  }
247 
248  if(celltree_input){
249  // runNo and subRunNo, perhaps other info in the future
250  TFile *input_runinfo = TFile::Open((m_cfg["input_filename"].asString()).c_str());
251  TTree *run = (TTree*)input_runinfo->Get("/Event/Sim");
252  if (!run) {
253  log->warn("MagnifySink: runinfo: no tree: /Event/Sim in input file");
254  }
255  else{
256  run->SetBranchStatus("*",0);
257 
258  int run_no, subrun_no, event_no;
259  run->SetBranchStatus("eventNo",1);
260  run->SetBranchAddress("eventNo" , &event_no);
261  run->SetBranchStatus("runNo",1);
262  run->SetBranchAddress("runNo" , &run_no);
263  rtree->Branch("runNo",&run_no,"runNo/I");
264  run->SetBranchStatus("subRunNo",1);
265  run->SetBranchAddress("subRunNo", &subrun_no);
266  rtree->Branch("subRunNo",&subrun_no,"subRunNo/I");
267 
268  unsigned int entries = run->GetEntries();
269  bool legalevt = false;
270  for(unsigned int ent = 0; ent<entries; ent++)
271  {
272  int siz = run->GetEntry(ent);
273  if(siz>0 && event_no == frame_number )
274  {
275  legalevt = true;
276  break;
277  }
278  }
279  if(!legalevt){
280  THROW(ValueError() << errmsg{"MagnifySink: event number out of range!"});
281  }
282  delete input_runinfo;
283  input_runinfo = nullptr;
284  } // Event/Sim found
285  } // celltree input
286 
287  rtree->Fill();
288  }
289 
290 
291  // Now deal with "shunting" input Magnify data to output.
292  std::string ifname = m_cfg["input_filename"].asString();
293  if (ifname.empty()) {
294  // good, we shouldn't be peeking into the input file anyways.
295  return;
296  }
297  auto toshunt = getset(m_cfg["shunt"]);
298  if (toshunt.empty()) {
299  log->warn("MagnifySink: no objects to copy but given input: {}", ifname);
300  return;
301  }
302  log->debug("MagnifySink: sneaking peaks into input file: {}", ifname);
303 
304 
305  TFile *input_tf = TFile::Open(ifname.c_str());
306  for (auto name : toshunt) {
307  if (name == "Trun" && !truncfg.empty()) { // no double dipping
308  continue;
309  }
310  TObject* obj = input_tf->Get(name.c_str());
311 
312  if (!obj) {
313  log->warn("MagnifySink: warning: failed to find input object: \"{}\" for copying to output", name);
314  }
315 
316  TTree* tree = dynamic_cast<TTree*>(obj);
317  if (tree) {
318  log->debug("MagnifySink: copying tree: \"{}\"", name);
319  tree = tree->CloneTree();
320  tree->SetDirectory(output_tf);
321  continue;
322  }
323 
324  TH1* hist = dynamic_cast<TH1*>(obj);
325  if (hist) {
326  log->debug("MagnifySink: copying hist: \"{}\"", name);
327  hist->SetDirectory(output_tf);
328  continue;
329  }
330 
331  log->warn("MagnifySink: warning: not copying object of unknown type: \"{}\"",name);
332  }
333 
334  delete input_tf;
335  input_tf = nullptr;
336 
337 }
338 
339 bool Root::MagnifySink::operator()(const IFrame::pointer& frame, IFrame::pointer& out_frame)
340 {
341  out_frame = frame;
342  if (!frame) {
343  // eos
344  log->debug("MagnifySink: EOS");
345  return true;
346  }
347  if (frame->traces()->empty()) {
348  log->debug("MagnifySink: passing through empty frame ID {}", frame->ident());
349  return true;
350  }
351 
352  const std::string ofname = m_cfg["output_filename"].asString();
353  const std::string mode = m_cfg["root_file_mode"].asString();
354  log->debug("MagnifySink: opening for output: {} with mode {}", ofname, mode);
355  TFile* output_tf = TFile::Open(ofname.c_str(), mode.c_str());
356 
357  for (auto tag : getset(m_cfg["frames"])) {
358 
359  auto trace_tag = tag;
360  auto trace_has_tag = m_cfg["trace_has_tag"].asBool();
361  if(!trace_has_tag){
362  trace_tag = "";
363  log->debug("MagnifySink: set desired trace tag to \"\" as cfg::trace_has_tag=false");
364  }
365 
366  ITrace::vector traces_byplane[3], traces = FrameTools::tagged_traces(frame, trace_tag);
367  if (traces.empty()) {
368  log->warn("MagnifySink: no tagged traces for \"{}\"", tag);
369  continue;
370  }
371 
372  log->debug("MagnifySink: tag: \"{}\" with {} traces", tag, traces.size());
373 
374  auto binnings = collate_byplane(traces, m_anode, traces_byplane);
375  // if (binnings.size() == 4) {
376  Binning tbin = binnings[3];
377  for (int iplane=0; iplane < 3; ++iplane) {
378  if (traces_byplane[iplane].empty()) continue;
379  const std::string name = Form("h%c_%s", 'u'+iplane, tag.c_str());
380  Binning cbin = binnings[iplane];
381  std::stringstream ss;
382  ss << "MagnifySink:"
383  << " cbin:"<<cbin.nbins()<<"["<<cbin.min() << "," << cbin.max() << "]"
384  << " tbin:"<<tbin.nbins()<<"["<<tbin.min() << "," << tbin.max() << "]";
385  log->debug(ss.str());
386 
387  // consider to add nrebin ...
388  int nbins = tbin.nbins()/m_nrebin;
389 
390  TH2F* hist = new TH2F(name.c_str(), name.c_str(),
391  cbin.nbins(), cbin.min(), cbin.max(),
392  nbins, tbin.min(), tbin.max());
393 
394  hist->SetDirectory(output_tf);
395 
396  for (auto trace : traces_byplane[iplane]) {
397  const int tbin1 = trace->tbin();
398  const int ch = trace->channel();
399  auto const& charges = trace->charge();
400  for (size_t itick=0; itick < charges.size(); ++itick) {
401  // Using what should be an identical call to
402  // Fill() ends up with a file that is more than
403  // two times bigger:
404  // 826M Jul 27 18:22 orig-bl-nf-fill-tbin.root
405  // 342M Jul 27 18:28 orig-bl-nf-setbincontent-tplus1.root
406  //hist->Fill(ch, tbin+itick+0.5, charges[itick]);
407  // edit: it's due to saving errors.
408 
409  int ibin = (tbin1-tbin.min()+itick)/m_nrebin;
410 
411  hist->SetBinContent(cbin.bin(ch)+1, ibin+1,
412  charges[itick]+hist->GetBinContent(cbin.bin(ch)+1, ibin+1));
413  }
414  }
415  }
416  // }
417  }
418 
419 
420  // Handle any trace summaries
421  for (auto tag : getset(m_cfg["summaries"])) {
422  //auto traces = get_tagged_traces(frame, tag);
423  auto traces = FrameTools::tagged_traces(frame, tag);
424  if (traces.empty()) {
425  log->warn("MagnifySink: no traces tagged with \"{}\", skipping summary", tag);
426  continue;
427  }
428  auto const& summary = frame->trace_summary(tag);
429  if (summary.empty()) {
430  log->warn("MagnifySink: warning: empty summary tagged with \"{}\", skipping summary", tag);
431  continue;
432  }
433 
434  std::string oper = get<std::string>(m_cfg["summary_operator"], tag, "sum");
435 
436  log->debug("MagnifySink: saving summaries tagged with \"{}\" into per-plane hists", tag);
437 
438  // warning: this is going to get ugly. we jump through these
439  // hoops to avoid hard coding microboone channel ranges and
440  // allow for sparse summaries on the domain of channels;
441  const int ntot = traces.size();
442  std::vector<int> perplane_channels[3];
443  std::vector<double> perplane_values[3];
444  for (int ind=0; ind<ntot; ++ind) {
445  const int chid = traces[ind]->channel();
446  const int iplane = m_anode->resolve(chid).index();
447  perplane_channels[iplane].push_back(chid);
448  perplane_values[iplane].push_back(summary[ind]);
449  }
450  for (int iplane=0; iplane<3; ++iplane) {
451  std::vector<int>& chans = perplane_channels[iplane];
452  std::vector<double>& vals = perplane_values[iplane];
453  if (!chans.empty()) {
454  auto mme = std::minmax_element(chans.begin(), chans.end());
455  const int ch0 = *mme.first;
456  const int chf = *mme.second;
457  const std::string hname = Form("h%c_%s", 'u'+iplane, tag.c_str());
458  TH1F* hist = new TH1F(hname.c_str(), hname.c_str(),
459  chf-ch0+1, ch0, chf);
460  for (size_t ind=0; ind<chans.size(); ++ind) {
461  const int x = chans[ind]+0.5;
462  const double val = vals[ind];
463  if (oper == "set") {
464  int bin = hist->FindBin(x);
465  hist->SetBinContent(bin, val);
466  }
467  else {
468  hist->Fill(x, val);
469  }
470  }
471  hist->SetDirectory(output_tf);
472  }
473  }
474  }
475 
476 
477  {
478  std::unordered_map<std::string, std::string> cmmkey2treename;
479  for (auto jcmmtree : m_cfg["cmmtree"]) {
480  cmmkey2treename[jcmmtree[0].asString()] = jcmmtree[1].asString();
481  }
482 
483  Waveform::ChannelMaskMap input_cmm = frame->masks();
484  for (auto const& it: input_cmm) {
485  auto cmmkey = it.first;
486  auto ct = cmmkey2treename.find(cmmkey);
487  if (ct == cmmkey2treename.end()) {
488  log->warn("MagnifySink: warning: found channel mask \"{}\", but no tree configured to accept it",
489  cmmkey);
490  continue;
491  }
492 
493  auto treename = ct->second;
494 
495  log->debug("MagnifySink: saving channel mask \"{}\" to tree \"{}\"",
496  cmmkey, treename);
497 
498  TTree *tree = new TTree(treename.c_str(), treename.c_str());
499  int chid=0, plane=0, start_time=0, end_time=0;
500  tree->Branch("chid",&chid,"chid/I");
501  tree->Branch("plane",&plane,"plane/I");
502  tree->Branch("start_time",&start_time,"start_time/I");
503  tree->Branch("end_time",&end_time,"end_time/I");
504  tree->SetDirectory(output_tf);
505 
506  for (auto const &chmask : it.second){
507  chid = chmask.first;
508  plane = m_anode->resolve(chid).index();
509  auto mask = chmask.second;
510  for (size_t ind = 0; ind < mask.size(); ++ind){
511  start_time = mask[ind].first;
512  end_time = mask[ind].second;
513  tree->Fill();
514  }
515  }
516  }
517  }
518 
519  do_shunt(output_tf);
520 
521 
522  auto count = output_tf->Write();
523  log->debug("MagnifySink: closing output file {}, wrote {} bytes", ofname, count);
524  output_tf->Close();
525  delete output_tf;
526  output_tf = nullptr;
527  return true;
528 }
529 
530 // Local Variables:
531 // mode: c++
532 // c-basic-offset: 4
533 // End:
static QCString name
Definition: declinfo.cpp:673
std::shared_ptr< const IFrame > pointer
Definition: IData.h:19
std::string string
Definition: nybbler.cc:12
std::vector< WireCell::Binning > collate_byplane(const ITrace::vector &traces, const IAnodePlane::pointer anode, ITrace::vector byplane[])
boost::error_info< struct tag_errmsg, std::string > errmsg
Definition: Exceptions.h:54
struct vector vector
cfg
Definition: dbjson.py:29
std::vector< pointer > vector
Definition: IData.h:21
std::unordered_set< std::string > string_set_t
double max() const
Definition: Binning.h:52
QAsciiDict< Entry > fn
static ITrace::vector tagged_traces(IFrame::pointer frame, IFrame::tag_t tag)
def configure(cfg)
Definition: cuda.py:34
std::map< std::string, ChannelMasks > ChannelMaskMap
Collect channel masks by some label.
Definition: Waveform.h:59
double min() const
Definition: Binning.h:47
WIRECELL_FACTORY(MagnifySink, WireCell::Root::MagnifySink, WireCell::IFrameFilter, WireCell::IConfigurable) using namespace WireCell
#define THROW(e)
Definition: Exceptions.h:25
logptr_t logger(std::string name)
Definition: Logging.cxx:71
Thrown when a wrong value has been encountered.
Definition: Exceptions.h:37
void log(source_loc source, level::level_enum lvl, const char *fmt, const Args &...args)
Definition: spdlog.h:165
Definition: Main.h:22
Json::Value Configuration
Definition: Configuration.h:50
QTextStream & bin(QTextStream &s)
string_set_t getset(const WireCell::Configuration &cfg)
list x
Definition: train.py:276
def summary(store)
Definition: info.py:119
const GenericPointer< typename T::ValueType > & pointer
Definition: pointer.h:1124
int ntot
Definition: train_cnn.py:133
int nbins() const
Definition: Binning.h:42
std::size_t n
Definition: format.h:3399
decltype(auto) constexpr empty(T &&obj)
ADL-aware version of std::empty.
Definition: StdUtils.h:92
QTextStream & endl(QTextStream &s)
unsigned int run