TruthSmearer.cxx
Go to the documentation of this file.
4 #include "WireCellUtil/Units.h"
5 #include "WireCellUtil/Point.h"
10 
11 #include <string>
12 
15 
16 
17 using namespace std;
18 using namespace WireCell;
19 
20 /// based on Gen::Ductor
21 /// without convolution
22 /// just smearing of charge depos in time and wire dimenstions
23 /// (time_smear = 0, wire_smear = 1) means "Charge Truth"
24 /// otherwires, "Signal Truth" with signal processing residual
25 
27  : m_anode_tn("AnodePlane")
28  , m_rng_tn("Random")
29  , m_start_time(0.0*units::ns)
30  , m_readout_time(5.0*units::ms)
31  , m_tick(0.5*units::us)
32  , m_drift_speed(1.0*units::mm/units::us)
33  , m_time_smear(1.4*units::us) // Fig.15 in arXiv:1802.08709
34  , m_wire_smear_ind(0.75) // Fig. 16 in arXiv: 1802.08709
35  , m_wire_smear_col(0.95) // Fig. 16 in arXiv: 1802.08709
36  , m_smear_response_tn("smear_response") // interface
37  , m_truth_gain(-1.0)
38  , m_nsigma(3.0)
39  , m_fluctuate(true)
40  , m_continuous(true)
41  , m_frame_count(0)
42 {
43 }
44 
45 WireCell::Configuration Gen::TruthSmearer::default_configuration() const
46 {
48 
49  /// How many Gaussian sigma due to diffusion to keep before truncating.
50  put(cfg, "nsigma", m_nsigma);
51 
52  /// Whether to fluctuate the final Gaussian deposition.
53  put(cfg, "fluctuate", m_fluctuate);
54 
55  /// The initial time for this ductor
56  put(cfg, "start_time", m_start_time);
57 
58  /// The time span for each readout.
59  put(cfg, "readout_time", m_readout_time);
60 
61  /// The sample period
62  put(cfg, "tick", m_tick);
63 
64  /// If false then determine start time of each readout based on the
65  /// input depos. This option is useful when running WCT sim on a
66  /// source of depos which have already been "chunked" in time. If
67  /// true then this Ductor will continuously simulate all time in
68  /// "readout_time" frames leading to empty frames in the case of
69  /// some readout time with no depos.
70  put(cfg, "continuous", true);
71 
72  /// The nominal speed of drifting electrons
73  put(cfg, "drift_speed", m_drift_speed);
74 
75  /// Gaussian longitudinal (time) smearing -- time filter in SP
76  put(cfg, "time_smear", m_time_smear);
77 
78  /// Discrete wire smear -- wire filter in Fig.16 arXiv: 1802.08709
79  /// the charge within one wire pitch is equally weighted, i.e.
80  /// no impact position dependency
81  put(cfg, "wire_smear_ind", m_wire_smear_ind);
82  put(cfg, "wire_smear_col", m_wire_smear_col);
83 
84  /// Transverse (wire) smearing -- re-distribute the charge onto
85  /// nearby wires. A function of impact position, like field response.
86  /// Attention: this is the residual field response (true / average).
87  /// Normalization issue -- true, see percentage level charge bias
88  /// for point source of charge at various locations within a wire.
89  /// An interface, not used are present.
90  put(cfg, "smear_response", m_smear_response_tn);
91 
92  /// gain for truth -- sign of charge in the output
93  put(cfg, "truth_gain", m_truth_gain);
94 
95  /// Allow for a custom starting frame number
96  put(cfg, "first_frame_number", m_frame_count);
97 
98  /// Name of component providing the anode plane.
99  put(cfg, "anode", m_anode_tn);
100  put(cfg, "rng", m_rng_tn);
101 
102  return cfg;
103 }
104 
106 {
107  m_anode_tn = get<string>(cfg, "anode", m_anode_tn);
108  m_anode = Factory::find_tn<IAnodePlane>(m_anode_tn);
109  if (!m_anode) {
110  cerr << "TruthSmearer["<<(void*)this <<"]: failed to get anode: \"" << m_anode_tn << "\"\n";
111  return; // fixme: should throw something!
112  }
113 
114  m_nsigma = get<double>(cfg, "nsigma", m_nsigma);
115  m_continuous = get<bool>(cfg, "continuous", m_continuous);
116  m_fluctuate = get<bool>(cfg, "fluctuate", m_fluctuate);
117  m_rng = nullptr;
118  if (m_fluctuate) {
119  m_rng_tn = get(cfg, "rng", m_rng_tn);
120  m_rng = Factory::find_tn<IRandom>(m_rng_tn);
121  }
122 
123  m_readout_time = get<double>(cfg, "readout_time", m_readout_time);
124  m_tick = get<double>(cfg, "tick", m_tick);
125  m_start_time = get<double>(cfg, "start_time", m_start_time);
126  m_drift_speed = get<double>(cfg, "drift_speed", m_drift_speed);
127  m_time_smear = get<double>(cfg, "time_smear", m_time_smear);
128  m_wire_smear_ind = get<double>(cfg, "wire_smear_ind", m_wire_smear_ind);
129  m_wire_smear_col = get<double>(cfg, "wire_smear_col", m_wire_smear_col);
130 
131  /// An interface, not used. Exact "Truth" after signal processing.
132  m_smear_response_tn = get<string>(cfg, "smear_response", m_smear_response_tn);
133 
134  m_truth_gain = get<double>(cfg, "truth_gain", m_truth_gain);
135 
136  m_frame_count = get<int>(cfg, "first_frame_number", m_frame_count);
137 
138 }
139 
141 {
142  ITrace::vector traces;
143  double tick = -1;
144 
145  for (auto face : m_anode->faces()) {
146 
147  // Select the depos which are in this face's sensitive volume
148  IDepo::vector face_depos;
149  auto bb = face->sensitive();
150  for (auto depo : m_depos) {
151  if (bb.inside(depo->pos())) {
152  face_depos.push_back(depo);
153  }
154  }
155 
156  { // debugging
157  auto ray = bb.bounds();
158  cerr << "TruthSmearer: anode:"<<m_anode->ident()<<" face:" << face->ident() << ": processing " << face_depos.size() << " depos "
159  << "with bb: "<< ray.first << " --> " << ray.second <<"\n";
160  }
161 
162  for (auto plane : face->planes()) {
163 
164  const Pimpos* pimpos = plane->pimpos();
165 
168 
169  if (tick < 0) { // fixme: assume same tick for all.
170  tick = tbins.binsize();
171  }
172 
173  Gen::BinnedDiffusion bindiff(*pimpos, tbins, m_nsigma, m_rng);
174  for (auto depo : face_depos) {
175  // time filter smearing
176  double extent_time = depo->extent_long()/m_drift_speed;
177  bindiff.add(depo, sqrt(extent_time*extent_time+m_time_smear*m_time_smear), depo->extent_tran());
178  }
179 
180  auto& wires = plane->wires();
181  int planeid = plane->ident();
182 
183  double wire_smear = 1.0;
184  if(planeid == 2){
185  wire_smear = m_wire_smear_col;
186  }
187  else if(planeid == 0 || planeid == 1){
188  wire_smear = m_wire_smear_ind;
189  }
190  else{
191  std::cerr<<"Truthsmearer: planeid "<< planeid << " cannot be identified!"<<std::endl;
192  }
193 
194  auto ib = pimpos->impact_binning();
195  auto rb = pimpos->region_binning();
196 
197  const double pitch = rb.binsize();
198  const double impact = ib.binsize();
199  const int nwires = rb.nbins();
200  for (int iwire=0; iwire<nwires; ++iwire) {
201 
202  /// Similar to ImpactZipper::waveform
203  /// No convolution
204  /// m_waveform from BinnedDiffusion::impact_data()
205 
206  const double wire_pos = rb.center(iwire);
207 
208  // impact positions within +/-1 wires
209  const int min_impact = ib.edge_index(wire_pos - 1.5*pitch + 0.1*impact);
210  const int max_impact = ib.edge_index(wire_pos + 1.5*pitch - 0.1*impact);
211  const int nsamples = bindiff.tbins().nbins();
212 
213  // total waveform for iwire
214  Waveform::realseq_t total_spectrum(nsamples, 0.0);
215 
216  // ATTENTION: the bin center of max_imapct is in +2 wire pitch
217  for(int imp = min_impact; imp < max_impact; imp++) {
218 
219  // charge weight
220  double wire_weight = 1.0;
221 
222  /// it is doable to read in a JsonArray of the wire smear
223  /// and use relative bin to target wire as array index
224  /// fo each wire (or impact position) smear
225  const double rel_imp_pos = ib.center(imp) - wire_pos;
226  const double dist_to_wire = abs(rel_imp_pos/rb.binsize());
227  if( dist_to_wire <0.5){
228  wire_weight = wire_smear*1.0;
229  }
230  else if( dist_to_wire >0.5 && dist_to_wire <1.5 ){
231  wire_weight = 0.5*(1.0-wire_smear);
232  }
233  else{
234  // should not happen
235  wire_weight = 0.0;
236  std::cerr<<"TruthSmearer: impact "<< imp << " position: "
237  << ib.center(imp) <<" out of +/-1 wire region or at wire boundary, wire pitch: "
238  << rb.binsize() <<", "<< pitch <<", target wire position: "<< wire_pos
239  << std::endl;
240  }
241 
242  // ImpactData
243  auto id = bindiff.impact_data(imp);
244  if(!id) {
245  continue;
246  }
247 
248  Waveform::realseq_t charge_spectrum = id->waveform();
249  if (charge_spectrum.empty()) {
250  std::cerr<<"impactZipper: no charge spectrum for absolute impact number: "<< imp <<endl;
251  continue;
252  }
253 
254  //debugging
255  /* std::cout<<"TruthSmearer: planeid: " << planeid << " relative impact position: "<< rel_imp_pos */
256  /* << " charge weight: "<<wire_weight*m_truth_gain<<endl; */
257  /* auto xx = Waveform::edge(charge_spectrum); */
258  /* //debugging */
259  /* std::cout<<"TruthSmearer: wire: "<< iwire << " impact: " << imp << " charge spectrum edges: "<< xx.first << ", " << xx.second << std::endl; */
260  Waveform::scale(charge_spectrum, wire_weight*m_truth_gain);
261  Waveform::increase(total_spectrum, charge_spectrum);
262  }
263 
264  bindiff.erase(0, min_impact);
265 
266  /// end: charge wire smearing per wire filter
267 
268  auto mm = Waveform::edge(total_spectrum);
269  if (mm.first == (int)total_spectrum.size()) { // all zero
270  //std::cout<<"TruthSmearer: all zero wave spectrum at wire: "<< iwire << std::endl;
271  continue;
272  }
273 
274  int chid = wires[iwire]->channel();
275  int tbin = mm.first;
276  ITrace::ChargeSequence charge(total_spectrum.begin()+mm.first, total_spectrum.begin()+mm.second);
277  auto trace = make_shared<SimpleTrace>(chid, tbin, charge);
278  traces.push_back(trace);
279  }
280  }
281  }
282 
283  auto frame = make_shared<SimpleFrame>(m_frame_count, m_start_time, traces, tick);
284  frames.push_back(frame);
285 
286  // fixme: what about frame overflow here? If the depos extend
287  // beyond the readout where does their info go? 2nd order,
288  // diffusion and finite field response can cause depos near the
289  // end of the readout to have some portion of their waveforms
290  // lost?
291  m_depos.clear();
292 
293  m_start_time += m_readout_time;
294  ++m_frame_count;
295 }
296 
297 // Return true if ready to start processing and capture start time if
298 // in continuous mode.
300 {
301  if (!depo) {
302  return true;
303  }
304 
305  if (!m_continuous) {
306  if (m_depos.empty()) {
307  m_start_time = depo->time();
308  return false;
309  }
310  }
311 
312  // Note: we use this depo time even if it may not actually be
313  // inside our sensitive volume.
314  bool ok = depo->time() > m_start_time + m_readout_time;
315  return ok;
316 }
317 
319 {
320  if (start_processing(depo)) {
321  process(frames);
322  }
323 
324  if (depo) {
325  m_depos.push_back(depo);
326  }
327  else {
328  frames.push_back(nullptr);
329  }
330 
331  return true;
332 }
333 
const int nwires
Sequence< real_t > realseq_t
Definition: Waveform.h:31
bool add(IDepo::pointer deposition, double sigma_time, double sigma_pitch)
const Binning & region_binning() const
Definition: Pimpos.h:109
void process(output_queue &frames)
bool start_processing(const input_pointer &depo)
STL namespace.
void put(Configuration &cfg, const std::string &dotpath, const T &val)
Put value in configuration at the dotted path.
cfg
Definition: dbjson.py:29
std::vector< pointer > vector
Definition: IData.h:21
IRandom::pointer m_rng
Definition: TruthSmearer.h:34
std::pair< int, int > edge(const realseq_t &wave)
Definition: Waveform.cxx:121
const double tick
void erase(int begin_impact_index, int end_impact_index)
std::deque< output_pointer > output_queue
double binsize() const
Definition: Binning.h:73
Binning tbins(nticks, t0, t0+readout_time)
const Binning & impact_binning() const
Definition: Pimpos.h:113
T abs(T value)
void increase(Sequence< Val > &seq, Val scalar)
Increase (shift) sequence values by scalar.
Definition: Waveform.h:129
virtual void configure(const WireCell::Configuration &config)
Accept a configuration.
IAnodePlane::pointer m_anode
Definition: TruthSmearer.h:33
static const double mm
Definition: Units.h:73
virtual bool operator()(const input_pointer &depo, output_queue &frames)
The calling signature:
ImpactData::pointer impact_data(int bin) const
Definition: Main.h:22
void scale(Sequence< Val > &seq, Val scalar)
Scale (multiply) sequence values by scalar.
Definition: Waveform.h:146
static const double us
Definition: Units.h:101
Pimpos pimpos(nwires, min_wire_pitch, max_wire_pitch)
static const double ms
Definition: Units.h:100
Pitch-Impact-Position.
Definition: Pimpos.h:36
const Binning & tbins() const
WIRECELL_FACTORY(TruthSmearer, WireCell::Gen::TruthSmearer, WireCell::IDuctor, WireCell::IConfigurable) using namespace std
Json::Value Configuration
Definition: Configuration.h:50
std::shared_ptr< const IDepo > input_pointer
int nbins() const
Definition: Binning.h:42
QAsciiDict< Entry > ns
std::vector< float > ChargeSequence
Sequential collection of charge.
Definition: ITrace.h:21
QTextStream & endl(QTextStream &s)